From 64ff24f27f37c981a48c819f565d83e6d3aa2534 Mon Sep 17 00:00:00 2001 From: Warmist Date: Fri, 14 Jun 2013 10:20:36 +0300 Subject: [PATCH 001/676] Added building filter to advfort. --- scripts/gui/advfort.lua | 56 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/scripts/gui/advfort.lua b/scripts/gui/advfort.lua index e29042e54..150aed52b 100644 --- a/scripts/gui/advfort.lua +++ b/scripts/gui/advfort.lua @@ -1,4 +1,6 @@ -- allows to do jobs in adv. mode. + +--keybinding, change to your hearts content. Only the key part. keybinds={ nextJob={key="CUSTOM_SHIFT_T",desc="Next job in the list"}, prevJob={key="CUSTOM_SHIFT_R",desc="Previous job in the list"}, @@ -10,7 +12,17 @@ up_alt2={key="CURSOR_UP_Z_AUX",desc="Use job up"}, use_same={key="A_MOVE_SAME_SQUARE",desc="Use job at the tile you are standing"}, workshop={key="CHANGETAB",desc="Show building menu"}, } - +-- building filters +build_filter={ +forbid_all=true, --this forbits all except the "allow" +allow={"MetalSmithsForge"}, --ignored if forbit_all=false +forbid={"Custom"} --ignored if forbit_all==true +} +build_filter.HUMANish={ +forbid_all=true, +allow={"Masons"}, +forbid={} +} local gui = require 'gui' local wid=require 'gui.widgets' @@ -24,8 +36,35 @@ local tile_attrs = df.tiletype.attrs settings={build_by_items=false,check_inv=false,df_assign=true} - - +function hasValue(tbl,val) + for k,v in pairs(tbl) do + if v==val then + return true + end + end + return false +end +function reverseRaceLookup(id) + return df.global.world.raws.creatures.all[id].creature_id +end +function deon_filter(name,type_id,subtype_id,custom_id, parent) + print(name) + local adv=df.global.world.units.active[0] + local race_filter=build_filter[reverseRaceLookup(adv.race)] + if race_filter then + if race_filter.forbid_all then + return hasValue(race_filter.allow,name) + else + return not hasValue(race_filter.forbid,name) + end + else + if build_filter.forbid_all then + return hasValue(build_filter.allow,name) + else + return not hasValue(build_filter.forbid,name) + end + end +end local mode_name for k,v in ipairs({...}) do --setting parsing if v=="-c" or v=="--cheat" then @@ -176,6 +215,14 @@ function SetCreatureRef(args) end end +function SetWebRef(args) + local pos=args.pos + for k,v in pairs(df.global.world.items.other.ANY_WEBS) do + if v.pos.x==pos.x and v.pos.y==pos.y and v.pos.z==pos.z then + job.general_refs:insert("#",{new=df.general_ref_item,item_id=v.id}) + end + end +end function SetPatientRef(args) local job=args.job local pos=args.pos @@ -785,7 +832,7 @@ function AssignJobToBuild(args) if bld~=nil then CheckAndFinishBuilding(args,bld) else - bdialog.BuildingDialog{on_select=dfhack.curry(BuildingChosen,args),hide_none=true}:show() + bdialog.BuildingDialog{on_select=dfhack.curry(BuildingChosen,args),hide_none=true,building_filter=deon_filter}:show() end return true end @@ -854,6 +901,7 @@ actions={ {"Build" ,AssignJobToBuild,{NoConstructedBuilding}}, {"BuildLast" ,BuildLast,{NoConstructedBuilding}}, {"Clean" ,df.job_type.Clean,{}}, + {"GatherWebs" ,df.job_type.CollectWebs,{--[[HasWeb]]},{SetWebRef}}, } From dec0b202eeb4a2dfedf61dd306906266286a0d19 Mon Sep 17 00:00:00 2001 From: Warmist Date: Fri, 21 Jun 2013 22:47:23 +0300 Subject: [PATCH 002/676] Added rendermax plugin- a bunch of rendering hax in one. Small fix to skeleton. --- plugins/CMakeLists.txt | 4 + plugins/rendermax/CMakeLists.txt | 39 ++ plugins/rendermax/renderer_opengl.hpp | 658 ++++++++++++++++++++++++++ plugins/skeleton/CMakeLists.txt | 2 +- 4 files changed, 702 insertions(+), 1 deletion(-) create mode 100644 plugins/rendermax/CMakeLists.txt create mode 100644 plugins/rendermax/renderer_opengl.hpp diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 29acba8a1..63adc00c2 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -170,3 +170,7 @@ OPTION(BUILD_SKELETON "Build the skeleton plugin." OFF) if(BUILD_SKELETON) add_subdirectory(skeleton) endif() +OPTION(BUILD_RENDERMAX "Build the rendermax alt-renderers plugin." OFF) +if(BUILD_RENDERMAX) + add_subdirectory(rendermax) +endif() diff --git a/plugins/rendermax/CMakeLists.txt b/plugins/rendermax/CMakeLists.txt new file mode 100644 index 000000000..7b9d7bdf7 --- /dev/null +++ b/plugins/rendermax/CMakeLists.txt @@ -0,0 +1,39 @@ +PROJECT (rendermax) +Find_Package(SDL REQUIRED) +find_package(OpenGL REQUIRED) +find_package(GLUT REQUIRED) +# A list of source files +SET(PROJECT_SRCS + rendermax.cpp +) +# A list of headers +SET(PROJECT_HDRS + renderer_opengl.hpp +) +SET_SOURCE_FILES_PROPERTIES( ${PROJECT_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}) +include_directories(${SDL_INCLUDE_DIR}) + +#linux +IF(UNIX) + add_definitions(-DLINUX_BUILD) + SET(PROJECT_LIBS + # add any extra linux libs here + ${SDL_LIBRARY} + opengl + ${PROJECT_LIBS} + ) +# windows +ELSE(UNIX) + SET(PROJECT_LIBS + # add any extra windows libs here + ${SDL_LIBRARY} + opengl + ${PROJECT_LIBS} + $(NOINHERIT) + ) +ENDIF(UNIX) +# this makes sure all the stuff is put in proper places and linked to dfhack +DFHACK_PLUGIN(rendermax ${PROJECT_SRCS} LINK_LIBRARIES ${PROJECT_LIBS}) diff --git a/plugins/rendermax/renderer_opengl.hpp b/plugins/rendermax/renderer_opengl.hpp new file mode 100644 index 000000000..60c912d05 --- /dev/null +++ b/plugins/rendermax/renderer_opengl.hpp @@ -0,0 +1,658 @@ +//original file from https://github.com/Baughn/Dwarf-Fortress--libgraphics- +#include "Core.h" +#include +#include "df/renderer.h" +#include "df/init.h" +#include "df/enabler.h" +#include "df/zoom_commands.h" +#include "df/texture_handler.h" + +using df::renderer; +using df::init; +using df::enabler; + +#include "SDL.h" + +#ifdef __APPLE__ +#include +#include +//#include +#else +#ifdef _WIN32 +#include +#endif +#include +#include +//#include +#endif +bool isFullscreen() +{ + return df::global::enabler->fullscreen; +} + +template +struct Either { + bool isL; + union { + L left; + R right; + }; + Either(const L &l) { + isL = true; + left = l; + } + Either(const R &r) { + isL = false; + right = r; + } +}; + +// STANDARD +class renderer_opengl : public renderer { +public: + virtual bool uses_opengl() { return true; } + +protected: + SDL_Surface *screen; + + int dispx, dispy; // Cache of the current font size + + bool init_video(int w, int h) { + // Get ourselves an opengl-enabled SDL window + uint32_t flags = SDL_HWSURFACE | SDL_OPENGL; + + // Set it up for windowed or fullscreen, depending. + if (isFullscreen()) { + flags |= SDL_FULLSCREEN; + } else { + if (!df::global::init->display.flag.is_set(INIT_DISPLAY_FLAG_NOT_RESIZABLE)) + flags |= SDL_RESIZABLE; + } + + // Setup OpenGL attributes + SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, init.window.flag.has_flag(INIT_WINDOW_FLAG_VSYNC_ON)); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, + init.display.flag.has_flag(INIT_DISPLAY_FLAG_SINGLE_BUFFER) ? 0 : 1); + + // (Re)create the window + screen = SDL_SetVideoMode(w, h, 32, flags); + + if (!screen) return false; + + // Test double-buffering status + int test; + SDL_GL_GetAttribute(SDL_GL_DOUBLEBUFFER, &test); + if (test != ((init.display.flag.has_flag(INIT_DISPLAY_FLAG_SINGLE_BUFFER)) ? 0 : 1)) { + if (isFullscreen()); + //errorlog << "Requested single-buffering not available\n" << flush; + else + report_error("OpenGL", "Requested single-buffering not available"); + } + + // (Re)initialize GLEW. Technically only needs to be done once on + // linux, but on windows forgetting will cause crashes. + glewInit(); + + // Set the viewport and clear + glViewport(0, 0, screen->w, screen->h); + glClear(GL_COLOR_BUFFER_BIT); + + return true; + } + + // Vertexes, foreground color, background color, texture coordinates + GLfloat *vertexes, *fg, *bg, *tex; + + void write_tile_vertexes(GLfloat x, GLfloat y, GLfloat *vertex) { + vertex[0] = x; // Upper left + vertex[1] = y; + vertex[2] = x+1; // Upper right + vertex[3] = y; + vertex[4] = x; // Lower left + vertex[5] = y+1; + vertex[6] = x; // Lower left again (triangle 2) + vertex[7] = y+1; + vertex[8] = x+1; // Upper right + vertex[9] = y; + vertex[10] = x+1; // Lower right + vertex[11] = y+1; + } + + virtual void allocate(int tiles) { + vertexes = static_cast(realloc(vertexes, sizeof(GLfloat) * tiles * 2 * 6)); + assert(vertexes); + fg = static_cast(realloc(fg, sizeof(GLfloat) * tiles * 4 * 6)); + assert(fg); + bg = static_cast(realloc(bg, sizeof(GLfloat) * tiles * 4 * 6)); + assert(bg); + tex = static_cast(realloc(tex, sizeof(GLfloat) * tiles * 2 * 6)); + assert(tex); + + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(2, GL_FLOAT, 0, vertexes); + } + + virtual void init_opengl() { + //enabler.textures.upload_textures(); + } + + virtual void uninit_opengl() { + //enabler.textures.remove_uploaded_textures(); + } + + virtual void draw(int vertex_count) { + // Render the background colors + glDisable(GL_TEXTURE_2D); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisable(GL_BLEND); + glDisable(GL_ALPHA_TEST); + glColorPointer(4, GL_FLOAT, 0, bg); + glDrawArrays(GL_TRIANGLES, 0, vertex_count); + // Render the foreground, colors and textures both + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_NOTEQUAL, 0); + glEnable(GL_TEXTURE_2D); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glTexCoordPointer(2, GL_FLOAT, 0, tex); + glColorPointer(4, GL_FLOAT, 0, fg); + glDrawArrays(GL_TRIANGLES, 0, vertex_count); + + printGLError(); + } + + void write_tile_arrays(int x, int y, GLfloat *fg, GLfloat *bg, GLfloat *tex) { + Either id = screen_to_texid(x, y); + if (id.isL) { // An ordinary tile + const gl_texpos *txt = df::global::enabler->textures.gl_texpos; + // TODO: Only bother to set the one that's actually read in flat-shading mode + // And set flat-shading mode. + for (int i = 0; i < 6; i++) { + *(fg++) = id.left.r; + *(fg++) = id.left.g; + *(fg++) = id.left.b; + *(fg++) = 1; + + *(bg++) = id.left.br; + *(bg++) = id.left.bg; + *(bg++) = id.left.bb; + *(bg++) = 1; + } + // Set texture coordinates + *(tex++) = txt[id.left.texpos].left; // Upper left + *(tex++) = txt[id.left.texpos].bottom; + *(tex++) = txt[id.left.texpos].right; // Upper right + *(tex++) = txt[id.left.texpos].bottom; + *(tex++) = txt[id.left.texpos].left; // Lower left + *(tex++) = txt[id.left.texpos].top; + + *(tex++) = txt[id.left.texpos].left; // Lower left + *(tex++) = txt[id.left.texpos].top; + *(tex++) = txt[id.left.texpos].right; // Upper right + *(tex++) = txt[id.left.texpos].bottom; + *(tex++) = txt[id.left.texpos].right; // Lower right + *(tex++) = txt[id.left.texpos].top; + } else { + // TODO + } + } + +public: + void update_tile(int x, int y) { + const int tile = x*gps.dimy + y; + // Update the arrays + GLfloat *fg = this->fg + tile * 4 * 6; + GLfloat *bg = this->bg + tile * 4 * 6; + GLfloat *tex = this->tex + tile * 2 * 6; + write_tile_arrays(x, y, fg, bg, tex); + } + + void update_all() { + glClear(GL_COLOR_BUFFER_BIT); + for (int x = 0; x < gps.dimx; x++) + for (int y = 0; y < gps.dimy; y++) + update_tile(x, y); + } + + void render() { + draw(gps.dimx*gps.dimy*6); + if (init.display.flag.has_flag(INIT_DISPLAY_FLAG_ARB_SYNC) && GL_ARB_sync) { + assert(df::global::enabler->sync == NULL); + df::global::enabler->sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + } + SDL_GL_SwapBuffers(); + } + + renderer_opengl() { + // Init member variables so realloc'll work + screen = NULL; + vertexes = NULL; + fg = NULL; + bg = NULL; + tex = NULL; + zoom_steps = forced_steps = 0; + + // Disable key repeat + SDL_EnableKeyRepeat(0, 0); + // Set window title/icon. + SDL_WM_SetCaption(GAME_TITLE_STRING, NULL); + SDL_Surface *icon = IMG_Load("data/art/icon.png"); + if (icon != NULL) { + SDL_WM_SetIcon(icon, NULL); + // The icon's surface doesn't get used past this point. + SDL_FreeSurface(icon); + } + + // Find the current desktop resolution if fullscreen resolution is auto + if (init.display.desired_fullscreen_width == 0 || + init.display.desired_fullscreen_height == 0) { + const struct SDL_VideoInfo *info = SDL_GetVideoInfo(); + init.display.desired_fullscreen_width = info->current_w; + init.display.desired_fullscreen_height = info->current_h; + } + + // Initialize our window + bool worked = init_video(isFullscreen() ? + init.display.desired_fullscreen_width : + init.display.desired_windowed_width, + isFullscreen() ? + init.display.desired_fullscreen_height : + init.display.desired_windowed_height); + + // Fallback to windowed mode if fullscreen fails + if (!worked && isFullscreen()) { + df::global::enabler->fullscreen = false; + report_error("SDL initialization failure, trying windowed mode", SDL_GetError()); + worked = init_video(init.display.desired_windowed_width, + init.display.desired_windowed_height); + } + // Quit if windowed fails + if (!worked) { + report_error("SDL initialization failure", SDL_GetError()); + exit(EXIT_FAILURE); + } + + // Initialize opengl + init_opengl(); + } + + virtual ~renderer_opengl() { + free(vertexes); + free(fg); + free(bg); + free(tex); + } + + int zoom_steps, forced_steps; + int natural_w, natural_h; // How large our view would be if it wasn't zoomed + + void zoom(df::zoom_commands cmd) { + pair before = compute_zoom(true); + int before_steps = zoom_steps; + switch (cmd) { + case zoom_in: zoom_steps -= init.input.zoom_speed; break; + case zoom_out: zoom_steps += init.input.zoom_speed; break; + case zoom_reset: + zoom_steps = 0; + case zoom_resetgrid: + compute_forced_zoom(); + break; + } + pair after = compute_zoom(true); + if (after == before && (cmd == zoom_in || cmd == zoom_out)) + zoom_steps = before_steps; + else + reshape(after); + } + + void compute_forced_zoom() { + forced_steps = 0; + pair zoomed = compute_zoom(); + while (zoomed.first < MIN_GRID_X || zoomed.second < MIN_GRID_Y) { + forced_steps++; + zoomed = compute_zoom(); + } + while (zoomed.first > MAX_GRID_X || zoomed.second > MAX_GRID_Y) { + forced_steps--; + zoomed = compute_zoom(); + } + } + + pair compute_zoom(bool clamp = false) { + const int dispx = isFullscreen() ? + init.font.large_font_dispx : + init.font.small_font_dispx; + const int dispy = isFullscreen() ? + init.font.large_font_dispy : + init.font.small_font_dispy; + int w, h; + if (dispx < dispy) { + w = natural_w + zoom_steps + forced_steps; + h = double(natural_h) * (double(w) / double(natural_w)); + } else { + h = natural_h + zoom_steps + forced_steps; + w = double(natural_w) * (double(h) / double(natural_h)); + } + if (clamp) { + w = MIN(MAX(w, MIN_GRID_X), MAX_GRID_X); + h = MIN(MAX(h, MIN_GRID_Y), MAX_GRID_Y); + } + return make_pair(w,h); + } + + // Parameters: grid units + void reshape(pair size) { + int w = MIN(MAX(size.first, MIN_GRID_X), MAX_GRID_X); + int h = MIN(MAX(size.second, MIN_GRID_Y), MAX_GRID_Y); +#ifdef DEBUG + cout << "Resizing grid to " << w << "x" << h << endl; +#endif + gps_allocate(w, h); + reshape_gl(); + } + + int off_x, off_y, size_x, size_y; + + bool get_mouse_coords(int &x, int &y) { + int mouse_x, mouse_y; + SDL_GetMouseState(&mouse_x, &mouse_y); + mouse_x -= off_x; mouse_y -= off_y; + if (mouse_x < 0 || mouse_y < 0 || + mouse_x >= size_x || mouse_y >= size_y) + return false; // Out of bounds + x = double(mouse_x) / double(size_x) * double(gps.dimx); + y = double(mouse_y) / double(size_y) * double(gps.dimy); + return true; + } + + virtual void reshape_gl() { + // Allocate array memory + allocate(gps.dimx * gps.dimy); + // Initialize the vertex array + int tile = 0; + for (GLfloat x = 0; x < gps.dimx; x++) + for (GLfloat y = 0; y < gps.dimy; y++, tile++) + write_tile_vertexes(x, y, vertexes + 6*2*tile); + // Setup invariant state + glEnableClientState(GL_COLOR_ARRAY); + /// Set up our coordinate system + if (forced_steps + zoom_steps == 0 && + init.display.flag.has_flag(INIT_DISPLAY_FLAG_BLACK_SPACE)) { + size_x = gps.dimx * dispx; + size_y = gps.dimy * dispy; + off_x = (screen->w - size_x) / 2; + off_y = (screen->h - size_y) / 2; + } else { + // If we're zooming (or just not using black space), we use the + // entire window. + size_x = screen->w; + size_y = screen->h; + off_x = off_y = 0; + } + glViewport(off_x, off_y, size_x, size_y); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluOrtho2D(0, gps.dimx, gps.dimy, 0); + } + + // Parameters: window size + void resize(int w, int h) { + // (Re)calculate grid-size + dispx = isFullscreen() ? + init.font.large_font_dispx : + init.font.small_font_dispx; + dispy = isFullscreen() ? + init.font.large_font_dispy : + init.font.small_font_dispy; + natural_w = MAX(w / dispx,1); + natural_h = MAX(h / dispy,1); + // Compute forced_steps so we satisfy our grid-size limits + compute_forced_zoom(); + // Force a full display cycle + gps.force_full_display_count = 1; + df::global::enabler->flag |= ENABLERFLAG_RENDER; + // Reinitialize the video + uninit_opengl(); + init_video(w, h); + init_opengl(); + // Only reshape if we're free to pick grid size + if (df::global::enabler->overridden_grid_sizes.size() == 0) + reshape(compute_zoom()); + } + + // Parameters: grid size + void grid_resize(int w, int h) { + reshape(make_pair(w, h)); + } + +public: + void set_fullscreen() { + if (isFullscreen()) { + init.display.desired_windowed_width = screen->w; + init.display.desired_windowed_height = screen->h; + resize(init.display.desired_fullscreen_width, + init.display.desired_fullscreen_height); + } else { + resize(init.display.desired_windowed_width, init.display.desired_windowed_height); + } + } +}; + +// Specialization for PARTIAL:0 +class renderer_once : public renderer_opengl { + int tile_count; + +protected: + void update_tile(int x, int y) { + write_tile_vertexes(x, y, vertexes + tile_count * 6 * 2); + write_tile_arrays(x, y, + fg + tile_count * 6 * 4, + bg + tile_count * 6 * 4, + tex + tile_count * 6 * 2); + tile_count++; + } + + void draw(int dummy) { + renderer_opengl::draw(tile_count*6); + tile_count = 0; + } + +public: + renderer_once() { + tile_count = 0; + } +}; + +// PARTIAL:N +class renderer_partial : public renderer_opengl { + int buffersz; + list erasz; // Previous eras + int current_erasz; // And the current one + int sum_erasz; + int head, tail; // First unused tile, first used tile respectively + int redraw_count; // Number of eras to max out at + + void update_tile(int x, int y) { + write_tile_vertexes(x, y, vertexes + head * 6 * 2); + write_tile_arrays(x, y, + fg + head * 6 * 4, + bg + head * 6 * 4, + tex + head * 6 * 2); + head = (head + 1) % buffersz; + current_erasz++; sum_erasz++; + if (head == tail) { + //gamelog << "Expanding partial-printing buffer" << endl; + // Buffer is full, expand it. + renderer_opengl::allocate(buffersz * 2); + // Move the tail to the end of the newly allocated space + tail += buffersz; + memmove(vertexes + tail * 6 * 2, vertexes + head * 6 * 2, sizeof(GLfloat) * 6 * 2 * (buffersz - head)); + memmove(fg + tail * 6 * 4, fg + head * 6 * 4, sizeof(GLfloat) * 6 * 4 * (buffersz - head)); + memmove(bg + tail * 6 * 4, fg + head * 6 * 4, sizeof(GLfloat) * 6 * 4 * (buffersz - head)); + memmove(tex + tail * 6 * 2, fg + head * 6 * 2, sizeof(GLfloat) * 6 * 2 * (buffersz - head)); + // And finish. + buffersz *= 2; + } + } + + void allocate(int tile_count) { + assert(false); + } + + virtual void reshape_gl() { + // TODO: This function is duplicate code w/base class reshape_gl + // Setup invariant state + glEnableClientState(GL_COLOR_ARRAY); + /// Set up our coordinate system + if (forced_steps + zoom_steps == 0 && + init.display.flag.has_flag(INIT_DISPLAY_FLAG_BLACK_SPACE)) { + size_x = gps.dimx * dispx; + size_y = gps.dimy * dispy; + off_x = (screen->w - size_x) / 2; + off_y = (screen->h - size_y) / 2; + } else { + // If we're zooming (or just not using black space), we use the + // entire window. + size_x = screen->w; + size_y = screen->h; + off_x = off_y = 0; + } + glViewport(off_x, off_y, size_x, size_y); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluOrtho2D(0, gps.dimx, gps.dimy, 0); + } + + void draw_arrays(GLfloat *vertexes, GLfloat *fg, GLfloat *bg, GLfloat *tex, int tile_count) { + // Set vertex pointer + glVertexPointer(2, GL_FLOAT, 0, vertexes); + // Render the background colors + glDisable(GL_TEXTURE_2D); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisable(GL_BLEND); + glDisable(GL_ALPHA_TEST); + glColorPointer(4, GL_FLOAT, 0, bg); + glDrawArrays(GL_TRIANGLES, 0, tile_count * 6); + // Render the foreground, colors and textures both + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_NOTEQUAL, 0); + glEnable(GL_TEXTURE_2D); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glColorPointer(4, GL_FLOAT, 0, fg); + glTexCoordPointer(2, GL_FLOAT, 0, tex); + glDrawArrays(GL_TRIANGLES, 0, tile_count * 6); + } + + void draw(int dummy) { + if (tail > head) { + // We're straddling the end of the array, so have to do this in two steps + draw_arrays(vertexes + tail * 6 * 2, + fg + tail * 6 * 4, + bg + tail * 6 * 4, + tex + tail * 6 * 2, + buffersz - tail); + draw_arrays(vertexes, fg, bg, tex, head-1); + } else { + draw_arrays(vertexes + tail * 6 * 2, + fg + tail * 6 * 4, + bg + tail * 6 * 4, + tex + tail * 6 * 2, + sum_erasz); + } + + printGLError(); + erasz.push_back(current_erasz); current_erasz = 0; + if (erasz.size() == redraw_count) { + // Right, time to retire the oldest era. + tail = (tail + erasz.front()) % buffersz; + sum_erasz -= erasz.front(); + erasz.pop_front(); + } + } + +public: + renderer_partial() { + redraw_count = init.display.partial_print_count; + buffersz = 2048; + renderer_opengl::allocate(buffersz); + current_erasz = head = tail = sum_erasz = 0; + } +}; + +class renderer_accum_buffer : public renderer_once { + void draw(int vertex_count) { + // Copy the previous frame's buffer back in + glAccum(GL_RETURN, 1); + renderer_once::draw(vertex_count); + // Store the screen contents back to the buffer + glAccum(GL_LOAD, 1); + } +}; + +class renderer_framebuffer : public renderer_once { + GLuint framebuffer, fb_texture; + + void init_opengl() { + glGenFramebuffersEXT(1, &framebuffer); + // Allocate FBO texture memory + glGenTextures(1, &fb_texture); + glBindTexture(GL_TEXTURE_2D, fb_texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, + screen->w, screen->h, + 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); + + // Bind texture to FBO + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, framebuffer); + glFramebufferTexture2DEXT(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, + GL_TEXTURE_2D, fb_texture, 0); + renderer_once::init_opengl(); + } + + void uninit_opengl() { + renderer_once::uninit_opengl(); + glDeleteTextures(1, &fb_texture); + glDeleteFramebuffersEXT(1, &framebuffer); + } + + void draw(int vertex_count) { + // Bind the framebuffer + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, framebuffer); + glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, 0); + // Draw + renderer_once::draw(vertex_count); + // Draw the framebuffer to screen + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0); + glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, framebuffer); + glBlitFramebufferEXT(0,0, screen->w, screen->h, + 0,0, screen->w, screen->h, + GL_COLOR_BUFFER_BIT, GL_NEAREST); + printGLError(); + } +}; + +class renderer_vbo : public renderer_opengl { + GLuint vbo; // Vertexes only + + void init_opengl() { + renderer_opengl::init_opengl(); + glGenBuffersARB(1, &vbo); + glBindBufferARB(GL_ARRAY_BUFFER_ARB, vbo); + glBufferDataARB(GL_ARRAY_BUFFER_ARB, gps.dimx*gps.dimy*6*2*sizeof(GLfloat), vertexes, GL_STATIC_DRAW_ARB); + glVertexPointer(2, GL_FLOAT, 0, 0); + glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); + } + + void uninit_opengl() { + glDeleteBuffersARB(1, &vbo); + renderer_opengl::uninit_opengl(); + } +}; diff --git a/plugins/skeleton/CMakeLists.txt b/plugins/skeleton/CMakeLists.txt index 542f08fe3..794f4a5d8 100644 --- a/plugins/skeleton/CMakeLists.txt +++ b/plugins/skeleton/CMakeLists.txt @@ -24,7 +24,7 @@ IF(UNIX) # windows ELSE(UNIX) SET(PROJECT_LIBS - # add any extra linux libs here + # add any extra windows libs here ${PROJECT_LIBS} $(NOINHERIT) ) From 55d845992cb8b9f9861c14738d0b4c810bfa8ad9 Mon Sep 17 00:00:00 2001 From: Warmist Date: Sat, 22 Jun 2013 03:45:42 +0300 Subject: [PATCH 003/676] rendermax: removed all the opengl and sdl stuff. Now it work with magic alone. --- plugins/rendermax/CMakeLists.txt | 10 +- plugins/rendermax/renderer_opengl.hpp | 792 ++++++-------------------- 2 files changed, 161 insertions(+), 641 deletions(-) diff --git a/plugins/rendermax/CMakeLists.txt b/plugins/rendermax/CMakeLists.txt index 7b9d7bdf7..c781bde48 100644 --- a/plugins/rendermax/CMakeLists.txt +++ b/plugins/rendermax/CMakeLists.txt @@ -1,7 +1,5 @@ PROJECT (rendermax) -Find_Package(SDL REQUIRED) -find_package(OpenGL REQUIRED) -find_package(GLUT REQUIRED) + # A list of source files SET(PROJECT_SRCS rendermax.cpp @@ -14,23 +12,19 @@ SET_SOURCE_FILES_PROPERTIES( ${PROJECT_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}) -include_directories(${SDL_INCLUDE_DIR}) + #linux IF(UNIX) add_definitions(-DLINUX_BUILD) SET(PROJECT_LIBS # add any extra linux libs here - ${SDL_LIBRARY} - opengl ${PROJECT_LIBS} ) # windows ELSE(UNIX) SET(PROJECT_LIBS # add any extra windows libs here - ${SDL_LIBRARY} - opengl ${PROJECT_LIBS} $(NOINHERIT) ) diff --git a/plugins/rendermax/renderer_opengl.hpp b/plugins/rendermax/renderer_opengl.hpp index 60c912d05..7c4fea2fa 100644 --- a/plugins/rendermax/renderer_opengl.hpp +++ b/plugins/rendermax/renderer_opengl.hpp @@ -6,653 +6,179 @@ #include "df/enabler.h" #include "df/zoom_commands.h" #include "df/texture_handler.h" +#include "df/graphic.h" using df::renderer; using df::init; using df::enabler; -#include "SDL.h" - -#ifdef __APPLE__ -#include -#include -//#include -#else -#ifdef _WIN32 -#include -#endif -#include -#include -//#include -#endif -bool isFullscreen() +struct old_opengl:public renderer { - return df::global::enabler->fullscreen; -} - -template -struct Either { - bool isL; - union { - L left; - R right; - }; - Either(const L &l) { - isL = true; - left = l; - } - Either(const R &r) { - isL = false; - right = r; - } + void* sdlSurface; + int32_t dispx,dispy; + float *vertexes, *fg, *bg, *tex; + int32_t zoom_steps,forced_steps,natural_w,natural_h; + int32_t off_x,off_y,size_x,size_y; }; - -// STANDARD -class renderer_opengl : public renderer { -public: - virtual bool uses_opengl() { return true; } - -protected: - SDL_Surface *screen; - - int dispx, dispy; // Cache of the current font size - - bool init_video(int w, int h) { - // Get ourselves an opengl-enabled SDL window - uint32_t flags = SDL_HWSURFACE | SDL_OPENGL; - - // Set it up for windowed or fullscreen, depending. - if (isFullscreen()) { - flags |= SDL_FULLSCREEN; - } else { - if (!df::global::init->display.flag.is_set(INIT_DISPLAY_FLAG_NOT_RESIZABLE)) - flags |= SDL_RESIZABLE; - } - - // Setup OpenGL attributes - SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, init.window.flag.has_flag(INIT_WINDOW_FLAG_VSYNC_ON)); - SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, - init.display.flag.has_flag(INIT_DISPLAY_FLAG_SINGLE_BUFFER) ? 0 : 1); - - // (Re)create the window - screen = SDL_SetVideoMode(w, h, 32, flags); - - if (!screen) return false; - - // Test double-buffering status - int test; - SDL_GL_GetAttribute(SDL_GL_DOUBLEBUFFER, &test); - if (test != ((init.display.flag.has_flag(INIT_DISPLAY_FLAG_SINGLE_BUFFER)) ? 0 : 1)) { - if (isFullscreen()); - //errorlog << "Requested single-buffering not available\n" << flush; - else - report_error("OpenGL", "Requested single-buffering not available"); - } - - // (Re)initialize GLEW. Technically only needs to be done once on - // linux, but on windows forgetting will cause crashes. - glewInit(); - - // Set the viewport and clear - glViewport(0, 0, screen->w, screen->h); - glClear(GL_COLOR_BUFFER_BIT); - - return true; - } - - // Vertexes, foreground color, background color, texture coordinates - GLfloat *vertexes, *fg, *bg, *tex; - - void write_tile_vertexes(GLfloat x, GLfloat y, GLfloat *vertex) { - vertex[0] = x; // Upper left - vertex[1] = y; - vertex[2] = x+1; // Upper right - vertex[3] = y; - vertex[4] = x; // Lower left - vertex[5] = y+1; - vertex[6] = x; // Lower left again (triangle 2) - vertex[7] = y+1; - vertex[8] = x+1; // Upper right - vertex[9] = y; - vertex[10] = x+1; // Lower right - vertex[11] = y+1; - } - - virtual void allocate(int tiles) { - vertexes = static_cast(realloc(vertexes, sizeof(GLfloat) * tiles * 2 * 6)); - assert(vertexes); - fg = static_cast(realloc(fg, sizeof(GLfloat) * tiles * 4 * 6)); - assert(fg); - bg = static_cast(realloc(bg, sizeof(GLfloat) * tiles * 4 * 6)); - assert(bg); - tex = static_cast(realloc(tex, sizeof(GLfloat) * tiles * 2 * 6)); - assert(tex); - - glEnableClientState(GL_VERTEX_ARRAY); - glVertexPointer(2, GL_FLOAT, 0, vertexes); - } - - virtual void init_opengl() { - //enabler.textures.upload_textures(); - } - - virtual void uninit_opengl() { - //enabler.textures.remove_uploaded_textures(); - } - - virtual void draw(int vertex_count) { - // Render the background colors - glDisable(GL_TEXTURE_2D); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - glDisable(GL_BLEND); - glDisable(GL_ALPHA_TEST); - glColorPointer(4, GL_FLOAT, 0, bg); - glDrawArrays(GL_TRIANGLES, 0, vertex_count); - // Render the foreground, colors and textures both - glEnable(GL_ALPHA_TEST); - glAlphaFunc(GL_NOTEQUAL, 0); - glEnable(GL_TEXTURE_2D); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glTexCoordPointer(2, GL_FLOAT, 0, tex); - glColorPointer(4, GL_FLOAT, 0, fg); - glDrawArrays(GL_TRIANGLES, 0, vertex_count); - - printGLError(); - } - - void write_tile_arrays(int x, int y, GLfloat *fg, GLfloat *bg, GLfloat *tex) { - Either id = screen_to_texid(x, y); - if (id.isL) { // An ordinary tile - const gl_texpos *txt = df::global::enabler->textures.gl_texpos; - // TODO: Only bother to set the one that's actually read in flat-shading mode - // And set flat-shading mode. - for (int i = 0; i < 6; i++) { - *(fg++) = id.left.r; - *(fg++) = id.left.g; - *(fg++) = id.left.b; - *(fg++) = 1; - - *(bg++) = id.left.br; - *(bg++) = id.left.bg; - *(bg++) = id.left.bb; - *(bg++) = 1; - } - // Set texture coordinates - *(tex++) = txt[id.left.texpos].left; // Upper left - *(tex++) = txt[id.left.texpos].bottom; - *(tex++) = txt[id.left.texpos].right; // Upper right - *(tex++) = txt[id.left.texpos].bottom; - *(tex++) = txt[id.left.texpos].left; // Lower left - *(tex++) = txt[id.left.texpos].top; - - *(tex++) = txt[id.left.texpos].left; // Lower left - *(tex++) = txt[id.left.texpos].top; - *(tex++) = txt[id.left.texpos].right; // Upper right - *(tex++) = txt[id.left.texpos].bottom; - *(tex++) = txt[id.left.texpos].right; // Lower right - *(tex++) = txt[id.left.texpos].top; - } else { - // TODO - } - } - -public: - void update_tile(int x, int y) { - const int tile = x*gps.dimy + y; - // Update the arrays - GLfloat *fg = this->fg + tile * 4 * 6; - GLfloat *bg = this->bg + tile * 4 * 6; - GLfloat *tex = this->tex + tile * 2 * 6; - write_tile_arrays(x, y, fg, bg, tex); - } - - void update_all() { - glClear(GL_COLOR_BUFFER_BIT); - for (int x = 0; x < gps.dimx; x++) - for (int y = 0; y < gps.dimy; y++) - update_tile(x, y); - } - - void render() { - draw(gps.dimx*gps.dimy*6); - if (init.display.flag.has_flag(INIT_DISPLAY_FLAG_ARB_SYNC) && GL_ARB_sync) { - assert(df::global::enabler->sync == NULL); - df::global::enabler->sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); - } - SDL_GL_SwapBuffers(); - } - - renderer_opengl() { - // Init member variables so realloc'll work - screen = NULL; - vertexes = NULL; - fg = NULL; - bg = NULL; - tex = NULL; - zoom_steps = forced_steps = 0; - - // Disable key repeat - SDL_EnableKeyRepeat(0, 0); - // Set window title/icon. - SDL_WM_SetCaption(GAME_TITLE_STRING, NULL); - SDL_Surface *icon = IMG_Load("data/art/icon.png"); - if (icon != NULL) { - SDL_WM_SetIcon(icon, NULL); - // The icon's surface doesn't get used past this point. - SDL_FreeSurface(icon); - } - - // Find the current desktop resolution if fullscreen resolution is auto - if (init.display.desired_fullscreen_width == 0 || - init.display.desired_fullscreen_height == 0) { - const struct SDL_VideoInfo *info = SDL_GetVideoInfo(); - init.display.desired_fullscreen_width = info->current_w; - init.display.desired_fullscreen_height = info->current_h; - } - - // Initialize our window - bool worked = init_video(isFullscreen() ? - init.display.desired_fullscreen_width : - init.display.desired_windowed_width, - isFullscreen() ? - init.display.desired_fullscreen_height : - init.display.desired_windowed_height); - - // Fallback to windowed mode if fullscreen fails - if (!worked && isFullscreen()) { - df::global::enabler->fullscreen = false; - report_error("SDL initialization failure, trying windowed mode", SDL_GetError()); - worked = init_video(init.display.desired_windowed_width, - init.display.desired_windowed_height); - } - // Quit if windowed fails - if (!worked) { - report_error("SDL initialization failure", SDL_GetError()); - exit(EXIT_FAILURE); - } - - // Initialize opengl - init_opengl(); - } - - virtual ~renderer_opengl() { - free(vertexes); - free(fg); - free(bg); - free(tex); - } - - int zoom_steps, forced_steps; - int natural_w, natural_h; // How large our view would be if it wasn't zoomed - - void zoom(df::zoom_commands cmd) { - pair before = compute_zoom(true); - int before_steps = zoom_steps; - switch (cmd) { - case zoom_in: zoom_steps -= init.input.zoom_speed; break; - case zoom_out: zoom_steps += init.input.zoom_speed; break; - case zoom_reset: - zoom_steps = 0; - case zoom_resetgrid: - compute_forced_zoom(); - break; - } - pair after = compute_zoom(true); - if (after == before && (cmd == zoom_in || cmd == zoom_out)) - zoom_steps = before_steps; - else - reshape(after); - } - - void compute_forced_zoom() { - forced_steps = 0; - pair zoomed = compute_zoom(); - while (zoomed.first < MIN_GRID_X || zoomed.second < MIN_GRID_Y) { - forced_steps++; - zoomed = compute_zoom(); - } - while (zoomed.first > MAX_GRID_X || zoomed.second > MAX_GRID_Y) { - forced_steps--; - zoomed = compute_zoom(); - } - } - - pair compute_zoom(bool clamp = false) { - const int dispx = isFullscreen() ? - init.font.large_font_dispx : - init.font.small_font_dispx; - const int dispy = isFullscreen() ? - init.font.large_font_dispy : - init.font.small_font_dispy; - int w, h; - if (dispx < dispy) { - w = natural_w + zoom_steps + forced_steps; - h = double(natural_h) * (double(w) / double(natural_w)); - } else { - h = natural_h + zoom_steps + forced_steps; - w = double(natural_w) * (double(h) / double(natural_h)); +struct renderer_wrap : public renderer { +private: + void set_to_null() { + screen = NULL; + screentexpos = NULL; + screentexpos_addcolor = NULL; + screentexpos_grayscale = NULL; + screentexpos_cf = NULL; + screentexpos_cbr = NULL; + screen_old = NULL; + screentexpos_old = NULL; + screentexpos_addcolor_old = NULL; + screentexpos_grayscale_old = NULL; + screentexpos_cf_old = NULL; + screentexpos_cbr_old = NULL; + } + + void copy_from_inner() { + screen = parent->screen; + screentexpos = parent->screentexpos; + screentexpos_addcolor = parent->screentexpos_addcolor; + screentexpos_grayscale = parent->screentexpos_grayscale; + screentexpos_cf = parent->screentexpos_cf; + screentexpos_cbr = parent->screentexpos_cbr; + screen_old = parent->screen_old; + screentexpos_old = parent->screentexpos_old; + screentexpos_addcolor_old = parent->screentexpos_addcolor_old; + screentexpos_grayscale_old = parent->screentexpos_grayscale_old; + screentexpos_cf_old = parent->screentexpos_cf_old; + screentexpos_cbr_old = parent->screentexpos_cbr_old; + } + + void copy_to_inner() { + parent->screen = screen; + parent->screentexpos = screentexpos; + parent->screentexpos_addcolor = screentexpos_addcolor; + parent->screentexpos_grayscale = screentexpos_grayscale; + parent->screentexpos_cf = screentexpos_cf; + parent->screentexpos_cbr = screentexpos_cbr; + parent->screen_old = screen_old; + parent->screentexpos_old = screentexpos_old; + parent->screentexpos_addcolor_old = screentexpos_addcolor_old; + parent->screentexpos_grayscale_old = screentexpos_grayscale_old; + parent->screentexpos_cf_old = screentexpos_cf_old; + parent->screentexpos_cbr_old = screentexpos_cbr_old; } - if (clamp) { - w = MIN(MAX(w, MIN_GRID_X), MAX_GRID_X); - h = MIN(MAX(h, MIN_GRID_Y), MAX_GRID_Y); - } - return make_pair(w,h); - } - - // Parameters: grid units - void reshape(pair size) { - int w = MIN(MAX(size.first, MIN_GRID_X), MAX_GRID_X); - int h = MIN(MAX(size.second, MIN_GRID_Y), MAX_GRID_Y); -#ifdef DEBUG - cout << "Resizing grid to " << w << "x" << h << endl; -#endif - gps_allocate(w, h); - reshape_gl(); - } - - int off_x, off_y, size_x, size_y; - - bool get_mouse_coords(int &x, int &y) { - int mouse_x, mouse_y; - SDL_GetMouseState(&mouse_x, &mouse_y); - mouse_x -= off_x; mouse_y -= off_y; - if (mouse_x < 0 || mouse_y < 0 || - mouse_x >= size_x || mouse_y >= size_y) - return false; // Out of bounds - x = double(mouse_x) / double(size_x) * double(gps.dimx); - y = double(mouse_y) / double(size_y) * double(gps.dimy); - return true; - } - - virtual void reshape_gl() { - // Allocate array memory - allocate(gps.dimx * gps.dimy); - // Initialize the vertex array - int tile = 0; - for (GLfloat x = 0; x < gps.dimx; x++) - for (GLfloat y = 0; y < gps.dimy; y++, tile++) - write_tile_vertexes(x, y, vertexes + 6*2*tile); - // Setup invariant state - glEnableClientState(GL_COLOR_ARRAY); - /// Set up our coordinate system - if (forced_steps + zoom_steps == 0 && - init.display.flag.has_flag(INIT_DISPLAY_FLAG_BLACK_SPACE)) { - size_x = gps.dimx * dispx; - size_y = gps.dimy * dispy; - off_x = (screen->w - size_x) / 2; - off_y = (screen->h - size_y) / 2; - } else { - // If we're zooming (or just not using black space), we use the - // entire window. - size_x = screen->w; - size_y = screen->h; - off_x = off_y = 0; - } - glViewport(off_x, off_y, size_x, size_y); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - gluOrtho2D(0, gps.dimx, gps.dimy, 0); - } - - // Parameters: window size - void resize(int w, int h) { - // (Re)calculate grid-size - dispx = isFullscreen() ? - init.font.large_font_dispx : - init.font.small_font_dispx; - dispy = isFullscreen() ? - init.font.large_font_dispy : - init.font.small_font_dispy; - natural_w = MAX(w / dispx,1); - natural_h = MAX(h / dispy,1); - // Compute forced_steps so we satisfy our grid-size limits - compute_forced_zoom(); - // Force a full display cycle - gps.force_full_display_count = 1; - df::global::enabler->flag |= ENABLERFLAG_RENDER; - // Reinitialize the video - uninit_opengl(); - init_video(w, h); - init_opengl(); - // Only reshape if we're free to pick grid size - if (df::global::enabler->overridden_grid_sizes.size() == 0) - reshape(compute_zoom()); - } - - // Parameters: grid size - void grid_resize(int w, int h) { - reshape(make_pair(w, h)); - } - public: - void set_fullscreen() { - if (isFullscreen()) { - init.display.desired_windowed_width = screen->w; - init.display.desired_windowed_height = screen->h; - resize(init.display.desired_fullscreen_width, - init.display.desired_fullscreen_height); - } else { - resize(init.display.desired_windowed_width, init.display.desired_windowed_height); + renderer_wrap(renderer* parent):parent(parent) + { + copy_from_inner(); } - } -}; + virtual void update_tile(int32_t x, int32_t y) { -// Specialization for PARTIAL:0 -class renderer_once : public renderer_opengl { - int tile_count; - + copy_to_inner(); + parent->update_tile(x,y); + }; + virtual void update_all() { + copy_to_inner(); + parent->update_all(); + }; + virtual void render() { + copy_to_inner(); + parent->render(); + }; + virtual void set_fullscreen() { + copy_to_inner(); + parent->set_fullscreen(); + }; + virtual void zoom(df::zoom_commands z) { + copy_to_inner(); + parent->zoom(z); + }; + virtual void resize(int32_t w, int32_t h) { + copy_to_inner(); + parent->resize(w,h); + copy_from_inner(); + }; + virtual void grid_resize(int32_t w, int32_t h) { + copy_to_inner(); + parent->grid_resize(w,h); + copy_from_inner(); + }; + virtual ~renderer_wrap() { + df::global::enabler->renderer=parent; + }; + virtual bool get_mouse_coords(int32_t* x, int32_t* y) { + return parent->get_mouse_coords(x,y); + }; + virtual bool uses_opengl() { + return parent->uses_opengl(); + }; protected: - void update_tile(int x, int y) { - write_tile_vertexes(x, y, vertexes + tile_count * 6 * 2); - write_tile_arrays(x, y, - fg + tile_count * 6 * 4, - bg + tile_count * 6 * 4, - tex + tile_count * 6 * 2); - tile_count++; - } - - void draw(int dummy) { - renderer_opengl::draw(tile_count*6); - tile_count = 0; - } - -public: - renderer_once() { - tile_count = 0; - } + renderer* parent; }; - -// PARTIAL:N -class renderer_partial : public renderer_opengl { - int buffersz; - list erasz; // Previous eras - int current_erasz; // And the current one - int sum_erasz; - int head, tail; // First unused tile, first used tile respectively - int redraw_count; // Number of eras to max out at - - void update_tile(int x, int y) { - write_tile_vertexes(x, y, vertexes + head * 6 * 2); - write_tile_arrays(x, y, - fg + head * 6 * 4, - bg + head * 6 * 4, - tex + head * 6 * 2); - head = (head + 1) % buffersz; - current_erasz++; sum_erasz++; - if (head == tail) { - //gamelog << "Expanding partial-printing buffer" << endl; - // Buffer is full, expand it. - renderer_opengl::allocate(buffersz * 2); - // Move the tail to the end of the newly allocated space - tail += buffersz; - memmove(vertexes + tail * 6 * 2, vertexes + head * 6 * 2, sizeof(GLfloat) * 6 * 2 * (buffersz - head)); - memmove(fg + tail * 6 * 4, fg + head * 6 * 4, sizeof(GLfloat) * 6 * 4 * (buffersz - head)); - memmove(bg + tail * 6 * 4, fg + head * 6 * 4, sizeof(GLfloat) * 6 * 4 * (buffersz - head)); - memmove(tex + tail * 6 * 2, fg + head * 6 * 2, sizeof(GLfloat) * 6 * 2 * (buffersz - head)); - // And finish. - buffersz *= 2; - } - } - - void allocate(int tile_count) { - assert(false); - } - - virtual void reshape_gl() { - // TODO: This function is duplicate code w/base class reshape_gl - // Setup invariant state - glEnableClientState(GL_COLOR_ARRAY); - /// Set up our coordinate system - if (forced_steps + zoom_steps == 0 && - init.display.flag.has_flag(INIT_DISPLAY_FLAG_BLACK_SPACE)) { - size_x = gps.dimx * dispx; - size_y = gps.dimy * dispy; - off_x = (screen->w - size_x) / 2; - off_y = (screen->h - size_y) / 2; - } else { - // If we're zooming (or just not using black space), we use the - // entire window. - size_x = screen->w; - size_y = screen->h; - off_x = off_y = 0; - } - glViewport(off_x, off_y, size_x, size_y); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - gluOrtho2D(0, gps.dimx, gps.dimy, 0); - } - - void draw_arrays(GLfloat *vertexes, GLfloat *fg, GLfloat *bg, GLfloat *tex, int tile_count) { - // Set vertex pointer - glVertexPointer(2, GL_FLOAT, 0, vertexes); - // Render the background colors - glDisable(GL_TEXTURE_2D); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - glDisable(GL_BLEND); - glDisable(GL_ALPHA_TEST); - glColorPointer(4, GL_FLOAT, 0, bg); - glDrawArrays(GL_TRIANGLES, 0, tile_count * 6); - // Render the foreground, colors and textures both - glEnable(GL_ALPHA_TEST); - glAlphaFunc(GL_NOTEQUAL, 0); - glEnable(GL_TEXTURE_2D); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glColorPointer(4, GL_FLOAT, 0, fg); - glTexCoordPointer(2, GL_FLOAT, 0, tex); - glDrawArrays(GL_TRIANGLES, 0, tile_count * 6); - } - - void draw(int dummy) { - if (tail > head) { - // We're straddling the end of the array, so have to do this in two steps - draw_arrays(vertexes + tail * 6 * 2, - fg + tail * 6 * 4, - bg + tail * 6 * 4, - tex + tail * 6 * 2, - buffersz - tail); - draw_arrays(vertexes, fg, bg, tex, head-1); - } else { - draw_arrays(vertexes + tail * 6 * 2, - fg + tail * 6 * 4, - bg + tail * 6 * 4, - tex + tail * 6 * 2, - sum_erasz); - } - - printGLError(); - erasz.push_back(current_erasz); current_erasz = 0; - if (erasz.size() == redraw_count) { - // Right, time to retire the oldest era. - tail = (tail + erasz.front()) % buffersz; - sum_erasz -= erasz.front(); - erasz.pop_front(); +struct renderer_trippy : public renderer_wrap { +private: + float rFloat() + { + return rand()/(float)RAND_MAX; + } + void colorizeTile(int x,int y) + { + const int tile = x*(df::global::gps->dimy) + y; + old_opengl* p=reinterpret_cast(parent); + float *fg = p->fg + tile * 4 * 6; + float *bg = p->bg + tile * 4 * 6; + float *tex = p->tex + tile * 2 * 6; + const float val=1/2.0; + + float r=rFloat()*val - val/2; + float g=rFloat()*val - val/2; + float b=rFloat()*val - val/2; + + float backr=rFloat()*val - val/2; + float backg=rFloat()*val - val/2; + float backb=rFloat()*val - val/2; + for (int i = 0; i < 6; i++) { + *(fg++) += r; + *(fg++) += g; + *(fg++) += b; + *(fg++) = 1; + + *(bg++) += backr; + *(bg++) += backg; + *(bg++) += backb; + *(bg++) = 1; + } } - } - public: - renderer_partial() { - redraw_count = init.display.partial_print_count; - buffersz = 2048; - renderer_opengl::allocate(buffersz); - current_erasz = head = tail = sum_erasz = 0; - } -}; - -class renderer_accum_buffer : public renderer_once { - void draw(int vertex_count) { - // Copy the previous frame's buffer back in - glAccum(GL_RETURN, 1); - renderer_once::draw(vertex_count); - // Store the screen contents back to the buffer - glAccum(GL_LOAD, 1); - } + renderer_trippy(renderer* parent):renderer_wrap(parent) + { + } + virtual void update_tile(int32_t x, int32_t y) { + renderer_wrap::update_tile(x,y); + colorizeTile(x,y); + }; + virtual void update_all() { + renderer_wrap::update_all(); + for (int x = 0; x < df::global::gps->dimx; x++) + for (int y = 0; y < df::global::gps->dimy; y++) + colorizeTile(x,y); + }; }; - -class renderer_framebuffer : public renderer_once { - GLuint framebuffer, fb_texture; - - void init_opengl() { - glGenFramebuffersEXT(1, &framebuffer); - // Allocate FBO texture memory - glGenTextures(1, &fb_texture); - glBindTexture(GL_TEXTURE_2D, fb_texture); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, - screen->w, screen->h, - 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); - glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); +struct renderer_test : public renderer_wrap { +private: - // Bind texture to FBO - glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, framebuffer); - glFramebufferTexture2DEXT(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, - GL_TEXTURE_2D, fb_texture, 0); - renderer_once::init_opengl(); - } - - void uninit_opengl() { - renderer_once::uninit_opengl(); - glDeleteTextures(1, &fb_texture); - glDeleteFramebuffersEXT(1, &framebuffer); - } - - void draw(int vertex_count) { - // Bind the framebuffer - glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, framebuffer); - glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, 0); - // Draw - renderer_once::draw(vertex_count); - // Draw the framebuffer to screen - glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0); - glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, framebuffer); - glBlitFramebufferEXT(0,0, screen->w, screen->h, - 0,0, screen->w, screen->h, - GL_COLOR_BUFFER_BIT, GL_NEAREST); - printGLError(); - } -}; - -class renderer_vbo : public renderer_opengl { - GLuint vbo; // Vertexes only - - void init_opengl() { - renderer_opengl::init_opengl(); - glGenBuffersARB(1, &vbo); - glBindBufferARB(GL_ARRAY_BUFFER_ARB, vbo); - glBufferDataARB(GL_ARRAY_BUFFER_ARB, gps.dimx*gps.dimy*6*2*sizeof(GLfloat), vertexes, GL_STATIC_DRAW_ARB); - glVertexPointer(2, GL_FLOAT, 0, 0); - glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); - } - - void uninit_opengl() { - glDeleteBuffersARB(1, &vbo); - renderer_opengl::uninit_opengl(); - } -}; +public: + renderer_test(renderer* parent):renderer_wrap(parent) + { + } + virtual void update_tile(int32_t x, int32_t y) { + renderer_wrap::update_tile(x,y); + //some sort of mutex or sth? + //and then map read + }; + virtual void update_all() { + renderer_wrap::update_all(); + //some sort of mutex or sth? + //and then map read + //same stuff for all of them i guess... + }; +}; \ No newline at end of file From f68852b721f69ebe04bd2d533c1f57cedb612a00 Mon Sep 17 00:00:00 2001 From: Warmist Date: Sat, 22 Jun 2013 13:29:58 +0300 Subject: [PATCH 004/676] Forgot to add the plugin itself. Also some improvements --- plugins/rendermax/renderer_opengl.hpp | 50 +++++++++++++- plugins/rendermax/rendermax.cpp | 99 +++++++++++++++++++++++++++ 2 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 plugins/rendermax/rendermax.cpp diff --git a/plugins/rendermax/renderer_opengl.hpp b/plugins/rendermax/renderer_opengl.hpp index 7c4fea2fa..09d96ae45 100644 --- a/plugins/rendermax/renderer_opengl.hpp +++ b/plugins/rendermax/renderer_opengl.hpp @@ -1,4 +1,7 @@ //original file from https://github.com/Baughn/Dwarf-Fortress--libgraphics- +#include "tinythread.h" +#include "fast_mutex.h" + #include "Core.h" #include #include "df/renderer.h" @@ -165,20 +168,65 @@ public: }; struct renderer_test : public renderer_wrap { private: - + void colorizeTile(int x,int y) + { + const int tile = x*(df::global::gps->dimy) + y; + old_opengl* p=reinterpret_cast(parent); + float *fg = p->fg + tile * 4 * 6; + float *bg = p->bg + tile * 4 * 6; + float *tex = p->tex + tile * 2 * 6; + float v=opacity[tile]; + for (int i = 0; i < 6; i++) { + *(fg++) *= v; + *(fg++) *= v; + *(fg++) *= v; + *(fg++) = 1; + + *(bg++) *= v; + *(bg++) *= v; + *(bg++) *= v; + *(bg++) = 1; + } + } + void reinitOpacity(int w,int h) + { + tthread::lock_guard guard(dataMutex); + opacity.resize(w*h); + } + void reinitOpacity() + { + reinitOpacity(df::global::gps->dimy,df::global::gps->dimx); + } public: + tthread::fast_mutex dataMutex; + std::vector opacity; renderer_test(renderer* parent):renderer_wrap(parent) { + reinitOpacity(); } virtual void update_tile(int32_t x, int32_t y) { renderer_wrap::update_tile(x,y); + tthread::lock_guard guard(dataMutex); + colorizeTile(x,y); //some sort of mutex or sth? //and then map read }; virtual void update_all() { renderer_wrap::update_all(); + tthread::lock_guard guard(dataMutex); + for (int x = 0; x < df::global::gps->dimx; x++) + for (int y = 0; y < df::global::gps->dimy; y++) + colorizeTile(x,y); //some sort of mutex or sth? //and then map read //same stuff for all of them i guess... }; + virtual void grid_resize(int32_t w, int32_t h) { + renderer_wrap::grid_resize(w,h); + reinitOpacity(w,h); + }; + virtual void resize(int32_t w, int32_t h) { + renderer_wrap::resize(w,h); + reinitOpacity(w,h); + } }; \ No newline at end of file diff --git a/plugins/rendermax/rendermax.cpp b/plugins/rendermax/rendermax.cpp new file mode 100644 index 000000000..69709ac96 --- /dev/null +++ b/plugins/rendermax/rendermax.cpp @@ -0,0 +1,99 @@ +#include +#include + +#include "Core.h" +#include "Console.h" +#include "Export.h" +#include "PluginManager.h" + +#include +#include "df/renderer.h" +#include "df/enabler.h" + +#include "renderer_opengl.hpp" + +using namespace DFHack; +using std::vector; +using std::string; +enum RENDERER_MODE +{ + MODE_DEFAULT,MODE_TRIPPY,MODE_TRUECOLOR +}; +RENDERER_MODE current_mode=MODE_DEFAULT; +static command_result rendermax(color_ostream &out, vector & parameters); + +DFHACK_PLUGIN("rendermax"); + + +DFhackCExport command_result plugin_init (color_ostream &out, std::vector &commands) +{ + commands.push_back(PluginCommand( + "rendermax", "switch rendering engine.", rendermax, false, + " rendermax trippy\n" + " rendermax disable\n" + )); + return CR_OK; +} +void removeOld() +{ + if(current_mode!=MODE_DEFAULT) + delete df::global::enabler->renderer; + current_mode=MODE_DEFAULT; +} +void installNew(df::renderer* r,RENDERER_MODE newMode) +{ + df::global::enabler->renderer=r; + current_mode=newMode; +} +static command_result rendermax(color_ostream &out, vector & parameters) +{ + if(parameters.size()==0) + return CR_WRONG_USAGE; + string cmd=parameters[0]; + if(cmd=="trippy") + { + removeOld(); + installNew(new renderer_trippy(df::global::enabler->renderer),MODE_TRIPPY); + return CR_OK; + } + else if(cmd=="test") + { + if(current_mode==MODE_TRUECOLOR && parameters.size()==2) + { + renderer_test* r=reinterpret_cast(df::global::enabler->renderer); + tthread::lock_guard guard(r->dataMutex); + int h=df::global::gps->dimy; + int w=df::global::gps->dimx; + int cx=w/2; + int cy=h/2; + int rad=cx; + if(rad>cy)rad=cy; + rad/=2; + int radsq=rad*rad; + for(int i=-rad;iopacity[(cx+i)*h+(cy+j)]=(radsq-i*i-j*j)/(float)radsq; + } + return CR_OK; + } + else + { + removeOld(); + installNew(new renderer_test(df::global::enabler->renderer),MODE_TRUECOLOR); + return CR_OK; + } + + } + else if(cmd=="disable") + { + if(current_mode==MODE_DEFAULT) + out.print("%s\n","Not installed, doing nothing."); + else + removeOld(); + + return CR_OK; + } + return CR_WRONG_USAGE; +} \ No newline at end of file From 584640f12c42e7fe5752bad94eec422301091ef4 Mon Sep 17 00:00:00 2001 From: Warmist Date: Sat, 22 Jun 2013 14:14:35 +0300 Subject: [PATCH 005/676] Added help for truecolor light testing thing. Also added colors. --- plugins/rendermax/renderer_opengl.hpp | 51 +++++++++++++++++++-------- plugins/rendermax/rendermax.cpp | 35 +++++++++++++----- 2 files changed, 63 insertions(+), 23 deletions(-) diff --git a/plugins/rendermax/renderer_opengl.hpp b/plugins/rendermax/renderer_opengl.hpp index 09d96ae45..525460b71 100644 --- a/plugins/rendermax/renderer_opengl.hpp +++ b/plugins/rendermax/renderer_opengl.hpp @@ -166,6 +166,27 @@ public: colorizeTile(x,y); }; }; + +struct lightCell +{ + float r,g,b; + lightCell():r(0),g(0),b(0) + { + + } + lightCell(float r,float g,float b):r(r),g(g),b(b) + { + + } + lightCell operator*(float val) + { + return lightCell(r*val,g*val,b*val); + } + lightCell operator+(const lightCell& other) + { + return lightCell(r+other.r,g+other.g,b+other.b); + } +}; struct renderer_test : public renderer_wrap { private: void colorizeTile(int x,int y) @@ -175,34 +196,34 @@ private: float *fg = p->fg + tile * 4 * 6; float *bg = p->bg + tile * 4 * 6; float *tex = p->tex + tile * 2 * 6; - float v=opacity[tile]; + lightCell light=lightGrid[tile]; for (int i = 0; i < 6; i++) { - *(fg++) *= v; - *(fg++) *= v; - *(fg++) *= v; + *(fg++) *= light.r; + *(fg++) *= light.g; + *(fg++) *= light.b; *(fg++) = 1; - *(bg++) *= v; - *(bg++) *= v; - *(bg++) *= v; + *(bg++) *= light.r; + *(bg++) *= light.g; + *(bg++) *= light.b; *(bg++) = 1; } } - void reinitOpacity(int w,int h) + void reinitLightGrid(int w,int h) { tthread::lock_guard guard(dataMutex); - opacity.resize(w*h); + lightGrid.resize(w*h); } - void reinitOpacity() + void reinitLightGrid() { - reinitOpacity(df::global::gps->dimy,df::global::gps->dimx); + reinitLightGrid(df::global::gps->dimy,df::global::gps->dimx); } public: tthread::fast_mutex dataMutex; - std::vector opacity; + std::vector lightGrid; renderer_test(renderer* parent):renderer_wrap(parent) { - reinitOpacity(); + reinitLightGrid(); } virtual void update_tile(int32_t x, int32_t y) { renderer_wrap::update_tile(x,y); @@ -223,10 +244,10 @@ public: }; virtual void grid_resize(int32_t w, int32_t h) { renderer_wrap::grid_resize(w,h); - reinitOpacity(w,h); + reinitLightGrid(w,h); }; virtual void resize(int32_t w, int32_t h) { renderer_wrap::resize(w,h); - reinitOpacity(w,h); + reinitLightGrid(w,h); } }; \ No newline at end of file diff --git a/plugins/rendermax/rendermax.cpp b/plugins/rendermax/rendermax.cpp index 69709ac96..85232cde9 100644 --- a/plugins/rendermax/rendermax.cpp +++ b/plugins/rendermax/rendermax.cpp @@ -30,6 +30,7 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector & parameters installNew(new renderer_trippy(df::global::enabler->renderer),MODE_TRIPPY); return CR_OK; } - else if(cmd=="test") + else if(cmd=="truecolor") { + if(current_mode!=MODE_TRUECOLOR) + { + removeOld(); + installNew(new renderer_test(df::global::enabler->renderer),MODE_TRUECOLOR); + } if(current_mode==MODE_TRUECOLOR && parameters.size()==2) { + lightCell red(1,0,0),green(0,1,0),blue(0,0,1),white(1,1,1); + lightCell cur=white; + lightCell dim(0.2,0.2,0.2); + string col=parameters[1]; + if(col=="red") + cur=red; + else if(col=="green") + cur=green; + else if(col=="blue") + cur=blue; + renderer_test* r=reinterpret_cast(df::global::enabler->renderer); tthread::lock_guard guard(r->dataMutex); int h=df::global::gps->dimy; @@ -70,20 +87,22 @@ static command_result rendermax(color_ostream &out, vector & parameters if(rad>cy)rad=cy; rad/=2; int radsq=rad*rad; + for(size_t i=0;ilightGrid.size();i++) + { + r->lightGrid[i]=dim; + } for(int i=-rad;iopacity[(cx+i)*h+(cy+j)]=(radsq-i*i-j*j)/(float)radsq; + { + float val=(radsq-i*i-j*j)/(float)radsq; + r->lightGrid[(cx+i)*h+(cy+j)]=dim+cur*val; + } } return CR_OK; } - else - { - removeOld(); - installNew(new renderer_test(df::global::enabler->renderer),MODE_TRUECOLOR); - return CR_OK; - } + } else if(cmd=="disable") From 39da5babee55afec29f1e68102c8d75a205b181b Mon Sep 17 00:00:00 2001 From: Warmist Date: Sat, 22 Jun 2013 17:40:52 +0300 Subject: [PATCH 006/676] Implemented lua interface. --- plugins/rendermax/CMakeLists.txt | 2 + plugins/rendermax/renderer_opengl.hpp | 83 +++++++++++++ plugins/rendermax/rendermax.cpp | 168 +++++++++++++++++++++++++- 3 files changed, 250 insertions(+), 3 deletions(-) diff --git a/plugins/rendermax/CMakeLists.txt b/plugins/rendermax/CMakeLists.txt index c781bde48..a7422a2cd 100644 --- a/plugins/rendermax/CMakeLists.txt +++ b/plugins/rendermax/CMakeLists.txt @@ -19,12 +19,14 @@ IF(UNIX) add_definitions(-DLINUX_BUILD) SET(PROJECT_LIBS # add any extra linux libs here + lua ${PROJECT_LIBS} ) # windows ELSE(UNIX) SET(PROJECT_LIBS # add any extra windows libs here + lua ${PROJECT_LIBS} $(NOINHERIT) ) diff --git a/plugins/rendermax/renderer_opengl.hpp b/plugins/rendermax/renderer_opengl.hpp index 525460b71..1cd9d1bac 100644 --- a/plugins/rendermax/renderer_opengl.hpp +++ b/plugins/rendermax/renderer_opengl.hpp @@ -250,4 +250,87 @@ public: renderer_wrap::resize(w,h); reinitLightGrid(w,h); } +}; + +struct rgba +{ + float r,g,b,a; +}; +struct renderer_lua : public renderer_wrap { +private: + void overwriteTile(int x,int y) + { + const int tile = xyToTile(x,y); + old_opengl* p=reinterpret_cast(parent); + float *fg = p->fg + tile * 4 * 6; + float *bg = p->bg + tile * 4 * 6; + float *tex = p->tex + tile * 2 * 6; + lightCell fm=foreMult[tile]; + lightCell fo=foreOffset[tile]; + + lightCell bm=backMult[tile]; + lightCell bo=backOffset[tile]; + for (int i = 0; i < 6; i++) { + rgba* fore=reinterpret_cast(fg); + fore->r=fore->r*fm.r+fo.r; + fore->g=fore->g*fm.g+fo.g; + fore->b=fore->b*fm.b+fo.b; + + fg+=4; + rgba* back=reinterpret_cast(bg); + back->r=back->r*bm.r+bo.r; + back->g=back->g*bm.g+bo.g; + back->b=back->b*bm.b+bo.b; + bg+=4; + } + } + void reinitGrids(int w,int h) + { + tthread::lock_guard guard(dataMutex); + foreOffset.resize(w*h); + foreMult.resize(w*h); + backOffset.resize(w*h); + backMult.resize(w*h); + } + void reinitGrids() + { + reinitGrids(df::global::gps->dimy,df::global::gps->dimx); + } +public: + tthread::fast_mutex dataMutex; + std::vector foreOffset,foreMult; + std::vector backOffset,backMult; + inline int xyToTile(int x, int y) + { + return x*(df::global::gps->dimy) + y; + } + renderer_lua(renderer* parent):renderer_wrap(parent) + { + reinitGrids(); + } + virtual void update_tile(int32_t x, int32_t y) { + renderer_wrap::update_tile(x,y); + tthread::lock_guard guard(dataMutex); + overwriteTile(x,y); + //some sort of mutex or sth? + //and then map read + }; + virtual void update_all() { + renderer_wrap::update_all(); + tthread::lock_guard guard(dataMutex); + for (int x = 0; x < df::global::gps->dimx; x++) + for (int y = 0; y < df::global::gps->dimy; y++) + overwriteTile(x,y); + //some sort of mutex or sth? + //and then map read + //same stuff for all of them i guess... + }; + virtual void grid_resize(int32_t w, int32_t h) { + renderer_wrap::grid_resize(w,h); + reinitGrids(w,h); + }; + virtual void resize(int32_t w, int32_t h) { + renderer_wrap::resize(w,h); + reinitGrids(w,h); + } }; \ No newline at end of file diff --git a/plugins/rendermax/rendermax.cpp b/plugins/rendermax/rendermax.cpp index 85232cde9..cc4b77301 100644 --- a/plugins/rendermax/rendermax.cpp +++ b/plugins/rendermax/rendermax.cpp @@ -1,6 +1,8 @@ #include #include +#include + #include "Core.h" #include "Console.h" #include "Export.h" @@ -17,7 +19,7 @@ using std::vector; using std::string; enum RENDERER_MODE { - MODE_DEFAULT,MODE_TRIPPY,MODE_TRUECOLOR + MODE_DEFAULT,MODE_TRIPPY,MODE_TRUECOLOR,MODE_LUA }; RENDERER_MODE current_mode=MODE_DEFAULT; static command_result rendermax(color_ostream &out, vector & parameters); @@ -46,6 +48,159 @@ void installNew(df::renderer* r,RENDERER_MODE newMode) df::global::enabler->renderer=r; current_mode=newMode; } +static void lockGrids() +{ + if(current_mode!=MODE_LUA) + return ; + renderer_lua* r=reinterpret_cast(df::global::enabler->renderer); + r->dataMutex.lock(); +} +static void unlockGrids() +{ + if(current_mode!=MODE_LUA) + return ; + renderer_lua* r=reinterpret_cast(df::global::enabler->renderer); + r->dataMutex.unlock(); +} +static void resetGrids() +{ + if(current_mode!=MODE_LUA) + return ; + renderer_lua* r=reinterpret_cast(df::global::enabler->renderer); + for(size_t i=0;iforeMult.size();i++) + { + r->foreMult[i]=lightCell(1,1,1); + r->foreOffset[i]=lightCell(0,0,0); + r->backMult[i]=lightCell(1,1,1); + r->backOffset[i]=lightCell(0,0,0); + } +} +static int getGridsSize() +{ + if(current_mode!=MODE_LUA) + return -1; + renderer_lua* r=reinterpret_cast(df::global::enabler->renderer); + lua_pushnumber(L,df::global::gps->dimx); + lua_pushnumber(L,df::global::gps->dimy); + return 2; +} +static int getCell(lua_State* L) +{ + if(current_mode!=MODE_LUA) + return 0; + renderer_lua* r=reinterpret_cast(df::global::enabler->renderer); + int x=luaL_checknumber(L,1); + int y=luaL_checknumber(L,2); + int id=r->xyToTile(x,y); + lightCell fo=r->foreOffset[id]; + lightCell fm=r->foreMult[id]; + lightCell bo=r->backOffset[id]; + lightCell bm=r->backMult[id]; + lua_newtable(L); + + lua_newtable(L); + lua_pushnumber(L,fo.r); + lua_setfield(L,-2,"r"); + lua_pushnumber(L,fo.g); + lua_setfield(L,-2,"g"); + lua_pushnumber(L,fo.b); + lua_setfield(L,-2,"b"); + lua_setfield(L,-2,"fo"); + + lua_newtable(L); + lua_pushnumber(L,fm.r); + lua_setfield(L,-2,"r"); + lua_pushnumber(L,fm.g); + lua_setfield(L,-2,"g"); + lua_pushnumber(L,fm.b); + lua_setfield(L,-2,"b"); + lua_setfield(L,-2,"fm"); + + lua_newtable(L); + lua_pushnumber(L,bo.r); + lua_setfield(L,-2,"r"); + lua_pushnumber(L,bo.g); + lua_setfield(L,-2,"g"); + lua_pushnumber(L,bo.b); + lua_setfield(L,-2,"b"); + lua_setfield(L,-2,"bo"); + + lua_newtable(L); + lua_pushnumber(L,bm.r); + lua_setfield(L,-2,"r"); + lua_pushnumber(L,bm.g); + lua_setfield(L,-2,"g"); + lua_pushnumber(L,bm.b); + lua_setfield(L,-2,"b"); + lua_setfield(L,-2,"bm"); + return 1; +} +static int setCell(lua_State* L) +{ + if(current_mode!=MODE_LUA) + return 0; + renderer_lua* r=reinterpret_cast(df::global::enabler->renderer); + int x=luaL_checknumber(L,1); + int y=luaL_checknumber(L,2); + + lightCell fo; + lua_getfield(L,3,"fo"); + lua_getfield(L,-1,"r"); + fo.r=lua_tonumber(L,-1);lua_pop(L,1); + lua_getfield(L,-1,"g"); + fo.g=lua_tonumber(L,-1);lua_pop(L,1); + lua_getfield(L,-1,"b"); + fo.b=lua_tonumber(L,-1);lua_pop(L,1); + lightCell fm; + lua_getfield(L,3,"fm"); + lua_getfield(L,-1,"r"); + fm.r=lua_tonumber(L,-1);lua_pop(L,1); + lua_getfield(L,-1,"g"); + fm.g=lua_tonumber(L,-1);lua_pop(L,1); + lua_getfield(L,-1,"b"); + fm.b=lua_tonumber(L,-1);lua_pop(L,1); + + lightCell bo; + lua_getfield(L,3,"bo"); + lua_getfield(L,-1,"r"); + bo.r=lua_tonumber(L,-1);lua_pop(L,1); + lua_getfield(L,-1,"g"); + bo.g=lua_tonumber(L,-1);lua_pop(L,1); + lua_getfield(L,-1,"b"); + bo.b=lua_tonumber(L,-1);lua_pop(L,1); + + lightCell bm; + lua_getfield(L,3,"bm"); + lua_getfield(L,-1,"r"); + bm.r=lua_tonumber(L,-1);lua_pop(L,1); + lua_getfield(L,-1,"g"); + bm.g=lua_tonumber(L,-1);lua_pop(L,1); + lua_getfield(L,-1,"b"); + bm.b=lua_tonumber(L,-1);lua_pop(L,1); + int id=r->xyToTile(x,y); + r->foreMult[id]=fm; + r->foreOffset[id]=fo; + r->backMult[id]=bm; + r->backOffset[id]=bo; + return 0; +} +bool isEnabled() +{ + return current_mode==MODE_LUA; +} +DFHACK_PLUGIN_LUA_FUNCTIONS { + DFHACK_LUA_FUNCTION(isEnabled), + DFHACK_LUA_FUNCTION(lockGrids), + DFHACK_LUA_FUNCTION(unlockGrids), + DFHACK_LUA_FUNCTION(getGridsSize), + DFHACK_LUA_FUNCTION(resetGrids), + DFHACK_LUA_END +}; +DFHACK_PLUGIN_LUA_COMMANDS { + DFHACK_LUA_COMMAND(getCell), + DFHACK_LUA_COMMAND(setCell), + DFHACK_LUA_END +}; static command_result rendermax(color_ostream &out, vector & parameters) { if(parameters.size()==0) @@ -102,8 +257,15 @@ static command_result rendermax(color_ostream &out, vector & parameters } return CR_OK; } - - + } + else if(cmd=="lua") + { + removeOld(); + installNew(new renderer_lua(df::global::enabler->renderer),MODE_LUA); + lockGrids(); + resetGrids(); + unlockGrids(); + return CR_OK; } else if(cmd=="disable") { From 6e57c6446d1f1ab6c08aebdbdf19cbd2abebf656 Mon Sep 17 00:00:00 2001 From: Warmist Date: Sat, 22 Jun 2013 23:18:09 +0300 Subject: [PATCH 007/676] Added light script. Can be ran with "static" to recalculate only on '~' press. --- scripts/devel/light.lua | 238 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 238 insertions(+) create mode 100644 scripts/devel/light.lua diff --git a/scripts/devel/light.lua b/scripts/devel/light.lua new file mode 100644 index 000000000..4152876ae --- /dev/null +++ b/scripts/devel/light.lua @@ -0,0 +1,238 @@ +-- an experimental lighting engine for df +local gui = require 'gui' +local guidm = require 'gui.dwarfmode' +local render = require 'plugins.rendermax' + +local levelDim=0.05 +local tile_attrs = df.tiletype.attrs + +local args={...} + +function setCell(x,y,cell) + cell=cell or {} + cell.fm=cell.fm or {r=1,g=1,b=1} + cell.bm=cell.bm or {r=1,g=1,b=1} + cell.fo=cell.fo or {r=0,g=0,b=0} + cell.bo=cell.bo or {r=0,g=0,b=0} + render.setCell(x,y,cell) +end +function getCursorPos() + local g_cursor=df.global.cursor + if g_cursor.x ~= -30000 then + return copyall(g_cursor) + end +end +function falloff(color,sqDist,maxdist) + local v1=1/(sqDist/maxdist+1) + local v2=v1-1/(1+maxdist*maxdist) + local v=v2/(1-1/(1+maxdist*maxdist)) + return {r=v*color.r,g=v*color.g,b=v*color.b} +end +function blend(c1,c2) +return {r=math.max(c1.r,c2.r), + g=math.max(c1.g,c2.g), + b=math.max(c1.b,c2.b)} +end +LightOverlay=defclass(LightOverlay,guidm.DwarfOverlay) +LightOverlay.ATTRS { + lightMap={}, + dynamic=true +} +function LightOverlay.init(args) +end + +function lightPassable(shape) + if shape==df.tiletype_shape.WALL or + shape==df.tiletype_shape.BROOK_BED or + shape==df.tiletype_shape.TREE then + return false + else + return true + end +end +function LightOverlay:placeLightFov(pos,radius,color,f,rays) + f=f or falloff + local raycount=rays or 25 + local vp=self:getViewport() + local map = self.df_layout.map + local off=math.random(0,math.pi) + local done={} + for d=0,math.pi*2,math.pi*2/raycount do + local dx,dy + dx=math.cos(d+off) + dy=math.sin(d+off) + local cx=0 + local cy=0 + + for dt=0,radius,0.01 do + if math.abs(math.floor(dt*dx)-cx)>0 or math.abs(math.floor(dt*dy)-cy)> 0then + local x=cx+pos.x + local y=cy+pos.y + + if x>0 and y>0 and x<=map.width and y<=map.height and not done[tile] then + local tile=x+y*map.width + done[tile]=true + local ncol=f(color,dt*dt,radius) + local ocol=self.lightMap[tile] or {r=0,g=0,b=0} + ncol=blend(ncol,ocol) + self.lightMap[tile]=ncol + + + if --(ncol.r==ocol.r and ncol.g==ocol.g and ncol.b==ocol.b) or + not self.ocupancy[tile] then + break + end + end + cx=math.floor(dt*dx) + cy=math.floor(dt*dy) + end + end + end +end +function LightOverlay:placeLight(pos,radius,color,f) + f=f or falloff + local vp=self:getViewport() + local map = self.df_layout.map + + for i=-radius,radius do + for j=-radius,radius do + local x=pos.x+i+1 + local y=pos.y+j+1 + if x>0 and y>0 and x<=map.width and y<=map.height then + local tile=x+y*map.width + local ncol=f(color,(i*i+j*j),radius) + local ocol=self.lightMap[tile] or {r=0,g=0,b=0} + self.lightMap[tile]=blend(ncol,ocol) + end + end + end +end +function LightOverlay:calculateLightLava() + local vp=self:getViewport() + local map = self.df_layout.map + for i=map.x1,map.x2 do + for j=map.y1,map.y2 do + local pos={x=i+vp.x1-1,y=j+vp.y1-1,z=vp.z} + local pos2={x=i+vp.x1-1,y=j+vp.y1-1,z=vp.z-1} + local t1=dfhack.maps.getTileFlags(pos) + local shape=tile_attrs[dfhack.maps.getTileType(pos)].shape + local t2=dfhack.maps.getTileFlags(pos2) + if (t1 and t1.liquid_type and t1.flow_size>0) or + (shape==df.tiletype_shape.EMPTY and t2 and t2.liquid_type and t2.flow_size>0) then + --self:placeLight({x=i,y=j},5,{r=0.8,g=0.2,b=0.2}) + self:placeLightFov({x=i,y=j},5,{r=0.8,g=0.2,b=0.2},nil,5) + end + end + end +end +function LightOverlay:calculateLightSun() + local vp=self:getViewport() + local map = self.df_layout.map + for i=map.x1,map.x2+1 do + for j=map.y1,map.y2+1 do + local pos={x=i+vp.x1-1,y=j+vp.y1-1,z=vp.z} + + local t1=dfhack.maps.getTileFlags(pos) + + if (t1 and t1.outside ) then + + self:placeLightFov({x=i,y=j},7,{r=1,g=1,b=1},nil,3) + end + end + end +end +function LightOverlay:calculateLightCursor() + local c=getCursorPos() + + if c then + + local vp=self:getViewport() + local pos=vp:tileToScreen(c) + --self:placeLight(pos,11,{r=0.96,g=0.84,b=0.03}) + self:placeLightFov({x=pos.x+1,y=pos.y+1},11,{r=0.96,g=0.84,b=0.03}) + + end +end +function LightOverlay:buildOcupancy() + self.ocupancy={} + local vp=self:getViewport() + local map = self.df_layout.map + for i=map.x1,map.x2+1 do + for j=map.y1,map.y2+1 do + local pos={x=i+vp.x1-1,y=j+vp.y1-1,z=vp.z} + local tile=i+j*map.width + local tt=dfhack.maps.getTileType(pos) + + if tt then + local shape=tile_attrs[tt].shape + self.ocupancy[tile]=lightPassable(shape) + end + end + end + +end +function LightOverlay:makeLightMap() + self.lightMap={} + self:buildOcupancy() + self:calculateLightCursor() + self:calculateLightLava() + self:calculateLightSun() +end +function LightOverlay:onIdle() + self._native.parent:logic() + if self.dynamic then + self:makeLightMap() + end +end +function LightOverlay:render(dc) + + + self:renderParent() + local vp=self:getViewport() + local map = self.df_layout.map + + self.lightMap=self.lightMap or {} + render.lockGrids() + df.global.gps.force_full_display_count=df.global.gps.force_full_display_count+1 + render.resetGrids() + for i=map.x1,map.x2 do + for j=map.y1,map.y2 do + local v=self.lightMap[i+j*map.width] + if v then + setCell(i,j,{fm=v,bm=v}) + else + local dimRgb={r=levelDim,g=levelDim,b=levelDim} + setCell(i,j,{fm=dimRgb,bm=dimRgb}) + end + end + end + render.unlockGrids() + +end +function LightOverlay:onDismiss() + render.lockGrids() + render.resetGrids() + render.unlockGrids() + df.global.gps.force_full_display_count=df.global.gps.force_full_display_count+1 +end +function LightOverlay:onInput(keys) + if keys.LEAVESCREEN then + self:dismiss() + else + self:sendInputToParent(keys) + + if keys.CHANGETAB then + self:updateLayout() + end + if keys.STRING_A126 and not self.dynamic then + self:makeLightMap() + end + end +end +if not render.isEnabled() then + qerror("Lua rendermode not enabled!") +end +local dyn=true +if #args>0 and args[1]=="static" then dyn=false end +local lview = LightOverlay{ dynamic=dyn} +lview:show() \ No newline at end of file From 843237d1c90763bfa9bd9cb218c1f4c876be71e3 Mon Sep 17 00:00:00 2001 From: Warmist Date: Sun, 23 Jun 2013 09:19:29 +0300 Subject: [PATCH 008/676] Fixed bug. --- plugins/rendermax/rendermax.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/rendermax/rendermax.cpp b/plugins/rendermax/rendermax.cpp index cc4b77301..89449f5be 100644 --- a/plugins/rendermax/rendermax.cpp +++ b/plugins/rendermax/rendermax.cpp @@ -33,6 +33,7 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector backOffset[i]=lightCell(0,0,0); } } -static int getGridsSize() +static int getGridsSize(lua_State* L) { if(current_mode!=MODE_LUA) return -1; @@ -192,13 +193,13 @@ DFHACK_PLUGIN_LUA_FUNCTIONS { DFHACK_LUA_FUNCTION(isEnabled), DFHACK_LUA_FUNCTION(lockGrids), DFHACK_LUA_FUNCTION(unlockGrids), - DFHACK_LUA_FUNCTION(getGridsSize), DFHACK_LUA_FUNCTION(resetGrids), DFHACK_LUA_END }; DFHACK_PLUGIN_LUA_COMMANDS { DFHACK_LUA_COMMAND(getCell), DFHACK_LUA_COMMAND(setCell), + DFHACK_LUA_COMMAND(getGridsSize), DFHACK_LUA_END }; static command_result rendermax(color_ostream &out, vector & parameters) From eb999cd0adf015a0eab716ec5eaeec62e2d2a94d Mon Sep 17 00:00:00 2001 From: Warmist Date: Sun, 23 Jun 2013 09:36:04 +0300 Subject: [PATCH 009/676] Another file forgotten --- plugins/lua/rendermax.lua | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 plugins/lua/rendermax.lua diff --git a/plugins/lua/rendermax.lua b/plugins/lua/rendermax.lua new file mode 100644 index 000000000..d44027c16 --- /dev/null +++ b/plugins/lua/rendermax.lua @@ -0,0 +1,4 @@ +local _ENV = mkmodule('plugins.rendermax') + + +return _ENV \ No newline at end of file From 446b4b9d5056117e3be34693d7d3d5ce23a72d8f Mon Sep 17 00:00:00 2001 From: Warmist Date: Sun, 23 Jun 2013 12:20:35 +0300 Subject: [PATCH 010/676] New line algo for lights --- plugins/rendermax/renderer_opengl.hpp | 13 ++++ plugins/rendermax/rendermax.cpp | 25 +++++++ scripts/devel/light.lua | 97 ++++++++++++++++++++++++--- 3 files changed, 126 insertions(+), 9 deletions(-) diff --git a/plugins/rendermax/renderer_opengl.hpp b/plugins/rendermax/renderer_opengl.hpp index 1cd9d1bac..c6d9d2e63 100644 --- a/plugins/rendermax/renderer_opengl.hpp +++ b/plugins/rendermax/renderer_opengl.hpp @@ -114,6 +114,19 @@ public: virtual bool uses_opengl() { return parent->uses_opengl(); }; + void invalidateRect(int32_t x,int32_t y,int32_t w,int32_t h) + { + for(int i=x;idimy + j; + screen_old[index*4]=screen[index*4]+1; + } + }; + void invalidate() + { + invalidateRect(0,0,df::global::gps->dimx,df::global::gps->dimy); + }; protected: renderer* parent; }; diff --git a/plugins/rendermax/rendermax.cpp b/plugins/rendermax/rendermax.cpp index 89449f5be..190e5f421 100644 --- a/plugins/rendermax/rendermax.cpp +++ b/plugins/rendermax/rendermax.cpp @@ -185,6 +185,30 @@ static int setCell(lua_State* L) r->backOffset[id]=bo; return 0; } +static int invalidate(lua_State* L) +{ + if(current_mode!=MODE_LUA) + return 0; + renderer_lua* r=reinterpret_cast(df::global::enabler->renderer); + if(lua_gettop(L)==0) + { + r->invalidate(); + } + else + { + int x,y,w,h; + lua_getfield(L,1,"x"); + x=lua_tonumber(L,-1);lua_pop(L,1); + lua_getfield(L,1,"y"); + y=lua_tonumber(L,-1);lua_pop(L,1); + lua_getfield(L,1,"w"); + w=lua_tonumber(L,-1);lua_pop(L,1); + lua_getfield(L,1,"h"); + h=lua_tonumber(L,-1);lua_pop(L,1); + r->invalidateRect(x,y,w,h); + } + return 0; +} bool isEnabled() { return current_mode==MODE_LUA; @@ -200,6 +224,7 @@ DFHACK_PLUGIN_LUA_COMMANDS { DFHACK_LUA_COMMAND(getCell), DFHACK_LUA_COMMAND(setCell), DFHACK_LUA_COMMAND(getGridsSize), + DFHACK_LUA_COMMAND(invalidate), DFHACK_LUA_END }; static command_result rendermax(color_ostream &out, vector & parameters) diff --git a/scripts/devel/light.lua b/scripts/devel/light.lua index 4152876ae..ff5580610 100644 --- a/scripts/devel/light.lua +++ b/scripts/devel/light.lua @@ -50,7 +50,82 @@ function lightPassable(shape) return true end end -function LightOverlay:placeLightFov(pos,radius,color,f,rays) +function circle(xm, ym,r,plot) + local x = -r + local y = 0 + local err = 2-2*r -- /* II. Quadrant */ + repeat + plot(xm-x, ym+y);--/* I. Quadrant */ + plot(xm-y, ym-x);--/* II. Quadrant */ + plot(xm+x, ym-y);--/* III. Quadrant */ + plot(xm+y, ym+x);--/* IV. Quadrant */ + r = err; + if (r <= y) then + y=y+1 + err =err+y*2+1; --/* e_xy+e_y < 0 */ + end + if (r > x or err > y) then + x=x+1 + err =err+x*2+1; --/* e_xy+e_x > 0 or no 2nd y-step */ + end + until (x >= 0); +end +function line(x0, y0, x1, y1,plot) + local dx = math.abs(x1-x0) + local dy = math.abs(y1-y0) + local sx,sy + if x0 < x1 then sx = 1 else sx = -1 end + if y0 < y1 then sy = 1 else sy = -1 end + local err = dx-dy + + while true do + if not plot(x0,y0) then + return + end + if x0 == x1 and y0 == y1 then + break + end + local e2 = 2*err + if e2 > -dy then + err = err - dy + x0 = x0 + sx + end + if x0 == x1 and y0 == y1 then + if not plot(x0,y0) then + return + end + break + end + if e2 < dx then + err = err + dx + y0 = y0 + sy + end + end +end +function LightOverlay:placeLightFov(pos,radius,color,f) + f=f or falloff + local vp=self:getViewport() + local map = self.df_layout.map + local ray=function(tx,ty) + + local setTile=function(x,y) + if x>0 and y>0 and x<=map.width and y<=map.height then + local dtsq=(pos.x-x)*(pos.x-x)+(pos.y-y)*(pos.y-y) + local tile=x+y*map.width + local ncol=f(color,dtsq,radius) + local ocol=self.lightMap[tile] or {r=0,g=0,b=0} + ncol=blend(ncol,ocol) + self.lightMap[tile]=ncol + + return self.ocupancy[tile] + end + return false + end + line(pos.x,pos.y,tx,ty,setTile) + end + circle(pos.x,pos.y,radius,ray) +end +function LightOverlay:placeLightFov2(pos,radius,color,f,rays) f=f or falloff local raycount=rays or 25 local vp=self:getViewport() @@ -115,12 +190,15 @@ function LightOverlay:calculateLightLava() local pos={x=i+vp.x1-1,y=j+vp.y1-1,z=vp.z} local pos2={x=i+vp.x1-1,y=j+vp.y1-1,z=vp.z-1} local t1=dfhack.maps.getTileFlags(pos) - local shape=tile_attrs[dfhack.maps.getTileType(pos)].shape - local t2=dfhack.maps.getTileFlags(pos2) - if (t1 and t1.liquid_type and t1.flow_size>0) or - (shape==df.tiletype_shape.EMPTY and t2 and t2.liquid_type and t2.flow_size>0) then - --self:placeLight({x=i,y=j},5,{r=0.8,g=0.2,b=0.2}) - self:placeLightFov({x=i,y=j},5,{r=0.8,g=0.2,b=0.2},nil,5) + local tt=dfhack.maps.getTileType(pos) + if tt then + local shape=tile_attrs[tt].shape + local t2=dfhack.maps.getTileFlags(pos2) + if (t1 and t1.liquid_type and t1.flow_size>0) or + (shape==df.tiletype_shape.EMPTY and t2 and t2.liquid_type and t2.flow_size>0) then + --self:placeLight({x=i,y=j},5,{r=0.8,g=0.2,b=0.2}) + self:placeLightFov({x=i,y=j},5,{r=0.8,g=0.2,b=0.2},nil,5) + end end end end @@ -193,7 +271,7 @@ function LightOverlay:render(dc) self.lightMap=self.lightMap or {} render.lockGrids() - df.global.gps.force_full_display_count=df.global.gps.force_full_display_count+1 + render.invalidate({x=map.x1,y=map.y1,w=map.width,h=map.height}) render.resetGrids() for i=map.x1,map.x2 do for j=map.y1,map.y2 do @@ -212,8 +290,9 @@ end function LightOverlay:onDismiss() render.lockGrids() render.resetGrids() + render.invalidate() render.unlockGrids() - df.global.gps.force_full_display_count=df.global.gps.force_full_display_count+1 + end function LightOverlay:onInput(keys) if keys.LEAVESCREEN then From 1ce43c6cc4ed0fe8cd77c4ddbc790ae202b01e6a Mon Sep 17 00:00:00 2001 From: Warmist Date: Sun, 23 Jun 2013 12:55:14 +0300 Subject: [PATCH 011/676] Light now fallsoff according to tile contents (water for now only) --- scripts/devel/light.lua | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/scripts/devel/light.lua b/scripts/devel/light.lua index ff5580610..f3d39e4db 100644 --- a/scripts/devel/light.lua +++ b/scripts/devel/light.lua @@ -107,17 +107,28 @@ function LightOverlay:placeLightFov(pos,radius,color,f) local vp=self:getViewport() local map = self.df_layout.map local ray=function(tx,ty) - + local power=copyall(color) + local lx=pos.x + local ly=pos.y local setTile=function(x,y) if x>0 and y>0 and x<=map.width and y<=map.height then - local dtsq=(pos.x-x)*(pos.x-x)+(pos.y-y)*(pos.y-y) + local dtsq=(lx-x)*(lx-x)+(ly-y)*(ly-y) + local dt=math.sqrt(dtsq) local tile=x+y*map.width - local ncol=f(color,dtsq,radius) + local ocol=self.lightMap[tile] or {r=0,g=0,b=0} - ncol=blend(ncol,ocol) + local ncol=blend(power,ocol) self.lightMap[tile]=ncol - - return self.ocupancy[tile] + local v=self.ocupancy[tile] + if dtsq>0 then + power.r=power.r*(v.r^dt) + power.g=power.g*(v.g^dt) + power.b=power.b*(v.b^dt) + end + lx=x + ly=y + local pwsq=power.r*power.r+power.g*power.g+power.b*power.b + return pwsq>levelDim*levelDim end return false end @@ -240,14 +251,21 @@ function LightOverlay:buildOcupancy() local pos={x=i+vp.x1-1,y=j+vp.y1-1,z=vp.z} local tile=i+j*map.width local tt=dfhack.maps.getTileType(pos) - + local t1=dfhack.maps.getTileFlags(pos) if tt then local shape=tile_attrs[tt].shape - self.ocupancy[tile]=lightPassable(shape) + if not lightPassable(shape) then + self.ocupancy[tile]={r=0,g=0,b=0} + else + if t1 and not t1.liquid_type and t1.flow_size>2 then + self.ocupancy[tile]={r=0.5,g=0.5,b=0.7} + else + self.ocupancy[tile]={r=0.8,g=0.8,b=0.8} + end + end end end end - end function LightOverlay:makeLightMap() self.lightMap={} From 4ce951f5b995607370929fa9490e233ba6687514 Mon Sep 17 00:00:00 2001 From: Warmist Date: Sun, 23 Jun 2013 13:28:22 +0300 Subject: [PATCH 012/676] light.lua: more help, changed screen exit key to '`'. Made it recalculate only when needed. --- scripts/devel/light.lua | 68 +++++++++++++++++++++++++++++++++-------- 1 file changed, 55 insertions(+), 13 deletions(-) diff --git a/scripts/devel/light.lua b/scripts/devel/light.lua index f3d39e4db..48324d797 100644 --- a/scripts/devel/light.lua +++ b/scripts/devel/light.lua @@ -1,4 +1,4 @@ --- an experimental lighting engine for df +-- an experimental lighting engine for df. param: "static" to not recalc when in game. press "~" to recalculate. "`" to exit local gui = require 'gui' local guidm = require 'gui.dwarfmode' local render = require 'plugins.rendermax' @@ -36,9 +36,12 @@ end LightOverlay=defclass(LightOverlay,guidm.DwarfOverlay) LightOverlay.ATTRS { lightMap={}, - dynamic=true + dynamic=true, + dirty=false, } -function LightOverlay.init(args) +function LightOverlay:init(args) + + self.tick=df.global.cur_year_tick_advmode end function lightPassable(shape) @@ -102,8 +105,14 @@ function line(x0, y0, x1, y1,plot) end end end -function LightOverlay:placeLightFov(pos,radius,color,f) - f=f or falloff +function LightOverlay:calculateFovs() + self.fovs=self.fovs or {} + self.precalc=self.precalc or {} + for k,v in ipairs(self.fovs) do + self:calculateFov(v.pos,v.radius,v.color) + end +end +function LightOverlay:calculateFov(pos,radius,color) local vp=self:getViewport() local map = self.df_layout.map local ray=function(tx,ty) @@ -115,9 +124,16 @@ function LightOverlay:placeLightFov(pos,radius,color,f) local dtsq=(lx-x)*(lx-x)+(ly-y)*(ly-y) local dt=math.sqrt(dtsq) local tile=x+y*map.width - + if self.precalc[tile] then + local tcol=blend(self.precalc[tile],power) + if tcol.r==self.precalc[tile].r and tcol.g==self.precalc[tile].g and self.precalc[tile].b==self.precalc[tile].b + and dtsq>0 then + return false + end + end local ocol=self.lightMap[tile] or {r=0,g=0,b=0} local ncol=blend(power,ocol) + self.lightMap[tile]=ncol local v=self.ocupancy[tile] if dtsq>0 then @@ -136,6 +152,17 @@ function LightOverlay:placeLightFov(pos,radius,color,f) end circle(pos.x,pos.y,radius,ray) end +function LightOverlay:placeLightFov(pos,radius,color) + local map = self.df_layout.map + local tile=pos.x+pos.y*map.width + local ocol=self.precalc[tile] or {r=0,g=0,b=0} + local ncol=blend(color,ocol) + self.precalc[tile]=ncol + local ocol=self.lightMap[tile] or {r=0,g=0,b=0} + local ncol=blend(color,ocol) + self.lightMap[tile]=ncol + table.insert(self.fovs,{pos=pos,radius=radius,color=color}) +end function LightOverlay:placeLightFov2(pos,radius,color,f,rays) f=f or falloff local raycount=rays or 25 @@ -208,7 +235,7 @@ function LightOverlay:calculateLightLava() if (t1 and t1.liquid_type and t1.flow_size>0) or (shape==df.tiletype_shape.EMPTY and t2 and t2.liquid_type and t2.flow_size>0) then --self:placeLight({x=i,y=j},5,{r=0.8,g=0.2,b=0.2}) - self:placeLightFov({x=i,y=j},5,{r=0.8,g=0.2,b=0.2},nil,5) + self:placeLightFov({x=i,y=j},5,{r=0.8,g=0.2,b=0.2},nil) end end end @@ -225,7 +252,7 @@ function LightOverlay:calculateLightSun() if (t1 and t1.outside ) then - self:placeLightFov({x=i,y=j},7,{r=1,g=1,b=1},nil,3) + self:placeLightFov({x=i,y=j},15,{r=1,g=1,b=1},nil) end end end @@ -267,22 +294,36 @@ function LightOverlay:buildOcupancy() end end end +function LightOverlay:changed() + if self.dirty or self.tick~=df.global.cur_year_tick_advmode then + self.dirty=false + self.tick=df.global.cur_year_tick_advmode + return true + end + return false +end function LightOverlay:makeLightMap() + if not self:changed() then + return + end + self.fovs={} + self.precalc={} self.lightMap={} + self:buildOcupancy() self:calculateLightCursor() self:calculateLightLava() self:calculateLightSun() + + self:calculateFovs() end function LightOverlay:onIdle() self._native.parent:logic() +end +function LightOverlay:render(dc) if self.dynamic then self:makeLightMap() end -end -function LightOverlay:render(dc) - - self:renderParent() local vp=self:getViewport() local map = self.df_layout.map @@ -313,7 +354,7 @@ function LightOverlay:onDismiss() end function LightOverlay:onInput(keys) - if keys.LEAVESCREEN then + if keys.STRING_A096 then self:dismiss() else self:sendInputToParent(keys) @@ -324,6 +365,7 @@ function LightOverlay:onInput(keys) if keys.STRING_A126 and not self.dynamic then self:makeLightMap() end + self.dirty=true end end if not render.isEnabled() then From a19200fbfe7b02ac640d37e63570d1c0672f98f9 Mon Sep 17 00:00:00 2001 From: Warmist Date: Sun, 23 Jun 2013 19:25:42 +0300 Subject: [PATCH 013/676] C++ version of light engine. --- plugins/rendermax/CMakeLists.txt | 2 + plugins/rendermax/renderer_light.cpp | 267 ++++++++++++++++++++++++++ plugins/rendermax/renderer_light.hpp | 109 +++++++++++ plugins/rendermax/renderer_opengl.hpp | 11 +- plugins/rendermax/rendermax.cpp | 46 ++++- 5 files changed, 430 insertions(+), 5 deletions(-) create mode 100644 plugins/rendermax/renderer_light.cpp create mode 100644 plugins/rendermax/renderer_light.hpp diff --git a/plugins/rendermax/CMakeLists.txt b/plugins/rendermax/CMakeLists.txt index a7422a2cd..fb8cc7724 100644 --- a/plugins/rendermax/CMakeLists.txt +++ b/plugins/rendermax/CMakeLists.txt @@ -3,10 +3,12 @@ PROJECT (rendermax) # A list of source files SET(PROJECT_SRCS rendermax.cpp + renderer_light.cpp ) # A list of headers SET(PROJECT_HDRS renderer_opengl.hpp + renderer_light.hpp ) SET_SOURCE_FILES_PROPERTIES( ${PROJECT_HDRS} PROPERTIES HEADER_FILE_ONLY TRUE) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp new file mode 100644 index 000000000..7ff122a37 --- /dev/null +++ b/plugins/rendermax/renderer_light.cpp @@ -0,0 +1,267 @@ +#include "renderer_light.hpp" + +#include + +#include "Types.h" + +#include "modules/Gui.h" +#include "modules/Screen.h" +#include "modules/Maps.h" + +#include "df/graphic.h" +#include "df/viewscreen_dwarfmodest.h" +#include "df/flow_info.h" + +using df::global::gps; +using namespace DFHack; +using df::coord2d; + +const float levelDim=0.2f; + +rect2d getMapViewport() +{ + const int AREA_MAP_WIDTH = 23; + const int MENU_WIDTH = 30; + if(!gps || !df::viewscreen_dwarfmodest::_identity.is_instance(DFHack::Gui::getCurViewscreen())) + return mkrect_wh(0,0,0,0); + int w=gps->dimx; + int h=gps->dimy; + int view_height=h-2; + int area_x2 = w-AREA_MAP_WIDTH-2; + int menu_x2=w-MENU_WIDTH-2; + int menu_x1=area_x2-MENU_WIDTH-1; + int view_rb=w-1; + + int area_pos=*df::global::ui_area_map_width; + int menu_pos=*df::global::ui_menu_width; + if(area_pos<3) + { + view_rb=area_x2; + } + if (menu_posmain.mode!=0) + { + if (menu_pos >= area_pos) + menu_pos = area_pos-1; + int menu_x = menu_x2; + if(menu_pos < 2) menu_x = menu_x1; + view_rb = menu_x; + } + return mkrect_wh(1,1,view_rb,view_height+1); +} +lightingEngineViewscreen::lightingEngineViewscreen(renderer_light* target):lightingEngine(target) +{ + reinit(); +} + +void lightingEngineViewscreen::reinit() +{ + if(!gps) + return; + w=gps->dimx; + h=gps->dimy; + size_t size=w*h; + lightMap.resize(size,lightCell(1,1,1)); + ocupancy.resize(size); +} +void plotCircle(int xm, int ym, int r,std::function setPixel) +{ + int x = -r, y = 0, err = 2-2*r; /* II. Quadrant */ + do { + setPixel(xm-x, ym+y); /* I. Quadrant */ + setPixel(xm-y, ym-x); /* II. Quadrant */ + setPixel(xm+x, ym-y); /* III. Quadrant */ + setPixel(xm+y, ym+x); /* IV. Quadrant */ + r = err; + if (r <= y) err += ++y*2+1; /* e_xy+e_y < 0 */ + if (r > x || err > y) err += ++x*2+1; /* e_xy+e_x > 0 or no 2nd y-step */ + } while (x < 0); +} +void plotLine(int x0, int y0, int x1, int y1,std::function setPixel) +{ + int dx = abs(x1-x0), sx = x0= dy) { err += dy; x0 += sx; rdx=sx;} /* e_xy+e_x > 0 */ + if (e2 <= dx) { err += dx; y0 += sy; rdy=sy;} /* e_xy+e_y < 0 */ + } +} +lightCell blend(lightCell a,lightCell b) +{ + return lightCell(std::max(a.r,b.r),std::max(a.g,b.g),std::max(a.b,b.b)); +} +bool lightingEngineViewscreen::lightUpCell(lightCell& power,int dx,int dy,int tx,int ty) +{ + + if(tx>=mapPort.first.x && ty>=mapPort.first.y && tx<=mapPort.second.x && ty<=mapPort.second.y) + { + size_t tile=getIndex(tx,ty); + float dsq=dx*dx+dy*dy; + lightCell& v=ocupancy[tile]; + bool wallhack=false; + bool outsidehack=false; + if(v.r+v.g+v.b==0) + wallhack=true; + if(v.r<0) + outsidehack=true; + if (dsq>0 && !wallhack && !outsidehack) + { + power.r=power.r*(pow(v.r,dsq)); + power.g=power.g*(pow(v.g,dsq)); + power.b=power.b*(pow(v.b,dsq)); + } + //float dt=sqrt(dsq); + lightCell oldCol=lightMap[tile]; + lightCell ncol=blend(power,oldCol); + lightMap[tile]=ncol; + + if(wallhack) + return false; + if(dsq>0 && outsidehack) + return false; + float pwsq=power.r*power.r+power.g*power.g+power.b*power.b; + return pwsq>levelDim*levelDim; + } + else + return false; +} +void lightingEngineViewscreen::doRay(lightCell power,int cx,int cy,int tx,int ty) +{ + using namespace std::placeholders; + lightCell curPower=power; + plotLine(cx,cy,tx,ty,std::bind(&lightingEngineViewscreen::lightUpCell,this,std::ref(curPower),_1,_2,_3,_4)); +} +void lightingEngineViewscreen::doFovs() +{ + mapPort=getMapViewport(); + using namespace std::placeholders; + for(size_t i=0;i guard(myRenderer->dataMutex); + if(lightMap.size()!=myRenderer->lightGrid.size()) + { + reinit(); + return; + } + std::swap(lightMap,myRenderer->lightGrid); + rect2d vp=getMapViewport(); + + //myRenderer->invalidateRect(vp.first.x,vp.first.y,vp.second.x-vp.first.x,vp.second.y-vp.first.y); + myRenderer->invalidate(); + //std::copy(lightMap.begin(),lightMap.end(),myRenderer->lightGrid.begin()); +} +void lightingEngineViewscreen::doOcupancyAndLights() +{ + lights.clear(); + rect2d vp=getMapViewport(); + + int window_x=*df::global::window_x; + int window_y=*df::global::window_y; + int window_z=*df::global::window_z; + int vpW=vp.second.x-vp.first.x; + int vpH=vp.second.y-vp.first.y; + for(int x=window_x;xbits.building) + { + curCell=lightCell(0,0,0); + } + else if(!d->bits.liquid_type && d->bits.flow_size>3 ) + { + curCell=lightCell(0.5f,0.5f,0.6f); + } + //todo constructions + + //lights + if((d->bits.liquid_type && d->bits.flow_size>0)|| (d2 && d2->bits.liquid_type && d2->bits.flow_size>0)) + { + lightSource lava={lightCell(0.8f,0.2f,0.2f),5,coord2d(wx,wy)}; + lights.push_back(lava); + } + if(d->bits.outside) + { + lightSource sun={lightCell(1,1,1),25,coord2d(wx,wy)}; + lights.push_back(sun); + curCell=lightCell(-1,-1,-1);//Marking as outside so no calculation is done on it + } + + } + + for(int blockx=window_x/16;blockx<(window_x+vpW)/16;blockx++) + for(int blocky=window_y/16;blocky<(window_x+vpW)/16;blocky++) + { + df::map_block* block=Maps::getBlock(blockx,blocky,window_z); + if(!block) + continue; + for(int i=0;iflows.size();i++) + { + df::flow_info* f=block->flows[i]; + if(f && f->density>0 && f->type==df::flow_type::Dragonfire || f->type==df::flow_type::Fire) + { + df::coord2d pos=f->pos; + int wx=pos.x-window_x+vp.first.x; + int wy=pos.y-window_y+vp.first.y; + if(wx>=vp.first.x && wy>=vp.first.y && wx<=vp.second.x && wy<=vp.second.y) + { + lightCell fireColor; + if(f->density>60) + { + fireColor=lightCell(0.98f,0.91f,0.30f); + } + else if(f->density>30) + { + fireColor=lightCell(0.93f,0.16f,0.16f); + } + else + { + fireColor=lightCell(0.64f,0.0f,0.0f); + } + lightSource fire={fireColor,f->density/5,coord2d(wx,wy)}; + lights.push_back(fire); + } + } + } + } +} \ No newline at end of file diff --git a/plugins/rendermax/renderer_light.hpp b/plugins/rendermax/renderer_light.hpp new file mode 100644 index 000000000..d0279fd02 --- /dev/null +++ b/plugins/rendermax/renderer_light.hpp @@ -0,0 +1,109 @@ +#ifndef RENDERER_LIGHT_INCLUDED +#define RENDERER_LIGHT_INCLUDED +#include "renderer_opengl.hpp" +#include "Types.h" + +struct renderer_light : public renderer_wrap { +private: + void colorizeTile(int x,int y) + { + const int tile = x*(df::global::gps->dimy) + y; + old_opengl* p=reinterpret_cast(parent); + float *fg = p->fg + tile * 4 * 6; + float *bg = p->bg + tile * 4 * 6; + float *tex = p->tex + tile * 2 * 6; + lightCell light=lightGrid[tile]; + for (int i = 0; i < 6; i++) { + *(fg++) *= light.r; + *(fg++) *= light.g; + *(fg++) *= light.b; + *(fg++) = 1; + + *(bg++) *= light.r; + *(bg++) *= light.g; + *(bg++) *= light.b; + *(bg++) = 1; + } + } + void reinitLightGrid(int w,int h) + { + tthread::lock_guard guard(dataMutex); + lightGrid.resize(w*h); + } + void reinitLightGrid() + { + reinitLightGrid(df::global::gps->dimy,df::global::gps->dimx); + } +public: + tthread::fast_mutex dataMutex; + std::vector lightGrid; + renderer_light(renderer* parent):renderer_wrap(parent) + { + reinitLightGrid(); + } + virtual void update_tile(int32_t x, int32_t y) { + renderer_wrap::update_tile(x,y); + tthread::lock_guard guard(dataMutex); + colorizeTile(x,y); + }; + virtual void update_all() { + renderer_wrap::update_all(); + tthread::lock_guard guard(dataMutex); + for (int x = 0; x < df::global::gps->dimx; x++) + for (int y = 0; y < df::global::gps->dimy; y++) + colorizeTile(x,y); + }; + virtual void grid_resize(int32_t w, int32_t h) { + renderer_wrap::grid_resize(w,h); + reinitLightGrid(w,h); + }; + virtual void resize(int32_t w, int32_t h) { + renderer_wrap::resize(w,h); + reinitLightGrid(w,h); + } +}; +class lightingEngine +{ +public: + lightingEngine(renderer_light* target):myRenderer(target){} + + virtual void reinit()=0; + virtual void calculate()=0; + + virtual void updateWindow()=0; + +protected: + renderer_light* myRenderer; +}; +struct lightSource +{ + lightCell power; + int radius; + df::coord2d pos; +}; +class lightingEngineViewscreen:public lightingEngine +{ +public: + lightingEngineViewscreen(renderer_light* target); + + void reinit(); + void calculate(); + + void updateWindow(); + +private: + void doOcupancyAndLights(); + void doRay(lightCell power,int cx,int cy,int tx,int ty); + void doFovs(); + bool lightUpCell(lightCell& power,int dx,int dy,int tx,int ty); + size_t inline getIndex(int x,int y) + { + return x*h+y; + } + std::vector lightMap; + std::vector ocupancy; + std::vector lights; + int w,h; + DFHack::rect2d mapPort; +}; +#endif \ No newline at end of file diff --git a/plugins/rendermax/renderer_opengl.hpp b/plugins/rendermax/renderer_opengl.hpp index c6d9d2e63..6b9e5ebed 100644 --- a/plugins/rendermax/renderer_opengl.hpp +++ b/plugins/rendermax/renderer_opengl.hpp @@ -1,4 +1,7 @@ //original file from https://github.com/Baughn/Dwarf-Fortress--libgraphics- +#ifndef RENDERER_OPENGL_INCLUDED +#define RENDERER_OPENGL_INCLUDED + #include "tinythread.h" #include "fast_mutex.h" @@ -120,12 +123,13 @@ public: for(int j=y;jdimy + j; - screen_old[index*4]=screen[index*4]+1; + screen_old[index*4]=0; } }; void invalidate() { - invalidateRect(0,0,df::global::gps->dimx,df::global::gps->dimy); + //invalidateRect(0,0,df::global::gps->dimx,df::global::gps->dimy); + df::global::gps->force_full_display_count++; }; protected: renderer* parent; @@ -346,4 +350,5 @@ public: renderer_wrap::resize(w,h); reinitGrids(w,h); } -}; \ No newline at end of file +}; +#endif \ No newline at end of file diff --git a/plugins/rendermax/rendermax.cpp b/plugins/rendermax/rendermax.cpp index 190e5f421..0a61d1e4a 100644 --- a/plugins/rendermax/rendermax.cpp +++ b/plugins/rendermax/rendermax.cpp @@ -13,15 +13,18 @@ #include "df/enabler.h" #include "renderer_opengl.hpp" +#include "renderer_light.hpp" using namespace DFHack; using std::vector; using std::string; enum RENDERER_MODE { - MODE_DEFAULT,MODE_TRIPPY,MODE_TRUECOLOR,MODE_LUA + MODE_DEFAULT,MODE_TRIPPY,MODE_TRUECOLOR,MODE_LUA,MODE_LIGHT }; RENDERER_MODE current_mode=MODE_DEFAULT; +lightingEngine *engine=NULL; + static command_result rendermax(color_ostream &out, vector & parameters); DFHACK_PLUGIN("rendermax"); @@ -34,6 +37,7 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector renderer; current_mode=MODE_DEFAULT; + if(current_mode==MODE_LIGHT) + { + if(engine) + delete engine; + engine=0; + } } void installNew(df::renderer* r,RENDERER_MODE newMode) { @@ -249,7 +259,7 @@ static command_result rendermax(color_ostream &out, vector & parameters { lightCell red(1,0,0),green(0,1,0),blue(0,0,1),white(1,1,1); lightCell cur=white; - lightCell dim(0.2,0.2,0.2); + lightCell dim(0.2f,0.2f,0.2f); string col=parameters[1]; if(col=="red") cur=red; @@ -293,6 +303,24 @@ static command_result rendermax(color_ostream &out, vector & parameters unlockGrids(); return CR_OK; } + else if(cmd=="light") + { + if(current_mode==MODE_LIGHT) + { + engine->calculate(); + engine->updateWindow(); + } + else + { + removeOld(); + renderer_light *myRender=new renderer_light(df::global::enabler->renderer); + installNew(myRender,MODE_LIGHT); + engine=new lightingEngineViewscreen(myRender); + engine->calculate(); + engine->updateWindow(); + } + return CR_OK; + } else if(cmd=="disable") { if(current_mode==MODE_DEFAULT) @@ -303,4 +331,18 @@ static command_result rendermax(color_ostream &out, vector & parameters return CR_OK; } return CR_WRONG_USAGE; +} +DFhackCExport command_result plugin_onupdate (color_ostream &out) +{ + if(engine) + { + engine->calculate(); + engine->updateWindow(); + } + return CR_OK; +} +DFhackCExport command_result plugin_shutdown(color_ostream &) +{ + removeOld(); + return CR_OK; } \ No newline at end of file From 04f2d4ea58665cf059eae88b404060078855d5fd Mon Sep 17 00:00:00 2001 From: Japa Date: Sun, 23 Jun 2013 23:51:25 +0530 Subject: [PATCH 014/676] Made windows transmit colored light. --- plugins/rendermax/renderer_light.cpp | 43 +++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index 7ff122a37..27b5e188e 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -11,6 +11,9 @@ #include "df/graphic.h" #include "df/viewscreen_dwarfmodest.h" #include "df/flow_info.h" +#include "df/world.h" +#include "df/building.h" + using df::global::gps; using namespace DFHack; @@ -177,6 +180,9 @@ void lightingEngineViewscreen::updateWindow() myRenderer->invalidate(); //std::copy(lightMap.begin(),lightMap.end(),myRenderer->lightGrid.begin()); } + +static size_t max_list_size = 100000; // Avoid iterating over huge lists + void lightingEngineViewscreen::doOcupancyAndLights() { lights.clear(); @@ -204,10 +210,45 @@ void lightingEngineViewscreen::doOcupancyAndLights() if(!o || !d ) continue; - if(shape==df::tiletype_shape::BROOK_BED || shape==df::tiletype_shape::WALL || shape==df::tiletype_shape::TREE || o->bits.building) + if(shape==df::tiletype_shape::BROOK_BED || shape==df::tiletype_shape::WALL || shape==df::tiletype_shape::TREE /*|| o->bits.building*/) { curCell=lightCell(0,0,0); } + else if(o->bits.building) + { + // Fixme: don't iterate the list every frame + size_t count = df::global::world->buildings.all.size(); + if (count <= max_list_size) + { + for(size_t i = 0; i < count; i++) + { + df::building *bld = df::global::world->buildings.all[i]; + + if (window_z == bld->z && + x >= bld->x1 && x <= bld->x2 && + y >= bld->y1 && y <= bld->y2) + { + df::building_type type = bld->getType(); + + if (type == df::enums::building_type::WindowGlass) + { + if(bld->mat_type == 3)//green glass + { + curCell=lightCell(0.1f,0.9f,0.5f); + } + else if(bld->mat_type == 4)//clear glass + { + curCell=lightCell(0.5f,0.95f,0.9f); + } + else if(bld->mat_type == 5)//crystal glass + { + curCell=lightCell(0.75f,0.95f,0.95f); + } + } + } + } + } + } else if(!d->bits.liquid_type && d->bits.flow_size>3 ) { curCell=lightCell(0.5f,0.5f,0.6f); From 2fef60708d30b56906e20e249e9fa674e9e056b9 Mon Sep 17 00:00:00 2001 From: Japa Date: Mon, 24 Jun 2013 00:14:06 +0530 Subject: [PATCH 015/676] Added some more operators to the cell color class. --- plugins/rendermax/renderer_light.cpp | 6 +++--- plugins/rendermax/renderer_opengl.hpp | 11 +++++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index 27b5e188e..7cd1d5164 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -234,15 +234,15 @@ void lightingEngineViewscreen::doOcupancyAndLights() { if(bld->mat_type == 3)//green glass { - curCell=lightCell(0.1f,0.9f,0.5f); + curCell*=lightCell(0.1f,0.9f,0.5f); } else if(bld->mat_type == 4)//clear glass { - curCell=lightCell(0.5f,0.95f,0.9f); + curCell*=lightCell(0.5f,0.95f,0.9f); } else if(bld->mat_type == 5)//crystal glass { - curCell=lightCell(0.75f,0.95f,0.95f); + curCell*=lightCell(0.75f,0.95f,0.95f); } } } diff --git a/plugins/rendermax/renderer_opengl.hpp b/plugins/rendermax/renderer_opengl.hpp index 6b9e5ebed..7c3819c57 100644 --- a/plugins/rendermax/renderer_opengl.hpp +++ b/plugins/rendermax/renderer_opengl.hpp @@ -199,6 +199,17 @@ struct lightCell { return lightCell(r*val,g*val,b*val); } + lightCell operator*(lightCell cell) + { + return lightCell(r*cell.r,g*cell.g,b*cell.b); + } + lightCell operator*=(lightCell cell) + { + r*=cell.r; + g*=cell.g; + b*=cell.b; + return lightCell(r,g,b); + } lightCell operator+(const lightCell& other) { return lightCell(r+other.r,g+other.g,b+other.b); From 14c494dbfe2a4c504e59cf8e69c7eff18032ef65 Mon Sep 17 00:00:00 2001 From: Japa Date: Mon, 24 Jun 2013 02:35:03 +0530 Subject: [PATCH 016/676] Made sky light be more realistic, decreasing through water, and ignoring tile designations otherwise. --- plugins/rendermax/renderer_light.cpp | 80 +++++++++++++++++++++++++-- plugins/rendermax/renderer_opengl.hpp | 8 +++ 2 files changed, 83 insertions(+), 5 deletions(-) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index 7cd1d5164..7319c6fe5 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -193,6 +193,78 @@ void lightingEngineViewscreen::doOcupancyAndLights() int window_z=*df::global::window_z; int vpW=vp.second.x-vp.first.x; int vpH=vp.second.y-vp.first.y; + int endBlockx = (window_x+vpW); + int endBlocky = (window_y+vpH); + if(endBlockx >= df::global::world->map.x_count_block) endBlockx = df::global::world->map.x_count_block-1; + if(endBlocky >= df::global::world->map.y_count_block) endBlocky = df::global::world->map.y_count_block-1; + for(int blockx=window_x/16;blockx<=endBlockx;blockx++) + for(int blocky=window_y/16;blocky<=endBlocky;blocky++) + { + + lightCell cellArray[16][16]; + for(int block_x = 0; block_x < 16; block_x++) + for(int block_y = 0; block_y < 16; block_y++) + { + cellArray[block_x][block_y] = lightCell(1,1,1); + } + int totalBlank = 0; + int topLevel = df::global::world->map.z_count-1; + for(int ZZ = topLevel; (ZZ >= window_z) && totalBlank < 257; ZZ--) + { + df::map_block* block=Maps::getBlock(blockx,blocky,ZZ); + totalBlank = 0; + for(int block_x = 0; block_x < 16; block_x++) + for(int block_y = 0; block_y < 16; block_y++) + { + if(block) + { + df::tiletype type = block->tiletype[block_x][block_y]; + df::tile_designation d = block->designation[block_x][block_y]; + df::tile_occupancy o = block->occupancy[block_x][block_y]; + df::tiletype_shape shape = ENUM_ATTR(tiletype,shape,type); + df::tiletype_shape_basic basic_shape = ENUM_ATTR(tiletype_shape, basic_shape, shape); + + if(basic_shape==df::tiletype_shape_basic::Wall) + { + cellArray[block_x][block_y]=lightCell(0,0,0); + } + else if(basic_shape==df::tiletype_shape_basic::Floor || basic_shape==df::tiletype_shape_basic::Ramp || basic_shape==df::tiletype_shape_basic::Stair) + { + if(ZZ!=window_z) + { + cellArray[block_x][block_y]=lightCell(0,0,0); + } + } + if(d.bits.liquid_type == df::enums::tile_liquid::Water && d.bits.flow_size) + { + cellArray[block_x][block_y] *= (lightCell(1,1,1) - (lightCell(1,1,1) - lightCell(0.63f,0.63f,0.75f))*(d.bits.flow_size/7)); + } + else if(d.bits.liquid_type == df::enums::tile_liquid::Magma && d.bits.flow_size > 3) + { + cellArray[block_x][block_y]=lightCell(0,0,0); + } + } + if(cellArray[block_x][block_y].r < 0.003f && cellArray[block_x][block_y].g < 0.003f && cellArray[block_x][block_y].b < 0.003f) + totalBlank++; + } + } + for(int block_x = 0; block_x < 16; block_x++) + for(int block_y = 0; block_y < 16; block_y++) + { + df::coord2d pos; + pos.x = blockx*16+block_x; + pos.y = blocky*16+block_y; + int wx=pos.x-window_x+vp.first.x; + int wy=pos.y-window_y+vp.first.y; + + if(cellArray[block_x][block_y].r >= 0.003f && cellArray[block_x][block_y].g >= 0.003f && cellArray[block_x][block_y].b >= 0.003f) + { + lightSource sun={cellArray[block_x][block_y],25,coord2d(wx,wy)}; + lights.push_back(sun); + } + } + + } for(int x=window_x;xbits.outside) + if(d->bits.outside && d->bits.flow_size==0) { - lightSource sun={lightCell(1,1,1),25,coord2d(wx,wy)}; - lights.push_back(sun); curCell=lightCell(-1,-1,-1);//Marking as outside so no calculation is done on it } } - for(int blockx=window_x/16;blockx<(window_x+vpW)/16;blockx++) - for(int blocky=window_y/16;blocky<(window_x+vpW)/16;blocky++) + for(int blockx=window_x/16;blockx<=endBlockx;blockx++) + for(int blocky=window_y/16;blocky<=endBlocky;blocky++) { df::map_block* block=Maps::getBlock(blockx,blocky,window_z); if(!block) diff --git a/plugins/rendermax/renderer_opengl.hpp b/plugins/rendermax/renderer_opengl.hpp index 7c3819c57..aa14a8c70 100644 --- a/plugins/rendermax/renderer_opengl.hpp +++ b/plugins/rendermax/renderer_opengl.hpp @@ -194,11 +194,19 @@ struct lightCell lightCell(float r,float g,float b):r(r),g(g),b(b) { + } + lightCell operator-(lightCell cell) + { + return lightCell(r-cell.r,g-cell.g,b-cell.b); } lightCell operator*(float val) { return lightCell(r*val,g*val,b*val); } + lightCell operator/(float val) + { + return lightCell(r/val,g/val,b/val); + } lightCell operator*(lightCell cell) { return lightCell(r*cell.r,g*cell.g,b*cell.b); From f92e4c91b895a4153e8881b9025f8a9f681fe002 Mon Sep 17 00:00:00 2001 From: Warmist Date: Sun, 23 Jun 2013 20:45:05 +0300 Subject: [PATCH 017/676] Performance improvements, added flickering cursor light. --- plugins/rendermax/renderer_light.cpp | 87 +++++++++++++++++++++------ plugins/rendermax/renderer_light.hpp | 17 +++++- plugins/rendermax/renderer_opengl.hpp | 4 ++ 3 files changed, 87 insertions(+), 21 deletions(-) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index 7319c6fe5..4ca686983 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -65,6 +65,7 @@ void lightingEngineViewscreen::reinit() size_t size=w*h; lightMap.resize(size,lightCell(1,1,1)); ocupancy.resize(size); + lights.resize(size); } void plotCircle(int xm, int ym, int r,std::function setPixel) { @@ -108,18 +109,22 @@ bool lightingEngineViewscreen::lightUpCell(lightCell& power,int dx,int dy,int tx size_t tile=getIndex(tx,ty); float dsq=dx*dx+dy*dy; lightCell& v=ocupancy[tile]; + lightSource& ls=lights[tile]; bool wallhack=false; - bool outsidehack=false; if(v.r+v.g+v.b==0) wallhack=true; - if(v.r<0) - outsidehack=true; - if (dsq>0 && !wallhack && !outsidehack) + + if (dsq>0 && !wallhack) { power.r=power.r*(pow(v.r,dsq)); power.g=power.g*(pow(v.g,dsq)); power.b=power.b*(pow(v.b,dsq)); } + if(ls.radius>0 && dsq>0) + { + if(power0 && outsidehack) - return false; float pwsq=power.r*power.r+power.g*power.g+power.b*power.b; return pwsq>levelDim*levelDim; } @@ -145,17 +148,32 @@ void lightingEngineViewscreen::doFovs() { mapPort=getMapViewport(); using namespace std::placeholders; - for(size_t i=0;i0) + { + lightCell power=csource.power; + int radius =csource.radius; + if(csource.flicker) + { + float flicker=(rand()/(float)RAND_MAX)/2.0f+0.5f; + radius*=flicker; + power=power*flicker; + } + plotCircle(i,j,radius, + std::bind(&lightingEngineViewscreen::doRay,this,power,i,j,_1,_2)); + } + } } void lightingEngineViewscreen::calculate() { rect2d vp=getMapViewport(); const lightCell dim(levelDim,levelDim,levelDim); lightMap.assign(lightMap.size(),lightCell(1,1,1)); + lights.assign(lights.size(),lightSource()); for(int i=vp.first.x;i0; + lights[tileId].combine(light); + if(light.flicker) + lights[tileId].flicker=true; + return wasLight; +} void lightingEngineViewscreen::doOcupancyAndLights() { - lights.clear(); + rect2d vp=getMapViewport(); int window_x=*df::global::window_x; @@ -270,8 +301,9 @@ void lightingEngineViewscreen::doOcupancyAndLights() { int wx=x-window_x+vp.first.x; int wy=y-window_y+vp.first.y; - lightCell& curCell=ocupancy[getIndex(wx,wy)]; - curCell=lightCell(0.8f,0.8f,0.8f); + int tile=getIndex(wx,wy); + lightCell& curCell=ocupancy[tile]; + curCell=lightCell(0.85f,0.85f,0.85f); df::tiletype* type = Maps::getTileType(x,y,window_z); if(!type) continue; @@ -328,14 +360,19 @@ void lightingEngineViewscreen::doOcupancyAndLights() //todo constructions //lights - if((d->bits.liquid_type && d->bits.flow_size>0)|| (d2 && d2->bits.liquid_type && d2->bits.flow_size>0)) + if((d->bits.liquid_type && d->bits.flow_size>0)|| + ( + (shape==df::tiletype_shape::EMPTY || shape==df::tiletype_shape::RAMP_TOP || shape==df::tiletype_shape::STAIR_DOWN || shape==df::tiletype_shape::STAIR_UPDOWN ) + && d2 && d2->bits.liquid_type && d2->bits.flow_size>0) + ) { - lightSource lava={lightCell(0.8f,0.2f,0.2f),5,coord2d(wx,wy)}; - lights.push_back(lava); + lightSource lava(lightCell(0.8f,0.2f,0.2f),5); + addLight(tile,lava); } if(d->bits.outside && d->bits.flow_size==0) { - curCell=lightCell(-1,-1,-1);//Marking as outside so no calculation is done on it + lightSource sun(lightCell(1,1,1),15); + addLight(tile,sun); } } @@ -354,6 +391,7 @@ void lightingEngineViewscreen::doOcupancyAndLights() df::coord2d pos=f->pos; int wx=pos.x-window_x+vp.first.x; int wy=pos.y-window_y+vp.first.y; + int tile=getIndex(wx,wy); if(wx>=vp.first.x && wy>=vp.first.y && wx<=vp.second.x && wy<=vp.second.y) { lightCell fireColor; @@ -369,10 +407,19 @@ void lightingEngineViewscreen::doOcupancyAndLights() { fireColor=lightCell(0.64f,0.0f,0.0f); } - lightSource fire={fireColor,f->density/5,coord2d(wx,wy)}; - lights.push_back(fire); + lightSource fire(fireColor,f->density/5); + addLight(tile,fire); } } } } + if(df::global::cursor->x>-30000) + { + lightSource cursor(lightCell(0.96f,0.84f,0.03f),11); + cursor.flicker=true; + int wx=df::global::cursor->x-window_x+vp.first.x; + int wy=df::global::cursor->y-window_y+vp.first.y; + int tile=getIndex(wx,wy); + addLight(tile,cursor); + } } \ No newline at end of file diff --git a/plugins/rendermax/renderer_light.hpp b/plugins/rendermax/renderer_light.hpp index d0279fd02..541e4280f 100644 --- a/plugins/rendermax/renderer_light.hpp +++ b/plugins/rendermax/renderer_light.hpp @@ -79,7 +79,21 @@ struct lightSource { lightCell power; int radius; - df::coord2d pos; + bool flicker; + lightSource():power(0,0,0),radius(0),flicker(false) + { + + } + lightSource(lightCell power,int radius):power(power),radius(radius),flicker(false) + { + + } + float powerSquared()const + { + return power.r*power.r+power.g*power.g+power.b*power.b; + } + void combine(const lightSource& other); + }; class lightingEngineViewscreen:public lightingEngine { @@ -96,6 +110,7 @@ private: void doRay(lightCell power,int cx,int cy,int tx,int ty); void doFovs(); bool lightUpCell(lightCell& power,int dx,int dy,int tx,int ty); + bool addLight(int tileId,const lightSource& light); size_t inline getIndex(int x,int y) { return x*h+y; diff --git a/plugins/rendermax/renderer_opengl.hpp b/plugins/rendermax/renderer_opengl.hpp index aa14a8c70..6d21a8b4c 100644 --- a/plugins/rendermax/renderer_opengl.hpp +++ b/plugins/rendermax/renderer_opengl.hpp @@ -222,6 +222,10 @@ struct lightCell { return lightCell(r+other.r,g+other.g,b+other.b); } + bool operator<(const lightCell& other) + { + return r Date: Sun, 23 Jun 2013 21:29:03 +0300 Subject: [PATCH 018/676] Fixed non-circular light. Do not show hidden tiles. --- plugins/rendermax/renderer_light.cpp | 23 +++++++++++++++-------- plugins/rendermax/renderer_light.hpp | 2 +- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index 4ca686983..7c0a20bc4 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -108,6 +108,7 @@ bool lightingEngineViewscreen::lightUpCell(lightCell& power,int dx,int dy,int tx { size_t tile=getIndex(tx,ty); float dsq=dx*dx+dy*dy; + float dt=sqrt(dsq); lightCell& v=ocupancy[tile]; lightSource& ls=lights[tile]; bool wallhack=false; @@ -116,9 +117,9 @@ bool lightingEngineViewscreen::lightUpCell(lightCell& power,int dx,int dy,int tx if (dsq>0 && !wallhack) { - power.r=power.r*(pow(v.r,dsq)); - power.g=power.g*(pow(v.g,dsq)); - power.b=power.b*(pow(v.b,dsq)); + power.r=power.r*(pow(v.r,dt)); + power.g=power.g*(pow(v.g,dt)); + power.b=power.b*(pow(v.b,dt)); } if(ls.radius>0 && dsq>0) { @@ -189,6 +190,7 @@ void lightingEngineViewscreen::updateWindow() if(lightMap.size()!=myRenderer->lightGrid.size()) { reinit(); + myRenderer->invalidate(); return; } std::swap(lightMap,myRenderer->lightGrid); @@ -216,7 +218,9 @@ bool lightingEngineViewscreen::addLight(int tileId,const lightSource& light) } void lightingEngineViewscreen::doOcupancyAndLights() { - + lightSource sun(lightCell(1,1,1),15); + lightSource lava(lightCell(0.8f,0.2f,0.2f),5); + rect2d vp=getMapViewport(); int window_x=*df::global::window_x; @@ -306,15 +310,18 @@ void lightingEngineViewscreen::doOcupancyAndLights() curCell=lightCell(0.85f,0.85f,0.85f); df::tiletype* type = Maps::getTileType(x,y,window_z); if(!type) + { + //unallocated, do sky + addLight(tile,sun); continue; + } df::tiletype_shape shape = ENUM_ATTR(tiletype,shape,*type); df::tile_designation* d=Maps::getTileDesignation(x,y,window_z); df::tile_designation* d2=Maps::getTileDesignation(x,y,window_z-1); df::tile_occupancy* o=Maps::getTileOccupancy(x,y,window_z); if(!o || !d ) continue; - - if(shape==df::tiletype_shape::BROOK_BED || shape==df::tiletype_shape::WALL || shape==df::tiletype_shape::TREE /*|| o->bits.building*/) + if(shape==df::tiletype_shape::BROOK_BED || shape==df::tiletype_shape::WALL || shape==df::tiletype_shape::TREE || d->bits.hidden ) { curCell=lightCell(0,0,0); } @@ -366,12 +373,12 @@ void lightingEngineViewscreen::doOcupancyAndLights() && d2 && d2->bits.liquid_type && d2->bits.flow_size>0) ) { - lightSource lava(lightCell(0.8f,0.2f,0.2f),5); + addLight(tile,lava); } if(d->bits.outside && d->bits.flow_size==0) { - lightSource sun(lightCell(1,1,1),15); + addLight(tile,sun); } diff --git a/plugins/rendermax/renderer_light.hpp b/plugins/rendermax/renderer_light.hpp index 541e4280f..9520380e1 100644 --- a/plugins/rendermax/renderer_light.hpp +++ b/plugins/rendermax/renderer_light.hpp @@ -59,7 +59,7 @@ public: }; virtual void resize(int32_t w, int32_t h) { renderer_wrap::resize(w,h); - reinitLightGrid(w,h); + reinitLightGrid(); } }; class lightingEngine From 1c4b5c2d385a2f4fb3357057b8803734b9cdfd88 Mon Sep 17 00:00:00 2001 From: Warmist Date: Sun, 23 Jun 2013 21:49:17 +0300 Subject: [PATCH 019/676] Update water a bit. --- plugins/rendermax/renderer_light.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index 7c0a20bc4..0141abda8 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -362,7 +362,7 @@ void lightingEngineViewscreen::doOcupancyAndLights() } else if(!d->bits.liquid_type && d->bits.flow_size>3 ) { - curCell=lightCell(0.5f,0.5f,0.6f); + curCell*=lightCell(0.7f,0.7f,0.8f); } //todo constructions From 471eb9c02d536f376a5a1204f12f83b9fa6da5b1 Mon Sep 17 00:00:00 2001 From: Warmist Date: Sun, 23 Jun 2013 22:28:26 +0300 Subject: [PATCH 020/676] Torches for statues, candles on the tables. --- plugins/rendermax/renderer_light.cpp | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index 0141abda8..72253e442 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -220,7 +220,9 @@ void lightingEngineViewscreen::doOcupancyAndLights() { lightSource sun(lightCell(1,1,1),15); lightSource lava(lightCell(0.8f,0.2f,0.2f),5); - + lightSource candle(lightCell(0.96f,0.84f,0.03f),5); + candle.flicker=true; + lightSource torch(lightCell(0.96f,0.5f,0.1f),8); rect2d vp=getMapViewport(); int window_x=*df::global::window_x; @@ -356,6 +358,25 @@ void lightingEngineViewscreen::doOcupancyAndLights() curCell*=lightCell(0.75f,0.95f,0.95f); } } + if (type == df::enums::building_type::Table) + { + addLight(tile,candle); + } + if (type==df::enums::building_type::Statue) + { + addLight(tile,torch); + } + if (type==df::enums::building_type::WindowGem) + { + DFHack::MaterialInfo mat(bld->mat_index,bld->mat_type); + if(mat.isInorganic()) + { + int color=mat.inorganic->material.basic_color[0]; + curCell*=lightCell(df::global::enabler->ccolor[color][0]/255.0f, + df::global::enabler->ccolor[color][1]/255.0f, + df::global::enabler->ccolor[color][2]/255.0f); + } + } } } } @@ -364,7 +385,6 @@ void lightingEngineViewscreen::doOcupancyAndLights() { curCell*=lightCell(0.7f,0.7f,0.8f); } - //todo constructions //lights if((d->bits.liquid_type && d->bits.flow_size>0)|| @@ -388,6 +408,7 @@ void lightingEngineViewscreen::doOcupancyAndLights() for(int blocky=window_y/16;blocky<=endBlocky;blocky++) { df::map_block* block=Maps::getBlock(blockx,blocky,window_z); + if(!block) continue; for(int i=0;iflows.size();i++) From ee7902c5294acb15abfe9c8d5577e68fd7e45afb Mon Sep 17 00:00:00 2001 From: Warmist Date: Sun, 23 Jun 2013 23:56:01 +0300 Subject: [PATCH 021/676] Fixed unload/disable crash. Added glowing plants. --- plugins/rendermax/renderer_light.cpp | 71 +++++++++++++++++++++++++--- plugins/rendermax/renderer_light.hpp | 8 +++- plugins/rendermax/rendermax.cpp | 49 ++++++++++--------- 3 files changed, 97 insertions(+), 31 deletions(-) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index 72253e442..12fde09aa 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -1,6 +1,7 @@ #include "renderer_light.hpp" #include +#include #include "Types.h" @@ -13,7 +14,9 @@ #include "df/flow_info.h" #include "df/world.h" #include "df/building.h" - +#include "df/building_doorst.h" +#include "df/plant.h" +#include "df/plant_raw.h" using df::global::gps; using namespace DFHack; @@ -54,6 +57,7 @@ rect2d getMapViewport() lightingEngineViewscreen::lightingEngineViewscreen(renderer_light* target):lightingEngine(target) { reinit(); + initRawSpecific(); } void lightingEngineViewscreen::reinit() @@ -216,15 +220,47 @@ bool lightingEngineViewscreen::addLight(int tileId,const lightSource& light) lights[tileId].flicker=true; return wasLight; } +lightCell getStandartColor(int colorId) +{ + return lightCell(df::global::enabler->ccolor[colorId][0]/255.0f, + df::global::enabler->ccolor[colorId][1]/255.0f, + df::global::enabler->ccolor[colorId][2]/255.0f); +} +int getPlantNumber(const std::string& id) +{ + std::vector& vec=df::plant_raw::get_vector(); + for(int i=0;iid==id) + return i; + } + return -1; +} +void addPlant(const std::string& id,std::map& map,const lightSource& v) +{ + int nId=getPlantNumber(id); + if(nId>0) + { + map[nId]=v; + } +} +void lightingEngineViewscreen::initRawSpecific() +{ + addPlant("TOWER_CAP",glowPlants,lightSource(lightCell(0.65,0.65,0.65),6)); + addPlant("MUSHROOM_CUP_DIMPLE",glowPlants,lightSource(lightCell(0.03,0.03,0.9),3)); + addPlant("CAVE MOSS",glowPlants,lightSource(lightCell(0.3,0.3,0.9),2)); + addPlant("MUSHROOM_HELMET_PLUMP",glowPlants,lightSource(lightCell(0.5,0.2,0.9),2)); +} +static size_t max_list_size = 100000; // Avoid iterating over huge lists void lightingEngineViewscreen::doOcupancyAndLights() { lightSource sun(lightCell(1,1,1),15); lightSource lava(lightCell(0.8f,0.2f,0.2f),5); lightSource candle(lightCell(0.96f,0.84f,0.03f),5); - candle.flicker=true; - lightSource torch(lightCell(0.96f,0.5f,0.1f),8); + lightSource torch(lightCell(0.9f,0.75f,0.3f),8); rect2d vp=getMapViewport(); + int window_x=*df::global::window_x; int window_y=*df::global::window_y; int window_z=*df::global::window_z; @@ -371,12 +407,16 @@ void lightingEngineViewscreen::doOcupancyAndLights() DFHack::MaterialInfo mat(bld->mat_index,bld->mat_type); if(mat.isInorganic()) { - int color=mat.inorganic->material.basic_color[0]; - curCell*=lightCell(df::global::enabler->ccolor[color][0]/255.0f, - df::global::enabler->ccolor[color][1]/255.0f, - df::global::enabler->ccolor[color][2]/255.0f); + int color=mat.inorganic->material.build_color[0]+8*mat.inorganic->material.build_color[2]; + curCell*=getStandartColor(color); } } + if(type==df::enums::building_type::Door) + { + df::building_doorst* door=static_cast(bld); + if(door->door_flags.bits.closed) + curCell*=lightCell(0,0,0); + } } } } @@ -440,6 +480,23 @@ void lightingEngineViewscreen::doOcupancyAndLights() } } } + for(int i=0;iplants.size();i++) + { + df::plant* cPlant=block->plants[i]; + + df::coord2d pos=cPlant->pos; + int wx=pos.x-window_x+vp.first.x; + int wy=pos.y-window_y+vp.first.y; + int tile=getIndex(wx,wy); + if(wx>=vp.first.x && wy>=vp.first.y && wx<=vp.second.x && wy<=vp.second.y) + { + auto it=glowPlants.find(cPlant->material); + if(it!=glowPlants.end()) + { + addLight(tile,it->second); + } + } + } } if(df::global::cursor->x>-30000) { diff --git a/plugins/rendermax/renderer_light.hpp b/plugins/rendermax/renderer_light.hpp index 9520380e1..3744fd5bc 100644 --- a/plugins/rendermax/renderer_light.hpp +++ b/plugins/rendermax/renderer_light.hpp @@ -2,7 +2,7 @@ #define RENDERER_LIGHT_INCLUDED #include "renderer_opengl.hpp" #include "Types.h" - +#include struct renderer_light : public renderer_wrap { private: void colorizeTile(int x,int y) @@ -111,6 +111,7 @@ private: void doFovs(); bool lightUpCell(lightCell& power,int dx,int dy,int tx,int ty); bool addLight(int tileId,const lightSource& light); + void initRawSpecific(); size_t inline getIndex(int x,int y) { return x*h+y; @@ -118,6 +119,11 @@ private: std::vector lightMap; std::vector ocupancy; std::vector lights; + + + std::map glowPlants; + std::map glowVeins; + int w,h; DFHack::rect2d mapPort; }; diff --git a/plugins/rendermax/rendermax.cpp b/plugins/rendermax/rendermax.cpp index 0a61d1e4a..acfa9f92c 100644 --- a/plugins/rendermax/rendermax.cpp +++ b/plugins/rendermax/rendermax.cpp @@ -20,7 +20,7 @@ using std::vector; using std::string; enum RENDERER_MODE { - MODE_DEFAULT,MODE_TRIPPY,MODE_TRUECOLOR,MODE_LUA,MODE_LIGHT + MODE_DEFAULT,MODE_TRIPPY,MODE_TRUECOLOR,MODE_LUA,MODE_LIGHT,MODE_LIGHT_OFF }; RENDERER_MODE current_mode=MODE_DEFAULT; lightingEngine *engine=NULL; @@ -47,12 +47,6 @@ void removeOld() if(current_mode!=MODE_DEFAULT) delete df::global::enabler->renderer; current_mode=MODE_DEFAULT; - if(current_mode==MODE_LIGHT) - { - if(engine) - delete engine; - engine=0; - } } void installNew(df::renderer* r,RENDERER_MODE newMode) { @@ -241,6 +235,11 @@ static command_result rendermax(color_ostream &out, vector & parameters { if(parameters.size()==0) return CR_WRONG_USAGE; + if(!df::global::enabler->renderer->uses_opengl()) + { + out.printerr("Sorry, this plugin needs open gl enabled printmode. Try STANDARD or other non-2d"); + return CR_FAILURE; + } string cmd=parameters[0]; if(cmd=="trippy") { @@ -305,26 +304,20 @@ static command_result rendermax(color_ostream &out, vector & parameters } else if(cmd=="light") { - if(current_mode==MODE_LIGHT) - { - engine->calculate(); - engine->updateWindow(); - } - else - { - removeOld(); - renderer_light *myRender=new renderer_light(df::global::enabler->renderer); - installNew(myRender,MODE_LIGHT); - engine=new lightingEngineViewscreen(myRender); - engine->calculate(); - engine->updateWindow(); - } + removeOld(); + renderer_light *myRender=new renderer_light(df::global::enabler->renderer); + installNew(myRender,MODE_LIGHT); + engine=new lightingEngineViewscreen(myRender); + engine->calculate(); + engine->updateWindow(); return CR_OK; } else if(cmd=="disable") { if(current_mode==MODE_DEFAULT) out.print("%s\n","Not installed, doing nothing."); + else if(current_mode==MODE_LIGHT) + current_mode=MODE_LIGHT_OFF; else removeOld(); @@ -336,9 +329,19 @@ DFhackCExport command_result plugin_onupdate (color_ostream &out) { if(engine) { - engine->calculate(); - engine->updateWindow(); + if(current_mode==MODE_LIGHT_OFF) + { + delete engine; + engine=0; + removeOld(); + } + else + { + engine->calculate(); + engine->updateWindow(); + } } + return CR_OK; } DFhackCExport command_result plugin_shutdown(color_ostream &) From 83265efaa8d9f493001616a9a80b093588a9842a Mon Sep 17 00:00:00 2001 From: Warmist Date: Mon, 24 Jun 2013 00:10:52 +0300 Subject: [PATCH 022/676] fix for map_block lights, fix for gem window color, dimmed the plant lights. --- plugins/rendermax/renderer_light.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index 12fde09aa..ef282882a 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -247,9 +247,9 @@ void addPlant(const std::string& id,std::map& map,const lightSo void lightingEngineViewscreen::initRawSpecific() { addPlant("TOWER_CAP",glowPlants,lightSource(lightCell(0.65,0.65,0.65),6)); - addPlant("MUSHROOM_CUP_DIMPLE",glowPlants,lightSource(lightCell(0.03,0.03,0.9),3)); - addPlant("CAVE MOSS",glowPlants,lightSource(lightCell(0.3,0.3,0.9),2)); - addPlant("MUSHROOM_HELMET_PLUMP",glowPlants,lightSource(lightCell(0.5,0.2,0.9),2)); + addPlant("MUSHROOM_CUP_DIMPLE",glowPlants,lightSource(lightCell(0.03,0.03,0.5),3)); + addPlant("CAVE MOSS",glowPlants,lightSource(lightCell(0.1,0.1,0.4),2)); + addPlant("MUSHROOM_HELMET_PLUMP",glowPlants,lightSource(lightCell(0.2,0.1,0.6),2)); } static size_t max_list_size = 100000; // Avoid iterating over huge lists void lightingEngineViewscreen::doOcupancyAndLights() @@ -407,7 +407,7 @@ void lightingEngineViewscreen::doOcupancyAndLights() DFHack::MaterialInfo mat(bld->mat_index,bld->mat_type); if(mat.isInorganic()) { - int color=mat.inorganic->material.build_color[0]+8*mat.inorganic->material.build_color[2]; + int color=mat.inorganic->material.basic_color[0]+8*mat.inorganic->material.basic_color[2]; curCell*=getStandartColor(color); } } From f745de3afff651302bdb6b321d42740e166f8bca Mon Sep 17 00:00:00 2001 From: Warmist Date: Mon, 24 Jun 2013 00:36:43 +0300 Subject: [PATCH 023/676] Added transparency to ice walls --- plugins/rendermax/renderer_light.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index ef282882a..05a474170 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -245,7 +245,7 @@ void addPlant(const std::string& id,std::map& map,const lightSo } } void lightingEngineViewscreen::initRawSpecific() -{ +{ addPlant("TOWER_CAP",glowPlants,lightSource(lightCell(0.65,0.65,0.65),6)); addPlant("MUSHROOM_CUP_DIMPLE",glowPlants,lightSource(lightCell(0.03,0.03,0.5),3)); addPlant("CAVE MOSS",glowPlants,lightSource(lightCell(0.1,0.1,0.4),2)); @@ -357,11 +357,15 @@ void lightingEngineViewscreen::doOcupancyAndLights() df::tile_designation* d=Maps::getTileDesignation(x,y,window_z); df::tile_designation* d2=Maps::getTileDesignation(x,y,window_z-1); df::tile_occupancy* o=Maps::getTileOccupancy(x,y,window_z); + df::tiletype_material m=ENUM_ATTR(tiletype,material,*type); if(!o || !d ) continue; if(shape==df::tiletype_shape::BROOK_BED || shape==df::tiletype_shape::WALL || shape==df::tiletype_shape::TREE || d->bits.hidden ) { - curCell=lightCell(0,0,0); + if(shape==df::tiletype_shape::WALL && m==df::tiletype_material::FROZEN_LIQUID) + curCell*=lightCell(0.7,0.7,0.9); + else + curCell=lightCell(0,0,0); } else if(o->bits.building) { @@ -425,7 +429,6 @@ void lightingEngineViewscreen::doOcupancyAndLights() { curCell*=lightCell(0.7f,0.7f,0.8f); } - //lights if((d->bits.liquid_type && d->bits.flow_size>0)|| ( From 2195c4fe280ae02008bf9b78a8ea94ae9b32c5a6 Mon Sep 17 00:00:00 2001 From: Warmist Date: Mon, 24 Jun 2013 00:53:39 +0300 Subject: [PATCH 024/676] Small fixes --- plugins/rendermax/renderer_light.cpp | 44 +++++++++++++--------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index 05a474170..4ee960562 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -286,37 +286,35 @@ void lightingEngineViewscreen::doOcupancyAndLights() { df::map_block* block=Maps::getBlock(blockx,blocky,ZZ); totalBlank = 0; + if(block) for(int block_x = 0; block_x < 16; block_x++) for(int block_y = 0; block_y < 16; block_y++) { - if(block) - { - df::tiletype type = block->tiletype[block_x][block_y]; - df::tile_designation d = block->designation[block_x][block_y]; - df::tile_occupancy o = block->occupancy[block_x][block_y]; - df::tiletype_shape shape = ENUM_ATTR(tiletype,shape,type); - df::tiletype_shape_basic basic_shape = ENUM_ATTR(tiletype_shape, basic_shape, shape); + df::tiletype type = block->tiletype[block_x][block_y]; + df::tile_designation d = block->designation[block_x][block_y]; + df::tile_occupancy o = block->occupancy[block_x][block_y]; + df::tiletype_shape shape = ENUM_ATTR(tiletype,shape,type); + df::tiletype_shape_basic basic_shape = ENUM_ATTR(tiletype_shape, basic_shape, shape); - if(basic_shape==df::tiletype_shape_basic::Wall) - { - cellArray[block_x][block_y]=lightCell(0,0,0); - } - else if(basic_shape==df::tiletype_shape_basic::Floor || basic_shape==df::tiletype_shape_basic::Ramp || basic_shape==df::tiletype_shape_basic::Stair) - { - if(ZZ!=window_z) - { - cellArray[block_x][block_y]=lightCell(0,0,0); - } - } - if(d.bits.liquid_type == df::enums::tile_liquid::Water && d.bits.flow_size) - { - cellArray[block_x][block_y] *= (lightCell(1,1,1) - (lightCell(1,1,1) - lightCell(0.63f,0.63f,0.75f))*(d.bits.flow_size/7)); - } - else if(d.bits.liquid_type == df::enums::tile_liquid::Magma && d.bits.flow_size > 3) + if(basic_shape==df::tiletype_shape_basic::Wall) + { + cellArray[block_x][block_y]=lightCell(0,0,0); + } + else if(basic_shape==df::tiletype_shape_basic::Floor || basic_shape==df::tiletype_shape_basic::Ramp || basic_shape==df::tiletype_shape_basic::Stair) + { + if(ZZ!=window_z) { cellArray[block_x][block_y]=lightCell(0,0,0); } } + if(d.bits.liquid_type == df::enums::tile_liquid::Water && d.bits.flow_size) + { + cellArray[block_x][block_y] *= (lightCell(1,1,1) - (lightCell(1,1,1) - lightCell(0.63f,0.63f,0.75f))*(d.bits.flow_size/7)); + } + else if(d.bits.liquid_type == df::enums::tile_liquid::Magma && d.bits.flow_size > 3) + { + cellArray[block_x][block_y]=lightCell(0,0,0); + } if(cellArray[block_x][block_y].r < 0.003f && cellArray[block_x][block_y].g < 0.003f && cellArray[block_x][block_y].b < 0.003f) totalBlank++; } From f173810e2aa8b80d7e969d6cedde3d379527e935 Mon Sep 17 00:00:00 2001 From: Warmist Date: Mon, 24 Jun 2013 01:26:25 +0300 Subject: [PATCH 025/676] Fixed skylight --- plugins/rendermax/renderer_light.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index 4ee960562..54de4008c 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -282,7 +282,7 @@ void lightingEngineViewscreen::doOcupancyAndLights() } int totalBlank = 0; int topLevel = df::global::world->map.z_count-1; - for(int ZZ = topLevel; (ZZ >= window_z) && totalBlank < 257; ZZ--) + for(int ZZ = topLevel; (ZZ >= window_z) && totalBlank < 256; ZZ--) { df::map_block* block=Maps::getBlock(blockx,blocky,ZZ); totalBlank = 0; @@ -327,7 +327,7 @@ void lightingEngineViewscreen::doOcupancyAndLights() pos.y = blocky*16+block_y; int wx=pos.x-window_x+vp.first.x; int wy=pos.y-window_y+vp.first.y; - + if(wx>=vp.first.x && wy>=vp.first.y && wx<=vp.second.x && wy<=vp.second.y) if(cellArray[block_x][block_y].r >= 0.003f && cellArray[block_x][block_y].g >= 0.003f && cellArray[block_x][block_y].b >= 0.003f) { lightSource sun={cellArray[block_x][block_y],25,coord2d(wx,wy)}; From 31de296f3138ffe4beec73419390390f8bb98f57 Mon Sep 17 00:00:00 2001 From: Japa Date: Mon, 24 Jun 2013 05:10:01 +0530 Subject: [PATCH 026/676] Added some fast checking for obstacles around lights, resulting in a huge speedup outside. --- plugins/rendermax/renderer_light.cpp | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index 54de4008c..20ea4705f 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -168,8 +168,23 @@ void lightingEngineViewscreen::doFovs() radius*=flicker; power=power*flicker; } - plotCircle(i,j,radius, - std::bind(&lightingEngineViewscreen::doRay,this,power,i,j,_1,_2)); + int surrounds = 0; + lightCell curPower; + + if(lightUpCell(curPower = power, 0, 0,i+0, j+0)) + { + surrounds += lightUpCell(curPower = power, 0, 1,i+0, j+1); + surrounds += lightUpCell(curPower = power, 1, 1,i+1, j+1); + surrounds += lightUpCell(curPower = power, 1, 0,i+1, j+0); + surrounds += lightUpCell(curPower = power, 1,-1,i+1, j-1); + surrounds += lightUpCell(curPower = power, 0,-1,i+0, j-1); + surrounds += lightUpCell(curPower = power,-1,-1,i-1, j-1); + surrounds += lightUpCell(curPower = power,-1, 0,i-1, j+0); + surrounds += lightUpCell(curPower = power,-1, 1,i-1, j+1); + } + if(surrounds) + plotCircle(i,j,radius, + std::bind(&lightingEngineViewscreen::doRay,this,power,i,j,_1,_2)); } } } From 0a413e179e1818fb6bbb613d16601cb02ebf6da7 Mon Sep 17 00:00:00 2001 From: Japa Date: Mon, 24 Jun 2013 05:35:16 +0530 Subject: [PATCH 027/676] Made the low water check actually use floats. --- plugins/rendermax/renderer_light.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index 20ea4705f..7cd8e980f 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -324,7 +324,7 @@ void lightingEngineViewscreen::doOcupancyAndLights() } if(d.bits.liquid_type == df::enums::tile_liquid::Water && d.bits.flow_size) { - cellArray[block_x][block_y] *= (lightCell(1,1,1) - (lightCell(1,1,1) - lightCell(0.63f,0.63f,0.75f))*(d.bits.flow_size/7)); + cellArray[block_x][block_y] *= (lightCell(1,1,1) - (lightCell(1,1,1) - lightCell(0.63f,0.63f,0.75f))*((float)d.bits.flow_size/7.0f)); } else if(d.bits.liquid_type == df::enums::tile_liquid::Magma && d.bits.flow_size > 3) { @@ -499,7 +499,7 @@ void lightingEngineViewscreen::doOcupancyAndLights() for(int i=0;iplants.size();i++) { df::plant* cPlant=block->plants[i]; - + df::coord2d pos=cPlant->pos; int wx=pos.x-window_x+vp.first.x; int wy=pos.y-window_y+vp.first.y; From 2058a822a33e09816f139a4ae15be6ae30405831 Mon Sep 17 00:00:00 2001 From: jj Date: Mon, 24 Jun 2013 00:45:45 +0200 Subject: [PATCH 028/676] rendermax: make citizen glow --- plugins/rendermax/renderer_light.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index 7cd8e980f..091d1c68f 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -2,12 +2,14 @@ #include #include +#include #include "Types.h" #include "modules/Gui.h" #include "modules/Screen.h" #include "modules/Maps.h" +#include "modules/Units.h" #include "df/graphic.h" #include "df/viewscreen_dwarfmodest.h" @@ -523,4 +525,15 @@ void lightingEngineViewscreen::doOcupancyAndLights() int tile=getIndex(wx,wy); addLight(tile,cursor); } -} \ No newline at end of file + lightSource citizen(lightCell(0.80f,0.80f,0.90f),6); + for (int i=0;iunits.active.size();++i) + { + df::unit *u = df::global::world->units.active[i]; + if (u->pos.z != window_z || + (u->pos.x < window_x || u->pos.x >= window_x+vpW) || + (u->pos.y < window_y || u->pos.y >= window_y+vpH)) + continue; + if (DFHack::Units::isCitizen(u)) + addLight(getIndex(u->pos.x-window_x+1, u->pos.y-window_y+1),citizen); + } +} From 9a8ee755f8449f179a70c3d7f670e2e2eceb922a Mon Sep 17 00:00:00 2001 From: jj Date: Mon, 24 Jun 2013 01:54:09 +0200 Subject: [PATCH 029/676] rendermax: basic daylight --- plugins/rendermax/renderer_light.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index 091d1c68f..753fefcba 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -271,13 +271,16 @@ void lightingEngineViewscreen::initRawSpecific() static size_t max_list_size = 100000; // Avoid iterating over huge lists void lightingEngineViewscreen::doOcupancyAndLights() { - lightSource sun(lightCell(1,1,1),15); + // TODO better curve (+red dawn ?) + float daycol = abs((*df::global::cur_year_tick % 1200) - 600.0) / 600.0; + lightCell sky_col(daycol, daycol, daycol); + lightSource sky(sky_col, 15); + lightSource lava(lightCell(0.8f,0.2f,0.2f),5); lightSource candle(lightCell(0.96f,0.84f,0.03f),5); lightSource torch(lightCell(0.9f,0.75f,0.3f),8); rect2d vp=getMapViewport(); - - + int window_x=*df::global::window_x; int window_y=*df::global::window_y; int window_z=*df::global::window_z; @@ -295,7 +298,7 @@ void lightingEngineViewscreen::doOcupancyAndLights() for(int block_x = 0; block_x < 16; block_x++) for(int block_y = 0; block_y < 16; block_y++) { - cellArray[block_x][block_y] = lightCell(1,1,1); + cellArray[block_x][block_y] = sky_col; } int totalBlank = 0; int topLevel = df::global::world->map.z_count-1; @@ -365,7 +368,7 @@ void lightingEngineViewscreen::doOcupancyAndLights() if(!type) { //unallocated, do sky - addLight(tile,sun); + addLight(tile,sky); continue; } df::tiletype_shape shape = ENUM_ATTR(tiletype,shape,*type); @@ -456,8 +459,7 @@ void lightingEngineViewscreen::doOcupancyAndLights() } if(d->bits.outside && d->bits.flow_size==0) { - - addLight(tile,sun); + addLight(tile,sky); } } From 4abff416d9a5bb8d035f6e417cbc201c22f3bdf5 Mon Sep 17 00:00:00 2001 From: Japa Date: Mon, 24 Jun 2013 14:20:22 +0530 Subject: [PATCH 030/676] Some performance fixes and tweaks. Opaque blocks can now emit light. --- plugins/rendermax/renderer_light.cpp | 22 ++++++++++++++++------ plugins/rendermax/renderer_light.hpp | 2 +- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index 753fefcba..c6b8f67e0 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -25,6 +25,7 @@ using namespace DFHack; using df::coord2d; const float levelDim=0.2f; +const float RootTwo = 1.4142135623730950488016887242097f; rect2d getMapViewport() { @@ -94,8 +95,9 @@ void plotLine(int x0, int y0, int x1, int y1,std::function=mapPort.first.x && ty>=mapPort.first.y && tx<=mapPort.second.x && ty<=mapPort.second.y) { size_t tile=getIndex(tx,ty); - float dsq=dx*dx+dy*dy; - float dt=sqrt(dsq); + int dsq=dx*dx+dy*dy; + float dt=1; + if(dsq == 1) + dt=1; + else if(dsq == 2) + dt = RootTwo; + else if(dsq == 0) + dt = 0; + else + dt=sqrt((float)dsq); lightCell& v=ocupancy[tile]; lightSource& ls=lights[tile]; bool wallhack=false; @@ -173,7 +183,7 @@ void lightingEngineViewscreen::doFovs() int surrounds = 0; lightCell curPower; - if(lightUpCell(curPower = power, 0, 0,i+0, j+0)) + lightUpCell(curPower = power, 0, 0,i+0, j+0); { surrounds += lightUpCell(curPower = power, 0, 1,i+0, j+1); surrounds += lightUpCell(curPower = power, 1, 1,i+1, j+1); @@ -521,7 +531,7 @@ void lightingEngineViewscreen::doOcupancyAndLights() if(df::global::cursor->x>-30000) { lightSource cursor(lightCell(0.96f,0.84f,0.03f),11); - cursor.flicker=true; + cursor.flicker=false; int wx=df::global::cursor->x-window_x+vp.first.x; int wy=df::global::cursor->y-window_y+vp.first.y; int tile=getIndex(wx,wy); diff --git a/plugins/rendermax/renderer_light.hpp b/plugins/rendermax/renderer_light.hpp index 3744fd5bc..98ea502bd 100644 --- a/plugins/rendermax/renderer_light.hpp +++ b/plugins/rendermax/renderer_light.hpp @@ -127,4 +127,4 @@ private: int w,h; DFHack::rect2d mapPort; }; -#endif \ No newline at end of file +#endif From 4d7a7425d029b91520206930693d60b401c2f5a7 Mon Sep 17 00:00:00 2001 From: Warmist Date: Mon, 24 Jun 2013 03:47:42 +0300 Subject: [PATCH 031/676] Prelimenary work on advmode --- plugins/rendermax/renderer_light.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index c6b8f67e0..1c7c07ecb 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -13,6 +13,7 @@ #include "df/graphic.h" #include "df/viewscreen_dwarfmodest.h" +#include "df/viewscreen_dungeonmodest.h" #include "df/flow_info.h" #include "df/world.h" #include "df/building.h" @@ -32,7 +33,15 @@ rect2d getMapViewport() const int AREA_MAP_WIDTH = 23; const int MENU_WIDTH = 30; if(!gps || !df::viewscreen_dwarfmodest::_identity.is_instance(DFHack::Gui::getCurViewscreen())) - return mkrect_wh(0,0,0,0); + { + if(gps && df::viewscreen_dungeonmodest::_identity.is_instance(DFHack::Gui::getCurViewscreen())) + { + return mkrect_wh(0,0,gps->dimx,gps->dimy); + } + else + return mkrect_wh(0,0,0,0); + + } int w=gps->dimx; int h=gps->dimy; int view_height=h-2; From de8a76e37f327ef7c8ab0c3d8d375be4c3fcd0c9 Mon Sep 17 00:00:00 2001 From: jj Date: Mon, 24 Jun 2013 02:22:30 +0200 Subject: [PATCH 032/676] rendermax: sunlight travels down stairs --- plugins/rendermax/renderer_light.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index 1c7c07ecb..13904f5dc 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -339,13 +339,17 @@ void lightingEngineViewscreen::doOcupancyAndLights() { cellArray[block_x][block_y]=lightCell(0,0,0); } - else if(basic_shape==df::tiletype_shape_basic::Floor || basic_shape==df::tiletype_shape_basic::Ramp || basic_shape==df::tiletype_shape_basic::Stair) + else if(basic_shape==df::tiletype_shape_basic::Floor || basic_shape==df::tiletype_shape_basic::Ramp || shape==df::tiletype_shape::STAIR_UP) { if(ZZ!=window_z) { cellArray[block_x][block_y]=lightCell(0,0,0); } } + else if(shape==df::tiletype_shape::STAIR_DOWN || shape==df::tiletype_shape::STAIR_UPDOWN) + { + cellArray[block_x][block_y]*=lightCell(0.9,0.9,0.9); + } if(d.bits.liquid_type == df::enums::tile_liquid::Water && d.bits.flow_size) { cellArray[block_x][block_y] *= (lightCell(1,1,1) - (lightCell(1,1,1) - lightCell(0.63f,0.63f,0.75f))*((float)d.bits.flow_size/7.0f)); From 20d4707b009975a926ef7f71a501529d44cbcbd9 Mon Sep 17 00:00:00 2001 From: jj Date: Mon, 24 Jun 2013 02:27:43 +0200 Subject: [PATCH 033/676] rendermax: lights off when sleeping --- plugins/rendermax/renderer_light.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index 13904f5dc..a5ee756dd 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -558,7 +558,7 @@ void lightingEngineViewscreen::doOcupancyAndLights() (u->pos.x < window_x || u->pos.x >= window_x+vpW) || (u->pos.y < window_y || u->pos.y >= window_y+vpH)) continue; - if (DFHack::Units::isCitizen(u)) + if (DFHack::Units::isCitizen(u) && !u->counters.unconscious) addLight(getIndex(u->pos.x-window_x+1, u->pos.y-window_y+1),citizen); } } From 74bed5529b452add9b6f79189833a9c309883653 Mon Sep 17 00:00:00 2001 From: Warmist Date: Mon, 24 Jun 2013 18:18:57 +0300 Subject: [PATCH 034/676] Made light plugin read configuration file (rendermax.lua) --- plugins/rendermax/renderer_light.cpp | 237 ++++++++++++++++++++++----- plugins/rendermax/renderer_light.hpp | 50 +++++- plugins/rendermax/rendermax.cpp | 23 ++- 3 files changed, 259 insertions(+), 51 deletions(-) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index a5ee756dd..f50f2e016 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -5,6 +5,7 @@ #include #include "Types.h" +#include "LuaTools.h" #include "modules/Gui.h" #include "modules/Screen.h" @@ -69,7 +70,8 @@ rect2d getMapViewport() lightingEngineViewscreen::lightingEngineViewscreen(renderer_light* target):lightingEngine(target) { reinit(); - initRawSpecific(); + defaultSettings(); + loadSettings(); } void lightingEngineViewscreen::reinit() @@ -280,24 +282,43 @@ void addPlant(const std::string& id,std::map& map,const lightSo map[nId]=v; } } -void lightingEngineViewscreen::initRawSpecific() -{ - addPlant("TOWER_CAP",glowPlants,lightSource(lightCell(0.65,0.65,0.65),6)); - addPlant("MUSHROOM_CUP_DIMPLE",glowPlants,lightSource(lightCell(0.03,0.03,0.5),3)); - addPlant("CAVE MOSS",glowPlants,lightSource(lightCell(0.1,0.1,0.4),2)); - addPlant("MUSHROOM_HELMET_PLUMP",glowPlants,lightSource(lightCell(0.2,0.1,0.6),2)); +void lightingEngineViewscreen::applyMaterial(int tileId,const matLightDef& mat,float size) +{ + if(mat.isTransparent) + ocupancy[tileId]*=mat.transparency; + else + ocupancy[tileId]=lightCell(0,0,0); + if(mat.isEmiting) + addLight(tileId,mat.makeSource(size)); +} +bool lightingEngineViewscreen::applyMaterial(int tileId,int matType,int matIndex,float size,const matLightDef* def) +{ + auto it=matDefs.find(std::make_pair(matType,matIndex)); + if(it!=matDefs.end()) + { + applyMaterial(tileId,it->second,size); + return true; + } + else if(def) + { + applyMaterial(tileId,*def,size); + } + return false; } static size_t max_list_size = 100000; // Avoid iterating over huge lists void lightingEngineViewscreen::doOcupancyAndLights() { // TODO better curve (+red dawn ?) - float daycol = abs((*df::global::cur_year_tick % 1200) - 600.0) / 600.0; + float daycol = abs((*df::global::cur_year_tick % 1200) - 600.0) / 400.0; lightCell sky_col(daycol, daycol, daycol); lightSource sky(sky_col, 15); - lightSource lava(lightCell(0.8f,0.2f,0.2f),5); lightSource candle(lightCell(0.96f,0.84f,0.03f),5); lightSource torch(lightCell(0.9f,0.75f,0.3f),8); + + //perfectly blocking material + matLightDef matWall(lightCell(0,0,0)); + rect2d vp=getMapViewport(); int window_x=*df::global::window_x; @@ -386,7 +407,8 @@ void lightingEngineViewscreen::doOcupancyAndLights() int wy=y-window_y+vp.first.y; int tile=getIndex(wx,wy); lightCell& curCell=ocupancy[tile]; - curCell=lightCell(0.85f,0.85f,0.85f); + if(matAmbience.isTransparent) + curCell=matAmbience.transparency; df::tiletype* type = Maps::getTileType(x,y,window_z); if(!type) { @@ -403,8 +425,9 @@ void lightingEngineViewscreen::doOcupancyAndLights() continue; if(shape==df::tiletype_shape::BROOK_BED || shape==df::tiletype_shape::WALL || shape==df::tiletype_shape::TREE || d->bits.hidden ) { + //TODO split into wall ,etc... if(shape==df::tiletype_shape::WALL && m==df::tiletype_material::FROZEN_LIQUID) - curCell*=lightCell(0.7,0.7,0.9); + applyMaterial(tile,matIce); else curCell=lightCell(0,0,0); } @@ -424,20 +447,9 @@ void lightingEngineViewscreen::doOcupancyAndLights() { df::building_type type = bld->getType(); - if (type == df::enums::building_type::WindowGlass) + if (type == df::enums::building_type::WindowGlass || type==df::enums::building_type::WindowGem) { - if(bld->mat_type == 3)//green glass - { - curCell*=lightCell(0.1f,0.9f,0.5f); - } - else if(bld->mat_type == 4)//clear glass - { - curCell*=lightCell(0.5f,0.95f,0.9f); - } - else if(bld->mat_type == 5)//crystal glass - { - curCell*=lightCell(0.75f,0.95f,0.95f); - } + applyMaterial(tile,bld->mat_type,bld->mat_index); } if (type == df::enums::building_type::Table) { @@ -447,20 +459,11 @@ void lightingEngineViewscreen::doOcupancyAndLights() { addLight(tile,torch); } - if (type==df::enums::building_type::WindowGem) - { - DFHack::MaterialInfo mat(bld->mat_index,bld->mat_type); - if(mat.isInorganic()) - { - int color=mat.inorganic->material.basic_color[0]+8*mat.inorganic->material.basic_color[2]; - curCell*=getStandartColor(color); - } - } if(type==df::enums::building_type::Door) { df::building_doorst* door=static_cast(bld); if(door->door_flags.bits.closed) - curCell*=lightCell(0,0,0); + applyMaterial(tile,bld->mat_type,bld->mat_index,1,&matWall); } } } @@ -478,7 +481,7 @@ void lightingEngineViewscreen::doOcupancyAndLights() ) { - addLight(tile,lava); + addLight(tile,matLava.makeSource()); } if(d->bits.outside && d->bits.flow_size==0) { @@ -533,11 +536,7 @@ void lightingEngineViewscreen::doOcupancyAndLights() int tile=getIndex(wx,wy); if(wx>=vp.first.x && wy>=vp.first.y && wx<=vp.second.x && wy<=vp.second.y) { - auto it=glowPlants.find(cPlant->material); - if(it!=glowPlants.end()) - { - addLight(tile,it->second); - } + applyMaterial(tile,cPlant->material,-1); } } } @@ -548,7 +547,7 @@ void lightingEngineViewscreen::doOcupancyAndLights() int wx=df::global::cursor->x-window_x+vp.first.x; int wy=df::global::cursor->y-window_y+vp.first.y; int tile=getIndex(wx,wy); - addLight(tile,cursor); + applyMaterial(tile,matCursor); } lightSource citizen(lightCell(0.80f,0.80f,0.90f),6); for (int i=0;iunits.active.size();++i) @@ -562,3 +561,159 @@ void lightingEngineViewscreen::doOcupancyAndLights() addLight(getIndex(u->pos.x-window_x+1, u->pos.y-window_y+1),citizen); } } +lightCell lua_parseLightCell(lua_State* L) +{ + lightCell ret; + + lua_pushnumber(L,1); + lua_gettable(L,-2); + ret.r=lua_tonumber(L,-1); + lua_pop(L,1); + + lua_pushnumber(L,2); + lua_gettable(L,-2); + ret.g=lua_tonumber(L,-1); + lua_pop(L,1); + + lua_pushnumber(L,3); + lua_gettable(L,-2); + ret.b=lua_tonumber(L,-1); + lua_pop(L,1); + + Lua::GetOutput(L)->print("got cell(%f,%f,%f)\n",ret.r,ret.g,ret.b); + return ret; +} +matLightDef lua_parseMatDef(lua_State* L) +{ + + matLightDef ret; + lua_getfield(L,-1,"tr"); + if(ret.isTransparent=!lua_isnil(L,-1)) + { + ret.transparency=lua_parseLightCell(L); + } + lua_pop(L,1); + + lua_getfield(L,-1,"em"); + if(ret.isEmiting=!lua_isnil(L,-1)) + { + ret.emitColor=lua_parseLightCell(L); + lua_pop(L,1); + lua_getfield(L,-1,"rad"); + if(lua_isnil(L,-1)) + { + lua_pop(L,1); + luaL_error(L,"Material has emittance but no radius"); + } + else + ret.radius=lua_tonumber(L,-1); + lua_pop(L,1); + } + else + lua_pop(L,1); + //todo flags + return ret; +} +int lightingEngineViewscreen::parseMaterials(lua_State* L) +{ + auto engine= (lightingEngineViewscreen*)lua_touserdata(L, 1); + engine->matDefs.clear(); + //color_ostream* os=Lua::GetOutput(L); + Lua::StackUnwinder unwinder(L); + lua_getfield(L,2,"materials"); + if(!lua_istable(L,-1)) + { + luaL_error(L,"Materials table not found."); + return 0; + } + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + int type=lua_tonumber(L,-2); + //os->print("Processing type:%d\n",type); + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + int index=lua_tonumber(L,-2); + //os->print("\tProcessing index:%d\n",index); + engine->matDefs[std::make_pair(type,index)]=lua_parseMatDef(L); + + lua_pop(L, 1); + } + lua_pop(L, 1); + } + return 0; +} +#define LOAD_SPECIAL(lua_name,class_name) \ + lua_getfield(L,-1,#lua_name);\ + if(!lua_isnil(L,-1))engine->class_name=lua_parseMatDef(L);\ + lua_pop(L,1) +int lightingEngineViewscreen::parseSpecial(lua_State* L) +{ + auto engine= (lightingEngineViewscreen*)lua_touserdata(L, 1); + Lua::StackUnwinder unwinder(L); + lua_getfield(L,2,"special"); + if(!lua_istable(L,-1)) + { + luaL_error(L,"Special table not found."); + return 0; + } + LOAD_SPECIAL(LAVA,matLava); + LOAD_SPECIAL(FROZEN_LIQUID,matIce); + LOAD_SPECIAL(AMBIENT,matAmbience); + LOAD_SPECIAL(CURSOR,matCursor); + return 0; +} +#undef LOAD_SPECIAL +void lightingEngineViewscreen::defaultSettings() +{ + matAmbience=matLightDef(lightCell(0.85f,0.85f,0.85f)); + matLava=matLightDef(lightCell(0.8f,0.2f,0.2f),lightCell(0.8f,0.2f,0.2f),5); + matIce=matLightDef(lightCell(0.7f,0.7f,0.9f)); + matCursor=matLightDef(lightCell(0.96f,0.84f,0.03f),11); + matCursor.flicker=true; +} +void lightingEngineViewscreen::loadSettings() +{ + + const std::string settingsfile="rendermax.lua"; + CoreSuspender lock; + color_ostream_proxy out(Core::getInstance().getConsole()); + + lua_State* s=DFHack::Lua::Core::State; + lua_newtable(s); + int env=lua_gettop(s); + try{ + int ret=luaL_loadfile(s,settingsfile.c_str()); + if(ret==LUA_ERRFILE) + { + out.printerr("File not found:%s\n",settingsfile.c_str()); + } + else if(ret==LUA_ERRSYNTAX) + { + out.printerr("Syntax error:\n\t%s\n",lua_tostring(s,-1)); + } + else + { + lua_pushvalue(s,env); + + if(Lua::SafeCall(out,s,1,0)) + { + lua_pushcfunction(s, parseMaterials); + lua_pushlightuserdata(s, this); + lua_pushvalue(s,env); + Lua::SafeCall(out,s,2,0); + out.print("%d materials loaded\n",matDefs.size()); + + lua_pushcfunction(s, parseSpecial); + lua_pushlightuserdata(s, this); + lua_pushvalue(s,env); + Lua::SafeCall(out,s,2,0); + + } + } + } + catch(std::exception& e) + { + out.printerr("%s",e.what()); + } + lua_pop(s,1); +} diff --git a/plugins/rendermax/renderer_light.hpp b/plugins/rendermax/renderer_light.hpp index 98ea502bd..108b686e0 100644 --- a/plugins/rendermax/renderer_light.hpp +++ b/plugins/rendermax/renderer_light.hpp @@ -72,6 +72,8 @@ public: virtual void updateWindow()=0; + virtual void loadSettings()=0; + protected: renderer_light* myRenderer; }; @@ -95,6 +97,29 @@ struct lightSource void combine(const lightSource& other); }; +struct matLightDef +{ + int mat_index; + int mat_type; + bool isTransparent; + lightCell transparency; + bool isEmiting; + bool sizeModifiesPower; + bool sizeModifiesRange; + bool flicker; + lightCell emitColor; + int radius; + matLightDef(){} + matLightDef(lightCell transparency,lightCell emit,int rad):isTransparent(true),isEmiting(true), + transparency(transparency),emitColor(emit),radius(rad){} + matLightDef(lightCell emit,int rad):isTransparent(false),isEmiting(true),emitColor(emit),radius(rad){} + matLightDef(lightCell transparency):isTransparent(true),isEmiting(false),transparency(transparency){} + lightSource makeSource(float size=1) const + { + //TODO implement sizeModifiesPower/range + return lightSource(emitColor,radius); + } +}; class lightingEngineViewscreen:public lightingEngine { public: @@ -105,24 +130,41 @@ public: void updateWindow(); + void loadSettings(); private: + void doOcupancyAndLights(); + void doRay(lightCell power,int cx,int cy,int tx,int ty); void doFovs(); bool lightUpCell(lightCell& power,int dx,int dy,int tx,int ty); bool addLight(int tileId,const lightSource& light); - void initRawSpecific(); + //apply material to cell + void applyMaterial(int tileId,const matLightDef& mat,float size=1); + //try to find and apply material, if failed return false, and if def!=null then apply def. + bool applyMaterial(int tileId,int matType,int matIndex,float size=1,const matLightDef* def=NULL); size_t inline getIndex(int x,int y) { return x*h+y; } + //maps std::vector lightMap; std::vector ocupancy; std::vector lights; + //settings - - std::map glowPlants; - std::map glowVeins; + ///set up sane settings if setting file does not exist. + void defaultSettings(); + + static int parseMaterials(lua_State* L); + static int parseSpecial(lua_State* L); + //special stuff + matLightDef matLava; + matLightDef matIce; + matLightDef matAmbience; + matLightDef matCursor; + //materials + std::map,matLightDef> matDefs; int w,h; DFHack::rect2d mapPort; diff --git a/plugins/rendermax/rendermax.cpp b/plugins/rendermax/rendermax.cpp index acfa9f92c..dda0d5d2c 100644 --- a/plugins/rendermax/rendermax.cpp +++ b/plugins/rendermax/rendermax.cpp @@ -304,12 +304,23 @@ static command_result rendermax(color_ostream &out, vector & parameters } else if(cmd=="light") { - removeOld(); - renderer_light *myRender=new renderer_light(df::global::enabler->renderer); - installNew(myRender,MODE_LIGHT); - engine=new lightingEngineViewscreen(myRender); - engine->calculate(); - engine->updateWindow(); + if(current_mode!=MODE_LIGHT || current_mode!=MODE_LIGHT_OFF) + { + removeOld(); + renderer_light *myRender=new renderer_light(df::global::enabler->renderer); + installNew(myRender,MODE_LIGHT); + engine=new lightingEngineViewscreen(myRender); + engine->calculate(); + engine->updateWindow(); + } + else if(current_mode==MODE_LIGHT && parameters.size()>1) + { + if(parameters[1]=="reload") + engine->loadSettings(); + } + else + out.printerr("Light mode already enabled"); + return CR_OK; } else if(cmd=="disable") From 4ccc95bc97633a09e5a8a1027f02bd19c1a93d8b Mon Sep 17 00:00:00 2001 From: Warmist Date: Tue, 25 Jun 2013 00:59:32 +0300 Subject: [PATCH 035/676] Major rewrite of opacity calculation. --- plugins/rendermax/renderer_light.cpp | 423 +++++++++++++++----------- plugins/rendermax/renderer_light.hpp | 14 +- plugins/rendermax/renderer_opengl.hpp | 18 +- 3 files changed, 266 insertions(+), 189 deletions(-) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index f50f2e016..765613bac 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -4,12 +4,13 @@ #include #include -#include "Types.h" + #include "LuaTools.h" #include "modules/Gui.h" #include "modules/Screen.h" #include "modules/Maps.h" + #include "modules/Units.h" #include "df/graphic.h" @@ -65,7 +66,7 @@ rect2d getMapViewport() if(menu_pos < 2) menu_x = menu_x1; view_rb = menu_x; } - return mkrect_wh(1,1,view_rb,view_height+1); + return mkrect_wh(1,1,view_rb,view_height); } lightingEngineViewscreen::lightingEngineViewscreen(renderer_light* target):lightingEngine(target) { @@ -235,6 +236,10 @@ void lightingEngineViewscreen::updateWindow() myRenderer->invalidate(); return; } + + //if(showOcupancy) + //std::swap(ocupancy,myRenderer->lightGrid); + //else std::swap(lightMap,myRenderer->lightGrid); rect2d vp=getMapViewport(); @@ -282,6 +287,14 @@ void addPlant(const std::string& id,std::map& map,const lightSo map[nId]=v; } } +matLightDef* lightingEngineViewscreen::getMaterial(int matType,int matIndex) +{ + auto it=matDefs.find(std::make_pair(matType,matIndex)); + if(it!=matDefs.end()) + return &it->second; + else + return NULL; +} void lightingEngineViewscreen::applyMaterial(int tileId,const matLightDef& mat,float size) { if(mat.isTransparent) @@ -293,10 +306,10 @@ void lightingEngineViewscreen::applyMaterial(int tileId,const matLightDef& mat,f } bool lightingEngineViewscreen::applyMaterial(int tileId,int matType,int matIndex,float size,const matLightDef* def) { - auto it=matDefs.find(std::make_pair(matType,matIndex)); - if(it!=matDefs.end()) + matLightDef* m=getMaterial(matType,matIndex); + if(m) { - applyMaterial(tileId,it->second,size); + applyMaterial(tileId,*m,size); return true; } else if(def) @@ -305,208 +318,223 @@ bool lightingEngineViewscreen::applyMaterial(int tileId,int matType,int matIndex } return false; } -static size_t max_list_size = 100000; // Avoid iterating over huge lists -void lightingEngineViewscreen::doOcupancyAndLights() +lightCell lightingEngineViewscreen::propogateSun(MapExtras::Block* b, int x,int y,const lightCell& in,bool lastLevel) { - // TODO better curve (+red dawn ?) - float daycol = abs((*df::global::cur_year_tick % 1200) - 600.0) / 400.0; - lightCell sky_col(daycol, daycol, daycol); - lightSource sky(sky_col, 15); - - lightSource candle(lightCell(0.96f,0.84f,0.03f),5); - lightSource torch(lightCell(0.9f,0.75f,0.3f),8); - - //perfectly blocking material - matLightDef matWall(lightCell(0,0,0)); - - rect2d vp=getMapViewport(); + const lightCell matStairCase(0.9f,0.9f,0.9f); + lightCell ret=in; + coord2d innerCoord(x,y); + df::tiletype type = b->tiletypeAt(innerCoord); + df::tile_designation d = b->DesignationAt(innerCoord); + //df::tile_occupancy o = b->OccupancyAt(innerCoord); + df::tiletype_shape shape = ENUM_ATTR(tiletype,shape,type); + df::tiletype_shape_basic basic_shape = ENUM_ATTR(tiletype_shape, basic_shape, shape); + DFHack::t_matpair mat=b->staticMaterialAt(innerCoord); + df::tiletype_material tileMat= ENUM_ATTR(tiletype,material,type); + matLightDef* lightDef; + if(tileMat==df::tiletype_material::FROZEN_LIQUID) + lightDef=&matIce; + else + lightDef=getMaterial(mat.mat_type,mat.mat_index); + + if(!lightDef || !lightDef->isTransparent) + lightDef=&matWall; + if(basic_shape==df::tiletype_shape_basic::Wall) + { + ret*=lightDef->transparency; + } + else if(basic_shape==df::tiletype_shape_basic::Floor || basic_shape==df::tiletype_shape_basic::Ramp || shape==df::tiletype_shape::STAIR_UP) + { + if(!lastLevel) + ret*=lightDef->transparency; //TODO modify because floors have less material + } + else if(shape==df::tiletype_shape::STAIR_DOWN || shape==df::tiletype_shape::STAIR_UPDOWN) + { + ret*=matStairCase; + } + if(d.bits.liquid_type == df::enums::tile_liquid::Water && d.bits.flow_size) + { + ret *=matWater.transparency;// (lightCell(1,1,1) - (lightCell(1,1,1) - matWater)*((float)d.bits.flow_size/7.0f)); + } + else if(d.bits.liquid_type == df::enums::tile_liquid::Magma && d.bits.flow_size > 3) + { + ret *=matLava.transparency; + } + return ret; +} +coord2d lightingEngineViewscreen::worldToViewportCoord(const coord2d& in,const rect2d& r,const coord2d& window2d) +{ + return in-window2d+r.first; +} +bool lightingEngineViewscreen::isInViewport(const coord2d& in,const rect2d& r) +{ + if(in.x>=r.first.x && in.y>=r.first.y && in.x= df::global::world->map.x_count_block) endBlockx = df::global::world->map.x_count_block-1; - if(endBlocky >= df::global::world->map.y_count_block) endBlocky = df::global::world->map.y_count_block-1; - for(int blockx=window_x/16;blockx<=endBlockx;blockx++) - for(int blocky=window_y/16;blocky<=endBlocky;blocky++) + rect2d vp=getMapViewport(); + coord2d vpSize=rect_size(vp); + rect2d blockVp; + blockVp.first=window2d/16; + blockVp.second=(window2d+vpSize)/16; + blockVp.second.x=std::min(blockVp.second.x,(int16_t)df::global::world->map.x_count_block); + blockVp.second.y=std::min(blockVp.second.y,(int16_t)df::global::world->map.y_count_block); + //endof mess + for(int blockX=blockVp.first.x;blockX<=blockVp.second.x;blockX++) + for(int blockY=blockVp.first.y;blockY<=blockVp.second.y;blockY++) { - lightCell cellArray[16][16]; for(int block_x = 0; block_x < 16; block_x++) for(int block_y = 0; block_y < 16; block_y++) + cellArray[block_x][block_y] = sky.power; + + int emptyCell=0; + for(int z=df::global::world->map.z_count-1;z>=window_z && emptyCell<256;z--) { - cellArray[block_x][block_y] = sky_col; - } - int totalBlank = 0; - int topLevel = df::global::world->map.z_count-1; - for(int ZZ = topLevel; (ZZ >= window_z) && totalBlank < 256; ZZ--) - { - df::map_block* block=Maps::getBlock(blockx,blocky,ZZ); - totalBlank = 0; - if(block) + MapExtras::Block* b=map.BlockAt(DFCoord(blockX,blockY,z)); + if(!b) + continue; + emptyCell=0; for(int block_x = 0; block_x < 16; block_x++) for(int block_y = 0; block_y < 16; block_y++) { - df::tiletype type = block->tiletype[block_x][block_y]; - df::tile_designation d = block->designation[block_x][block_y]; - df::tile_occupancy o = block->occupancy[block_x][block_y]; - df::tiletype_shape shape = ENUM_ATTR(tiletype,shape,type); - df::tiletype_shape_basic basic_shape = ENUM_ATTR(tiletype_shape, basic_shape, shape); - - if(basic_shape==df::tiletype_shape_basic::Wall) - { - cellArray[block_x][block_y]=lightCell(0,0,0); - } - else if(basic_shape==df::tiletype_shape_basic::Floor || basic_shape==df::tiletype_shape_basic::Ramp || shape==df::tiletype_shape::STAIR_UP) - { - if(ZZ!=window_z) - { - cellArray[block_x][block_y]=lightCell(0,0,0); - } - } - else if(shape==df::tiletype_shape::STAIR_DOWN || shape==df::tiletype_shape::STAIR_UPDOWN) - { - cellArray[block_x][block_y]*=lightCell(0.9,0.9,0.9); - } - if(d.bits.liquid_type == df::enums::tile_liquid::Water && d.bits.flow_size) - { - cellArray[block_x][block_y] *= (lightCell(1,1,1) - (lightCell(1,1,1) - lightCell(0.63f,0.63f,0.75f))*((float)d.bits.flow_size/7.0f)); - } - else if(d.bits.liquid_type == df::enums::tile_liquid::Magma && d.bits.flow_size > 3) - { - cellArray[block_x][block_y]=lightCell(0,0,0); - } - if(cellArray[block_x][block_y].r < 0.003f && cellArray[block_x][block_y].g < 0.003f && cellArray[block_x][block_y].b < 0.003f) - totalBlank++; + lightCell& curCell=cellArray[block_x][block_y]; + curCell=propogateSun(b,block_x,block_y,curCell,z==window_z); + if(curCell.dot(curCell)<0.003f) + emptyCell++; } } + if(emptyCell==256) + continue; for(int block_x = 0; block_x < 16; block_x++) for(int block_y = 0; block_y < 16; block_y++) { + lightCell& curCell=cellArray[block_x][block_y]; df::coord2d pos; - pos.x = blockx*16+block_x; - pos.y = blocky*16+block_y; - int wx=pos.x-window_x+vp.first.x; - int wy=pos.y-window_y+vp.first.y; - if(wx>=vp.first.x && wy>=vp.first.y && wx<=vp.second.x && wy<=vp.second.y) - if(cellArray[block_x][block_y].r >= 0.003f && cellArray[block_x][block_y].g >= 0.003f && cellArray[block_x][block_y].b >= 0.003f) + pos.x = blockX*16+block_x; + pos.y = blockY*16+block_y; + pos=worldToViewportCoord(pos,vp,window2d); + if(isInViewport(pos,vp) && curCell.dot(curCell)>0.003f) { - lightSource sun={cellArray[block_x][block_y],25,coord2d(wx,wy)}; - lights.push_back(sun); + lightSource sun=lightSource(curCell,15); + addLight(getIndex(pos.x,pos.y),sun); } } - } - for(int x=window_x;xmap.x_count_block); + blockVp.second.y=std::min(blockVp.second.y,(int16_t)df::global::world->map.y_count_block); + + for(int blockX=blockVp.first.x;blockX<=blockVp.second.x;blockX++) + for(int blockY=blockVp.first.y;blockY<=blockVp.second.y;blockY++) { - int wx=x-window_x+vp.first.x; - int wy=y-window_y+vp.first.y; - int tile=getIndex(wx,wy); - lightCell& curCell=ocupancy[tile]; - if(matAmbience.isTransparent) - curCell=matAmbience.transparency; - df::tiletype* type = Maps::getTileType(x,y,window_z); - if(!type) - { - //unallocated, do sky - addLight(tile,sky); - continue; - } - df::tiletype_shape shape = ENUM_ATTR(tiletype,shape,*type); - df::tile_designation* d=Maps::getTileDesignation(x,y,window_z); - df::tile_designation* d2=Maps::getTileDesignation(x,y,window_z-1); - df::tile_occupancy* o=Maps::getTileOccupancy(x,y,window_z); - df::tiletype_material m=ENUM_ATTR(tiletype,material,*type); - if(!o || !d ) - continue; - if(shape==df::tiletype_shape::BROOK_BED || shape==df::tiletype_shape::WALL || shape==df::tiletype_shape::TREE || d->bits.hidden ) + MapExtras::Block* b=cache.BlockAt(DFCoord(blockX,blockY,window_z)); + MapExtras::Block* bDown=cache.BlockAt(DFCoord(blockX,blockY,window_z-1)); + if(!b) + continue; //empty blocks fixed by sun propagation + + for(int block_x = 0; block_x < 16; block_x++) + for(int block_y = 0; block_y < 16; block_y++) { - //TODO split into wall ,etc... - if(shape==df::tiletype_shape::WALL && m==df::tiletype_material::FROZEN_LIQUID) - applyMaterial(tile,matIce); - else + df::coord2d pos; + pos.x = blockX*16+block_x; + pos.y = blockY*16+block_y; + df::coord2d gpos=pos; + pos=worldToViewportCoord(pos,vp,window2d); + if(!isInViewport(pos,vp)) + continue; + int tile=getIndex(pos.x,pos.y); + lightCell& curCell=ocupancy[tile]; + curCell=matAmbience.transparency; + + + df::tiletype type = b->tiletypeAt(gpos); + df::tile_designation d = b->DesignationAt(gpos); + //df::tile_occupancy o = b->OccupancyAt(gpos); + df::tiletype_shape shape = ENUM_ATTR(tiletype,shape,type); + df::tiletype_shape_basic basic_shape = ENUM_ATTR(tiletype_shape, basic_shape, shape); + df::tiletype_material tileMat= ENUM_ATTR(tiletype,material,type); + + DFHack::t_matpair mat=b->staticMaterialAt(gpos); + + matLightDef* lightDef=getMaterial(mat.mat_type,mat.mat_index); + if(!lightDef || !lightDef->isTransparent) + lightDef=&matWall; + if(shape==df::tiletype_shape::BROOK_BED || d.bits.hidden ) + { curCell=lightCell(0,0,0); - } - else if(o->bits.building) - { - // Fixme: don't iterate the list every frame - size_t count = df::global::world->buildings.all.size(); - if (count <= max_list_size) + } + else if(shape==df::tiletype_shape::WALL) + { + if(tileMat==df::tiletype_material::FROZEN_LIQUID) + applyMaterial(tile,matIce); + else + applyMaterial(tile,*lightDef); + } + else if(!d.bits.liquid_type && d.bits.flow_size>3 ) + { + applyMaterial(tile,matWater); + } + if(d.bits.liquid_type && d.bits.flow_size>0) { - for(size_t i = 0; i < count; i++) + applyMaterial(tile,matLava); + } + else if(shape==df::tiletype_shape::EMPTY || shape==df::tiletype_shape::RAMP_TOP + || shape==df::tiletype_shape::STAIR_DOWN || shape==df::tiletype_shape::STAIR_UPDOWN) + { + if(bDown) { - df::building *bld = df::global::world->buildings.all[i]; - - if (window_z == bld->z && - x >= bld->x1 && x <= bld->x2 && - y >= bld->y1 && y <= bld->y2) - { - df::building_type type = bld->getType(); - - if (type == df::enums::building_type::WindowGlass || type==df::enums::building_type::WindowGem) - { - applyMaterial(tile,bld->mat_type,bld->mat_index); - } - if (type == df::enums::building_type::Table) - { - addLight(tile,candle); - } - if (type==df::enums::building_type::Statue) - { - addLight(tile,torch); - } - if(type==df::enums::building_type::Door) - { - df::building_doorst* door=static_cast(bld); - if(door->door_flags.bits.closed) - applyMaterial(tile,bld->mat_type,bld->mat_index,1,&matWall); - } - } + df::tile_designation d2=bDown->DesignationAt(gpos); + if(d2.bits.liquid_type && d2.bits.flow_size>0) + { + applyMaterial(tile,matLava); + } } } } - else if(!d->bits.liquid_type && d->bits.flow_size>3 ) - { - curCell*=lightCell(0.7f,0.7f,0.8f); - } - //lights - if((d->bits.liquid_type && d->bits.flow_size>0)|| - ( - (shape==df::tiletype_shape::EMPTY || shape==df::tiletype_shape::RAMP_TOP || shape==df::tiletype_shape::STAIR_DOWN || shape==df::tiletype_shape::STAIR_UPDOWN ) - && d2 && d2->bits.liquid_type && d2->bits.flow_size>0) - ) - { - - addLight(tile,matLava.makeSource()); - } - if(d->bits.outside && d->bits.flow_size==0) - { - addLight(tile,sky); - } - - } - - for(int blockx=window_x/16;blockx<=endBlockx;blockx++) - for(int blocky=window_y/16;blocky<=endBlocky;blocky++) - { - df::map_block* block=Maps::getBlock(blockx,blocky,window_z); - - if(!block) - continue; + //flows + df::map_block* block=b->getRaw(); for(int i=0;iflows.size();i++) { df::flow_info* f=block->flows[i]; if(f && f->density>0 && f->type==df::flow_type::Dragonfire || f->type==df::flow_type::Fire) { df::coord2d pos=f->pos; - int wx=pos.x-window_x+vp.first.x; - int wy=pos.y-window_y+vp.first.y; - int tile=getIndex(wx,wy); - if(wx>=vp.first.x && wy>=vp.first.y && wx<=vp.second.x && wy<=vp.second.y) + pos=worldToViewportCoord(pos,vp,window2d); + int tile=getIndex(pos.x,pos.y); + if(isInViewport(pos,vp)) { lightCell fireColor; if(f->density>60) @@ -531,10 +559,9 @@ void lightingEngineViewscreen::doOcupancyAndLights() df::plant* cPlant=block->plants[i]; df::coord2d pos=cPlant->pos; - int wx=pos.x-window_x+vp.first.x; - int wy=pos.y-window_y+vp.first.y; - int tile=getIndex(wx,wy); - if(wx>=vp.first.x && wy>=vp.first.y && wx<=vp.second.x && wy<=vp.second.y) + pos=worldToViewportCoord(pos,vp,window2d); + int tile=getIndex(pos.x,pos.y); + if(isInViewport(pos,vp)) { applyMaterial(tile,cPlant->material,-1); } @@ -553,12 +580,45 @@ void lightingEngineViewscreen::doOcupancyAndLights() for (int i=0;iunits.active.size();++i) { df::unit *u = df::global::world->units.active[i]; - if (u->pos.z != window_z || - (u->pos.x < window_x || u->pos.x >= window_x+vpW) || - (u->pos.y < window_y || u->pos.y >= window_y+vpH)) - continue; + coord2d pos=worldToViewportCoord(coord2d(u->pos.x,u->pos.y),vp,window2d); + if(u->pos.z==window_z && isInViewport(pos,vp)) if (DFHack::Units::isCitizen(u) && !u->counters.unconscious) - addLight(getIndex(u->pos.x-window_x+1, u->pos.y-window_y+1),citizen); + addLight(getIndex(pos.x,pos.y),citizen); + } + //buildings + for(size_t i = 0; i < df::global::world->buildings.all.size(); i++) + { + df::building *bld = df::global::world->buildings.all[i]; + if(window_z!=bld->z) + continue; + df::coord2d p1(bld->x1,bld->y1); + df::coord2d p2(bld->x2,bld->y2); + p1=worldToViewportCoord(p1,vp,window2d); + p2=worldToViewportCoord(p1,vp,window2d); + if(isInViewport(p1,vp)||isInViewport(p2,vp)) + { + int tile=getIndex(p1.x,p1.y); //TODO multitile buildings. How they would work? + df::building_type type = bld->getType(); + + if (type == df::enums::building_type::WindowGlass || type==df::enums::building_type::WindowGem) + { + applyMaterial(tile,bld->mat_type,bld->mat_index); + } + if (type == df::enums::building_type::Table) + { + addLight(tile,candle); + } + if (type==df::enums::building_type::Statue) + { + addLight(tile,torch); + } + if(type==df::enums::building_type::Door) + { + df::building_doorst* door=static_cast(bld); + if(door->door_flags.bits.closed) + applyMaterial(tile,bld->mat_type,bld->mat_index,1,&matWall); + } + } } } lightCell lua_parseLightCell(lua_State* L) @@ -580,7 +640,7 @@ lightCell lua_parseLightCell(lua_State* L) ret.b=lua_tonumber(L,-1); lua_pop(L,1); - Lua::GetOutput(L)->print("got cell(%f,%f,%f)\n",ret.r,ret.g,ret.b); + //Lua::GetOutput(L)->print("got cell(%f,%f,%f)\n",ret.r,ret.g,ret.b); return ret; } matLightDef lua_parseMatDef(lua_State* L) @@ -657,6 +717,7 @@ int lightingEngineViewscreen::parseSpecial(lua_State* L) return 0; } LOAD_SPECIAL(LAVA,matLava); + LOAD_SPECIAL(WATER,matWater); LOAD_SPECIAL(FROZEN_LIQUID,matIce); LOAD_SPECIAL(AMBIENT,matAmbience); LOAD_SPECIAL(CURSOR,matCursor); @@ -667,9 +728,11 @@ void lightingEngineViewscreen::defaultSettings() { matAmbience=matLightDef(lightCell(0.85f,0.85f,0.85f)); matLava=matLightDef(lightCell(0.8f,0.2f,0.2f),lightCell(0.8f,0.2f,0.2f),5); + matWater=matLightDef(lightCell(0.6f,0.6f,0.8f)); matIce=matLightDef(lightCell(0.7f,0.7f,0.9f)); matCursor=matLightDef(lightCell(0.96f,0.84f,0.03f),11); matCursor.flicker=true; + matWall=matLightDef(lightCell(0,0,0)); } void lightingEngineViewscreen::loadSettings() { diff --git a/plugins/rendermax/renderer_light.hpp b/plugins/rendermax/renderer_light.hpp index 108b686e0..4d10951fc 100644 --- a/plugins/rendermax/renderer_light.hpp +++ b/plugins/rendermax/renderer_light.hpp @@ -3,6 +3,8 @@ #include "renderer_opengl.hpp" #include "Types.h" #include +#include "modules/MapCache.h" + struct renderer_light : public renderer_wrap { private: void colorizeTile(int x,int y) @@ -112,7 +114,7 @@ struct matLightDef matLightDef(){} matLightDef(lightCell transparency,lightCell emit,int rad):isTransparent(true),isEmiting(true), transparency(transparency),emitColor(emit),radius(rad){} - matLightDef(lightCell emit,int rad):isTransparent(false),isEmiting(true),emitColor(emit),radius(rad){} + matLightDef(lightCell emit,int rad):isTransparent(false),isEmiting(true),emitColor(emit),radius(rad),transparency(0,0,0){} matLightDef(lightCell transparency):isTransparent(true),isEmiting(false),transparency(transparency){} lightSource makeSource(float size=1) const { @@ -133,12 +135,18 @@ public: void loadSettings(); private: - void doOcupancyAndLights(); + df::coord2d worldToViewportCoord(const df::coord2d& in,const DFHack::rect2d& r,const df::coord2d& window2d) ; + bool isInViewport(const df::coord2d& in,const DFHack::rect2d& r); + void doSun(const lightSource& sky,MapExtras::MapCache& map); + void doOcupancyAndLights(); + lightCell propogateSun(MapExtras::Block* b, int x,int y,const lightCell& in,bool lastLevel); void doRay(lightCell power,int cx,int cy,int tx,int ty); void doFovs(); bool lightUpCell(lightCell& power,int dx,int dy,int tx,int ty); bool addLight(int tileId,const lightSource& light); + + matLightDef* getMaterial(int matType,int matIndex); //apply material to cell void applyMaterial(int tileId,const matLightDef& mat,float size=1); //try to find and apply material, if failed return false, and if def!=null then apply def. @@ -163,6 +171,8 @@ private: matLightDef matIce; matLightDef matAmbience; matLightDef matCursor; + matLightDef matWall; + matLightDef matWater; //materials std::map,matLightDef> matDefs; diff --git a/plugins/rendermax/renderer_opengl.hpp b/plugins/rendermax/renderer_opengl.hpp index 6d21a8b4c..cdab20af3 100644 --- a/plugins/rendermax/renderer_opengl.hpp +++ b/plugins/rendermax/renderer_opengl.hpp @@ -195,19 +195,19 @@ struct lightCell { } - lightCell operator-(lightCell cell) + lightCell operator-(lightCell cell) const { return lightCell(r-cell.r,g-cell.g,b-cell.b); } - lightCell operator*(float val) + lightCell operator*(float val)const { return lightCell(r*val,g*val,b*val); } - lightCell operator/(float val) + lightCell operator/(float val) const { return lightCell(r/val,g/val,b/val); } - lightCell operator*(lightCell cell) + lightCell operator*(lightCell cell) const { return lightCell(r*cell.r,g*cell.g,b*cell.b); } @@ -216,16 +216,20 @@ struct lightCell r*=cell.r; g*=cell.g; b*=cell.b; - return lightCell(r,g,b); + return *this; } - lightCell operator+(const lightCell& other) + lightCell operator+(const lightCell& other) const { return lightCell(r+other.r,g+other.g,b+other.b); } - bool operator<(const lightCell& other) + bool operator<(const lightCell& other) const { return r Date: Tue, 25 Jun 2013 01:25:38 +0300 Subject: [PATCH 036/676] Plantfix, viewport height fix, and fov viewport fix. --- plugins/rendermax/renderer_light.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index 765613bac..fea6999d1 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -66,7 +66,7 @@ rect2d getMapViewport() if(menu_pos < 2) menu_x = menu_x1; view_rb = menu_x; } - return mkrect_wh(1,1,view_rb,view_height); + return mkrect_wh(1,1,view_rb,view_height+1); } lightingEngineViewscreen::lightingEngineViewscreen(renderer_light* target):lightingEngine(target) { @@ -123,8 +123,7 @@ lightCell blend(lightCell a,lightCell b) } bool lightingEngineViewscreen::lightUpCell(lightCell& power,int dx,int dy,int tx,int ty) { - - if(tx>=mapPort.first.x && ty>=mapPort.first.y && tx<=mapPort.second.x && ty<=mapPort.second.y) + if(isInViewport(coord2d(tx,ty),mapPort)) { size_t tile=getIndex(tx,ty); int dsq=dx*dx+dy*dy; @@ -524,8 +523,11 @@ void lightingEngineViewscreen::doOcupancyAndLights() } } } - //flows + df::map_block* block=b->getRaw(); + if(!block) + continue; + //flows for(int i=0;iflows.size();i++) { df::flow_info* f=block->flows[i]; @@ -554,6 +556,7 @@ void lightingEngineViewscreen::doOcupancyAndLights() } } } + //plants for(int i=0;iplants.size();i++) { df::plant* cPlant=block->plants[i]; @@ -563,7 +566,7 @@ void lightingEngineViewscreen::doOcupancyAndLights() int tile=getIndex(pos.x,pos.y); if(isInViewport(pos,vp)) { - applyMaterial(tile,cPlant->material,-1); + applyMaterial(tile,419,cPlant->material); } } } From 0551661db2e5938dc11c864e5b36cbe9c9ab39e0 Mon Sep 17 00:00:00 2001 From: Japa Date: Tue, 25 Jun 2013 16:33:01 +0530 Subject: [PATCH 037/676] Added more advanced sun light calculation --- plugins/rendermax/renderer_light.cpp | 29 ++++++----- plugins/rendermax/renderer_light.hpp | 2 +- plugins/rendermax/renderer_opengl.hpp | 9 ++++ plugins/rendermax/rendermax.lua | 69 +++++++++++++++++++++++++++ 4 files changed, 95 insertions(+), 14 deletions(-) create mode 100644 plugins/rendermax/rendermax.lua diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index fea6999d1..962f49d28 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -144,9 +144,7 @@ bool lightingEngineViewscreen::lightUpCell(lightCell& power,int dx,int dy,int tx if (dsq>0 && !wallhack) { - power.r=power.r*(pow(v.r,dt)); - power.g=power.g*(pow(v.g,dt)); - power.b=power.b*(pow(v.b,dt)); + power*=v.power(dt); } if(ls.radius>0 && dsq>0) { @@ -294,10 +292,15 @@ matLightDef* lightingEngineViewscreen::getMaterial(int matType,int matIndex) else return NULL; } -void lightingEngineViewscreen::applyMaterial(int tileId,const matLightDef& mat,float size) +void lightingEngineViewscreen::applyMaterial(int tileId,const matLightDef& mat,float size, float thickness) { if(mat.isTransparent) - ocupancy[tileId]*=mat.transparency; + { + if(thickness > 0.999 && thickness < 1.001) + ocupancy[tileId]*=mat.transparency; + else + ocupancy[tileId]*=(mat.transparency.power(thickness)); + } else ocupancy[tileId]=lightCell(0,0,0); if(mat.isEmiting) @@ -345,19 +348,19 @@ lightCell lightingEngineViewscreen::propogateSun(MapExtras::Block* b, int x,int else if(basic_shape==df::tiletype_shape_basic::Floor || basic_shape==df::tiletype_shape_basic::Ramp || shape==df::tiletype_shape::STAIR_UP) { if(!lastLevel) - ret*=lightDef->transparency; //TODO modify because floors have less material + ret*=lightDef->transparency.power(1.0f/7.0f); } else if(shape==df::tiletype_shape::STAIR_DOWN || shape==df::tiletype_shape::STAIR_UPDOWN) { ret*=matStairCase; } - if(d.bits.liquid_type == df::enums::tile_liquid::Water && d.bits.flow_size) + if(d.bits.liquid_type == df::enums::tile_liquid::Water && d.bits.flow_size > 0) { - ret *=matWater.transparency;// (lightCell(1,1,1) - (lightCell(1,1,1) - matWater)*((float)d.bits.flow_size/7.0f)); + ret *=matWater.transparency.power((float)d.bits.flow_size/7.0f); } - else if(d.bits.liquid_type == df::enums::tile_liquid::Magma && d.bits.flow_size > 3) + else if(d.bits.liquid_type == df::enums::tile_liquid::Magma && d.bits.flow_size > 0) { - ret *=matLava.transparency; + ret *=matLava.transparency.power((float)d.bits.flow_size/7.0f); } return ret; } @@ -432,7 +435,7 @@ void lightingEngineViewscreen::doSun(const lightSource& sky,MapExtras::MapCache& void lightingEngineViewscreen::doOcupancyAndLights() { // TODO better curve (+red dawn ?) - float daycol = abs((*df::global::cur_year_tick % 1200) - 600.0) / 400.0; + float daycol = 1;//abs((*df::global::cur_year_tick % 1200) - 600.0) / 400.0; lightCell sky_col(daycol, daycol, daycol); lightSource sky(sky_col, 15); @@ -502,9 +505,9 @@ void lightingEngineViewscreen::doOcupancyAndLights() else applyMaterial(tile,*lightDef); } - else if(!d.bits.liquid_type && d.bits.flow_size>3 ) + else if(!d.bits.liquid_type && d.bits.flow_size>0 ) { - applyMaterial(tile,matWater); + applyMaterial(tile,matWater, 1, (float)d.bits.flow_size/7.0f); } if(d.bits.liquid_type && d.bits.flow_size>0) { diff --git a/plugins/rendermax/renderer_light.hpp b/plugins/rendermax/renderer_light.hpp index 4d10951fc..285caa5a2 100644 --- a/plugins/rendermax/renderer_light.hpp +++ b/plugins/rendermax/renderer_light.hpp @@ -148,7 +148,7 @@ private: matLightDef* getMaterial(int matType,int matIndex); //apply material to cell - void applyMaterial(int tileId,const matLightDef& mat,float size=1); + void applyMaterial(int tileId,const matLightDef& mat,float size=1, float thickness = 1); //try to find and apply material, if failed return false, and if def!=null then apply def. bool applyMaterial(int tileId,int matType,int matIndex,float size=1,const matLightDef* def=NULL); size_t inline getIndex(int x,int y) diff --git a/plugins/rendermax/renderer_opengl.hpp b/plugins/rendermax/renderer_opengl.hpp index cdab20af3..6beba522f 100644 --- a/plugins/rendermax/renderer_opengl.hpp +++ b/plugins/rendermax/renderer_opengl.hpp @@ -13,6 +13,7 @@ #include "df/zoom_commands.h" #include "df/texture_handler.h" #include "df/graphic.h" +#include using df::renderer; using df::init; @@ -230,6 +231,14 @@ struct lightCell { return r*other.r+g*other.g+b*other.b; } + lightCell power(const float exp) const + { + return lightCell(pow(r, exp), pow(g, exp), pow(b, exp)); + } + lightCell power(const int exp) const + { + return lightCell(pow(r, exp), pow(g, exp), pow(b, exp)); + } }; struct renderer_test : public renderer_wrap { private: diff --git a/plugins/rendermax/rendermax.lua b/plugins/rendermax/rendermax.lua new file mode 100644 index 000000000..762116c0b --- /dev/null +++ b/plugins/rendermax/rendermax.lua @@ -0,0 +1,69 @@ +--scroll down to the end for configuration +ret={...} +ret=ret[1] +ret.materials={} +ret.buildings={} +ret.special={} +for k,v in pairs(ret) do + _ENV[k]=v +end +-- add material by id (index,mat pair or token string or a type number), flags is a table of strings +-- supported flags: +-- flicker +-- sizeModifiesPower +-- sizeModifiesRange +function addMaterial(id,transparency,emitance,radius,flags) + local matinfo + if type(id)=="string" then + matinfo=dfhack.matinfo.find(id) + elseif type(id)=="table" then + matinfo=dfhack.matinfo.decode(id[1],id[2]) + else + matinfo=dfhack.matinfo.decode(id,0) + end + if matinfo==nil then + error("Material not found") + end + materials[matinfo.type]=materials[matinfo.type] or {} + materials[matinfo.type][matinfo.index]=makeMaterialDef(transparency,emitance,radius,flags) +end +function addBuilding(id,transparency,emitance,radius,flags) + --stuff +end +function makeMaterialDef(transparency,emitance,radius,flags) + local flg + if flags then + flg={} + for k,v in ipairs(flags) do + flg[v]=true + end + end + return {tr=transparency,em=emitance,rad=radius,flags=flg} +end +------------------------------------------------------------------------ +---------------- Configuration Starts Here ------------------------- +------------------------------------------------------------------------ +--special things +special.LAVA=makeMaterialDef({0.8,0.2,0.2},{0.8,0.2,0.2},5) +special.WATER=makeMaterialDef({0.5,0.5,0.8}) +special.FROZEN_LIQUID=makeMaterialDef({0.2,0.7,0.9}) -- ice +special.AMBIENT=makeMaterialDef({0.85,0.85,0.85}) --ambient fog +special.CURSOR=makeMaterialDef({1,1,1},{0.96,0.84,0.03},11, {"flicker"}) +--TODO dragonfire +--TODO daylight +--materials + + +-- glasses +addMaterial("GLASS_GREEN",{0.1,0.9,0.5}) +addMaterial("GLASS_CLEAR",{0.5,0.95,0.9}) +addMaterial("GLASS_CRYSTAL",{0.75,0.95,0.95}) +-- Plants +addMaterial("PLANT:TOWER_CAP",nil,{0.65,0.65,0.65},6) +addMaterial("PLANT:MUSHROOM_CUP_DIMPLE",nil,{0.03,0.03,0.5},3) +addMaterial("PLANT:CAVE MOSS",nil,{0.1,0.1,0.4},2) +addMaterial("PLANT:MUSHROOM_HELMET_PLUMP",nil,{0.2,0.1,0.6},2) +-- inorganics +addMaterial("INORGANIC:ADAMANTINE",{0.1,0.3,0.3},{0.1,0.3,0.3},4) +-- TODO gems +--buildings From b015149ac1b9abcd7ae669ec7d65ff7b19cccc9a Mon Sep 17 00:00:00 2001 From: Japa Date: Tue, 25 Jun 2013 16:44:44 +0530 Subject: [PATCH 038/676] Added cmake info for the config lua. --- plugins/rendermax/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/rendermax/CMakeLists.txt b/plugins/rendermax/CMakeLists.txt index fb8cc7724..a05294a09 100644 --- a/plugins/rendermax/CMakeLists.txt +++ b/plugins/rendermax/CMakeLists.txt @@ -35,3 +35,5 @@ ELSE(UNIX) ENDIF(UNIX) # this makes sure all the stuff is put in proper places and linked to dfhack DFHACK_PLUGIN(rendermax ${PROJECT_SRCS} LINK_LIBRARIES ${PROJECT_LIBS}) +install(FILES rendermax.lua + DESTINATION ${DFHACK_DATA_DESTINATION}) \ No newline at end of file From 13228855eb530bddf7afb188c3052e280b1dd709 Mon Sep 17 00:00:00 2001 From: Japa Date: Tue, 25 Jun 2013 23:13:55 +0530 Subject: [PATCH 039/676] added square light volume, and automatic light radius. --- plugins/rendermax/renderer_light.cpp | 38 +++++++++++++++++++++++++--- plugins/rendermax/renderer_light.hpp | 5 +--- 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index 962f49d28..9d064b959 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -30,6 +30,22 @@ using df::coord2d; const float levelDim=0.2f; const float RootTwo = 1.4142135623730950488016887242097f; +lightSource::lightSource(lightCell power,int radius):power(power),flicker(false) +{ + if(radius >= 0) + this->radius = radius; + else + { + float totalPower = power.r; + if(totalPower < power.g)totalPower = power.g; + if(totalPower < power.b)totalPower = power.b; + if(totalPower > 0 && levelDim > 0) + this->radius = (int)((log(levelDim/totalPower)/log(0.85f))) + 1; + else + this->radius = 0; + } +} + rect2d getMapViewport() { const int AREA_MAP_WIDTH = 23; @@ -99,6 +115,20 @@ void plotCircle(int xm, int ym, int r,std::function setPixel) if (r > x || err > y) err += ++x*2+1; /* e_xy+e_x > 0 or no 2nd y-step */ } while (x < 0); } +void plotSquare(int xm, int ym, int r,std::function setPixel) +{ + for(int x = 0; x <= r; x++) + { + setPixel(xm+r, ym+x); /* I.1 Quadrant */ + setPixel(xm+x, ym+r); /* I.2 Quadrant */ + setPixel(xm+r, ym-x); /* II.1 Quadrant */ + setPixel(xm+x, ym-r); /* II.2 Quadrant */ + setPixel(xm-r, ym-x); /* III.1 Quadrant */ + setPixel(xm-x, ym-r); /* III.2 Quadrant */ + setPixel(xm-r, ym+x); /* IV.1 Quadrant */ + setPixel(xm-x, ym+r); /* IV.2 Quadrant */ + } +} void plotLine(int x0, int y0, int x1, int y1,std::function setPixel) { int dx = abs(x1-x0), sx = x0x>-30000) { - lightSource cursor(lightCell(0.96f,0.84f,0.03f),11); - cursor.flicker=false; + //lightSource cursor(lightCell(9.6f,8.4f,0.3f),-1); + //cursor.flicker=false; int wx=df::global::cursor->x-window_x+vp.first.x; int wy=df::global::cursor->y-window_y+vp.first.y; int tile=getIndex(wx,wy); diff --git a/plugins/rendermax/renderer_light.hpp b/plugins/rendermax/renderer_light.hpp index 285caa5a2..668a729a5 100644 --- a/plugins/rendermax/renderer_light.hpp +++ b/plugins/rendermax/renderer_light.hpp @@ -88,10 +88,7 @@ struct lightSource { } - lightSource(lightCell power,int radius):power(power),radius(radius),flicker(false) - { - - } + lightSource(lightCell power,int radius); float powerSquared()const { return power.r*power.r+power.g*power.g+power.b*power.b; From 048395a2cd0f0e02c4a35a637ed0be2279707bbe Mon Sep 17 00:00:00 2001 From: Warmist Date: Tue, 25 Jun 2013 19:34:38 +0300 Subject: [PATCH 040/676] Added citizen lights(only lights, no oclusion might change later). Added custom dim level (for darker nights and tunnels). --- plugins/rendermax/renderer_light.cpp | 17 ++++++++++++----- plugins/rendermax/renderer_light.hpp | 4 +++- plugins/rendermax/rendermax.lua | 2 ++ 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index 9d064b959..fe5396f14 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -27,7 +27,7 @@ using df::global::gps; using namespace DFHack; using df::coord2d; -const float levelDim=0.2f; + const float RootTwo = 1.4142135623730950488016887242097f; lightSource::lightSource(lightCell power,int radius):power(power),flicker(false) @@ -605,21 +605,20 @@ void lightingEngineViewscreen::doOcupancyAndLights() } if(df::global::cursor->x>-30000) { - //lightSource cursor(lightCell(9.6f,8.4f,0.3f),-1); - //cursor.flicker=false; int wx=df::global::cursor->x-window_x+vp.first.x; int wy=df::global::cursor->y-window_y+vp.first.y; int tile=getIndex(wx,wy); applyMaterial(tile,matCursor); } - lightSource citizen(lightCell(0.80f,0.80f,0.90f),6); + //citizen only emit light, if defined + if(matCitizen.isEmiting) for (int i=0;iunits.active.size();++i) { df::unit *u = df::global::world->units.active[i]; coord2d pos=worldToViewportCoord(coord2d(u->pos.x,u->pos.y),vp,window2d); if(u->pos.z==window_z && isInViewport(pos,vp)) if (DFHack::Units::isCitizen(u) && !u->counters.unconscious) - addLight(getIndex(pos.x,pos.y),citizen); + addLight(getIndex(pos.x,pos.y),matCitizen.makeSource()); } //buildings for(size_t i = 0; i < df::global::world->buildings.all.size(); i++) @@ -757,6 +756,10 @@ int lightingEngineViewscreen::parseSpecial(lua_State* L) LOAD_SPECIAL(FROZEN_LIQUID,matIce); LOAD_SPECIAL(AMBIENT,matAmbience); LOAD_SPECIAL(CURSOR,matCursor); + LOAD_SPECIAL(CITIZEN,matCitizen); + lua_getfield(L,-1,"LevelDim"); + if(!lua_isnil(L,-1) && lua_isnumber(L,-1))engine->levelDim=lua_tonumber(L,-1); + lua_pop(L,1); return 0; } #undef LOAD_SPECIAL @@ -769,6 +772,8 @@ void lightingEngineViewscreen::defaultSettings() matCursor=matLightDef(lightCell(0.96f,0.84f,0.03f),11); matCursor.flicker=true; matWall=matLightDef(lightCell(0,0,0)); + matCitizen=matLightDef(lightCell(0.8f,0.8f,0.9f),6); + levelDim=0.2f; } void lightingEngineViewscreen::loadSettings() { @@ -785,6 +790,7 @@ void lightingEngineViewscreen::loadSettings() if(ret==LUA_ERRFILE) { out.printerr("File not found:%s\n",settingsfile.c_str()); + lua_pop(s,1); } else if(ret==LUA_ERRSYNTAX) { @@ -808,6 +814,7 @@ void lightingEngineViewscreen::loadSettings() Lua::SafeCall(out,s,2,0); } + } } catch(std::exception& e) diff --git a/plugins/rendermax/renderer_light.hpp b/plugins/rendermax/renderer_light.hpp index 668a729a5..307cf465b 100644 --- a/plugins/rendermax/renderer_light.hpp +++ b/plugins/rendermax/renderer_light.hpp @@ -108,7 +108,7 @@ struct matLightDef bool flicker; lightCell emitColor; int radius; - matLightDef(){} + matLightDef():isTransparent(false),isEmiting(false),transparency(0,0,0),emitColor(0,0,0),radius(0){} matLightDef(lightCell transparency,lightCell emit,int rad):isTransparent(true),isEmiting(true), transparency(transparency),emitColor(emit),radius(rad){} matLightDef(lightCell emit,int rad):isTransparent(false),isEmiting(true),emitColor(emit),radius(rad),transparency(0,0,0){} @@ -170,6 +170,8 @@ private: matLightDef matCursor; matLightDef matWall; matLightDef matWater; + matLightDef matCitizen; + float levelDim; //materials std::map,matLightDef> matDefs; diff --git a/plugins/rendermax/rendermax.lua b/plugins/rendermax/rendermax.lua index 762116c0b..86d4a2fe7 100644 --- a/plugins/rendermax/rendermax.lua +++ b/plugins/rendermax/rendermax.lua @@ -49,6 +49,8 @@ special.WATER=makeMaterialDef({0.5,0.5,0.8}) special.FROZEN_LIQUID=makeMaterialDef({0.2,0.7,0.9}) -- ice special.AMBIENT=makeMaterialDef({0.85,0.85,0.85}) --ambient fog special.CURSOR=makeMaterialDef({1,1,1},{0.96,0.84,0.03},11, {"flicker"}) +special.CITIZEN=makeMaterialDef(nil,{0.80f,0.80f,0.90f},6) +special.LevelDim=0.2 -- darkness. do not set to 0 --TODO dragonfire --TODO daylight --materials From 354ec370468a3b3b5ad9b8ff1550e8d4473455c9 Mon Sep 17 00:00:00 2001 From: Warmist Date: Tue, 25 Jun 2013 20:36:53 +0300 Subject: [PATCH 041/676] MAXIMUM SPEED UPDATE!!!!! --- plugins/rendermax/renderer_light.cpp | 14 ++++- plugins/rendermax/renderer_light.hpp | 2 + plugins/rendermax/renderer_opengl.hpp | 6 +- plugins/rendermax/rendermax.cpp | 87 ++++++++++++++++++++------- plugins/rendermax/rendermax.lua | 4 +- 5 files changed, 83 insertions(+), 30 deletions(-) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index fe5396f14..35bc047b4 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -239,6 +239,16 @@ void lightingEngineViewscreen::doFovs() } } } +void lightingEngineViewscreen::clear() +{ + lightMap.assign(lightMap.size(),lightCell(1,1,1)); + tthread::lock_guard guard(myRenderer->dataMutex); + if(lightMap.size()==myRenderer->lightGrid.size()) + { + std::swap(myRenderer->lightGrid,lightMap); + myRenderer->invalidate(); + } +} void lightingEngineViewscreen::calculate() { rect2d vp=getMapViewport(); @@ -270,8 +280,8 @@ void lightingEngineViewscreen::updateWindow() std::swap(lightMap,myRenderer->lightGrid); rect2d vp=getMapViewport(); - //myRenderer->invalidateRect(vp.first.x,vp.first.y,vp.second.x-vp.first.x,vp.second.y-vp.first.y); - myRenderer->invalidate(); + myRenderer->invalidateRect(vp.first.x,vp.first.y,vp.second.x-vp.first.x,vp.second.y-vp.first.y); + //myRenderer->invalidate(); //std::copy(lightMap.begin(),lightMap.end(),myRenderer->lightGrid.begin()); } diff --git a/plugins/rendermax/renderer_light.hpp b/plugins/rendermax/renderer_light.hpp index 307cf465b..e3122a8ca 100644 --- a/plugins/rendermax/renderer_light.hpp +++ b/plugins/rendermax/renderer_light.hpp @@ -75,6 +75,7 @@ public: virtual void updateWindow()=0; virtual void loadSettings()=0; + virtual void clear()=0; protected: renderer_light* myRenderer; @@ -130,6 +131,7 @@ public: void updateWindow(); void loadSettings(); + void clear(); private: df::coord2d worldToViewportCoord(const df::coord2d& in,const DFHack::rect2d& r,const df::coord2d& window2d) ; diff --git a/plugins/rendermax/renderer_opengl.hpp b/plugins/rendermax/renderer_opengl.hpp index 6beba522f..57e81295b 100644 --- a/plugins/rendermax/renderer_opengl.hpp +++ b/plugins/rendermax/renderer_opengl.hpp @@ -124,13 +124,13 @@ public: for(int j=y;jdimy + j; - screen_old[index*4]=0; + screen_old[index*4]=screen[index*4]+1;//ensure tile is different } }; void invalidate() { - //invalidateRect(0,0,df::global::gps->dimx,df::global::gps->dimy); - df::global::gps->force_full_display_count++; + invalidateRect(0,0,df::global::gps->dimx,df::global::gps->dimy); + //df::global::gps->force_full_display_count++; }; protected: renderer* parent; diff --git a/plugins/rendermax/rendermax.cpp b/plugins/rendermax/rendermax.cpp index dda0d5d2c..482dfa6cb 100644 --- a/plugins/rendermax/rendermax.cpp +++ b/plugins/rendermax/rendermax.cpp @@ -3,6 +3,8 @@ #include +#include + #include "Core.h" #include "Console.h" #include "Export.h" @@ -15,12 +17,18 @@ #include "renderer_opengl.hpp" #include "renderer_light.hpp" +#include "df/viewscreen_dwarfmodest.h" +#include "df/viewscreen_dungeonmodest.h" + +using df::viewscreen_dungeonmodest; +using df::viewscreen_dwarfmodest; + using namespace DFHack; using std::vector; using std::string; enum RENDERER_MODE { - MODE_DEFAULT,MODE_TRIPPY,MODE_TRUECOLOR,MODE_LUA,MODE_LIGHT,MODE_LIGHT_OFF + MODE_DEFAULT,MODE_TRIPPY,MODE_TRUECOLOR,MODE_LUA,MODE_LIGHT }; RENDERER_MODE current_mode=MODE_DEFAULT; lightingEngine *engine=NULL; @@ -42,10 +50,44 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector calculate(); + engine->updateWindow(); + } +}; +IMPLEMENT_VMETHOD_INTERPOSE(dwarmode_render_hook, render); + +struct dungeon_render_hook : viewscreen_dungeonmodest{ + typedef df::viewscreen_dungeonmodest interpose_base; + DEFINE_VMETHOD_INTERPOSE(void,render,()) + { + CoreSuspendClaimer suspend; + INTERPOSE_NEXT(render)(); + engine->calculate(); + engine->updateWindow(); + } +}; +IMPLEMENT_VMETHOD_INTERPOSE(dungeon_render_hook, render); + void removeOld() { + if(engine) + { + CoreSuspender lock; + INTERPOSE_HOOK(dwarmode_render_hook,render).apply(false); + INTERPOSE_HOOK(dungeon_render_hook,render).apply(false); + delete engine; + + } if(current_mode!=MODE_DEFAULT) delete df::global::enabler->renderer; + current_mode=MODE_DEFAULT; } void installNew(df::renderer* r,RENDERER_MODE newMode) @@ -231,6 +273,10 @@ DFHACK_PLUGIN_LUA_COMMANDS { DFHACK_LUA_COMMAND(invalidate), DFHACK_LUA_END }; + + + + static command_result rendermax(color_ostream &out, vector & parameters) { if(parameters.size()==0) @@ -304,19 +350,22 @@ static command_result rendermax(color_ostream &out, vector & parameters } else if(cmd=="light") { - if(current_mode!=MODE_LIGHT || current_mode!=MODE_LIGHT_OFF) + if(current_mode!=MODE_LIGHT) { removeOld(); renderer_light *myRender=new renderer_light(df::global::enabler->renderer); installNew(myRender,MODE_LIGHT); engine=new lightingEngineViewscreen(myRender); - engine->calculate(); - engine->updateWindow(); + INTERPOSE_HOOK(dwarmode_render_hook,render).apply(true); + INTERPOSE_HOOK(dungeon_render_hook,render).apply(true); } else if(current_mode==MODE_LIGHT && parameters.size()>1) { if(parameters[1]=="reload") + { + CoreSuspender suspend; engine->loadSettings(); + } } else out.printerr("Light mode already enabled"); @@ -327,8 +376,6 @@ static command_result rendermax(color_ostream &out, vector & parameters { if(current_mode==MODE_DEFAULT) out.print("%s\n","Not installed, doing nothing."); - else if(current_mode==MODE_LIGHT) - current_mode=MODE_LIGHT_OFF; else removeOld(); @@ -336,27 +383,21 @@ static command_result rendermax(color_ostream &out, vector & parameters } return CR_WRONG_USAGE; } -DFhackCExport command_result plugin_onupdate (color_ostream &out) + +DFhackCExport command_result plugin_shutdown(color_ostream &) +{ + removeOld(); + return CR_OK; +} +DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) { - if(engine) + if(event==SC_VIEWSCREEN_CHANGED) { - if(current_mode==MODE_LIGHT_OFF) + CoreSuspendClaimer suspender; + if(current_mode==MODE_LIGHT) { - delete engine; - engine=0; - removeOld(); - } - else - { - engine->calculate(); - engine->updateWindow(); + engine->clear(); } } - - return CR_OK; -} -DFhackCExport command_result plugin_shutdown(color_ostream &) -{ - removeOld(); return CR_OK; } \ No newline at end of file diff --git a/plugins/rendermax/rendermax.lua b/plugins/rendermax/rendermax.lua index 86d4a2fe7..4b19e2485 100644 --- a/plugins/rendermax/rendermax.lua +++ b/plugins/rendermax/rendermax.lua @@ -49,8 +49,8 @@ special.WATER=makeMaterialDef({0.5,0.5,0.8}) special.FROZEN_LIQUID=makeMaterialDef({0.2,0.7,0.9}) -- ice special.AMBIENT=makeMaterialDef({0.85,0.85,0.85}) --ambient fog special.CURSOR=makeMaterialDef({1,1,1},{0.96,0.84,0.03},11, {"flicker"}) -special.CITIZEN=makeMaterialDef(nil,{0.80f,0.80f,0.90f},6) -special.LevelDim=0.2 -- darkness. do not set to 0 +special.CITIZEN=makeMaterialDef(nil,{0.80,0.80,0.90},6) +special.LevelDim=0.2 -- darkness. Do not set to 0 --TODO dragonfire --TODO daylight --materials From 5b0cb86278aea19dd7e0dfe55ff8afc9fa07344c Mon Sep 17 00:00:00 2001 From: Japa Date: Wed, 26 Jun 2013 19:12:14 +0530 Subject: [PATCH 042/676] Made the lighting engine multithreaded. --- plugins/rendermax/CMakeLists.txt | 4 +- plugins/rendermax/renderer_light.cpp | 184 +++++++++++++++++++++------ plugins/rendermax/renderer_light.hpp | 24 +++- 3 files changed, 167 insertions(+), 45 deletions(-) diff --git a/plugins/rendermax/CMakeLists.txt b/plugins/rendermax/CMakeLists.txt index a05294a09..ef6621dc1 100644 --- a/plugins/rendermax/CMakeLists.txt +++ b/plugins/rendermax/CMakeLists.txt @@ -22,6 +22,7 @@ IF(UNIX) SET(PROJECT_LIBS # add any extra linux libs here lua + dfhack-tinythread ${PROJECT_LIBS} ) # windows @@ -29,6 +30,7 @@ ELSE(UNIX) SET(PROJECT_LIBS # add any extra windows libs here lua + dfhack-tinythread ${PROJECT_LIBS} $(NOINHERIT) ) @@ -36,4 +38,4 @@ ENDIF(UNIX) # this makes sure all the stuff is put in proper places and linked to dfhack DFHACK_PLUGIN(rendermax ${PROJECT_SRCS} LINK_LIBRARIES ${PROJECT_LIBS}) install(FILES rendermax.lua - DESTINATION ${DFHACK_DATA_DESTINATION}) \ No newline at end of file + DESTINATION ${DFHACK_DATA_DESTINATION}) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index 35bc047b4..64c866e0d 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -4,6 +4,7 @@ #include #include +#include "tinythread.h" #include "LuaTools.h" @@ -23,13 +24,103 @@ #include "df/plant.h" #include "df/plant_raw.h" +#include + using df::global::gps; using namespace DFHack; using df::coord2d; - +using namespace tthread; const float RootTwo = 1.4142135623730950488016887242097f; + +void lightingEngineViewscreen::lightWorkerThread(void * arg) +{ + int thisIndex; + std::vector canvas; + while(1) + { + writeMutex.lock(); + writeMutex.unlock(); //Don't start till write access is given. + indexMutex.lock(); + if(nextIndex == -1) //The worker threads should keep going until, and including, index 0. + { + indexMutex.unlock(); + break; + } + else if(nextIndex == -2) + { + indexMutex.unlock(); + return; + } + else + { + thisIndex = nextIndex; + nextIndex--; + indexMutex.unlock(); + if(canvas.size() != lightMap.size()) + { + canvas.resize(lightMap.size(), lightCell(0,0,0)); + } + doLight(canvas, thisIndex); + + } + } + writeMutex.lock(); + for(int i = 0; i < canvas.size(); i++) + { + lightMap[i] = blend(lightMap[i], canvas[i]); + + } + writeMutex.unlock(); + canvas.assign(canvas.size(),lightCell(0,0,0)); +} + +lightingEngineViewscreen::~lightingEngineViewscreen() +{ + indexMutex.lock(); + nextIndex = -2; + indexMutex.unlock(); + writeMutex.unlock(); + for(int i = 0; i < threadList.size(); i++) + { + if(threadList[i]) + threadList[i]->join(); + } +} + +void threadStub(void * arg) +{ + if(arg) + ((lightingEngineViewscreen*)arg)->lightWorkerThread(0); +} + +void lightingEngineViewscreen::doLightThreads() +{ + nextIndex = 0; + int num_threads = thread::hardware_concurrency(); + if(num_threads < 1) num_threads = 1; + if(threadList.empty()) + { + threadList.resize(num_threads, NULL); + } + for(int i = 0; i < num_threads; i++) + { + threadList[i] = new thread(threadStub, this); + } + nextIndex = lightMap.size() - 1; //start at the largest valid index + writeMutex.unlock(); + for(int i = 0; i < num_threads; i++) + { + threadList[i]->join(); + delete threadList[i]; + threadList[i]=0; + } + writeMutex.lock(); +} + + + lightSource::lightSource(lightCell power,int radius):power(power),flicker(false) { if(radius >= 0) @@ -89,6 +180,7 @@ lightingEngineViewscreen::lightingEngineViewscreen(renderer_light* target):light reinit(); defaultSettings(); loadSettings(); + writeMutex.lock(); //This is needed for later when the threads will all want to write to the buffer. } void lightingEngineViewscreen::reinit() @@ -147,11 +239,15 @@ void plotLine(int x0, int y0, int x1, int y1,std::function & target, lightCell& power,int dx,int dy,int tx,int ty) { if(isInViewport(coord2d(tx,ty),mapPort)) { @@ -182,9 +278,9 @@ bool lightingEngineViewscreen::lightUpCell(lightCell& power,int dx,int dy,int tx return false; } //float dt=sqrt(dsq); - lightCell oldCol=lightMap[tile]; - lightCell ncol=blend(power,oldCol); - lightMap[tile]=ncol; + lightCell oldCol=target[tile]; + lightCell ncol=blendMax(power,oldCol); + target[tile]=ncol; if(wallhack) return false; @@ -194,50 +290,56 @@ bool lightingEngineViewscreen::lightUpCell(lightCell& power,int dx,int dy,int tx else return false; } -void lightingEngineViewscreen::doRay(lightCell power,int cx,int cy,int tx,int ty) +void lightingEngineViewscreen::doRay(std::vector & target, lightCell power,int cx,int cy,int tx,int ty) { using namespace std::placeholders; lightCell curPower=power; - plotLine(cx,cy,tx,ty,std::bind(&lightingEngineViewscreen::lightUpCell,this,std::ref(curPower),_1,_2,_3,_4)); + plotLine(cx,cy,tx,ty,std::bind(&lightingEngineViewscreen::lightUpCell,this,std::ref(target),std::ref(curPower),_1,_2,_3,_4)); } -void lightingEngineViewscreen::doFovs() + +void lightingEngineViewscreen::doLight(std::vector & target, int index) { - mapPort=getMapViewport(); using namespace std::placeholders; - - for(int i=mapPort.first.x;i0) + { + coord2d coord = getCoords(index); + int i = coord.x; + int j = coord.y; + lightCell power=csource.power; + int radius =csource.radius; + if(csource.flicker) { - lightSource& csource=lights[getIndex(i,j)]; - if(csource.radius>0) - { - lightCell power=csource.power; - int radius =csource.radius; - if(csource.flicker) - { - float flicker=(rand()/(float)RAND_MAX)/2.0f+0.5f; - radius*=flicker; - power=power*flicker; - } - int surrounds = 0; - lightCell curPower; + float flicker=(rand()/(float)RAND_MAX)/2.0f+0.5f; + radius*=flicker; + power=power*flicker; + } + int surrounds = 0; + lightCell curPower; - lightUpCell(curPower = power, 0, 0,i+0, j+0); - { - surrounds += lightUpCell(curPower = power, 0, 1,i+0, j+1); - surrounds += lightUpCell(curPower = power, 1, 1,i+1, j+1); - surrounds += lightUpCell(curPower = power, 1, 0,i+1, j+0); - surrounds += lightUpCell(curPower = power, 1,-1,i+1, j-1); - surrounds += lightUpCell(curPower = power, 0,-1,i+0, j-1); - surrounds += lightUpCell(curPower = power,-1,-1,i-1, j-1); - surrounds += lightUpCell(curPower = power,-1, 0,i-1, j+0); - surrounds += lightUpCell(curPower = power,-1, 1,i-1, j+1); - } - if(surrounds) - plotSquare(i,j,radius, - std::bind(&lightingEngineViewscreen::doRay,this,power,i,j,_1,_2)); - } + lightUpCell(target, curPower = power, 0, 0,i+0, j+0); + { + surrounds += lightUpCell(target, curPower = power, 0, 1,i+0, j+1); + surrounds += lightUpCell(target, curPower = power, 1, 1,i+1, j+1); + surrounds += lightUpCell(target, curPower = power, 1, 0,i+1, j+0); + surrounds += lightUpCell(target, curPower = power, 1,-1,i+1, j-1); + surrounds += lightUpCell(target, curPower = power, 0,-1,i+0, j-1); + surrounds += lightUpCell(target, curPower = power,-1,-1,i-1, j-1); + surrounds += lightUpCell(target, curPower = power,-1, 0,i-1, j+0); + surrounds += lightUpCell(target, curPower = power,-1, 1,i-1, j+1); } + if(surrounds) + { + plotSquare(i,j,radius, + std::bind(&lightingEngineViewscreen::doRay,this,std::ref(target),power,i,j,_1,_2)); + } + } +} + +void lightingEngineViewscreen::doFovs() +{ + mapPort=getMapViewport(); + doLightThreads(); } void lightingEngineViewscreen::clear() { diff --git a/plugins/rendermax/renderer_light.hpp b/plugins/rendermax/renderer_light.hpp index e3122a8ca..290863aee 100644 --- a/plugins/rendermax/renderer_light.hpp +++ b/plugins/rendermax/renderer_light.hpp @@ -124,7 +124,7 @@ class lightingEngineViewscreen:public lightingEngine { public: lightingEngineViewscreen(renderer_light* target); - + ~lightingEngineViewscreen(); void reinit(); void calculate(); @@ -140,9 +140,10 @@ private: void doSun(const lightSource& sky,MapExtras::MapCache& map); void doOcupancyAndLights(); lightCell propogateSun(MapExtras::Block* b, int x,int y,const lightCell& in,bool lastLevel); - void doRay(lightCell power,int cx,int cy,int tx,int ty); + void doRay(std::vector & target, lightCell power,int cx,int cy,int tx,int ty); void doFovs(); - bool lightUpCell(lightCell& power,int dx,int dy,int tx,int ty); + void doLight(std::vector & target, int index); + bool lightUpCell(std::vector & target, lightCell& power,int dx,int dy,int tx,int ty); bool addLight(int tileId,const lightSource& light); matLightDef* getMaterial(int matType,int matIndex); @@ -154,10 +155,25 @@ private: { return x*h+y; } + df::coord2d inline getCoords(int index) + { + return df::coord2d(index/h, index%h); + } //maps std::vector lightMap; + std::vector lightMap2; std::vector ocupancy; std::vector lights; + + //Threading stuff + tthread::mutex indexMutex; + tthread::mutex writeMutex; + int nextIndex; + std::vector threadList; + void doLightThreads(); +public: + void lightWorkerThread(void * arg); +private: //settings ///set up sane settings if setting file does not exist. @@ -180,4 +196,6 @@ private: int w,h; DFHack::rect2d mapPort; }; +lightCell blend(lightCell a,lightCell b); +lightCell blendMax(lightCell a,lightCell b); #endif From 3433eb15fb3d1133e73eb1a331d19ea015e6a4ff Mon Sep 17 00:00:00 2001 From: Warmist Date: Tue, 25 Jun 2013 22:49:52 +0300 Subject: [PATCH 043/676] fixed (?) ice and made dragon blood glow a little. --- plugins/rendermax/renderer_light.cpp | 55 +++++++++++++++++++++++++--- plugins/rendermax/rendermax.lua | 2 + 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index 64c866e0d..f9b3f07e6 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -467,7 +467,7 @@ lightCell lightingEngineViewscreen::propogateSun(MapExtras::Block* b, int x,int const lightCell matStairCase(0.9f,0.9f,0.9f); lightCell ret=in; coord2d innerCoord(x,y); - df::tiletype type = b->tiletypeAt(innerCoord); + df::tiletype type = b->staticTiletypeAt(innerCoord); df::tile_designation d = b->DesignationAt(innerCoord); //df::tile_occupancy o = b->OccupancyAt(innerCoord); df::tiletype_shape shape = ENUM_ATTR(tiletype,shape,type); @@ -477,9 +477,18 @@ lightCell lightingEngineViewscreen::propogateSun(MapExtras::Block* b, int x,int matLightDef* lightDef; if(tileMat==df::tiletype_material::FROZEN_LIQUID) - lightDef=&matIce; - else - lightDef=getMaterial(mat.mat_type,mat.mat_index); + { + df::tiletype typeIce = b->tiletypeAt(innerCoord); + df::tiletype_shape shapeIce = ENUM_ATTR(tiletype,shape,typeIce); + df::tiletype_shape_basic basicShapeIce = ENUM_ATTR(tiletype_shape,basic_shape,shapeIce); + if(basicShapeIce==df::tiletype_shape_basic::Wall) + ret*=matIce.transparency; + else if(basicShapeIce==df::tiletype_shape_basic::Floor || basicShapeIce==df::tiletype_shape_basic::Ramp || shapeIce==df::tiletype_shape::STAIR_UP) + if(!lastLevel) + ret*=matIce.transparency.power(1.0f/7.0f); + } + + lightDef=getMaterial(mat.mat_type,mat.mat_index); if(!lightDef || !lightDef->isTransparent) lightDef=&matWall; @@ -489,6 +498,7 @@ lightCell lightingEngineViewscreen::propogateSun(MapExtras::Block* b, int x,int } else if(basic_shape==df::tiletype_shape_basic::Floor || basic_shape==df::tiletype_shape_basic::Ramp || shape==df::tiletype_shape::STAIR_UP) { + if(!lastLevel) ret*=lightDef->transparency.power(1.0f/7.0f); } @@ -630,9 +640,9 @@ void lightingEngineViewscreen::doOcupancyAndLights() df::tiletype_shape shape = ENUM_ATTR(tiletype,shape,type); df::tiletype_shape_basic basic_shape = ENUM_ATTR(tiletype_shape, basic_shape, shape); df::tiletype_material tileMat= ENUM_ATTR(tiletype,material,type); - + DFHack::t_matpair mat=b->staticMaterialAt(gpos); - + matLightDef* lightDef=getMaterial(mat.mat_type,mat.mat_index); if(!lightDef || !lightDef->isTransparent) lightDef=&matWall; @@ -667,6 +677,8 @@ void lightingEngineViewscreen::doOcupancyAndLights() } } } + + } df::map_block* block=b->getRaw(); @@ -714,6 +726,36 @@ void lightingEngineViewscreen::doOcupancyAndLights() applyMaterial(tile,419,cPlant->material); } } + //blood and other goo + for(int i=0;iblock_events.size();i++) + { + df::block_square_event* ev=block->block_events[i]; + df::block_square_event_type ev_type=ev->getType(); + if(ev_type==df::block_square_event_type::material_spatter) + { + df::block_square_event_material_spatterst* spatter=static_cast(ev); + matLightDef* m=getMaterial(spatter->mat_type,spatter->mat_index); + if(!m) + continue; + if(!m->isEmiting) + continue; + for(int x=0;x<16;x++) + for(int y=0;y<16;y++) + { + df::coord2d pos; + pos.x = blockX*16+x; + pos.y = blockY*16+y; + int16_t amount=spatter->amount[x][y]; + if(amount<=0) + continue; + pos=worldToViewportCoord(pos,vp,window2d); + if(isInViewport(pos,vp)) + { + addLight(getIndex(pos.x,pos.y),m->makeSource((float)amount/100)); + } + } + } + } } if(df::global::cursor->x>-30000) { @@ -767,6 +809,7 @@ void lightingEngineViewscreen::doOcupancyAndLights() } } } + } lightCell lua_parseLightCell(lua_State* L) { diff --git a/plugins/rendermax/rendermax.lua b/plugins/rendermax/rendermax.lua index 4b19e2485..4e335ec86 100644 --- a/plugins/rendermax/rendermax.lua +++ b/plugins/rendermax/rendermax.lua @@ -67,5 +67,7 @@ addMaterial("PLANT:CAVE MOSS",nil,{0.1,0.1,0.4},2) addMaterial("PLANT:MUSHROOM_HELMET_PLUMP",nil,{0.2,0.1,0.6},2) -- inorganics addMaterial("INORGANIC:ADAMANTINE",{0.1,0.3,0.3},{0.1,0.3,0.3},4) +-- creature stuff +addMaterial("CREATURE:DRAGON:BLOOD",nil,{0.6,0.1,0.1},4) -- TODO gems --buildings From 740e3ebc51e93d95940513f59b97abb6a4d46843 Mon Sep 17 00:00:00 2001 From: Warmist Date: Wed, 26 Jun 2013 01:18:26 +0300 Subject: [PATCH 044/676] Implemented building support. --- plugins/rendermax/renderer_light.cpp | 124 +++++++++++++++++++++++---- plugins/rendermax/renderer_light.hpp | 17 +++- plugins/rendermax/rendermax.lua | 61 ++++++++++++- 3 files changed, 181 insertions(+), 21 deletions(-) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index f9b3f07e6..311d2c4ef 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -434,6 +434,14 @@ matLightDef* lightingEngineViewscreen::getMaterial(int matType,int matIndex) else return NULL; } +buildingLightDef* lightingEngineViewscreen::getBuilding(df::building* bld) +{ + auto it=buildingDefs.find(std::make_tuple((int)bld->getType(),(int)bld->getSubtype(),(int)bld->getCustomType())); + if(it!=buildingDefs.end()) + return &it->second; + else + return NULL; +} void lightingEngineViewscreen::applyMaterial(int tileId,const matLightDef& mat,float size, float thickness) { if(mat.isTransparent) @@ -636,6 +644,11 @@ void lightingEngineViewscreen::doOcupancyAndLights() df::tiletype type = b->tiletypeAt(gpos); df::tile_designation d = b->DesignationAt(gpos); + if(d.bits.hidden) + { + curCell=lightCell(0,0,0); + continue; // do not process hidden stuff + } //df::tile_occupancy o = b->OccupancyAt(gpos); df::tiletype_shape shape = ENUM_ATTR(tiletype,shape,type); df::tiletype_shape_basic basic_shape = ENUM_ATTR(tiletype_shape, basic_shape, shape); @@ -646,7 +659,7 @@ void lightingEngineViewscreen::doOcupancyAndLights() matLightDef* lightDef=getMaterial(mat.mat_type,mat.mat_index); if(!lightDef || !lightDef->isTransparent) lightDef=&matWall; - if(shape==df::tiletype_shape::BROOK_BED || d.bits.hidden ) + if(shape==df::tiletype_shape::BROOK_BED ) { curCell=lightCell(0,0,0); } @@ -778,34 +791,58 @@ void lightingEngineViewscreen::doOcupancyAndLights() for(size_t i = 0; i < df::global::world->buildings.all.size(); i++) { df::building *bld = df::global::world->buildings.all[i]; + if(window_z!=bld->z) continue; + if(bld->getBuildStage()getMaxBuildStage()) //only work if fully built + continue; + df::coord2d p1(bld->x1,bld->y1); df::coord2d p2(bld->x2,bld->y2); p1=worldToViewportCoord(p1,vp,window2d); p2=worldToViewportCoord(p1,vp,window2d); if(isInViewport(p1,vp)||isInViewport(p2,vp)) { + int tile=getIndex(p1.x,p1.y); //TODO multitile buildings. How they would work? df::building_type type = bld->getType(); - - if (type == df::enums::building_type::WindowGlass || type==df::enums::building_type::WindowGem) - { - applyMaterial(tile,bld->mat_type,bld->mat_index); - } - if (type == df::enums::building_type::Table) + buildingLightDef* def=getBuilding(bld); + if(!def) + continue; + if(def->poweredOnly && bld->isUnpowered()) + continue; + if(type==df::enums::building_type::Door) { - addLight(tile,candle); + df::building_doorst* door=static_cast(bld); + if(!door->door_flags.bits.closed) + continue; } - if (type==df::enums::building_type::Statue) + + + if(def->useMaterial) { - addLight(tile,torch); + matLightDef* mat=getMaterial(bld->mat_type,bld->mat_index); + if(!mat)mat=&matWall; + if(def->light.isEmiting) + { + addLight(tile,def->light.makeSource()); + } + else if(mat->isEmiting) + { + addLight(tile,mat->makeSource()); + } + if(def->light.isTransparent) + { + ocupancy[tile]*=def->light.transparency; + } + else + { + ocupancy[tile]*=mat->transparency; + } } - if(type==df::enums::building_type::Door) + else { - df::building_doorst* door=static_cast(bld); - if(door->door_flags.bits.closed) - applyMaterial(tile,bld->mat_type,bld->mat_index,1,&matWall); + applyMaterial(tile,def->light); } } } @@ -833,6 +870,10 @@ lightCell lua_parseLightCell(lua_State* L) //Lua::GetOutput(L)->print("got cell(%f,%f,%f)\n",ret.r,ret.g,ret.b); return ret; } +#define GETLUAFLAG(field,name) lua_getfield(L,-1,"flags");\ + if(lua_isnil(L,-1)){field=false;}\ + else{lua_getfield(L,-1,#name);field=lua_isnil(L,-1);lua_pop(L,1);}\ + lua_pop(L,1) matLightDef lua_parseMatDef(lua_State* L) { @@ -861,7 +902,10 @@ matLightDef lua_parseMatDef(lua_State* L) } else lua_pop(L,1); - //todo flags + GETLUAFLAG(ret.flicker,"flicker"); + GETLUAFLAG(ret.useThickness,"useThickness"); + GETLUAFLAG(ret.sizeModifiesPower,"sizeModifiesPower"); + GETLUAFLAG(ret.sizeModifiesRange,"sizeModifiesRange"); return ret; } int lightingEngineViewscreen::parseMaterials(lua_State* L) @@ -918,6 +962,48 @@ int lightingEngineViewscreen::parseSpecial(lua_State* L) return 0; } #undef LOAD_SPECIAL +int lightingEngineViewscreen::parseBuildings(lua_State* L) +{ + auto engine= (lightingEngineViewscreen*)lua_touserdata(L, 1); + engine->buildingDefs.clear(); + Lua::StackUnwinder unwinder(L); + lua_getfield(L,2,"buildings"); + if(!lua_istable(L,-1)) + { + luaL_error(L,"Buildings table not found."); + return 0; + } + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + int type=lua_tonumber(L,-2); + if(!lua_istable(L,-1)) + { + luaL_error(L,"Broken building definitions."); + } + //os->print("Processing type:%d\n",type); + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + int subtype=lua_tonumber(L,-2); + //os->print("\tProcessing subtype:%d\n",index); + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + int custom=lua_tonumber(L,-2); + //os->print("\tProcessing custom:%d\n",index); + buildingLightDef current; + current.light=lua_parseMatDef(L); + engine->buildingDefs[std::make_tuple(type,subtype,custom)]=current; + GETLUAFLAG(current.poweredOnly,"poweredOnly"); + GETLUAFLAG(current.useMaterial,"useMaterial"); + lua_pop(L, 1); + } + + lua_pop(L, 1); + } + lua_pop(L, 1); + } + lua_pop(L,1); + return 0; +} void lightingEngineViewscreen::defaultSettings() { matAmbience=matLightDef(lightCell(0.85f,0.85f,0.85f)); @@ -967,7 +1053,12 @@ void lightingEngineViewscreen::loadSettings() lua_pushlightuserdata(s, this); lua_pushvalue(s,env); Lua::SafeCall(out,s,2,0); - + + lua_pushcfunction(s, parseBuildings); + lua_pushlightuserdata(s, this); + lua_pushvalue(s,env); + Lua::SafeCall(out,s,2,0); + out.print("%d buildings loaded\n",buildingDefs.size()); } } @@ -978,3 +1069,4 @@ void lightingEngineViewscreen::loadSettings() } lua_pop(s,1); } +#undef GETLUAFLAG diff --git a/plugins/rendermax/renderer_light.hpp b/plugins/rendermax/renderer_light.hpp index 290863aee..556d53a68 100644 --- a/plugins/rendermax/renderer_light.hpp +++ b/plugins/rendermax/renderer_light.hpp @@ -3,6 +3,7 @@ #include "renderer_opengl.hpp" #include "Types.h" #include +#include #include "modules/MapCache.h" struct renderer_light : public renderer_wrap { @@ -99,11 +100,10 @@ struct lightSource }; struct matLightDef { - int mat_index; - int mat_type; bool isTransparent; lightCell transparency; bool isEmiting; + bool useThickness; bool sizeModifiesPower; bool sizeModifiesRange; bool flicker; @@ -120,6 +120,12 @@ struct matLightDef return lightSource(emitColor,radius); } }; +struct buildingLightDef +{ + matLightDef light; + bool poweredOnly; + bool useMaterial; +}; class lightingEngineViewscreen:public lightingEngine { public: @@ -147,10 +153,13 @@ private: bool addLight(int tileId,const lightSource& light); matLightDef* getMaterial(int matType,int matIndex); + buildingLightDef* getBuilding(df::building* bld); + //apply material to cell void applyMaterial(int tileId,const matLightDef& mat,float size=1, float thickness = 1); //try to find and apply material, if failed return false, and if def!=null then apply def. bool applyMaterial(int tileId,int matType,int matIndex,float size=1,const matLightDef* def=NULL); + size_t inline getIndex(int x,int y) { return x*h+y; @@ -181,6 +190,7 @@ private: static int parseMaterials(lua_State* L); static int parseSpecial(lua_State* L); + static int parseBuildings(lua_State* L); //special stuff matLightDef matLava; matLightDef matIce; @@ -192,7 +202,8 @@ private: float levelDim; //materials std::map,matLightDef> matDefs; - + //buildings + std::map,buildingLightDef> buildingDefs; int w,h; DFHack::rect2d mapPort; }; diff --git a/plugins/rendermax/rendermax.lua b/plugins/rendermax/rendermax.lua index 4e335ec86..785cb8b1d 100644 --- a/plugins/rendermax/rendermax.lua +++ b/plugins/rendermax/rendermax.lua @@ -8,8 +8,9 @@ for k,v in pairs(ret) do _ENV[k]=v end -- add material by id (index,mat pair or token string or a type number), flags is a table of strings --- supported flags: +-- supported flags (but not implemented): -- flicker +-- useThickness -- use thickness of stuff in transparency calculation -- sizeModifiesPower -- sizeModifiesRange function addMaterial(id,transparency,emitance,radius,flags) @@ -27,8 +28,58 @@ function addMaterial(id,transparency,emitance,radius,flags) materials[matinfo.type]=materials[matinfo.type] or {} materials[matinfo.type][matinfo.index]=makeMaterialDef(transparency,emitance,radius,flags) end +function buildingLookUp(id) + local tokens={} + local lookup={ Workshop=df.workshop_type,Furnace=df.furnace_type,Trap=df.trap_type, + SiegeEngine=siegeengine_type} + for i in string.gmatch(id, "[^:]+") do + table.insert(tokens,i) + end + local ret={} + ret.type=df.building_type[tokens[1]] + if tokens[2] then + local type_array=lookup[tokens[1]] + if type_array then + ret.subtype=type_array[tokens[2]] + end + if tokens[2]=="Custom" and tokens[3] then --TODO cache for faster lookup + if ret.type==df.building_type.Workshop then + for k,v in pairs(df.global.world.raws.buildings.workshops) do + if v.code==tokens[3] then + ret.custom=v.id + break + end + end + elseif ret.type==df.building_type.Furnace then + for k,v in pairs(df.global.world.raws.buildings.furnaces) do + if v.code==tokens[3] then + ret.custom=v.id + break + end + end + end + end + end + return ret +end +-- add building by id (string e.g. "Statue" or "Workshop:Masons", flags is a table of strings +-- supported flags: +-- useMaterial --uses material, but the defined things overwrite +-- poweredOnly --glow only when powered function addBuilding(id,transparency,emitance,radius,flags) - --stuff + local bld=buildingLookUp(id) + local mat=makeMaterialDef(transparency,emitance,radius,flags) + buildings[bld.type]=buildings[bld.type] or {} + if bld.subtype then + if bld.custom then + buildings[bld.type][bld.subtype]=buildings[bld.type][bld.subtype] or {} + buildings[bld.type][bld.subtype][bld.custom]=mat + else + buildings[bld.type][bld.subtype]={[-1]=mat} + end + else + buildings[bld.type][-1]={[-1]=mat} + end end function makeMaterialDef(transparency,emitance,radius,flags) local flg @@ -43,6 +94,7 @@ end ------------------------------------------------------------------------ ---------------- Configuration Starts Here ------------------------- ------------------------------------------------------------------------ +is_computer_quantum=false -- will enable more costly parts in the future --special things special.LAVA=makeMaterialDef({0.8,0.2,0.2},{0.8,0.2,0.2},5) special.WATER=makeMaterialDef({0.5,0.5,0.8}) @@ -71,3 +123,8 @@ addMaterial("INORGANIC:ADAMANTINE",{0.1,0.3,0.3},{0.1,0.3,0.3},4) addMaterial("CREATURE:DRAGON:BLOOD",nil,{0.6,0.1,0.1},4) -- TODO gems --buildings +addBuilding("Statue",{1,1,1},{0.9,0.75,0.3},8) +addBuilding("Bed",{1,1,1},{0.3,0.2,0.0},2) +addBuilding("WindowGlass",nil,nil,0,{"useMaterial"}) +addBuilding("WindowGem",nil,nil,0,{"useMaterial"}) +addBuilding("Door",nil,nil,0,{"useMaterial"}) -- special case, only closed door obstruct/emit light \ No newline at end of file From 70e1320e6fb1f4710dfe88f58cc542e623bcd64f Mon Sep 17 00:00:00 2001 From: Japa Date: Wed, 26 Jun 2013 19:26:11 +0530 Subject: [PATCH 045/676] removed something I had for testing. --- plugins/rendermax/renderer_light.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/rendermax/renderer_light.hpp b/plugins/rendermax/renderer_light.hpp index 556d53a68..00ebc43ce 100644 --- a/plugins/rendermax/renderer_light.hpp +++ b/plugins/rendermax/renderer_light.hpp @@ -170,7 +170,6 @@ private: } //maps std::vector lightMap; - std::vector lightMap2; std::vector ocupancy; std::vector lights; From 7c0a734c97dbf90497882366d04756d564fa61e7 Mon Sep 17 00:00:00 2001 From: Warmist Date: Wed, 26 Jun 2013 20:19:30 +0300 Subject: [PATCH 046/676] Added building size. Removed material flags (on by default) --- plugins/rendermax/renderer_light.cpp | 50 +++++++++++++++++----------- plugins/rendermax/renderer_light.hpp | 13 +++++--- plugins/rendermax/rendermax.lua | 9 ++--- 3 files changed, 44 insertions(+), 28 deletions(-) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index 311d2c4ef..4c4c96401 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -402,6 +402,13 @@ bool lightingEngineViewscreen::addLight(int tileId,const lightSource& light) lights[tileId].flicker=true; return wasLight; } +void lightingEngineViewscreen::addOclusion(int tileId,const lightCell& c,float thickness) +{ + if(thickness > 0.999 && thickness < 1.001) + ocupancy[tileId]*=c; + else + ocupancy[tileId]*=(c.power(thickness)); +} lightCell getStandartColor(int colorId) { return lightCell(df::global::enabler->ccolor[colorId][0]/255.0f, @@ -446,32 +453,30 @@ void lightingEngineViewscreen::applyMaterial(int tileId,const matLightDef& mat,f { if(mat.isTransparent) { - if(thickness > 0.999 && thickness < 1.001) - ocupancy[tileId]*=mat.transparency; - else - ocupancy[tileId]*=(mat.transparency.power(thickness)); + addOclusion(tileId,mat.transparency,thickness); } else ocupancy[tileId]=lightCell(0,0,0); if(mat.isEmiting) addLight(tileId,mat.makeSource(size)); } -bool lightingEngineViewscreen::applyMaterial(int tileId,int matType,int matIndex,float size,const matLightDef* def) +bool lightingEngineViewscreen::applyMaterial(int tileId,int matType,int matIndex,float size,float thickness,const matLightDef* def) { matLightDef* m=getMaterial(matType,matIndex); if(m) { - applyMaterial(tileId,*m,size); + applyMaterial(tileId,*m,size,thickness); return true; } else if(def) { - applyMaterial(tileId,*def,size); + applyMaterial(tileId,*def,size,thickness); } return false; } lightCell lightingEngineViewscreen::propogateSun(MapExtras::Block* b, int x,int y,const lightCell& in,bool lastLevel) { + //TODO unify under addLight/addOclusion const lightCell matStairCase(0.9f,0.9f,0.9f); lightCell ret=in; coord2d innerCoord(x,y); @@ -647,7 +652,7 @@ void lightingEngineViewscreen::doOcupancyAndLights() if(d.bits.hidden) { curCell=lightCell(0,0,0); - continue; // do not process hidden stuff + continue; // do not process hidden stuff, TODO other hidden stuff } //df::tile_occupancy o = b->OccupancyAt(gpos); df::tiletype_shape shape = ENUM_ATTR(tiletype,shape,type); @@ -672,11 +677,11 @@ void lightingEngineViewscreen::doOcupancyAndLights() } else if(!d.bits.liquid_type && d.bits.flow_size>0 ) { - applyMaterial(tile,matWater, 1, (float)d.bits.flow_size/7.0f); + applyMaterial(tile,matWater, (float)d.bits.flow_size/7.0f, (float)d.bits.flow_size/7.0f); } if(d.bits.liquid_type && d.bits.flow_size>0) { - applyMaterial(tile,matLava); + applyMaterial(tile,matLava,(float)d.bits.flow_size/7.0f,(float)d.bits.flow_size/7.0f); } else if(shape==df::tiletype_shape::EMPTY || shape==df::tiletype_shape::RAMP_TOP || shape==df::tiletype_shape::STAIR_DOWN || shape==df::tiletype_shape::STAIR_UPDOWN) @@ -730,7 +735,8 @@ void lightingEngineViewscreen::doOcupancyAndLights() for(int i=0;iplants.size();i++) { df::plant* cPlant=block->plants[i]; - + if (cPlant->grow_counter <180000) //todo maybe smaller light/oclusion? + continue; df::coord2d pos=cPlant->pos; pos=worldToViewportCoord(pos,vp,window2d); int tile=getIndex(pos.x,pos.y); @@ -825,24 +831,24 @@ void lightingEngineViewscreen::doOcupancyAndLights() if(!mat)mat=&matWall; if(def->light.isEmiting) { - addLight(tile,def->light.makeSource()); + addLight(tile,def->light.makeSource(def->size)); } else if(mat->isEmiting) { - addLight(tile,mat->makeSource()); + addLight(tile,mat->makeSource(def->size)); } if(def->light.isTransparent) { - ocupancy[tile]*=def->light.transparency; + addOclusion(tile,def->light.transparency,def->size); } else { - ocupancy[tile]*=mat->transparency; + addOclusion(tile,mat->transparency,def->size); } } else { - applyMaterial(tile,def->light); + applyMaterial(tile,def->light,def->size,def->thickness); } } } @@ -903,9 +909,6 @@ matLightDef lua_parseMatDef(lua_State* L) else lua_pop(L,1); GETLUAFLAG(ret.flicker,"flicker"); - GETLUAFLAG(ret.useThickness,"useThickness"); - GETLUAFLAG(ret.sizeModifiesPower,"sizeModifiesPower"); - GETLUAFLAG(ret.sizeModifiesRange,"sizeModifiesRange"); return ret; } int lightingEngineViewscreen::parseMaterials(lua_State* L) @@ -994,6 +997,15 @@ int lightingEngineViewscreen::parseBuildings(lua_State* L) engine->buildingDefs[std::make_tuple(type,subtype,custom)]=current; GETLUAFLAG(current.poweredOnly,"poweredOnly"); GETLUAFLAG(current.useMaterial,"useMaterial"); + + lua_getfield(L,-1,"size"); + current.size=luaL_optnumber(L,-1,1); + lua_pop(L,1); + + lua_getfield(L,-1,"thickness"); + current.thickness=luaL_optnumber(L,-1,1); + lua_pop(L,1); + lua_pop(L, 1); } diff --git a/plugins/rendermax/renderer_light.hpp b/plugins/rendermax/renderer_light.hpp index 00ebc43ce..2e8b03c54 100644 --- a/plugins/rendermax/renderer_light.hpp +++ b/plugins/rendermax/renderer_light.hpp @@ -103,9 +103,6 @@ struct matLightDef bool isTransparent; lightCell transparency; bool isEmiting; - bool useThickness; - bool sizeModifiesPower; - bool sizeModifiesRange; bool flicker; lightCell emitColor; int radius; @@ -117,7 +114,10 @@ struct matLightDef lightSource makeSource(float size=1) const { //TODO implement sizeModifiesPower/range - return lightSource(emitColor,radius); + if(size>0.999 && size<1.001) + return lightSource(emitColor,radius); + else + return lightSource(emitColor*size,radius*size);//todo check if this is sane } }; struct buildingLightDef @@ -125,6 +125,8 @@ struct buildingLightDef matLightDef light; bool poweredOnly; bool useMaterial; + float thickness; + float size; }; class lightingEngineViewscreen:public lightingEngine { @@ -151,6 +153,7 @@ private: void doLight(std::vector & target, int index); bool lightUpCell(std::vector & target, lightCell& power,int dx,int dy,int tx,int ty); bool addLight(int tileId,const lightSource& light); + void addOclusion(int tileId,const lightCell& c,float thickness); matLightDef* getMaterial(int matType,int matIndex); buildingLightDef* getBuilding(df::building* bld); @@ -158,7 +161,7 @@ private: //apply material to cell void applyMaterial(int tileId,const matLightDef& mat,float size=1, float thickness = 1); //try to find and apply material, if failed return false, and if def!=null then apply def. - bool applyMaterial(int tileId,int matType,int matIndex,float size=1,const matLightDef* def=NULL); + bool applyMaterial(int tileId,int matType,int matIndex,float size=1,float thickness = 1,const matLightDef* def=NULL); size_t inline getIndex(int x,int y) { diff --git a/plugins/rendermax/rendermax.lua b/plugins/rendermax/rendermax.lua index 785cb8b1d..ed790be48 100644 --- a/plugins/rendermax/rendermax.lua +++ b/plugins/rendermax/rendermax.lua @@ -10,9 +10,6 @@ end -- add material by id (index,mat pair or token string or a type number), flags is a table of strings -- supported flags (but not implemented): -- flicker --- useThickness -- use thickness of stuff in transparency calculation --- sizeModifiesPower --- sizeModifiesRange function addMaterial(id,transparency,emitance,radius,flags) local matinfo if type(id)=="string" then @@ -66,9 +63,13 @@ end -- supported flags: -- useMaterial --uses material, but the defined things overwrite -- poweredOnly --glow only when powered -function addBuilding(id,transparency,emitance,radius,flags) +function addBuilding(id,transparency,emitance,radius,flags,size,thickness) + size=size or 1 + thickness=thickness or 1 local bld=buildingLookUp(id) local mat=makeMaterialDef(transparency,emitance,radius,flags) + mat.size=size + mat.thickness=thickness buildings[bld.type]=buildings[bld.type] or {} if bld.subtype then if bld.custom then From 70880897f6562f2aab8ee0ef9a80dced3f90ce32 Mon Sep 17 00:00:00 2001 From: Warmist Date: Wed, 26 Jun 2013 22:05:22 +0300 Subject: [PATCH 047/676] Added sun time manipulation, updated helpstring. --- plugins/rendermax/renderer_light.cpp | 19 +++++++++++++++---- plugins/rendermax/renderer_light.hpp | 5 ++++- plugins/rendermax/rendermax.cpp | 21 ++++++++++++++++++++- plugins/rendermax/rendermax.lua | 2 +- 4 files changed, 40 insertions(+), 7 deletions(-) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index 4c4c96401..79c0909a0 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -600,7 +600,12 @@ void lightingEngineViewscreen::doSun(const lightSource& sky,MapExtras::MapCache& void lightingEngineViewscreen::doOcupancyAndLights() { // TODO better curve (+red dawn ?) - float daycol = 0;//abs((*df::global::cur_year_tick % 1200) - 600.0) / 400.0; + float daycol; + if(dayHour<0) + daycol= abs((*df::global::cur_year_tick % 1200) - 600.0) / 600.0; + else + daycol= abs(fmod(dayHour+12.0f,24.0f)-12.0f)/12.0f; //1->12h 0->24h + lightCell sky_col(daycol, daycol, daycol); lightSource sky(sky_col, 15); @@ -880,6 +885,10 @@ lightCell lua_parseLightCell(lua_State* L) if(lua_isnil(L,-1)){field=false;}\ else{lua_getfield(L,-1,#name);field=lua_isnil(L,-1);lua_pop(L,1);}\ lua_pop(L,1) + +#define GETLUANUMBER(field,name) lua_getfield(L,-1,#name);\ + if(!lua_isnil(L,-1) && lua_isnumber(L,-1))field=lua_tonumber(L,-1);\ + lua_pop(L,1) matLightDef lua_parseMatDef(lua_State* L) { @@ -959,9 +968,9 @@ int lightingEngineViewscreen::parseSpecial(lua_State* L) LOAD_SPECIAL(AMBIENT,matAmbience); LOAD_SPECIAL(CURSOR,matCursor); LOAD_SPECIAL(CITIZEN,matCitizen); - lua_getfield(L,-1,"LevelDim"); - if(!lua_isnil(L,-1) && lua_isnumber(L,-1))engine->levelDim=lua_tonumber(L,-1); - lua_pop(L,1); + GETLUANUMBER(engine->levelDim,levelDim); + GETLUANUMBER(engine->dayHour,dayHour); + return 0; } #undef LOAD_SPECIAL @@ -1027,6 +1036,7 @@ void lightingEngineViewscreen::defaultSettings() matWall=matLightDef(lightCell(0,0,0)); matCitizen=matLightDef(lightCell(0.8f,0.8f,0.9f),6); levelDim=0.2f; + dayHour=-1; } void lightingEngineViewscreen::loadSettings() { @@ -1082,3 +1092,4 @@ void lightingEngineViewscreen::loadSettings() lua_pop(s,1); } #undef GETLUAFLAG +#undef GETLUANUMBER \ No newline at end of file diff --git a/plugins/rendermax/renderer_light.hpp b/plugins/rendermax/renderer_light.hpp index 2e8b03c54..465d46ce0 100644 --- a/plugins/rendermax/renderer_light.hpp +++ b/plugins/rendermax/renderer_light.hpp @@ -78,6 +78,7 @@ public: virtual void loadSettings()=0; virtual void clear()=0; + virtual void setHour(float h)=0; protected: renderer_light* myRenderer; }; @@ -182,11 +183,13 @@ private: int nextIndex; std::vector threadList; void doLightThreads(); + //misc + void setHour(float h){dayHour=h;}; public: void lightWorkerThread(void * arg); private: //settings - + float dayHour; //<0 to cycle ///set up sane settings if setting file does not exist. void defaultSettings(); diff --git a/plugins/rendermax/rendermax.cpp b/plugins/rendermax/rendermax.cpp index 482dfa6cb..6a1b40890 100644 --- a/plugins/rendermax/rendermax.cpp +++ b/plugins/rendermax/rendermax.cpp @@ -20,6 +20,8 @@ #include "df/viewscreen_dwarfmodest.h" #include "df/viewscreen_dungeonmodest.h" +#include + using df::viewscreen_dungeonmodest; using df::viewscreen_dwarfmodest; @@ -45,7 +47,9 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector |cycle - set time to x or cycle (same effect if x<0)\n" " rendermax disable\n" )); return CR_OK; @@ -366,6 +370,21 @@ static command_result rendermax(color_ostream &out, vector & parameters CoreSuspender suspend; engine->loadSettings(); } + else if(parameters[1]=="sun" && parameters.size()==3) + { + if(parameters[2]=="cycle") + { + engine->setHour(-1); + } + else + { + std::stringstream ss; + ss<>h; + engine->setHour(h); + } + } } else out.printerr("Light mode already enabled"); diff --git a/plugins/rendermax/rendermax.lua b/plugins/rendermax/rendermax.lua index ed790be48..4140a8068 100644 --- a/plugins/rendermax/rendermax.lua +++ b/plugins/rendermax/rendermax.lua @@ -104,8 +104,8 @@ special.AMBIENT=makeMaterialDef({0.85,0.85,0.85}) --ambient fog special.CURSOR=makeMaterialDef({1,1,1},{0.96,0.84,0.03},11, {"flicker"}) special.CITIZEN=makeMaterialDef(nil,{0.80,0.80,0.90},6) special.LevelDim=0.2 -- darkness. Do not set to 0 +special.dayHour=-1 -- <0 cycle, else hour of the day --TODO dragonfire ---TODO daylight --materials From b4091b5056f1da3dd91173e9598997f928639520 Mon Sep 17 00:00:00 2001 From: Warmist Date: Thu, 27 Jun 2013 00:14:03 +0300 Subject: [PATCH 048/676] Added day colors and day speed parameters. --- plugins/rendermax/renderer_light.cpp | 49 +++++++++++++++++++++++---- plugins/rendermax/renderer_light.hpp | 3 ++ plugins/rendermax/renderer_opengl.hpp | 4 +-- plugins/rendermax/rendermax.lua | 6 ++++ 4 files changed, 54 insertions(+), 8 deletions(-) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index 79c0909a0..8fccf81e6 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -274,7 +274,7 @@ bool lightingEngineViewscreen::lightUpCell(std::vector & target, ligh } if(ls.radius>0 && dsq>0) { - if(power12h 0->24h + daycol= fmod(dayHour,24.0f)/24.0f; //1->12h 0->24h - lightCell sky_col(daycol, daycol, daycol); - lightSource sky(sky_col, 15); + lightCell sky_col=getSkyColor(daycol); + lightSource sky(sky_col, 15);//auto calculate best size lightSource candle(lightCell(0.96f,0.84f,0.03f),5); lightSource torch(lightCell(0.9f,0.75f,0.3f),8); @@ -970,7 +991,18 @@ int lightingEngineViewscreen::parseSpecial(lua_State* L) LOAD_SPECIAL(CITIZEN,matCitizen); GETLUANUMBER(engine->levelDim,levelDim); GETLUANUMBER(engine->dayHour,dayHour); - + GETLUANUMBER(engine->daySpeed,daySpeed); + lua_getfield(L,-1,"dayColors"); + if(lua_istable(L,-1)) + { + engine->dayColors.clear(); + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + engine->dayColors.push_back(lua_parseLightCell(L)); + lua_pop(L,1); + } + lua_pop(L,1); + } return 0; } #undef LOAD_SPECIAL @@ -1037,6 +1069,10 @@ void lightingEngineViewscreen::defaultSettings() matCitizen=matLightDef(lightCell(0.8f,0.8f,0.9f),6); levelDim=0.2f; dayHour=-1; + daySpeed=1; + dayColors.push_back(lightCell(0,0,0)); + dayColors.push_back(lightCell(1,1,1)); + dayColors.push_back(lightCell(0,0,0)); } void lightingEngineViewscreen::loadSettings() { @@ -1075,6 +1111,7 @@ void lightingEngineViewscreen::loadSettings() lua_pushlightuserdata(s, this); lua_pushvalue(s,env); Lua::SafeCall(out,s,2,0); + out.print("%d day light colors loaded\n",dayColors.size()); lua_pushcfunction(s, parseBuildings); lua_pushlightuserdata(s, this); diff --git a/plugins/rendermax/renderer_light.hpp b/plugins/rendermax/renderer_light.hpp index 465d46ce0..e5a9db787 100644 --- a/plugins/rendermax/renderer_light.hpp +++ b/plugins/rendermax/renderer_light.hpp @@ -188,8 +188,11 @@ private: public: void lightWorkerThread(void * arg); private: + lightCell getSkyColor(float v); //settings + float daySpeed; float dayHour; //<0 to cycle + std::vector dayColors; // a gradient of colors, first to 0, last to 24 ///set up sane settings if setting file does not exist. void defaultSettings(); diff --git a/plugins/rendermax/renderer_opengl.hpp b/plugins/rendermax/renderer_opengl.hpp index 57e81295b..8ba35f5ea 100644 --- a/plugins/rendermax/renderer_opengl.hpp +++ b/plugins/rendermax/renderer_opengl.hpp @@ -223,9 +223,9 @@ struct lightCell { return lightCell(r+other.r,g+other.g,b+other.b); } - bool operator<(const lightCell& other) const + bool operator<=(const lightCell& other) const { - return r1200 cur_year_ticks per day. 2->600 ticks --TODO dragonfire --materials From d8b0b82122f842b74e341c78cbf13ec0e4c4ee95 Mon Sep 17 00:00:00 2001 From: Warmist Date: Thu, 27 Jun 2013 16:11:29 +0300 Subject: [PATCH 049/676] Added antialiased lights. They kind of suck. So I disabled them. --- plugins/rendermax/renderer_light.cpp | 114 +++++++++++++++++++------- plugins/rendermax/renderer_light.hpp | 2 +- plugins/rendermax/renderer_opengl.hpp | 28 +++++-- 3 files changed, 105 insertions(+), 39 deletions(-) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index 8fccf81e6..7b723756b 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -221,7 +221,7 @@ void plotSquare(int xm, int ym, int r,std::function setPixel) setPixel(xm-x, ym+r); /* IV.2 Quadrant */ } } -void plotLine(int x0, int y0, int x1, int y1,std::function setPixel) +void plotLine(int x0, int y0, int x1, int y1,lightCell power,std::function setPixel) { int dx = abs(x1-x0), sx = x0 setPixelAA) +{ + int dx = abs(x1-x0), sx = x0= -dx) { /* x step */ + if (x0 == x1) break; + + if (e2+dy < ed) + { + str=1-(e2+dy)/(float)ed; + sumPower+=setPixelAA(power*str,lrdx,lrdy,x0,y0+sy); + strsum+=str; + } + err -= dy; x0 += sx; rdx=sx; + } + if (2*e2 <= dy) { /* y step */ + if (y0 == y1) break; + + if (dx-e2 < ed) + { + str=1-(dx-e2)/(float)ed; + sumPower+=setPixelAA(power*str,lrdx,lrdy,x2+sx,y0); + strsum+=str; + } + err += dx; y0 += sy; rdy=sy; + } + if(strsum<0.001f) + return; + sumPower=sumPower/strsum; + if(sumPower.dot(sumPower)<0.00001f) + return; + power=sumPower; + } +} lightCell blendMax(lightCell a,lightCell b) { return lightCell(std::max(a.r,b.r),std::max(a.g,b.g),std::max(a.b,b.b)); @@ -247,7 +300,7 @@ lightCell blend(lightCell a,lightCell b) { return blendMax(a,b); } -bool lightingEngineViewscreen::lightUpCell(std::vector & target, lightCell& power,int dx,int dy,int tx,int ty) +lightCell lightingEngineViewscreen::lightUpCell(std::vector & target,lightCell power,int dx,int dy,int tx,int ty) { if(isInViewport(coord2d(tx,ty),mapPort)) { @@ -270,12 +323,12 @@ bool lightingEngineViewscreen::lightUpCell(std::vector & target, ligh if (dsq>0 && !wallhack) { - power*=v.power(dt); + power*=v.pow(dt); } if(ls.radius>0 && dsq>0) { if(power<=ls.power) - return false; + return lightCell(); } //float dt=sqrt(dsq); lightCell oldCol=target[tile]; @@ -283,20 +336,20 @@ bool lightingEngineViewscreen::lightUpCell(std::vector & target, ligh target[tile]=ncol; if(wallhack) - return false; - float pwsq=power.r*power.r+power.g*power.g+power.b*power.b; - return pwsq>levelDim*levelDim; + return lightCell(); + + + return power; } else - return false; + return lightCell(); } void lightingEngineViewscreen::doRay(std::vector & target, lightCell power,int cx,int cy,int tx,int ty) { using namespace std::placeholders; - lightCell curPower=power; - plotLine(cx,cy,tx,ty,std::bind(&lightingEngineViewscreen::lightUpCell,this,std::ref(target),std::ref(curPower),_1,_2,_3,_4)); + + plotLine(cx,cy,tx,ty,power,std::bind(&lightingEngineViewscreen::lightUpCell,this,std::ref(target),_1,_2,_3,_4,_5)); } - void lightingEngineViewscreen::doLight(std::vector & target, int index) { using namespace std::placeholders; @@ -314,21 +367,21 @@ void lightingEngineViewscreen::doLight(std::vector & target, int inde radius*=flicker; power=power*flicker; } - int surrounds = 0; - lightCell curPower; + lightCell surrounds; + - lightUpCell(target, curPower = power, 0, 0,i+0, j+0); + lightUpCell(target,power, 0, 0,i+0, j+0); { - surrounds += lightUpCell(target, curPower = power, 0, 1,i+0, j+1); - surrounds += lightUpCell(target, curPower = power, 1, 1,i+1, j+1); - surrounds += lightUpCell(target, curPower = power, 1, 0,i+1, j+0); - surrounds += lightUpCell(target, curPower = power, 1,-1,i+1, j-1); - surrounds += lightUpCell(target, curPower = power, 0,-1,i+0, j-1); - surrounds += lightUpCell(target, curPower = power,-1,-1,i-1, j-1); - surrounds += lightUpCell(target, curPower = power,-1, 0,i-1, j+0); - surrounds += lightUpCell(target, curPower = power,-1, 1,i-1, j+1); + surrounds += lightUpCell(target, power, 0, 1,i+0, j+1); + surrounds += lightUpCell(target, power, 1, 1,i+1, j+1); + surrounds += lightUpCell(target, power, 1, 0,i+1, j+0); + surrounds += lightUpCell(target, power, 1,-1,i+1, j-1); + surrounds += lightUpCell(target, power, 0,-1,i+0, j-1); + surrounds += lightUpCell(target, power,-1,-1,i-1, j-1); + surrounds += lightUpCell(target, power,-1, 0,i-1, j+0); + surrounds += lightUpCell(target, power,-1, 1,i-1, j+1); } - if(surrounds) + if(surrounds.dot(surrounds)>0.00001f) { plotSquare(i,j,radius, std::bind(&lightingEngineViewscreen::doRay,this,std::ref(target),power,i,j,_1,_2)); @@ -364,7 +417,6 @@ void lightingEngineViewscreen::calculate() } doOcupancyAndLights(); doFovs(); - //for each lightsource in viewscreen+x do light } void lightingEngineViewscreen::updateWindow() { @@ -407,7 +459,7 @@ void lightingEngineViewscreen::addOclusion(int tileId,const lightCell& c,float t if(thickness > 0.999 && thickness < 1.001) ocupancy[tileId]*=c; else - ocupancy[tileId]*=(c.power(thickness)); + ocupancy[tileId]*=(c.pow(thickness)); } lightCell getStandartColor(int colorId) { @@ -498,7 +550,7 @@ lightCell lightingEngineViewscreen::propogateSun(MapExtras::Block* b, int x,int ret*=matIce.transparency; else if(basicShapeIce==df::tiletype_shape_basic::Floor || basicShapeIce==df::tiletype_shape_basic::Ramp || shapeIce==df::tiletype_shape::STAIR_UP) if(!lastLevel) - ret*=matIce.transparency.power(1.0f/7.0f); + ret*=matIce.transparency.pow(1.0f/7.0f); } lightDef=getMaterial(mat.mat_type,mat.mat_index); @@ -513,7 +565,7 @@ lightCell lightingEngineViewscreen::propogateSun(MapExtras::Block* b, int x,int { if(!lastLevel) - ret*=lightDef->transparency.power(1.0f/7.0f); + ret*=lightDef->transparency.pow(1.0f/7.0f); } else if(shape==df::tiletype_shape::STAIR_DOWN || shape==df::tiletype_shape::STAIR_UPDOWN) { @@ -521,11 +573,11 @@ lightCell lightingEngineViewscreen::propogateSun(MapExtras::Block* b, int x,int } if(d.bits.liquid_type == df::enums::tile_liquid::Water && d.bits.flow_size > 0) { - ret *=matWater.transparency.power((float)d.bits.flow_size/7.0f); + ret *=matWater.transparency.pow((float)d.bits.flow_size/7.0f); } else if(d.bits.liquid_type == df::enums::tile_liquid::Magma && d.bits.flow_size > 0) { - ret *=matLava.transparency.power((float)d.bits.flow_size/7.0f); + ret *=matLava.transparency.pow((float)d.bits.flow_size/7.0f); } return ret; } @@ -628,7 +680,7 @@ void lightingEngineViewscreen::doOcupancyAndLights() daycol= fmod(dayHour,24.0f)/24.0f; //1->12h 0->24h lightCell sky_col=getSkyColor(daycol); - lightSource sky(sky_col, 15);//auto calculate best size + lightSource sky(sky_col, -1);//auto calculate best size lightSource candle(lightCell(0.96f,0.84f,0.03f),5); lightSource torch(lightCell(0.9f,0.75f,0.3f),8); diff --git a/plugins/rendermax/renderer_light.hpp b/plugins/rendermax/renderer_light.hpp index e5a9db787..58676e29c 100644 --- a/plugins/rendermax/renderer_light.hpp +++ b/plugins/rendermax/renderer_light.hpp @@ -152,7 +152,7 @@ private: void doRay(std::vector & target, lightCell power,int cx,int cy,int tx,int ty); void doFovs(); void doLight(std::vector & target, int index); - bool lightUpCell(std::vector & target, lightCell& power,int dx,int dy,int tx,int ty); + lightCell lightUpCell(std::vector & target, lightCell power,int dx,int dy,int tx,int ty); bool addLight(int tileId,const lightSource& light); void addOclusion(int tileId,const lightCell& c,float thickness); diff --git a/plugins/rendermax/renderer_opengl.hpp b/plugins/rendermax/renderer_opengl.hpp index 8ba35f5ea..151ff94f0 100644 --- a/plugins/rendermax/renderer_opengl.hpp +++ b/plugins/rendermax/renderer_opengl.hpp @@ -196,7 +196,7 @@ struct lightCell { } - lightCell operator-(lightCell cell) const + lightCell operator-(const lightCell& cell) const { return lightCell(r-cell.r,g-cell.g,b-cell.b); } @@ -208,17 +208,31 @@ struct lightCell { return lightCell(r/val,g/val,b/val); } - lightCell operator*(lightCell cell) const + lightCell operator*(const lightCell& cell) const { return lightCell(r*cell.r,g*cell.g,b*cell.b); } - lightCell operator*=(lightCell cell) + lightCell operator*=(float val) + { + r*=val; + g*=val; + b*=val; + return *this; + } + lightCell operator*=(const lightCell& cell) { r*=cell.r; g*=cell.g; b*=cell.b; return *this; } + lightCell operator+=(const lightCell& cell) + { + r+=cell.r; + g+=cell.g; + b+=cell.b; + return *this; + } lightCell operator+(const lightCell& other) const { return lightCell(r+other.r,g+other.g,b+other.b); @@ -231,13 +245,13 @@ struct lightCell { return r*other.r+g*other.g+b*other.b; } - lightCell power(const float exp) const + lightCell pow(const float exp) const { - return lightCell(pow(r, exp), pow(g, exp), pow(b, exp)); + return lightCell(std::pow(r, exp), std::pow(g, exp), std::pow(b, exp)); } - lightCell power(const int exp) const + lightCell pow(const int exp) const { - return lightCell(pow(r, exp), pow(g, exp), pow(b, exp)); + return lightCell(std::pow(r, exp), std::pow(g, exp), std::pow(b, exp)); } }; struct renderer_test : public renderer_wrap { From b12fd5a4c5d1205e6e7daef812d1273e67f1881e Mon Sep 17 00:00:00 2001 From: Japa Date: Thu, 27 Jun 2013 02:49:18 +0530 Subject: [PATCH 050/676] Changed sun calculation direction. --- plugins/rendermax/renderer_light.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index 7b723756b..ec30bcd1e 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -616,7 +616,7 @@ void lightingEngineViewscreen::doSun(const lightSource& sky,MapExtras::MapCache& cellArray[block_x][block_y] = sky.power; int emptyCell=0; - for(int z=df::global::world->map.z_count-1;z>=window_z && emptyCell<256;z--) + for(int z=window_z;z< df::global::world->map.z_count && emptyCell<256;z++) { MapExtras::Block* b=map.BlockAt(DFCoord(blockX,blockY,z)); if(!b) @@ -1181,4 +1181,4 @@ void lightingEngineViewscreen::loadSettings() lua_pop(s,1); } #undef GETLUAFLAG -#undef GETLUANUMBER \ No newline at end of file +#undef GETLUANUMBER From 28985167c9460433b6e93c66f7f541152499238e Mon Sep 17 00:00:00 2001 From: Warmist Date: Thu, 27 Jun 2013 18:11:50 +0300 Subject: [PATCH 051/676] Fixed bug with buildings and added gems to material definition. --- plugins/rendermax/renderer_light.cpp | 8 ++++++-- plugins/rendermax/renderer_light.hpp | 1 - plugins/rendermax/rendermax.lua | 12 +++++++++++- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index ec30bcd1e..4f5bdd90c 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -884,11 +884,15 @@ void lightingEngineViewscreen::doOcupancyAndLights() df::coord2d p1(bld->x1,bld->y1); df::coord2d p2(bld->x2,bld->y2); p1=worldToViewportCoord(p1,vp,window2d); - p2=worldToViewportCoord(p1,vp,window2d); + p2=worldToViewportCoord(p2,vp,window2d); if(isInViewport(p1,vp)||isInViewport(p2,vp)) { - int tile=getIndex(p1.x,p1.y); //TODO multitile buildings. How they would work? + int tile; + if(isInViewport(p1,vp)) + tile=getIndex(p1.x,p1.y); //TODO multitile buildings. How they would work? + else + tile=getIndex(p2.x,p2.y); df::building_type type = bld->getType(); buildingLightDef* def=getBuilding(bld); if(!def) diff --git a/plugins/rendermax/renderer_light.hpp b/plugins/rendermax/renderer_light.hpp index 58676e29c..5a53242b0 100644 --- a/plugins/rendermax/renderer_light.hpp +++ b/plugins/rendermax/renderer_light.hpp @@ -114,7 +114,6 @@ struct matLightDef matLightDef(lightCell transparency):isTransparent(true),isEmiting(false),transparency(transparency){} lightSource makeSource(float size=1) const { - //TODO implement sizeModifiesPower/range if(size>0.999 && size<1.001) return lightSource(emitColor,radius); else diff --git a/plugins/rendermax/rendermax.lua b/plugins/rendermax/rendermax.lua index 74e5fc2d0..df2619864 100644 --- a/plugins/rendermax/rendermax.lua +++ b/plugins/rendermax/rendermax.lua @@ -92,6 +92,16 @@ function makeMaterialDef(transparency,emitance,radius,flags) end return {tr=transparency,em=emitance,rad=radius,flags=flg} end +function colorFrom16(col16) + return copyall(df.global.enabler.ccolor[col16]) +end +function addGems() + for k,v in pairs(df.global.world.raws.inorganics) do + if v.material.flags.IS_GEM then + addMaterial("INORGANIC:"..v.id,colorFrom16(v.material.build_color[0])) + end + end +end ------------------------------------------------------------------------ ---------------- Configuration Starts Here ------------------------- ------------------------------------------------------------------------ @@ -128,7 +138,7 @@ addMaterial("PLANT:MUSHROOM_HELMET_PLUMP",nil,{0.2,0.1,0.6},2) addMaterial("INORGANIC:ADAMANTINE",{0.1,0.3,0.3},{0.1,0.3,0.3},4) -- creature stuff addMaterial("CREATURE:DRAGON:BLOOD",nil,{0.6,0.1,0.1},4) --- TODO gems +addGems() --buildings addBuilding("Statue",{1,1,1},{0.9,0.75,0.3},8) addBuilding("Bed",{1,1,1},{0.3,0.2,0.0},2) From 360b5d2e1859410e5dba089846591b31ae7a1497 Mon Sep 17 00:00:00 2001 From: Warmist Date: Fri, 28 Jun 2013 17:22:24 +0300 Subject: [PATCH 052/676] Fix for gems not getting right color. --- plugins/rendermax/rendermax.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/rendermax/rendermax.lua b/plugins/rendermax/rendermax.lua index df2619864..cbd7b3536 100644 --- a/plugins/rendermax/rendermax.lua +++ b/plugins/rendermax/rendermax.lua @@ -93,12 +93,13 @@ function makeMaterialDef(transparency,emitance,radius,flags) return {tr=transparency,em=emitance,rad=radius,flags=flg} end function colorFrom16(col16) - return copyall(df.global.enabler.ccolor[col16]) + local col=df.global.enabler.ccolor[col16] + return {col[0],col[1],col[2]} end function addGems() for k,v in pairs(df.global.world.raws.inorganics) do if v.material.flags.IS_GEM then - addMaterial("INORGANIC:"..v.id,colorFrom16(v.material.build_color[0])) + addMaterial("INORGANIC:"..v.id,colorFrom16(v.material.tile_color[0]+v.material.tile_color[2]*8)) end end end From 48f855c499501e1d2109f7f2792fc69c87aa24ac Mon Sep 17 00:00:00 2001 From: Warmist Date: Fri, 28 Jun 2013 23:34:47 +0300 Subject: [PATCH 053/676] Added occlusionON|occlusionOFF commands --- plugins/rendermax/renderer_light.cpp | 10 +++++----- plugins/rendermax/renderer_light.hpp | 5 +++++ plugins/rendermax/rendermax.cpp | 10 +++++++++- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index 4f5bdd90c..00a5ebd8f 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -175,7 +175,7 @@ rect2d getMapViewport() } return mkrect_wh(1,1,view_rb,view_height+1); } -lightingEngineViewscreen::lightingEngineViewscreen(renderer_light* target):lightingEngine(target) +lightingEngineViewscreen::lightingEngineViewscreen(renderer_light* target):lightingEngine(target),doDebug(false) { reinit(); defaultSettings(); @@ -428,10 +428,10 @@ void lightingEngineViewscreen::updateWindow() return; } - //if(showOcupancy) - //std::swap(ocupancy,myRenderer->lightGrid); - //else - std::swap(lightMap,myRenderer->lightGrid); + if(doDebug) + std::swap(ocupancy,myRenderer->lightGrid); + else + std::swap(lightMap,myRenderer->lightGrid); rect2d vp=getMapViewport(); myRenderer->invalidateRect(vp.first.x,vp.first.y,vp.second.x-vp.first.x,vp.second.y-vp.first.y); diff --git a/plugins/rendermax/renderer_light.hpp b/plugins/rendermax/renderer_light.hpp index 5a53242b0..ad90ca5eb 100644 --- a/plugins/rendermax/renderer_light.hpp +++ b/plugins/rendermax/renderer_light.hpp @@ -79,6 +79,7 @@ public: virtual void clear()=0; virtual void setHour(float h)=0; + virtual void debug(bool enable)=0; protected: renderer_light* myRenderer; }; @@ -140,6 +141,8 @@ public: void loadSettings(); void clear(); + + void debug(bool enable){doDebug=enable;}; private: df::coord2d worldToViewportCoord(const df::coord2d& in,const DFHack::rect2d& r,const df::coord2d& window2d) ; @@ -188,6 +191,8 @@ public: void lightWorkerThread(void * arg); private: lightCell getSkyColor(float v); + bool doDebug; + //settings float daySpeed; float dayHour; //<0 to cycle diff --git a/plugins/rendermax/rendermax.cpp b/plugins/rendermax/rendermax.cpp index 6a1b40890..24ba8c4d8 100644 --- a/plugins/rendermax/rendermax.cpp +++ b/plugins/rendermax/rendermax.cpp @@ -49,7 +49,8 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector |cycle - set time to x or cycle (same effect if x<0)\n" + " rendermax light sun |cycle - set time to x (in hours) or cycle (same effect if x<0)\n" + " rendermax light occlusionON|occlusionOFF - debug the occlusion map\n" " rendermax disable\n" )); return CR_OK; @@ -385,6 +386,13 @@ static command_result rendermax(color_ostream &out, vector & parameters engine->setHour(h); } } + else if(parameters[1]=="occlusionON") + { + engine->debug(true); + }else if(parameters[1]=="occlusionOFF") + { + engine->debug(false); + } } else out.printerr("Light mode already enabled"); From 0cf5eedc2a92d85a762689ecbf39dcc54ecc9b39 Mon Sep 17 00:00:00 2001 From: Warmist Date: Fri, 28 Jun 2013 23:51:52 +0300 Subject: [PATCH 054/676] Fixed potential error with building getting transparent on un-powering. --- plugins/rendermax/renderer_light.cpp | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index 00a5ebd8f..034dfe405 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -897,8 +897,6 @@ void lightingEngineViewscreen::doOcupancyAndLights() buildingLightDef* def=getBuilding(bld); if(!def) continue; - if(def->poweredOnly && bld->isUnpowered()) - continue; if(type==df::enums::building_type::Door) { df::building_doorst* door=static_cast(bld); @@ -911,13 +909,16 @@ void lightingEngineViewscreen::doOcupancyAndLights() { matLightDef* mat=getMaterial(bld->mat_type,bld->mat_index); if(!mat)mat=&matWall; - if(def->light.isEmiting) - { - addLight(tile,def->light.makeSource(def->size)); - } - else if(mat->isEmiting) + if(!def->poweredOnly || !bld->isUnpowered()) //not powered. Add occlusion only. { - addLight(tile,mat->makeSource(def->size)); + if(def->light.isEmiting) + { + addLight(tile,def->light.makeSource(def->size)); + } + else if(mat->isEmiting) + { + addLight(tile,mat->makeSource(def->size)); + } } if(def->light.isTransparent) { @@ -930,7 +931,10 @@ void lightingEngineViewscreen::doOcupancyAndLights() } else { - applyMaterial(tile,def->light,def->size,def->thickness); + if(!def->poweredOnly || !bld->isUnpowered())//not powered. Add occlusion only. + addOclusion(tile,def->light.transparency,def->size); + else + applyMaterial(tile,def->light,def->size,def->thickness); } } } From b248eb3e52e1b6b2cc0402c2d838b05950e3d43a Mon Sep 17 00:00:00 2001 From: Japa Date: Fri, 28 Jun 2013 21:17:54 +0300 Subject: [PATCH 055/676] Fixed broken buildings, also added floodgate functionality. Merge branch 'master' of git://github.com/JapaMala/dfhack --- plugins/rendermax/renderer_light.cpp | 7 +++++++ plugins/rendermax/renderer_light.hpp | 1 + plugins/rendermax/rendermax.lua | 3 ++- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index 034dfe405..1ad9ad24f 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -21,6 +21,7 @@ #include "df/world.h" #include "df/building.h" #include "df/building_doorst.h" +#include "df/building_floodgatest.h" #include "df/plant.h" #include "df/plant_raw.h" @@ -903,6 +904,12 @@ void lightingEngineViewscreen::doOcupancyAndLights() if(!door->door_flags.bits.closed) continue; } + else if(type==df::enums::building_type::Floodgate) + { + df::building_floodgatest* gate=static_cast(bld); + if(!gate->gate_flags.bits.closed) + continue; + } if(def->useMaterial) diff --git a/plugins/rendermax/renderer_light.hpp b/plugins/rendermax/renderer_light.hpp index ad90ca5eb..5466ff01f 100644 --- a/plugins/rendermax/renderer_light.hpp +++ b/plugins/rendermax/renderer_light.hpp @@ -128,6 +128,7 @@ struct buildingLightDef bool useMaterial; float thickness; float size; + buildingLightDef():poweredOnly(false),useMaterial(true),thickness(1.0f),size(1.0f){} }; class lightingEngineViewscreen:public lightingEngine { diff --git a/plugins/rendermax/rendermax.lua b/plugins/rendermax/rendermax.lua index cbd7b3536..c5e7c21a7 100644 --- a/plugins/rendermax/rendermax.lua +++ b/plugins/rendermax/rendermax.lua @@ -145,4 +145,5 @@ addBuilding("Statue",{1,1,1},{0.9,0.75,0.3},8) addBuilding("Bed",{1,1,1},{0.3,0.2,0.0},2) addBuilding("WindowGlass",nil,nil,0,{"useMaterial"}) addBuilding("WindowGem",nil,nil,0,{"useMaterial"}) -addBuilding("Door",nil,nil,0,{"useMaterial"}) -- special case, only closed door obstruct/emit light \ No newline at end of file +addBuilding("Door",nil,nil,0,{"useMaterial"}) -- special case, only closed door obstruct/emit light +addBuilding("Floodgate",nil,nil,0,{"useMaterial"}) -- special case, only closed door obstruct/emit light From ee1bad84fcc10a5a93d1dc418930dbbbe6b25e03 Mon Sep 17 00:00:00 2001 From: Warmist Date: Sat, 29 Jun 2013 20:55:07 +0300 Subject: [PATCH 056/676] Threading rewrite. --- plugins/rendermax/renderer_light.cpp | 424 +++++++++++++++------------ plugins/rendermax/renderer_light.hpp | 66 ++++- 2 files changed, 292 insertions(+), 198 deletions(-) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index 1ad9ad24f..919e411e1 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -35,93 +35,18 @@ using namespace tthread; const float RootTwo = 1.4142135623730950488016887242097f; -void lightingEngineViewscreen::lightWorkerThread(void * arg) +bool isInRect(const coord2d& pos,const rect2d& rect) { - int thisIndex; - std::vector canvas; - while(1) - { - writeMutex.lock(); - writeMutex.unlock(); //Don't start till write access is given. - indexMutex.lock(); - if(nextIndex == -1) //The worker threads should keep going until, and including, index 0. - { - indexMutex.unlock(); - break; - } - else if(nextIndex == -2) - { - indexMutex.unlock(); - return; - } - else - { - thisIndex = nextIndex; - nextIndex--; - indexMutex.unlock(); - if(canvas.size() != lightMap.size()) - { - canvas.resize(lightMap.size(), lightCell(0,0,0)); - } - doLight(canvas, thisIndex); - - } - } - writeMutex.lock(); - for(int i = 0; i < canvas.size(); i++) - { - lightMap[i] = blend(lightMap[i], canvas[i]); - - } - writeMutex.unlock(); - canvas.assign(canvas.size(),lightCell(0,0,0)); + if(pos.x>=rect.first.x && pos.y>=rect.first.y && pos.xjoin(); - } + threading.shutdown(); } -void threadStub(void * arg) -{ - if(arg) - ((lightingEngineViewscreen*)arg)->lightWorkerThread(0); -} - -void lightingEngineViewscreen::doLightThreads() -{ - nextIndex = 0; - int num_threads = thread::hardware_concurrency(); - if(num_threads < 1) num_threads = 1; - if(threadList.empty()) - { - threadList.resize(num_threads, NULL); - } - for(int i = 0; i < num_threads; i++) - { - threadList[i] = new thread(threadStub, this); - } - nextIndex = lightMap.size() - 1; //start at the largest valid index - writeMutex.unlock(); - for(int i = 0; i < num_threads; i++) - { - threadList[i]->join(); - delete threadList[i]; - threadList[i]=0; - } - writeMutex.lock(); -} - - - lightSource::lightSource(lightCell power,int radius):power(power),flicker(false) { if(radius >= 0) @@ -176,12 +101,14 @@ rect2d getMapViewport() } return mkrect_wh(1,1,view_rb,view_height+1); } -lightingEngineViewscreen::lightingEngineViewscreen(renderer_light* target):lightingEngine(target),doDebug(false) +lightingEngineViewscreen::lightingEngineViewscreen(renderer_light* target):lightingEngine(target),doDebug(false),threading(this) { reinit(); defaultSettings(); loadSettings(); - writeMutex.lock(); //This is needed for later when the threads will all want to write to the buffer. + int numTreads=tthread::thread::hardware_concurrency(); + if(numTreads==0)numTreads=1; + threading.start(numTreads); } void lightingEngineViewscreen::reinit() @@ -301,100 +228,6 @@ lightCell blend(lightCell a,lightCell b) { return blendMax(a,b); } -lightCell lightingEngineViewscreen::lightUpCell(std::vector & target,lightCell power,int dx,int dy,int tx,int ty) -{ - if(isInViewport(coord2d(tx,ty),mapPort)) - { - size_t tile=getIndex(tx,ty); - int dsq=dx*dx+dy*dy; - float dt=1; - if(dsq == 1) - dt=1; - else if(dsq == 2) - dt = RootTwo; - else if(dsq == 0) - dt = 0; - else - dt=sqrt((float)dsq); - lightCell& v=ocupancy[tile]; - lightSource& ls=lights[tile]; - bool wallhack=false; - if(v.r+v.g+v.b==0) - wallhack=true; - - if (dsq>0 && !wallhack) - { - power*=v.pow(dt); - } - if(ls.radius>0 && dsq>0) - { - if(power<=ls.power) - return lightCell(); - } - //float dt=sqrt(dsq); - lightCell oldCol=target[tile]; - lightCell ncol=blendMax(power,oldCol); - target[tile]=ncol; - - if(wallhack) - return lightCell(); - - - return power; - } - else - return lightCell(); -} -void lightingEngineViewscreen::doRay(std::vector & target, lightCell power,int cx,int cy,int tx,int ty) -{ - using namespace std::placeholders; - - plotLine(cx,cy,tx,ty,power,std::bind(&lightingEngineViewscreen::lightUpCell,this,std::ref(target),_1,_2,_3,_4,_5)); -} -void lightingEngineViewscreen::doLight(std::vector & target, int index) -{ - using namespace std::placeholders; - lightSource& csource=lights[index]; - if(csource.radius>0) - { - coord2d coord = getCoords(index); - int i = coord.x; - int j = coord.y; - lightCell power=csource.power; - int radius =csource.radius; - if(csource.flicker) - { - float flicker=(rand()/(float)RAND_MAX)/2.0f+0.5f; - radius*=flicker; - power=power*flicker; - } - lightCell surrounds; - - - lightUpCell(target,power, 0, 0,i+0, j+0); - { - surrounds += lightUpCell(target, power, 0, 1,i+0, j+1); - surrounds += lightUpCell(target, power, 1, 1,i+1, j+1); - surrounds += lightUpCell(target, power, 1, 0,i+1, j+0); - surrounds += lightUpCell(target, power, 1,-1,i+1, j-1); - surrounds += lightUpCell(target, power, 0,-1,i+0, j-1); - surrounds += lightUpCell(target, power,-1,-1,i-1, j-1); - surrounds += lightUpCell(target, power,-1, 0,i-1, j+0); - surrounds += lightUpCell(target, power,-1, 1,i-1, j+1); - } - if(surrounds.dot(surrounds)>0.00001f) - { - plotSquare(i,j,radius, - std::bind(&lightingEngineViewscreen::doRay,this,std::ref(target),power,i,j,_1,_2)); - } - } -} - -void lightingEngineViewscreen::doFovs() -{ - mapPort=getMapViewport(); - doLightThreads(); -} void lightingEngineViewscreen::clear() { lightMap.assign(lightMap.size(),lightCell(1,1,1)); @@ -417,7 +250,8 @@ void lightingEngineViewscreen::calculate() lightMap[getIndex(i,j)]=dim; } doOcupancyAndLights(); - doFovs(); + threading.signalDoneOcclusion(); + threading.waitForWrites(); } void lightingEngineViewscreen::updateWindow() { @@ -586,12 +420,7 @@ coord2d lightingEngineViewscreen::worldToViewportCoord(const coord2d& in,const r { return in-window2d+r.first; } -bool lightingEngineViewscreen::isInViewport(const coord2d& in,const rect2d& r) -{ - if(in.x>=r.first.x && in.y>=r.first.y && in.x0.003f) + if(isInRect(pos,vp) && curCell.dot(curCell)>0.003f) { lightSource sun=lightSource(curCell,15); addLight(getIndex(pos.x,pos.y),sun); @@ -719,7 +548,7 @@ void lightingEngineViewscreen::doOcupancyAndLights() pos.y = blockY*16+block_y; df::coord2d gpos=pos; pos=worldToViewportCoord(pos,vp,window2d); - if(!isInViewport(pos,vp)) + if(!isInRect(pos,vp)) continue; int tile=getIndex(pos.x,pos.y); lightCell& curCell=ocupancy[tile]; @@ -790,7 +619,7 @@ void lightingEngineViewscreen::doOcupancyAndLights() df::coord2d pos=f->pos; pos=worldToViewportCoord(pos,vp,window2d); int tile=getIndex(pos.x,pos.y); - if(isInViewport(pos,vp)) + if(isInRect(pos,vp)) { lightCell fireColor; if(f->density>60) @@ -819,7 +648,7 @@ void lightingEngineViewscreen::doOcupancyAndLights() df::coord2d pos=cPlant->pos; pos=worldToViewportCoord(pos,vp,window2d); int tile=getIndex(pos.x,pos.y); - if(isInViewport(pos,vp)) + if(isInRect(pos,vp)) { applyMaterial(tile,419,cPlant->material); } @@ -847,7 +676,7 @@ void lightingEngineViewscreen::doOcupancyAndLights() if(amount<=0) continue; pos=worldToViewportCoord(pos,vp,window2d); - if(isInViewport(pos,vp)) + if(isInRect(pos,vp)) { addLight(getIndex(pos.x,pos.y),m->makeSource((float)amount/100)); } @@ -868,7 +697,7 @@ void lightingEngineViewscreen::doOcupancyAndLights() { df::unit *u = df::global::world->units.active[i]; coord2d pos=worldToViewportCoord(coord2d(u->pos.x,u->pos.y),vp,window2d); - if(u->pos.z==window_z && isInViewport(pos,vp)) + if(u->pos.z==window_z && isInRect(pos,vp)) if (DFHack::Units::isCitizen(u) && !u->counters.unconscious) addLight(getIndex(pos.x,pos.y),matCitizen.makeSource()); } @@ -886,11 +715,11 @@ void lightingEngineViewscreen::doOcupancyAndLights() df::coord2d p2(bld->x2,bld->y2); p1=worldToViewportCoord(p1,vp,window2d); p2=worldToViewportCoord(p2,vp,window2d); - if(isInViewport(p1,vp)||isInViewport(p2,vp)) + if(isInRect(p1,vp)||isInRect(p2,vp)) { int tile; - if(isInViewport(p1,vp)) + if(isInRect(p1,vp)) tile=getIndex(p1.x,p1.y); //TODO multitile buildings. How they would work? else tile=getIndex(p2.x,p2.y); @@ -1197,3 +1026,216 @@ void lightingEngineViewscreen::loadSettings() } #undef GETLUAFLAG #undef GETLUANUMBER +/* + * Threading stuff + */ +lightThread::lightThread( lightThreadDispatch& dispatch ):dispatch(dispatch),isDone(false),myThread(0) +{ + +} +lightThread::~lightThread() +{ + if(myThread) + delete myThread; +} + +void lightThread::run() +{ + while(!isDone) + { + { + tthread::lock_guard guard(dispatch.occlusionMutex); + dispatch.occlusionDone.wait(dispatch.occlusionMutex);//wait for work + if(dispatch.unprocessed.size()==0) //spurious wake-up + continue; + if(dispatch.occlusion.size()!=canvas.size()) //oh no somebody resized stuff + canvas.resize(dispatch.occlusion.size()); + } + + + { //get my rectangle (any will do) + tthread::lock_guard guard(dispatch.unprocessedMutex); + myRect=dispatch.unprocessed.top(); + dispatch.unprocessed.pop(); + } + work(); + { + tthread::lock_guard guard(dispatch.writeLock); + combine();//write it back + dispatch.writeCount++; + } + dispatch.writesDone.notify_one();//tell about it to the dispatch. + } +} + +void lightThread::work() +{ + canvas.assign(canvas.size(),lightCell(0,0,0)); + for(int i=myRect.first.x;i0 && !wallhack) + { + power*=v.pow(dt); + } + if(ls.radius>0 && dsq>0) + { + if(power<=ls.power) + return lightCell(); + } + //float dt=sqrt(dsq); + lightCell oldCol=canvas[tile]; + lightCell ncol=blendMax(power,oldCol); + canvas[tile]=ncol; + + if(wallhack) + return lightCell(); + + + return power; + } + else + return lightCell(); +} +void lightThread::doRay(lightCell power,int cx,int cy,int tx,int ty) +{ + using namespace std::placeholders; + + plotLine(cx,cy,tx,ty,power,std::bind(&lightThread::lightUpCell,this,_1,_2,_3,_4,_5)); +} + +void lightThread::doLight( int x,int y ) +{ + using namespace std::placeholders; + lightSource& csource=dispatch.lights[x*dispatch.getH()+y]; + if(csource.radius>0) + { + lightCell power=csource.power; + int radius =csource.radius; + if(csource.flicker) + { + float flicker=(rand()/(float)RAND_MAX)/2.0f+0.5f; + radius*=flicker; + power=power*flicker; + } + lightCell surrounds; + lightUpCell( power, 0, 0,x, y); + for(int i=-1;i<2;i++) + for(int j=-1;j<2;j++) + if(i!=0||j!=0) + surrounds += lightUpCell( power, i, j,x+i, y+j); + if(surrounds.dot(surrounds)>0.00001f) + { + plotSquare(x,y,radius, + std::bind(&lightThread::doRay,this,power,x,y,_1,_2)); + } + } +} +void lightThreadDispatch::signalDoneOcclusion() +{ + { + tthread::lock_guard guardWrite(writeLock); + writeCount=0; + } + tthread::lock_guard guard(occlusionMutex); + + viewPort=getMapViewport(); + int threadCount=threadPool.size(); + int w=viewPort.second.x-viewPort.first.x; + int slicew=w/threadCount; + for(int i=0;ilights),occlusion(parent->ocupancy),lightMap(parent->lightMap),writeCount(0) +{ + +} + +void lightThreadDispatch::shutdown() +{ + for(int i=0;iisDone=true; + threadPool[i]->myThread->join(); + } + threadPool.clear(); +} + +int lightThreadDispatch::getW() +{ + return parent->getW(); +} + +int lightThreadDispatch::getH() +{ + return parent->getH(); +} +void threadStub(void * arg) +{ + if(arg) + ((lightThread*)arg)->run(); +} +void lightThreadDispatch::start(int count) +{ + for(int i=0;i nthread(new lightThread(*this)); + nthread->myThread=new tthread::thread(&threadStub,nthread.get()); + threadPool.push_back(std::move(nthread)); + } +} + +void lightThreadDispatch::waitForWrites() +{ + tthread::lock_guard guard(writeLock); + while(threadPool.size()>writeCount)//missed it somehow already. + { + writesDone.wait(writeLock); //if not, wait a bit + } +} diff --git a/plugins/rendermax/renderer_light.hpp b/plugins/rendermax/renderer_light.hpp index 5466ff01f..d14bbc33f 100644 --- a/plugins/rendermax/renderer_light.hpp +++ b/plugins/rendermax/renderer_light.hpp @@ -4,8 +4,10 @@ #include "Types.h" #include #include +#include +#include #include "modules/MapCache.h" - +bool isInRect(const df::coord2d& pos,const DFHack::rect2d& rect); struct renderer_light : public renderer_wrap { private: void colorizeTile(int x,int y) @@ -130,6 +132,56 @@ struct buildingLightDef float size; buildingLightDef():poweredOnly(false),useMaterial(true),thickness(1.0f),size(1.0f){} }; +class lightThread; +class lightingEngineViewscreen; +class lightThreadDispatch +{ + lightingEngineViewscreen *parent; +public: + DFHack::rect2d viewPort; + + std::vector > threadPool; + std::vector& lights; + + tthread::mutex occlusionMutex; + tthread::condition_variable occlusionDone; //all threads wait for occlusion to finish + tthread::mutex unprocessedMutex; + std::stack unprocessed; //stack of parts of map where lighting is not finished + std::vector& occlusion; + + tthread::mutex writeLock; //mutex for lightMap + std::vector& lightMap; + + tthread::condition_variable writesDone; + int writeCount; + + lightThreadDispatch(lightingEngineViewscreen* p); + void signalDoneOcclusion(); + void shutdown(); + void waitForWrites(); + + int getW(); + int getH(); + void start(int count); +}; +class lightThread +{ + std::vector canvas; + lightThreadDispatch& dispatch; + DFHack::rect2d myRect; + void work(); //main light calculation function + void combine(); //combine existing canvas into global lightmap +public: + tthread::thread *myThread; + bool isDone; //no mutex, because bool is atomic + lightThread(lightThreadDispatch& dispatch); + ~lightThread(); + void run(); +private: + void doLight(int x,int y); + void doRay(lightCell power,int cx,int cy,int tx,int ty); + lightCell lightUpCell(lightCell power,int dx,int dy,int tx,int ty); +}; class lightingEngineViewscreen:public lightingEngine { public: @@ -147,7 +199,7 @@ public: private: df::coord2d worldToViewportCoord(const df::coord2d& in,const DFHack::rect2d& r,const df::coord2d& window2d) ; - bool isInViewport(const df::coord2d& in,const DFHack::rect2d& r); + void doSun(const lightSource& sky,MapExtras::MapCache& map); void doOcupancyAndLights(); @@ -181,13 +233,12 @@ private: std::vector lights; //Threading stuff - tthread::mutex indexMutex; - tthread::mutex writeMutex; - int nextIndex; - std::vector threadList; - void doLightThreads(); + lightThreadDispatch threading; //misc void setHour(float h){dayHour=h;}; + + int getW()const {return w;} + int getH()const {return h;} public: void lightWorkerThread(void * arg); private: @@ -219,6 +270,7 @@ private: std::map,buildingLightDef> buildingDefs; int w,h; DFHack::rect2d mapPort; + friend lightThreadDispatch; }; lightCell blend(lightCell a,lightCell b); lightCell blendMax(lightCell a,lightCell b); From f34a4ce34efd141df5ef98c4973071501215b079 Mon Sep 17 00:00:00 2001 From: Warmist Date: Sun, 30 Jun 2013 15:05:59 +0300 Subject: [PATCH 057/676] Renamed lightCell to rgbf --- plugins/rendermax/renderer_light.cpp | 114 +++++++++++++------------- plugins/rendermax/renderer_light.hpp | 50 +++++------ plugins/rendermax/renderer_opengl.hpp | 60 +++++++------- plugins/rendermax/rendermax.cpp | 30 +++---- 4 files changed, 127 insertions(+), 127 deletions(-) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index 919e411e1..fc7202890 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -47,7 +47,7 @@ lightingEngineViewscreen::~lightingEngineViewscreen() threading.shutdown(); } -lightSource::lightSource(lightCell power,int radius):power(power),flicker(false) +lightSource::lightSource(rgbf power,int radius):power(power),flicker(false) { if(radius >= 0) this->radius = radius; @@ -118,7 +118,7 @@ void lightingEngineViewscreen::reinit() w=gps->dimx; h=gps->dimy; size_t size=w*h; - lightMap.resize(size,lightCell(1,1,1)); + lightMap.resize(size,rgbf(1,1,1)); ocupancy.resize(size); lights.resize(size); } @@ -149,7 +149,7 @@ void plotSquare(int xm, int ym, int r,std::function setPixel) setPixel(xm-x, ym+r); /* IV.2 Quadrant */ } } -void plotLine(int x0, int y0, int x1, int y1,lightCell power,std::function setPixel) +void plotLine(int x0, int y0, int x1, int y1,rgbf power,std::function setPixel) { int dx = abs(x1-x0), sx = x0 setPixelAA) +void plotLineAA(int x0, int y0, int x1, int y1,rgbf power,std::function setPixelAA) { int dx = abs(x1-x0), sx = x0 guard(myRenderer->dataMutex); if(lightMap.size()==myRenderer->lightGrid.size()) { @@ -241,8 +241,8 @@ void lightingEngineViewscreen::clear() void lightingEngineViewscreen::calculate() { rect2d vp=getMapViewport(); - const lightCell dim(levelDim,levelDim,levelDim); - lightMap.assign(lightMap.size(),lightCell(1,1,1)); + const rgbf dim(levelDim,levelDim,levelDim); + lightMap.assign(lightMap.size(),rgbf(1,1,1)); lights.assign(lights.size(),lightSource()); for(int i=vp.first.x;i 0.999 && thickness < 1.001) ocupancy[tileId]*=c; else ocupancy[tileId]*=(c.pow(thickness)); } -lightCell getStandartColor(int colorId) +rgbf getStandartColor(int colorId) { - return lightCell(df::global::enabler->ccolor[colorId][0]/255.0f, + return rgbf(df::global::enabler->ccolor[colorId][0]/255.0f, df::global::enabler->ccolor[colorId][1]/255.0f, df::global::enabler->ccolor[colorId][2]/255.0f); } @@ -343,7 +343,7 @@ void lightingEngineViewscreen::applyMaterial(int tileId,const matLightDef& mat,f addOclusion(tileId,mat.transparency,thickness); } else - ocupancy[tileId]=lightCell(0,0,0); + ocupancy[tileId]=rgbf(0,0,0); if(mat.isEmiting) addLight(tileId,mat.makeSource(size)); } @@ -361,11 +361,11 @@ bool lightingEngineViewscreen::applyMaterial(int tileId,int matType,int matIndex } return false; } -lightCell lightingEngineViewscreen::propogateSun(MapExtras::Block* b, int x,int y,const lightCell& in,bool lastLevel) +rgbf lightingEngineViewscreen::propogateSun(MapExtras::Block* b, int x,int y,const rgbf& in,bool lastLevel) { //TODO unify under addLight/addOclusion - const lightCell matStairCase(0.9f,0.9f,0.9f); - lightCell ret=in; + const rgbf matStairCase(0.9f,0.9f,0.9f); + rgbf ret=in; coord2d innerCoord(x,y); df::tiletype type = b->staticTiletypeAt(innerCoord); df::tile_designation d = b->DesignationAt(innerCoord); @@ -440,7 +440,7 @@ void lightingEngineViewscreen::doSun(const lightSource& sky,MapExtras::MapCache& for(int blockX=blockVp.first.x;blockX<=blockVp.second.x;blockX++) for(int blockY=blockVp.first.y;blockY<=blockVp.second.y;blockY++) { - lightCell cellArray[16][16]; + rgbf cellArray[16][16]; for(int block_x = 0; block_x < 16; block_x++) for(int block_y = 0; block_y < 16; block_y++) cellArray[block_x][block_y] = sky.power; @@ -455,7 +455,7 @@ void lightingEngineViewscreen::doSun(const lightSource& sky,MapExtras::MapCache& for(int block_x = 0; block_x < 16; block_x++) for(int block_y = 0; block_y < 16; block_y++) { - lightCell& curCell=cellArray[block_x][block_y]; + rgbf& curCell=cellArray[block_x][block_y]; curCell=propogateSun(b,block_x,block_y,curCell,z==window_z); if(curCell.dot(curCell)<0.003f) emptyCell++; @@ -466,7 +466,7 @@ void lightingEngineViewscreen::doSun(const lightSource& sky,MapExtras::MapCache& for(int block_x = 0; block_x < 16; block_x++) for(int block_y = 0; block_y < 16; block_y++) { - lightCell& curCell=cellArray[block_x][block_y]; + rgbf& curCell=cellArray[block_x][block_y]; df::coord2d pos; pos.x = blockX*16+block_x; pos.y = blockY*16+block_y; @@ -479,12 +479,12 @@ void lightingEngineViewscreen::doSun(const lightSource& sky,MapExtras::MapCache& } } } -lightCell lightingEngineViewscreen::getSkyColor(float v) +rgbf lightingEngineViewscreen::getSkyColor(float v) { if(dayColors.size()<2) { v=abs(fmod(v+0.5,1)-0.5)*2; - return lightCell(v,v,v); + return rgbf(v,v,v); } else { @@ -509,11 +509,11 @@ void lightingEngineViewscreen::doOcupancyAndLights() else daycol= fmod(dayHour,24.0f)/24.0f; //1->12h 0->24h - lightCell sky_col=getSkyColor(daycol); + rgbf sky_col=getSkyColor(daycol); lightSource sky(sky_col, -1);//auto calculate best size - lightSource candle(lightCell(0.96f,0.84f,0.03f),5); - lightSource torch(lightCell(0.9f,0.75f,0.3f),8); + lightSource candle(rgbf(0.96f,0.84f,0.03f),5); + lightSource torch(rgbf(0.9f,0.75f,0.3f),8); //perfectly blocking material @@ -551,7 +551,7 @@ void lightingEngineViewscreen::doOcupancyAndLights() if(!isInRect(pos,vp)) continue; int tile=getIndex(pos.x,pos.y); - lightCell& curCell=ocupancy[tile]; + rgbf& curCell=ocupancy[tile]; curCell=matAmbience.transparency; @@ -559,7 +559,7 @@ void lightingEngineViewscreen::doOcupancyAndLights() df::tile_designation d = b->DesignationAt(gpos); if(d.bits.hidden) { - curCell=lightCell(0,0,0); + curCell=rgbf(0,0,0); continue; // do not process hidden stuff, TODO other hidden stuff } //df::tile_occupancy o = b->OccupancyAt(gpos); @@ -574,7 +574,7 @@ void lightingEngineViewscreen::doOcupancyAndLights() lightDef=&matWall; if(shape==df::tiletype_shape::BROOK_BED ) { - curCell=lightCell(0,0,0); + curCell=rgbf(0,0,0); } else if(shape==df::tiletype_shape::WALL) { @@ -621,18 +621,18 @@ void lightingEngineViewscreen::doOcupancyAndLights() int tile=getIndex(pos.x,pos.y); if(isInRect(pos,vp)) { - lightCell fireColor; + rgbf fireColor; if(f->density>60) { - fireColor=lightCell(0.98f,0.91f,0.30f); + fireColor=rgbf(0.98f,0.91f,0.30f); } else if(f->density>30) { - fireColor=lightCell(0.93f,0.16f,0.16f); + fireColor=rgbf(0.93f,0.16f,0.16f); } else { - fireColor=lightCell(0.64f,0.0f,0.0f); + fireColor=rgbf(0.64f,0.0f,0.0f); } lightSource fire(fireColor,f->density/5); addLight(tile,fire); @@ -776,9 +776,9 @@ void lightingEngineViewscreen::doOcupancyAndLights() } } -lightCell lua_parseLightCell(lua_State* L) +rgbf lua_parseLightCell(lua_State* L) { - lightCell ret; + rgbf ret; lua_pushnumber(L,1); lua_gettable(L,-2); @@ -955,20 +955,20 @@ int lightingEngineViewscreen::parseBuildings(lua_State* L) } void lightingEngineViewscreen::defaultSettings() { - matAmbience=matLightDef(lightCell(0.85f,0.85f,0.85f)); - matLava=matLightDef(lightCell(0.8f,0.2f,0.2f),lightCell(0.8f,0.2f,0.2f),5); - matWater=matLightDef(lightCell(0.6f,0.6f,0.8f)); - matIce=matLightDef(lightCell(0.7f,0.7f,0.9f)); - matCursor=matLightDef(lightCell(0.96f,0.84f,0.03f),11); + matAmbience=matLightDef(rgbf(0.85f,0.85f,0.85f)); + matLava=matLightDef(rgbf(0.8f,0.2f,0.2f),rgbf(0.8f,0.2f,0.2f),5); + matWater=matLightDef(rgbf(0.6f,0.6f,0.8f)); + matIce=matLightDef(rgbf(0.7f,0.7f,0.9f)); + matCursor=matLightDef(rgbf(0.96f,0.84f,0.03f),11); matCursor.flicker=true; - matWall=matLightDef(lightCell(0,0,0)); - matCitizen=matLightDef(lightCell(0.8f,0.8f,0.9f),6); + matWall=matLightDef(rgbf(0,0,0)); + matCitizen=matLightDef(rgbf(0.8f,0.8f,0.9f),6); levelDim=0.2f; dayHour=-1; daySpeed=1; - dayColors.push_back(lightCell(0,0,0)); - dayColors.push_back(lightCell(1,1,1)); - dayColors.push_back(lightCell(0,0,0)); + dayColors.push_back(rgbf(0,0,0)); + dayColors.push_back(rgbf(1,1,1)); + dayColors.push_back(rgbf(0,0,0)); } void lightingEngineViewscreen::loadSettings() { @@ -1070,7 +1070,7 @@ void lightThread::run() void lightThread::work() { - canvas.assign(canvas.size(),lightCell(0,0,0)); + canvas.assign(canvas.size(),rgbf(0,0,0)); for(int i=myRect.first.x;i0 && dsq>0) { if(power<=ls.power) - return lightCell(); + return rgbf(); } //float dt=sqrt(dsq); - lightCell oldCol=canvas[tile]; - lightCell ncol=blendMax(power,oldCol); + rgbf oldCol=canvas[tile]; + rgbf ncol=blendMax(power,oldCol); canvas[tile]=ncol; if(wallhack) - return lightCell(); + return rgbf(); return power; } else - return lightCell(); + return rgbf(); } -void lightThread::doRay(lightCell power,int cx,int cy,int tx,int ty) +void lightThread::doRay(rgbf power,int cx,int cy,int tx,int ty) { using namespace std::placeholders; @@ -1146,7 +1146,7 @@ void lightThread::doLight( int x,int y ) lightSource& csource=dispatch.lights[x*dispatch.getH()+y]; if(csource.radius>0) { - lightCell power=csource.power; + rgbf power=csource.power; int radius =csource.radius; if(csource.flicker) { @@ -1154,7 +1154,7 @@ void lightThread::doLight( int x,int y ) radius*=flicker; power=power*flicker; } - lightCell surrounds; + rgbf surrounds; lightUpCell( power, 0, 0,x, y); for(int i=-1;i<2;i++) for(int j=-1;j<2;j++) diff --git a/plugins/rendermax/renderer_light.hpp b/plugins/rendermax/renderer_light.hpp index d14bbc33f..cb43c20be 100644 --- a/plugins/rendermax/renderer_light.hpp +++ b/plugins/rendermax/renderer_light.hpp @@ -17,7 +17,7 @@ private: float *fg = p->fg + tile * 4 * 6; float *bg = p->bg + tile * 4 * 6; float *tex = p->tex + tile * 2 * 6; - lightCell light=lightGrid[tile]; + rgbf light=lightGrid[tile]; for (int i = 0; i < 6; i++) { *(fg++) *= light.r; *(fg++) *= light.g; @@ -41,7 +41,7 @@ private: } public: tthread::fast_mutex dataMutex; - std::vector lightGrid; + std::vector lightGrid; renderer_light(renderer* parent):renderer_wrap(parent) { reinitLightGrid(); @@ -87,14 +87,14 @@ protected: }; struct lightSource { - lightCell power; + rgbf power; int radius; bool flicker; lightSource():power(0,0,0),radius(0),flicker(false) { } - lightSource(lightCell power,int radius); + lightSource(rgbf power,int radius); float powerSquared()const { return power.r*power.r+power.g*power.g+power.b*power.b; @@ -105,16 +105,16 @@ struct lightSource struct matLightDef { bool isTransparent; - lightCell transparency; + rgbf transparency; bool isEmiting; bool flicker; - lightCell emitColor; + rgbf emitColor; int radius; matLightDef():isTransparent(false),isEmiting(false),transparency(0,0,0),emitColor(0,0,0),radius(0){} - matLightDef(lightCell transparency,lightCell emit,int rad):isTransparent(true),isEmiting(true), + matLightDef(rgbf transparency,rgbf emit,int rad):isTransparent(true),isEmiting(true), transparency(transparency),emitColor(emit),radius(rad){} - matLightDef(lightCell emit,int rad):isTransparent(false),isEmiting(true),emitColor(emit),radius(rad),transparency(0,0,0){} - matLightDef(lightCell transparency):isTransparent(true),isEmiting(false),transparency(transparency){} + matLightDef(rgbf emit,int rad):isTransparent(false),isEmiting(true),emitColor(emit),radius(rad),transparency(0,0,0){} + matLightDef(rgbf transparency):isTransparent(true),isEmiting(false),transparency(transparency){} lightSource makeSource(float size=1) const { if(size>0.999 && size<1.001) @@ -147,10 +147,10 @@ public: tthread::condition_variable occlusionDone; //all threads wait for occlusion to finish tthread::mutex unprocessedMutex; std::stack unprocessed; //stack of parts of map where lighting is not finished - std::vector& occlusion; + std::vector& occlusion; tthread::mutex writeLock; //mutex for lightMap - std::vector& lightMap; + std::vector& lightMap; tthread::condition_variable writesDone; int writeCount; @@ -166,7 +166,7 @@ public: }; class lightThread { - std::vector canvas; + std::vector canvas; lightThreadDispatch& dispatch; DFHack::rect2d myRect; void work(); //main light calculation function @@ -179,8 +179,8 @@ public: void run(); private: void doLight(int x,int y); - void doRay(lightCell power,int cx,int cy,int tx,int ty); - lightCell lightUpCell(lightCell power,int dx,int dy,int tx,int ty); + void doRay(rgbf power,int cx,int cy,int tx,int ty); + rgbf lightUpCell(rgbf power,int dx,int dy,int tx,int ty); }; class lightingEngineViewscreen:public lightingEngine { @@ -203,13 +203,13 @@ private: void doSun(const lightSource& sky,MapExtras::MapCache& map); void doOcupancyAndLights(); - lightCell propogateSun(MapExtras::Block* b, int x,int y,const lightCell& in,bool lastLevel); - void doRay(std::vector & target, lightCell power,int cx,int cy,int tx,int ty); + rgbf propogateSun(MapExtras::Block* b, int x,int y,const rgbf& in,bool lastLevel); + void doRay(std::vector & target, rgbf power,int cx,int cy,int tx,int ty); void doFovs(); - void doLight(std::vector & target, int index); - lightCell lightUpCell(std::vector & target, lightCell power,int dx,int dy,int tx,int ty); + void doLight(std::vector & target, int index); + rgbf lightUpCell(std::vector & target, rgbf power,int dx,int dy,int tx,int ty); bool addLight(int tileId,const lightSource& light); - void addOclusion(int tileId,const lightCell& c,float thickness); + void addOclusion(int tileId,const rgbf& c,float thickness); matLightDef* getMaterial(int matType,int matIndex); buildingLightDef* getBuilding(df::building* bld); @@ -228,8 +228,8 @@ private: return df::coord2d(index/h, index%h); } //maps - std::vector lightMap; - std::vector ocupancy; + std::vector lightMap; + std::vector ocupancy; std::vector lights; //Threading stuff @@ -242,13 +242,13 @@ private: public: void lightWorkerThread(void * arg); private: - lightCell getSkyColor(float v); + rgbf getSkyColor(float v); bool doDebug; //settings float daySpeed; float dayHour; //<0 to cycle - std::vector dayColors; // a gradient of colors, first to 0, last to 24 + std::vector dayColors; // a gradient of colors, first to 0, last to 24 ///set up sane settings if setting file does not exist. void defaultSettings(); @@ -272,6 +272,6 @@ private: DFHack::rect2d mapPort; friend lightThreadDispatch; }; -lightCell blend(lightCell a,lightCell b); -lightCell blendMax(lightCell a,lightCell b); +rgbf blend(rgbf a,rgbf b); +rgbf blendMax(rgbf a,rgbf b); #endif diff --git a/plugins/rendermax/renderer_opengl.hpp b/plugins/rendermax/renderer_opengl.hpp index 151ff94f0..ce14f3762 100644 --- a/plugins/rendermax/renderer_opengl.hpp +++ b/plugins/rendermax/renderer_opengl.hpp @@ -185,73 +185,73 @@ public: }; }; -struct lightCell +struct rgbf { float r,g,b; - lightCell():r(0),g(0),b(0) + rgbf():r(0),g(0),b(0) { } - lightCell(float r,float g,float b):r(r),g(g),b(b) + rgbf(float r,float g,float b):r(r),g(g),b(b) { } - lightCell operator-(const lightCell& cell) const + rgbf operator-(const rgbf& cell) const { - return lightCell(r-cell.r,g-cell.g,b-cell.b); + return rgbf(r-cell.r,g-cell.g,b-cell.b); } - lightCell operator*(float val)const + rgbf operator*(float val)const { - return lightCell(r*val,g*val,b*val); + return rgbf(r*val,g*val,b*val); } - lightCell operator/(float val) const + rgbf operator/(float val) const { - return lightCell(r/val,g/val,b/val); + return rgbf(r/val,g/val,b/val); } - lightCell operator*(const lightCell& cell) const + rgbf operator*(const rgbf& cell) const { - return lightCell(r*cell.r,g*cell.g,b*cell.b); + return rgbf(r*cell.r,g*cell.g,b*cell.b); } - lightCell operator*=(float val) + rgbf operator*=(float val) { r*=val; g*=val; b*=val; return *this; } - lightCell operator*=(const lightCell& cell) + rgbf operator*=(const rgbf& cell) { r*=cell.r; g*=cell.g; b*=cell.b; return *this; } - lightCell operator+=(const lightCell& cell) + rgbf operator+=(const rgbf& cell) { r+=cell.r; g+=cell.g; b+=cell.b; return *this; } - lightCell operator+(const lightCell& other) const + rgbf operator+(const rgbf& other) const { - return lightCell(r+other.r,g+other.g,b+other.b); + return rgbf(r+other.r,g+other.g,b+other.b); } - bool operator<=(const lightCell& other) const + bool operator<=(const rgbf& other) const { return r<=other.r && g<=other.g && b<=other.b; } - float dot(const lightCell& other) const + float dot(const rgbf& other) const { return r*other.r+g*other.g+b*other.b; } - lightCell pow(const float exp) const + rgbf pow(const float exp) const { - return lightCell(std::pow(r, exp), std::pow(g, exp), std::pow(b, exp)); + return rgbf(std::pow(r, exp), std::pow(g, exp), std::pow(b, exp)); } - lightCell pow(const int exp) const + rgbf pow(const int exp) const { - return lightCell(std::pow(r, exp), std::pow(g, exp), std::pow(b, exp)); + return rgbf(std::pow(r, exp), std::pow(g, exp), std::pow(b, exp)); } }; struct renderer_test : public renderer_wrap { @@ -263,7 +263,7 @@ private: float *fg = p->fg + tile * 4 * 6; float *bg = p->bg + tile * 4 * 6; float *tex = p->tex + tile * 2 * 6; - lightCell light=lightGrid[tile]; + rgbf light=lightGrid[tile]; for (int i = 0; i < 6; i++) { *(fg++) *= light.r; *(fg++) *= light.g; @@ -287,7 +287,7 @@ private: } public: tthread::fast_mutex dataMutex; - std::vector lightGrid; + std::vector lightGrid; renderer_test(renderer* parent):renderer_wrap(parent) { reinitLightGrid(); @@ -332,11 +332,11 @@ private: float *fg = p->fg + tile * 4 * 6; float *bg = p->bg + tile * 4 * 6; float *tex = p->tex + tile * 2 * 6; - lightCell fm=foreMult[tile]; - lightCell fo=foreOffset[tile]; + rgbf fm=foreMult[tile]; + rgbf fo=foreOffset[tile]; - lightCell bm=backMult[tile]; - lightCell bo=backOffset[tile]; + rgbf bm=backMult[tile]; + rgbf bo=backOffset[tile]; for (int i = 0; i < 6; i++) { rgba* fore=reinterpret_cast(fg); fore->r=fore->r*fm.r+fo.r; @@ -365,8 +365,8 @@ private: } public: tthread::fast_mutex dataMutex; - std::vector foreOffset,foreMult; - std::vector backOffset,backMult; + std::vector foreOffset,foreMult; + std::vector backOffset,backMult; inline int xyToTile(int x, int y) { return x*(df::global::gps->dimy) + y; diff --git a/plugins/rendermax/rendermax.cpp b/plugins/rendermax/rendermax.cpp index 24ba8c4d8..e03de7ed2 100644 --- a/plugins/rendermax/rendermax.cpp +++ b/plugins/rendermax/rendermax.cpp @@ -121,10 +121,10 @@ static void resetGrids() renderer_lua* r=reinterpret_cast(df::global::enabler->renderer); for(size_t i=0;iforeMult.size();i++) { - r->foreMult[i]=lightCell(1,1,1); - r->foreOffset[i]=lightCell(0,0,0); - r->backMult[i]=lightCell(1,1,1); - r->backOffset[i]=lightCell(0,0,0); + r->foreMult[i]=rgbf(1,1,1); + r->foreOffset[i]=rgbf(0,0,0); + r->backMult[i]=rgbf(1,1,1); + r->backOffset[i]=rgbf(0,0,0); } } static int getGridsSize(lua_State* L) @@ -144,10 +144,10 @@ static int getCell(lua_State* L) int x=luaL_checknumber(L,1); int y=luaL_checknumber(L,2); int id=r->xyToTile(x,y); - lightCell fo=r->foreOffset[id]; - lightCell fm=r->foreMult[id]; - lightCell bo=r->backOffset[id]; - lightCell bm=r->backMult[id]; + rgbf fo=r->foreOffset[id]; + rgbf fm=r->foreMult[id]; + rgbf bo=r->backOffset[id]; + rgbf bm=r->backMult[id]; lua_newtable(L); lua_newtable(L); @@ -195,7 +195,7 @@ static int setCell(lua_State* L) int x=luaL_checknumber(L,1); int y=luaL_checknumber(L,2); - lightCell fo; + rgbf fo; lua_getfield(L,3,"fo"); lua_getfield(L,-1,"r"); fo.r=lua_tonumber(L,-1);lua_pop(L,1); @@ -203,7 +203,7 @@ static int setCell(lua_State* L) fo.g=lua_tonumber(L,-1);lua_pop(L,1); lua_getfield(L,-1,"b"); fo.b=lua_tonumber(L,-1);lua_pop(L,1); - lightCell fm; + rgbf fm; lua_getfield(L,3,"fm"); lua_getfield(L,-1,"r"); fm.r=lua_tonumber(L,-1);lua_pop(L,1); @@ -212,7 +212,7 @@ static int setCell(lua_State* L) lua_getfield(L,-1,"b"); fm.b=lua_tonumber(L,-1);lua_pop(L,1); - lightCell bo; + rgbf bo; lua_getfield(L,3,"bo"); lua_getfield(L,-1,"r"); bo.r=lua_tonumber(L,-1);lua_pop(L,1); @@ -221,7 +221,7 @@ static int setCell(lua_State* L) lua_getfield(L,-1,"b"); bo.b=lua_tonumber(L,-1);lua_pop(L,1); - lightCell bm; + rgbf bm; lua_getfield(L,3,"bm"); lua_getfield(L,-1,"r"); bm.r=lua_tonumber(L,-1);lua_pop(L,1); @@ -307,9 +307,9 @@ static command_result rendermax(color_ostream &out, vector & parameters } if(current_mode==MODE_TRUECOLOR && parameters.size()==2) { - lightCell red(1,0,0),green(0,1,0),blue(0,0,1),white(1,1,1); - lightCell cur=white; - lightCell dim(0.2f,0.2f,0.2f); + rgbf red(1,0,0),green(0,1,0),blue(0,0,1),white(1,1,1); + rgbf cur=white; + rgbf dim(0.2f,0.2f,0.2f); string col=parameters[1]; if(col=="red") cur=red; From 5ad3a7570fea204f00d1f1add1136b43890bc75c Mon Sep 17 00:00:00 2001 From: Warmist Date: Sun, 30 Jun 2013 22:03:07 +0300 Subject: [PATCH 058/676] Fixed a lot of disable lights bugs --- plugins/rendermax/renderer_light.cpp | 37 +++++++++++++++++++++++----- plugins/rendermax/renderer_light.hpp | 4 ++- plugins/rendermax/rendermax.cpp | 5 ++-- 3 files changed, 37 insertions(+), 9 deletions(-) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index fc7202890..fb5bd8f0e 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -1043,10 +1043,11 @@ void lightThread::run() { while(!isDone) { - { - tthread::lock_guard guard(dispatch.occlusionMutex); + {//wait for occlusion (and lights) to be ready + tthread::lock_guard guard(dispatch.occlusionMutex); + if(!dispatch.occlusionReady) dispatch.occlusionDone.wait(dispatch.occlusionMutex);//wait for work - if(dispatch.unprocessed.size()==0) //spurious wake-up + if(dispatch.unprocessed.size()==0 || !dispatch.occlusionReady) //spurious wake-up continue; if(dispatch.occlusion.size()!=canvas.size()) //oh no somebody resized stuff canvas.resize(dispatch.occlusion.size()); @@ -1055,8 +1056,18 @@ void lightThread::run() { //get my rectangle (any will do) tthread::lock_guard guard(dispatch.unprocessedMutex); + if (dispatch.unprocessed.size()==0) + { + //wtf?? why?! + continue; + } myRect=dispatch.unprocessed.top(); dispatch.unprocessed.pop(); + if (dispatch.unprocessed.size()==0) + { + dispatch.occlusionReady=false; + } + } work(); { @@ -1173,8 +1184,10 @@ void lightThreadDispatch::signalDoneOcclusion() tthread::lock_guard guardWrite(writeLock); writeCount=0; } - tthread::lock_guard guard(occlusionMutex); - + tthread::lock_guard guard1(occlusionMutex); + tthread::lock_guard guard2(unprocessedMutex); + while(!unprocessed.empty()) + unprocessed.pop(); viewPort=getMapViewport(); int threadCount=threadPool.size(); int w=viewPort.second.x-viewPort.first.x; @@ -1189,10 +1202,12 @@ void lightThreadDispatch::signalDoneOcclusion() area.second.x=viewPort.first.x+(i+1)*slicew; unprocessed.push(area); } + occlusionReady=true; occlusionDone.notify_all(); } -lightThreadDispatch::lightThreadDispatch( lightingEngineViewscreen* p ):parent(p),lights(parent->lights),occlusion(parent->ocupancy),lightMap(parent->lightMap),writeCount(0) +lightThreadDispatch::lightThreadDispatch( lightingEngineViewscreen* p ):parent(p),lights(parent->lights),occlusion(parent->ocupancy), + lightMap(parent->lightMap),writeCount(0),occlusionReady(false) { } @@ -1202,6 +1217,11 @@ void lightThreadDispatch::shutdown() for(int i=0;iisDone=true; + + } + occlusionDone.notify_all();//if stuck signal that you are done with stuff. + for(int i=0;imyThread->join(); } threadPool.clear(); @@ -1239,3 +1259,8 @@ void lightThreadDispatch::waitForWrites() writesDone.wait(writeLock); //if not, wait a bit } } + +lightThreadDispatch::~lightThreadDispatch() +{ + shutdown(); +} diff --git a/plugins/rendermax/renderer_light.hpp b/plugins/rendermax/renderer_light.hpp index cb43c20be..0f981cf8b 100644 --- a/plugins/rendermax/renderer_light.hpp +++ b/plugins/rendermax/renderer_light.hpp @@ -71,7 +71,7 @@ class lightingEngine { public: lightingEngine(renderer_light* target):myRenderer(target){} - + virtual ~lightingEngine(){} virtual void reinit()=0; virtual void calculate()=0; @@ -145,6 +145,7 @@ public: tthread::mutex occlusionMutex; tthread::condition_variable occlusionDone; //all threads wait for occlusion to finish + bool occlusionReady; tthread::mutex unprocessedMutex; std::stack unprocessed; //stack of parts of map where lighting is not finished std::vector& occlusion; @@ -156,6 +157,7 @@ public: int writeCount; lightThreadDispatch(lightingEngineViewscreen* p); + ~lightThreadDispatch(); void signalDoneOcclusion(); void shutdown(); void waitForWrites(); diff --git a/plugins/rendermax/rendermax.cpp b/plugins/rendermax/rendermax.cpp index e03de7ed2..c5fe90a66 100644 --- a/plugins/rendermax/rendermax.cpp +++ b/plugins/rendermax/rendermax.cpp @@ -88,7 +88,7 @@ void removeOld() INTERPOSE_HOOK(dwarmode_render_hook,render).apply(false); INTERPOSE_HOOK(dungeon_render_hook,render).apply(false); delete engine; - + engine=0; } if(current_mode!=MODE_DEFAULT) delete df::global::enabler->renderer; @@ -405,7 +405,8 @@ static command_result rendermax(color_ostream &out, vector & parameters out.print("%s\n","Not installed, doing nothing."); else removeOld(); - + CoreSuspender guard; + df::global::gps->force_full_display_count++; return CR_OK; } return CR_WRONG_USAGE; From c2db761a1b04fcb96bc5c099f6a3977ef40c9db6 Mon Sep 17 00:00:00 2001 From: Warmist Date: Tue, 2 Jul 2013 22:06:39 +0300 Subject: [PATCH 059/676] Rendermax: made it use unordered_map instead of map. --- plugins/rendermax/renderer_light.hpp | 40 +++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/plugins/rendermax/renderer_light.hpp b/plugins/rendermax/renderer_light.hpp index 0f981cf8b..0e3d2c986 100644 --- a/plugins/rendermax/renderer_light.hpp +++ b/plugins/rendermax/renderer_light.hpp @@ -2,10 +2,44 @@ #define RENDERER_LIGHT_INCLUDED #include "renderer_opengl.hpp" #include "Types.h" -#include #include #include #include +#include +// we are not using boost so let's cheat: +template +inline void hash_combine(std::size_t & seed, const T & v) +{ + std::hash hasher; + seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); +} + +namespace std +{ + template struct hash> + { + inline size_t operator()(const pair & v) const + { + size_t seed = 0; + ::hash_combine(seed, v.first); + ::hash_combine(seed, v.second); + return seed; + } + }; + template struct hash> + { + inline size_t operator()(const tuple & v) const + { + size_t seed = 0; + ::hash_combine(seed,get<0>(v)); + ::hash_combine(seed,get<1>(v)); + ::hash_combine(seed,get<2>(v)); + return seed; + } + }; +} +// now we can hash pairs and tuples + #include "modules/MapCache.h" bool isInRect(const df::coord2d& pos,const DFHack::rect2d& rect); struct renderer_light : public renderer_wrap { @@ -267,9 +301,9 @@ private: matLightDef matCitizen; float levelDim; //materials - std::map,matLightDef> matDefs; + std::unordered_map,matLightDef> matDefs; //buildings - std::map,buildingLightDef> buildingDefs; + std::unordered_map,buildingLightDef> buildingDefs; int w,h; DFHack::rect2d mapPort; friend lightThreadDispatch; From a3d3c34671bb606b56e430a1ba3b7229dbdfc080 Mon Sep 17 00:00:00 2001 From: Warmist Date: Wed, 3 Jul 2013 10:44:23 +0300 Subject: [PATCH 060/676] Bug fixes, perf improvement, don't load until there is a world. (breaks arena mode) --- NEWS | 2 ++ plugins/rendermax/renderer_light.cpp | 9 +++-- plugins/rendermax/renderer_light.hpp | 2 +- plugins/rendermax/rendermax.cpp | 51 ++++++++++++++++++++-------- 4 files changed, 46 insertions(+), 18 deletions(-) diff --git a/NEWS b/NEWS index b43ced4f0..2a3bbb39d 100644 --- a/NEWS +++ b/NEWS @@ -53,6 +53,8 @@ DFHack v0.34.11-r4 - outsideOnly: make raw-specified buildings impossible to build inside - resume: A plugin to help display and resume suspended constructions conveniently - stocks: An improved stocks display screen. + - rendermax: a heap of rendering experiments. Including one that does lighting. + Internals: - Core: there is now a per-save dfhack.init file for when the save is loaded, and another for when it is unloaded - EventManager: fixed job completion detection, fixed removal of TICK events, added EQUIPMENT_CHANGE event diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index fb5bd8f0e..1af05f176 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -105,7 +105,6 @@ lightingEngineViewscreen::lightingEngineViewscreen(renderer_light* target):light { reinit(); defaultSettings(); - loadSettings(); int numTreads=tthread::thread::hardware_concurrency(); if(numTreads==0)numTreads=1; threading.start(numTreads); @@ -240,6 +239,11 @@ void lightingEngineViewscreen::clear() } void lightingEngineViewscreen::calculate() { + if(lightMap.size()!=myRenderer->lightGrid.size()) + { + reinit(); + myRenderer->invalidate(); + } rect2d vp=getMapViewport(); const rgbf dim(levelDim,levelDim,levelDim); lightMap.assign(lightMap.size(),rgbf(1,1,1)); @@ -1035,8 +1039,7 @@ lightThread::lightThread( lightThreadDispatch& dispatch ):dispatch(dispatch),isD } lightThread::~lightThread() { - if(myThread) - delete myThread; + delete myThread; } void lightThread::run() diff --git a/plugins/rendermax/renderer_light.hpp b/plugins/rendermax/renderer_light.hpp index 0e3d2c986..5e768c27d 100644 --- a/plugins/rendermax/renderer_light.hpp +++ b/plugins/rendermax/renderer_light.hpp @@ -67,7 +67,7 @@ private: void reinitLightGrid(int w,int h) { tthread::lock_guard guard(dataMutex); - lightGrid.resize(w*h); + lightGrid.resize(w*h,rgbf(1,1,1)); } void reinitLightGrid() { diff --git a/plugins/rendermax/rendermax.cpp b/plugins/rendermax/rendermax.cpp index c5fe90a66..8cfe463bd 100644 --- a/plugins/rendermax/rendermax.cpp +++ b/plugins/rendermax/rendermax.cpp @@ -279,7 +279,41 @@ DFHACK_PLUGIN_LUA_COMMANDS { DFHACK_LUA_END }; +static void enable_hooks(bool enable) +{ + INTERPOSE_HOOK(dwarmode_render_hook,render).apply(enable); + INTERPOSE_HOOK(dungeon_render_hook,render).apply(enable); + if(enable && engine) + { + engine->loadSettings(); + } +} + +DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) +{ + switch(event) + { + case SC_VIEWSCREEN_CHANGED: + { + CoreSuspendClaimer suspender; + if(current_mode==MODE_LIGHT) + { + engine->clear(); + } + } + break; + case SC_WORLD_LOADED: + enable_hooks(true); + break; + case SC_WORLD_UNLOADED: + enable_hooks(false); + break; + default: + break; + } + return CR_OK; +} static command_result rendermax(color_ostream &out, vector & parameters) @@ -361,8 +395,9 @@ static command_result rendermax(color_ostream &out, vector & parameters renderer_light *myRender=new renderer_light(df::global::enabler->renderer); installNew(myRender,MODE_LIGHT); engine=new lightingEngineViewscreen(myRender); - INTERPOSE_HOOK(dwarmode_render_hook,render).apply(true); - INTERPOSE_HOOK(dungeon_render_hook,render).apply(true); + + if (Core::getInstance().isWorldLoaded()) + plugin_onstatechange(out, SC_WORLD_LOADED); } else if(current_mode==MODE_LIGHT && parameters.size()>1) { @@ -417,15 +452,3 @@ DFhackCExport command_result plugin_shutdown(color_ostream &) removeOld(); return CR_OK; } -DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) -{ - if(event==SC_VIEWSCREEN_CHANGED) - { - CoreSuspendClaimer suspender; - if(current_mode==MODE_LIGHT) - { - engine->clear(); - } - } - return CR_OK; -} \ No newline at end of file From 830d9900fb68fe19d51c4f13309965a2f101d71f Mon Sep 17 00:00:00 2001 From: Warmist Date: Wed, 3 Jul 2013 18:47:51 +0300 Subject: [PATCH 061/676] Fixed a bug that enabled rendermax even when it was supposed to be off. --- plugins/rendermax/renderer_light.cpp | 10 +++++----- plugins/rendermax/rendermax.cpp | 5 +++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index 1af05f176..1691cbb22 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -121,7 +121,7 @@ void lightingEngineViewscreen::reinit() ocupancy.resize(size); lights.resize(size); } -void plotCircle(int xm, int ym, int r,std::function setPixel) +void plotCircle(int xm, int ym, int r,const std::function& setPixel) { int x = -r, y = 0, err = 2-2*r; /* II. Quadrant */ do { @@ -134,7 +134,7 @@ void plotCircle(int xm, int ym, int r,std::function setPixel) if (r > x || err > y) err += ++x*2+1; /* e_xy+e_x > 0 or no 2nd y-step */ } while (x < 0); } -void plotSquare(int xm, int ym, int r,std::function setPixel) +void plotSquare(int xm, int ym, int r,const std::function& setPixel) { for(int x = 0; x <= r; x++) { @@ -148,7 +148,7 @@ void plotSquare(int xm, int ym, int r,std::function setPixel) setPixel(xm-x, ym+r); /* IV.2 Quadrant */ } } -void plotLine(int x0, int y0, int x1, int y1,rgbf power,std::function setPixel) +void plotLine(int x0, int y0, int x1, int y1,rgbf power,const std::function& setPixel) { int dx = abs(x1-x0), sx = x0 setPixelAA) +void plotLineAA(int x0, int y0, int x1, int y1,rgbf power,const std::function& setPixelAA) { int dx = abs(x1-x0), sx = x0lightGrid.size()) { reinit(); - myRenderer->invalidate(); + myRenderer->invalidate();//needs a lock? } rect2d vp=getMapViewport(); const rgbf dim(levelDim,levelDim,levelDim); diff --git a/plugins/rendermax/rendermax.cpp b/plugins/rendermax/rendermax.cpp index 8cfe463bd..b5a5618a6 100644 --- a/plugins/rendermax/rendermax.cpp +++ b/plugins/rendermax/rendermax.cpp @@ -292,6 +292,8 @@ static void enable_hooks(bool enable) DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) { + if(current_mode!=MODE_LIGHT) + return CR_OK; switch(event) { case SC_VIEWSCREEN_CHANGED: @@ -403,8 +405,7 @@ static command_result rendermax(color_ostream &out, vector & parameters { if(parameters[1]=="reload") { - CoreSuspender suspend; - engine->loadSettings(); + enable_hooks(true); } else if(parameters[1]=="sun" && parameters.size()==3) { From 72259c8eba37fd29a5a2cd13a2437c18b6748b73 Mon Sep 17 00:00:00 2001 From: Warmist Date: Sun, 22 Sep 2013 14:50:03 +0300 Subject: [PATCH 062/676] Added creature lights and item lights. --- plugins/rendermax/renderer_light.cpp | 181 +++++++++++++++++++++++---- plugins/rendermax/renderer_light.hpp | 36 +++++- plugins/rendermax/rendermax.lua | 79 +++++++++++- 3 files changed, 263 insertions(+), 33 deletions(-) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index 1691cbb22..4509c463d 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -24,6 +24,9 @@ #include "df/building_floodgatest.h" #include "df/plant.h" #include "df/plant_raw.h" +#include "df/item.h" +#include "df/items_other_id.h" +#include "df/unit.h" #include @@ -219,11 +222,11 @@ void plotLineAA(int x0, int y0, int x1, int y1,rgbf power,const std::functioninvalidateRect(vp.first.x,vp.first.y,vp.second.x-vp.first.x,vp.second.y-vp.first.y); - //myRenderer->invalidate(); - //std::copy(lightMap.begin(),lightMap.end(),myRenderer->lightGrid.begin()); } static size_t max_list_size = 100000; // Avoid iterating over huge lists @@ -324,7 +325,7 @@ void addPlant(const std::string& id,std::map& map,const lightSo map[nId]=v; } } -matLightDef* lightingEngineViewscreen::getMaterial(int matType,int matIndex) +matLightDef* lightingEngineViewscreen::getMaterialDef( int matType,int matIndex ) { auto it=matDefs.find(std::make_pair(matType,matIndex)); if(it!=matDefs.end()) @@ -332,7 +333,7 @@ matLightDef* lightingEngineViewscreen::getMaterial(int matType,int matIndex) else return NULL; } -buildingLightDef* lightingEngineViewscreen::getBuilding(df::building* bld) +buildingLightDef* lightingEngineViewscreen::getBuildingDef( df::building* bld ) { auto it=buildingDefs.find(std::make_tuple((int)bld->getType(),(int)bld->getSubtype(),(int)bld->getCustomType())); if(it!=buildingDefs.end()) @@ -340,6 +341,34 @@ buildingLightDef* lightingEngineViewscreen::getBuilding(df::building* bld) else return NULL; } +creatureLightDef* lightingEngineViewscreen::getCreatureDef(df::unit* u) +{ + auto it=creatureDefs.find(std::make_pair(int(u->race),int(u->caste))); + if(it!=creatureDefs.end()) + return &it->second; + else + { + auto it2=creatureDefs.find(std::make_pair(int(u->race),int(-1))); + if(it2!=creatureDefs.end()) + return &it2->second; + else + return NULL; + } +} +itemLightDef* lightingEngineViewscreen::getItemDef(df::item* it) +{ + auto iter=itemDefs.find(std::make_pair(int(it->getType()),int(it->getSubtype()))); + if(iter!=itemDefs.end()) + return &iter->second; + else + { + auto iter2=itemDefs.find(std::make_pair(int(it->getType()),int(-1))); + if(iter2!=itemDefs.end()) + return &iter2->second; + else + return NULL; + } +} void lightingEngineViewscreen::applyMaterial(int tileId,const matLightDef& mat,float size, float thickness) { if(mat.isTransparent) @@ -353,7 +382,7 @@ void lightingEngineViewscreen::applyMaterial(int tileId,const matLightDef& mat,f } bool lightingEngineViewscreen::applyMaterial(int tileId,int matType,int matIndex,float size,float thickness,const matLightDef* def) { - matLightDef* m=getMaterial(matType,matIndex); + matLightDef* m=getMaterialDef(matType,matIndex); if(m) { applyMaterial(tileId,*m,size,thickness); @@ -392,7 +421,7 @@ rgbf lightingEngineViewscreen::propogateSun(MapExtras::Block* b, int x,int y,con ret*=matIce.transparency.pow(1.0f/7.0f); } - lightDef=getMaterial(mat.mat_type,mat.mat_index); + lightDef=getMaterialDef(mat.mat_type,mat.mat_index); if(!lightDef || !lightDef->isTransparent) lightDef=&matWall; @@ -502,8 +531,6 @@ rgbf lightingEngineViewscreen::getSkyColor(float v) } void lightingEngineViewscreen::doOcupancyAndLights() { - // TODO better curve (+red dawn ?) - float daycol; if(dayHour<0) { @@ -515,11 +542,6 @@ void lightingEngineViewscreen::doOcupancyAndLights() rgbf sky_col=getSkyColor(daycol); lightSource sky(sky_col, -1);//auto calculate best size - - lightSource candle(rgbf(0.96f,0.84f,0.03f),5); - lightSource torch(rgbf(0.9f,0.75f,0.3f),8); - - //perfectly blocking material MapExtras::MapCache cache; doSun(sky,cache); @@ -543,7 +565,7 @@ void lightingEngineViewscreen::doOcupancyAndLights() MapExtras::Block* bDown=cache.BlockAt(DFCoord(blockX,blockY,window_z-1)); if(!b) continue; //empty blocks fixed by sun propagation - + for(int block_x = 0; block_x < 16; block_x++) for(int block_y = 0; block_y < 16; block_y++) { @@ -573,7 +595,7 @@ void lightingEngineViewscreen::doOcupancyAndLights() DFHack::t_matpair mat=b->staticMaterialAt(gpos); - matLightDef* lightDef=getMaterial(mat.mat_type,mat.mat_index); + matLightDef* lightDef=getMaterialDef(mat.mat_type,mat.mat_index); if(!lightDef || !lightDef->isTransparent) lightDef=&matWall; if(shape==df::tiletype_shape::BROOK_BED ) @@ -643,6 +665,7 @@ void lightingEngineViewscreen::doOcupancyAndLights() } } } + //plants for(int i=0;iplants.size();i++) { @@ -665,7 +688,7 @@ void lightingEngineViewscreen::doOcupancyAndLights() if(ev_type==df::block_square_event_type::material_spatter) { df::block_square_event_material_spatterst* spatter=static_cast(ev); - matLightDef* m=getMaterial(spatter->mat_type,spatter->mat_index); + matLightDef* m=getMaterialDef(spatter->mat_type,spatter->mat_index); if(!m) continue; if(!m->isEmiting) @@ -696,15 +719,47 @@ void lightingEngineViewscreen::doOcupancyAndLights() applyMaterial(tile,matCursor); } //citizen only emit light, if defined - if(matCitizen.isEmiting) + //or other creatures + if(matCitizen.isEmiting || creatureDefs.size()>0) for (int i=0;iunits.active.size();++i) { df::unit *u = df::global::world->units.active[i]; coord2d pos=worldToViewportCoord(coord2d(u->pos.x,u->pos.y),vp,window2d); if(u->pos.z==window_z && isInRect(pos,vp)) - if (DFHack::Units::isCitizen(u) && !u->counters.unconscious) - addLight(getIndex(pos.x,pos.y),matCitizen.makeSource()); + { + if (DFHack::Units::isCitizen(u) && !u->counters.unconscious) + addLight(getIndex(pos.x,pos.y),matCitizen.makeSource()); + creatureLightDef *def=getCreatureDef(u); + if(def && !u->flags1.bits.dead) + { + addLight(getIndex(pos.x,pos.y),def->light.makeSource()); + } + } + } + //items + if(itemDefs.size()>0) + { + std::vector& vec=df::global::world->items.other[items_other_id::IN_PLAY]; + for(size_t i=0;iequiped || mat->haul ||mat->inBuilding ||mat->inContainer) && curItem->flags.bits.in_inventory)|| //TODO split this up + (mat->onGround && curItem->flags.bits.on_ground) ) + { + if(mat->light.isEmiting) + addLight(getIndex(pos.x,pos.y),mat->light.makeSource()); + if(!mat->light.isTransparent) + addOclusion(getIndex(pos.x,pos.y),mat->light.transparency,1); + } + } + } } + //buildings for(size_t i = 0; i < df::global::world->buildings.all.size(); i++) { @@ -728,7 +783,7 @@ void lightingEngineViewscreen::doOcupancyAndLights() else tile=getIndex(p2.x,p2.y); df::building_type type = bld->getType(); - buildingLightDef* def=getBuilding(bld); + buildingLightDef* def=getBuildingDef(bld); if(!def) continue; if(type==df::enums::building_type::Door) @@ -747,7 +802,7 @@ void lightingEngineViewscreen::doOcupancyAndLights() if(def->useMaterial) { - matLightDef* mat=getMaterial(bld->mat_type,bld->mat_index); + matLightDef* mat=getMaterialDef(bld->mat_type,bld->mat_index); if(!mat)mat=&matWall; if(!def->poweredOnly || !bld->isUnpowered()) //not powered. Add occlusion only. { @@ -906,6 +961,72 @@ int lightingEngineViewscreen::parseSpecial(lua_State* L) return 0; } #undef LOAD_SPECIAL +int lightingEngineViewscreen::parseItems(lua_State* L) +{ + auto engine= (lightingEngineViewscreen*)lua_touserdata(L, 1); + engine->itemDefs.clear(); + Lua::StackUnwinder unwinder(L); + lua_getfield(L,2,"items"); + if(!lua_istable(L,-1)) + { + luaL_error(L,"Items table not found."); + return 0; + } + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + if(!lua_istable(L,-1)) + luaL_error(L,"Broken item definitions."); + lua_getfield(L,-1,"type"); + int type=lua_tonumber(L,-1); + lua_pop(L,1); + lua_getfield(L,-1,"subtype"); + int subtype=luaL_optinteger(L,-1,-1); + lua_pop(L,1); + itemLightDef item; + lua_getfield(L,-1,"light"); + item.light=lua_parseMatDef(L); + GETLUAFLAG(item.haul,"hauling"); + GETLUAFLAG(item.equiped,"equiped"); + GETLUAFLAG(item.inBuilding,"inBuilding"); + GETLUAFLAG(item.inContainer,"contained"); + GETLUAFLAG(item.onGround,"onGround"); + GETLUAFLAG(item.useMaterial,"useMaterial"); + engine->itemDefs[std::make_pair(type,subtype)]=item; + lua_pop(L,2); + } + lua_pop(L,1); + return 0; +} +int lightingEngineViewscreen::parseCreatures(lua_State* L) +{ + auto engine= (lightingEngineViewscreen*)lua_touserdata(L, 1); + engine->creatureDefs.clear(); + Lua::StackUnwinder unwinder(L); + lua_getfield(L,2,"creatures"); + if(!lua_istable(L,-1)) + { + luaL_error(L,"Creatures table not found."); + return 0; + } + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + if(!lua_istable(L,-1)) + luaL_error(L,"Broken creature definitions."); + lua_getfield(L,-1,"race"); + int race=lua_tonumber(L,-1); + lua_pop(L,1); + lua_getfield(L,-1,"caste"); + int caste=lua_tonumber(L,-1); + lua_pop(L,1); + creatureLightDef cr; + lua_getfield(L,-1,"light"); + cr.light=lua_parseMatDef(L); + engine->creatureDefs[std::make_pair(race,caste)]=cr; + lua_pop(L,2); + } + lua_pop(L,1); + return 0; +} int lightingEngineViewscreen::parseBuildings(lua_State* L) { auto engine= (lightingEngineViewscreen*)lua_touserdata(L, 1); @@ -1018,6 +1139,18 @@ void lightingEngineViewscreen::loadSettings() lua_pushvalue(s,env); Lua::SafeCall(out,s,2,0); out.print("%d buildings loaded\n",buildingDefs.size()); + + lua_pushcfunction(s, parseCreatures); + lua_pushlightuserdata(s, this); + lua_pushvalue(s,env); + Lua::SafeCall(out,s,2,0); + out.print("%d creatures loaded\n",creatureDefs.size()); + + lua_pushcfunction(s, parseItems); + lua_pushlightuserdata(s, this); + lua_pushvalue(s,env); + Lua::SafeCall(out,s,2,0); + out.print("%d items loaded\n",itemDefs.size()); } } @@ -1147,7 +1280,7 @@ rgbf lightThread::lightUpCell(rgbf power,int dx,int dy,int tx,int ty) else return rgbf(); } -void lightThread::doRay(rgbf power,int cx,int cy,int tx,int ty) +void lightThread::doRay(const rgbf& power,int cx,int cy,int tx,int ty) { using namespace std::placeholders; diff --git a/plugins/rendermax/renderer_light.hpp b/plugins/rendermax/renderer_light.hpp index 5e768c27d..75fb5ab6c 100644 --- a/plugins/rendermax/renderer_light.hpp +++ b/plugins/rendermax/renderer_light.hpp @@ -166,6 +166,22 @@ struct buildingLightDef float size; buildingLightDef():poweredOnly(false),useMaterial(true),thickness(1.0f),size(1.0f){} }; +struct itemLightDef +{ + matLightDef light; + bool haul; + bool equiped; + bool onGround; + bool inBuilding; + bool inContainer; + bool useMaterial; + itemLightDef():haul(true),equiped(true),onGround(true),inBuilding(false),inContainer(false),useMaterial(true){} +}; +struct creatureLightDef +{ + matLightDef light; + +}; class lightThread; class lightingEngineViewscreen; class lightThreadDispatch @@ -215,7 +231,7 @@ public: void run(); private: void doLight(int x,int y); - void doRay(rgbf power,int cx,int cy,int tx,int ty); + void doRay(const rgbf& power,int cx,int cy,int tx,int ty); rgbf lightUpCell(rgbf power,int dx,int dy,int tx,int ty); }; class lightingEngineViewscreen:public lightingEngine @@ -247,9 +263,11 @@ private: bool addLight(int tileId,const lightSource& light); void addOclusion(int tileId,const rgbf& c,float thickness); - matLightDef* getMaterial(int matType,int matIndex); - buildingLightDef* getBuilding(df::building* bld); - + matLightDef* getMaterialDef(int matType,int matIndex); + buildingLightDef* getBuildingDef(df::building* bld); + creatureLightDef* getCreatureDef(df::unit* u); + itemLightDef* getItemDef(df::item* it); + //apply material to cell void applyMaterial(int tileId,const matLightDef& mat,float size=1, float thickness = 1); //try to find and apply material, if failed return false, and if def!=null then apply def. @@ -291,6 +309,8 @@ private: static int parseMaterials(lua_State* L); static int parseSpecial(lua_State* L); static int parseBuildings(lua_State* L); + static int parseItems(lua_State* L); + static int parseCreatures(lua_State* L); //special stuff matLightDef matLava; matLightDef matIce; @@ -304,10 +324,14 @@ private: std::unordered_map,matLightDef> matDefs; //buildings std::unordered_map,buildingLightDef> buildingDefs; + //creatures + std::unordered_map,creatureLightDef> creatureDefs; + //items + std::unordered_map,itemLightDef> itemDefs; int w,h; DFHack::rect2d mapPort; friend lightThreadDispatch; }; -rgbf blend(rgbf a,rgbf b); -rgbf blendMax(rgbf a,rgbf b); +rgbf blend(const rgbf& a,const rgbf& b); +rgbf blendMax(const rgbf& a,const rgbf& b); #endif diff --git a/plugins/rendermax/rendermax.lua b/plugins/rendermax/rendermax.lua index c5e7c21a7..24b5c7fc8 100644 --- a/plugins/rendermax/rendermax.lua +++ b/plugins/rendermax/rendermax.lua @@ -4,6 +4,8 @@ ret=ret[1] ret.materials={} ret.buildings={} ret.special={} +ret.items={} +ret.creatures={} for k,v in pairs(ret) do _ENV[k]=v end @@ -28,7 +30,7 @@ end function buildingLookUp(id) local tokens={} local lookup={ Workshop=df.workshop_type,Furnace=df.furnace_type,Trap=df.trap_type, - SiegeEngine=siegeengine_type} + SiegeEngine=df.siegeengine_type} for i in string.gmatch(id, "[^:]+") do table.insert(tokens,i) end @@ -44,21 +46,86 @@ function buildingLookUp(id) for k,v in pairs(df.global.world.raws.buildings.workshops) do if v.code==tokens[3] then ret.custom=v.id - break + return ret end end elseif ret.type==df.building_type.Furnace then for k,v in pairs(df.global.world.raws.buildings.furnaces) do if v.code==tokens[3] then ret.custom=v.id - break + return ret end end end end + qerror("Invalid custom building:"..tokens[3]) end return ret end +function itemLookup(id) + local ret={} + local tokens={} + for i in string.gmatch(id, "[^:]+") do + table.insert(tokens,i) + end + ret.type=df.item_type[tokens[1]] + ret.subtype=-1 + if tokens[2] then + for k,v in ipairs(df.global.world.raws.itemdefs.all) do --todo lookup correct itemdef + if v.id==tokens[2] then + ret.subtype=v.subtype + return ret + end + end + qerror("Failed item subtype lookup:"..tokens[2]) + end + return ret +end +function creatureLookup(id) + local ret={} + local tokens={} + for i in string.gmatch(id, "[^:]+") do + table.insert(tokens,i) + end + for k,v in ipairs(df.global.world.raws.creatures.all) do + if v.creature_id==tokens[1] then + ret.type=k + if tokens[2] then + for k,v in ipairs(v.caste) do + if v.caste_id==tokens[2] then + ret.subtype=k + break + end + end + if ret.subtype==nil then + qerror("caste "..tokens[2].." for "..tokens[1].." not found") + end + end + return ret + end + end + qerror("Failed to find race:"..tokens[1]) +end +-- add creature by id ("DWARF" or "DWARF:MALE") +-- supported flags: +function addCreature(id,transparency,emitance,radius,flags) + local crId=creatureLookup(id) + local mat=makeMaterialDef(transparency,emitance,radius,flags) + table.insert(creatures,{race=crId.type,caste=crId.subtype or -1, light=mat}) +end +-- add item by id ( "TOTEM" or "WEAPON:PICK" or "WEAPON" for all the weapon types) +-- supported flags: +-- hauling --active when hauled TODO::currently all mean same thing... +-- equiped --active while equiped TODO::currently all mean same thing... +-- inBuilding --active in building TODO::currently all mean same thing... +-- contained --active in container TODO::currently all mean same thing... +-- onGround --active on ground +-- useMaterial --uses material, but the defined things overwrite +function addItem(id,transparency,emitance,radius,flags) + local itemId=itemLookup(id) + local mat=makeMaterialDef(transparency,emitance,radius,flags) + table.insert(items,{["type"]=itemId.type,subtype=itemId.subtype,light=mat}) +end -- add building by id (string e.g. "Statue" or "Workshop:Masons", flags is a table of strings -- supported flags: -- useMaterial --uses material, but the defined things overwrite @@ -147,3 +214,9 @@ addBuilding("WindowGlass",nil,nil,0,{"useMaterial"}) addBuilding("WindowGem",nil,nil,0,{"useMaterial"}) addBuilding("Door",nil,nil,0,{"useMaterial"}) -- special case, only closed door obstruct/emit light addBuilding("Floodgate",nil,nil,0,{"useMaterial"}) -- special case, only closed door obstruct/emit light +--creatures +addCreature("ELEMENTMAN_MAGMA",{0.8,0.2,0.2},{0.8,0.2,0.2},5) +--items +addItem("GEM",nil,nil,{"useMaterial","onGround"}) +addItem("ROUGH",nil,nil,{"useMaterial","onGround"}) +addItem("SMALLGEM",nil,nil,{"useMaterial","onGround"}) \ No newline at end of file From 372f861010b73995de868036b7568c922bbd9b4e Mon Sep 17 00:00:00 2001 From: Warmist Date: Sun, 26 Jan 2014 01:11:26 +0200 Subject: [PATCH 063/676] rendermax: fixed crashes (hopefully for real this time) --- plugins/rendermax/rendermax.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/rendermax/rendermax.cpp b/plugins/rendermax/rendermax.cpp index b5a5618a6..0a15807af 100644 --- a/plugins/rendermax/rendermax.cpp +++ b/plugins/rendermax/rendermax.cpp @@ -82,9 +82,9 @@ IMPLEMENT_VMETHOD_INTERPOSE(dungeon_render_hook, render); void removeOld() { + CoreSuspender lock; if(engine) { - CoreSuspender lock; INTERPOSE_HOOK(dwarmode_render_hook,render).apply(false); INTERPOSE_HOOK(dungeon_render_hook,render).apply(false); delete engine; From b5b01ea02bdf2bf483ed1ddbb7e92fa234e1c023 Mon Sep 17 00:00:00 2001 From: Warmist Date: Sun, 26 Jan 2014 03:26:41 +0200 Subject: [PATCH 064/676] rendermax: mainly organizing added comments, non-enabled code for adaptive eyes, --- plugins/rendermax/renderer_light.cpp | 13 +++++------ plugins/rendermax/renderer_light.hpp | 32 +++++++++++++++++++++++++--- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index 4509c463d..4df4ea9bf 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -1179,6 +1179,7 @@ void lightThread::run() { while(!isDone) { + //TODO: get area to process, and then process (by rounds): 1. occlusions, 2.sun, 3.lights(could be difficult, units/items etc...) {//wait for occlusion (and lights) to be ready tthread::lock_guard guard(dispatch.occlusionMutex); if(!dispatch.occlusionReady) @@ -1243,7 +1244,7 @@ rgbf lightThread::lightUpCell(rgbf power,int dx,int dy,int tx,int ty) size_t tile=tx*h+ty; int dsq=dx*dx+dy*dy; float dt=1; - if(dsq == 1) + if(dsq == 1)//array lookup might be faster still dt=1; else if(dsq == 2) dt = RootTwo; @@ -1263,10 +1264,10 @@ rgbf lightThread::lightUpCell(rgbf power,int dx,int dy,int tx,int ty) } if(ls.radius>0 && dsq>0) { - if(power<=ls.power) + if(power<=ls.power) //quit early if hitting another (stronger) lightsource return rgbf(); } - //float dt=sqrt(dsq); + rgbf oldCol=canvas[tile]; rgbf ncol=blendMax(power,oldCol); canvas[tile]=ncol; @@ -1302,12 +1303,12 @@ void lightThread::doLight( int x,int y ) power=power*flicker; } rgbf surrounds; - lightUpCell( power, 0, 0,x, y); + lightUpCell( power, 0, 0,x, y); //light up the source itself for(int i=-1;i<2;i++) for(int j=-1;j<2;j++) if(i!=0||j!=0) - surrounds += lightUpCell( power, i, j,x+i, y+j); - if(surrounds.dot(surrounds)>0.00001f) + surrounds += lightUpCell( power, i, j,x+i, y+j); //and this is wall hack (so that walls look nice) + if(surrounds.dot(surrounds)>0.00001f) //if we needed to light up the suroundings, then raycast { plotSquare(x,y,radius, std::bind(&lightThread::doRay,this,power,x,y,_1,_2)); diff --git a/plugins/rendermax/renderer_light.hpp b/plugins/rendermax/renderer_light.hpp index 75fb5ab6c..eadd497e6 100644 --- a/plugins/rendermax/renderer_light.hpp +++ b/plugins/rendermax/renderer_light.hpp @@ -44,6 +44,30 @@ namespace std bool isInRect(const df::coord2d& pos,const DFHack::rect2d& rect); struct renderer_light : public renderer_wrap { private: + float light_adaptation; + rgbf adapt_to_light(const rgbf& light) + { + const float influence=0.0001; + const float max_adapt=1; + const float min_adapt=0; + float intensity=(light.r+light.g+light.b)/3.0; + light_adaptation=intensity*influence+light_adaptation*(1-influence); + float delta=light_adaptation-intensity; + + rgbf ret; + ret.r=light.r-delta; + ret.g=light.g-delta; + ret.b=light.b-delta; + return ret; + //if light_adaptation/intensity~1 then draw 1,1,1 (i.e. totally adapted) + /* + 1. adapted -> 1,1,1 (full bright everything okay) delta=0 multiplier=? + 2. light adapted, real=dark -> darker delta>0 multiplier<1 + 3. dark adapted, real=light -> lighter delta<0 multiplier>1 + */ + //if light_adaptation/intensity!=0 then draw + + } void colorizeTile(int x,int y) { const int tile = x*(df::global::gps->dimy) + y; @@ -51,8 +75,9 @@ private: float *fg = p->fg + tile * 4 * 6; float *bg = p->bg + tile * 4 * 6; float *tex = p->tex + tile * 2 * 6; - rgbf light=lightGrid[tile]; - for (int i = 0; i < 6; i++) { + rgbf light=lightGrid[tile];//for light adaptation: rgbf light=adapt_to_light(lightGrid[tile]); + + for (int i = 0; i < 6; i++) { //oh how sse would do wonders here, or shaders... *(fg++) *= light.r; *(fg++) *= light.g; *(fg++) *= light.b; @@ -73,10 +98,11 @@ private: { reinitLightGrid(df::global::gps->dimy,df::global::gps->dimx); } + public: tthread::fast_mutex dataMutex; std::vector lightGrid; - renderer_light(renderer* parent):renderer_wrap(parent) + renderer_light(renderer* parent):renderer_wrap(parent),light_adaptation(1) { reinitLightGrid(); } From 50963c73faf5aadc9cca03a1df2968c7f2b85723 Mon Sep 17 00:00:00 2001 From: Warmist Date: Fri, 14 Feb 2014 11:50:12 +0200 Subject: [PATCH 065/676] rendermax: added adv mode lights also more proc-expensive diffuse mode. --- plugins/rendermax/renderer_light.cpp | 109 ++++++++++++++++++++++++--- plugins/rendermax/renderer_light.hpp | 14 ++-- plugins/rendermax/rendermax.cpp | 2 + plugins/rendermax/rendermax.lua | 5 +- 4 files changed, 113 insertions(+), 17 deletions(-) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index 4df4ea9bf..1127c6636 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -124,6 +124,7 @@ void lightingEngineViewscreen::reinit() ocupancy.resize(size); lights.resize(size); } + void plotCircle(int xm, int ym, int r,const std::function& setPixel) { int x = -r, y = 0, err = 2-2*r; /* II. Quadrant */ @@ -163,7 +164,7 @@ void plotLine(int x0, int y0, int x1, int y1,rgbf power,const std::function= dy) { err += dy; x0 += sx; rdx=sx;} /* e_xy+e_x > 0 */ if (e2 <= dx) { err += dx; y0 += sy; rdy=sy;} /* e_xy+e_y < 0 */ } + return ; } +void plotLineDiffuse(int x0, int y0, int x1, int y1,rgbf power,int num_diffuse,const std::function& setPixel,bool skip_hack=false) +{ + + int dx = abs(x1-x0), sx = x0= dy) { err += dy; x0 += sx; rdx=sx;} /* e_xy+e_x > 0 */ + if (e2 <= dx) { err += dx; y0 += sy; rdy=sy;} /* e_xy+e_y < 0 */ + if(num_diffuse>0 && dsq/4<(x1-x0)*(x1-x0)+(y1-y0)*(y1-y0))//reached center? + { + const float betta=0.25; + int nx=y1-y0; //right angle + int ny=x1-x0; + if((nx*nx+ny*ny)*betta*betta>2) + { + plotLineDiffuse(x0,y0,x0+nx*betta,y0+ny*betta,power,num_diffuse-1,setPixel,true); + plotLineDiffuse(x0,y0,x0-nx*betta,y0-ny*betta,power,num_diffuse-1,setPixel,true); + } + } + } + return ; +} void plotLineAA(int x0, int y0, int x1, int y1,rgbf power,const std::function& setPixelAA) { int dx = abs(x1-x0), sx = x0invalidate(); return; } - + + bool isAdventure=(*df::global::gametype==df::game_type::ADVENTURE_ARENA)|| + (*df::global::gametype==df::game_type::ADVENTURE_MAIN); + if(isAdventure) + { + fixAdvMode(adv_mode); + } + if(doDebug) std::swap(ocupancy,myRenderer->lightGrid); else @@ -278,9 +322,47 @@ void lightingEngineViewscreen::updateWindow() myRenderer->invalidateRect(vp.first.x,vp.first.y,vp.second.x-vp.first.x,vp.second.y-vp.first.y); } - -static size_t max_list_size = 100000; // Avoid iterating over huge lists - +void lightingEngineViewscreen::preRender() +{ + +} +void lightingEngineViewscreen::fixAdvMode(int mode) +{ + + MapExtras::MapCache mc; + const rgbf dim(levelDim,levelDim,levelDim); + rect2d vp=getMapViewport(); + int window_x=*df::global::window_x; + int window_y=*df::global::window_y; + int window_z=*df::global::window_z; + coord2d vpSize=rect_size(vp); + //mode 0-> make dark non-visible parts + if(mode==0) + { + for(int x=vp.first.x;x make everything visible, let the lighting hide stuff + else if(mode==1) + { + for(int x=vp.first.x;xtiletypeAt(gpos); df::tile_designation d = b->DesignationAt(gpos); - if(d.bits.hidden) + if(d.bits.hidden ) { curCell=rgbf(0,0,0); continue; // do not process hidden stuff, TODO other hidden stuff @@ -947,6 +1029,8 @@ int lightingEngineViewscreen::parseSpecial(lua_State* L) GETLUANUMBER(engine->levelDim,levelDim); GETLUANUMBER(engine->dayHour,dayHour); GETLUANUMBER(engine->daySpeed,daySpeed); + GETLUANUMBER(engine->num_diffuse,diffusionCount); + GETLUANUMBER(engine->adv_mode,advMode); lua_getfield(L,-1,"dayColors"); if(lua_istable(L,-1)) { @@ -1091,6 +1175,8 @@ void lightingEngineViewscreen::defaultSettings() levelDim=0.2f; dayHour=-1; daySpeed=1; + adv_mode=0; + num_diffuse=0; dayColors.push_back(rgbf(0,0,0)); dayColors.push_back(rgbf(1,1,1)); dayColors.push_back(rgbf(0,0,0)); @@ -1281,17 +1367,17 @@ rgbf lightThread::lightUpCell(rgbf power,int dx,int dy,int tx,int ty) else return rgbf(); } -void lightThread::doRay(const rgbf& power,int cx,int cy,int tx,int ty) +void lightThread::doRay(const rgbf& power,int cx,int cy,int tx,int ty,int num_diffuse) { using namespace std::placeholders; - - plotLine(cx,cy,tx,ty,power,std::bind(&lightThread::lightUpCell,this,_1,_2,_3,_4,_5)); + plotLineDiffuse(cx,cy,tx,ty,power,num_diffuse,std::bind(&lightThread::lightUpCell,this,_1,_2,_3,_4,_5)); } void lightThread::doLight( int x,int y ) { using namespace std::placeholders; lightSource& csource=dispatch.lights[x*dispatch.getH()+y]; + int num_diffuse=dispatch.num_diffusion; if(csource.radius>0) { rgbf power=csource.power; @@ -1310,8 +1396,9 @@ void lightThread::doLight( int x,int y ) surrounds += lightUpCell( power, i, j,x+i, y+j); //and this is wall hack (so that walls look nice) if(surrounds.dot(surrounds)>0.00001f) //if we needed to light up the suroundings, then raycast { + plotSquare(x,y,radius, - std::bind(&lightThread::doRay,this,power,x,y,_1,_2)); + std::bind(&lightThread::doRay,this,power,x,y,_1,_2,num_diffuse)); } } } @@ -1343,7 +1430,7 @@ void lightThreadDispatch::signalDoneOcclusion() occlusionDone.notify_all(); } -lightThreadDispatch::lightThreadDispatch( lightingEngineViewscreen* p ):parent(p),lights(parent->lights),occlusion(parent->ocupancy), +lightThreadDispatch::lightThreadDispatch( lightingEngineViewscreen* p ):parent(p),lights(parent->lights),occlusion(parent->ocupancy),num_diffusion(parent->num_diffuse), lightMap(parent->lightMap),writeCount(0),occlusionReady(false) { diff --git a/plugins/rendermax/renderer_light.hpp b/plugins/rendermax/renderer_light.hpp index eadd497e6..83d5da28f 100644 --- a/plugins/rendermax/renderer_light.hpp +++ b/plugins/rendermax/renderer_light.hpp @@ -47,7 +47,7 @@ private: float light_adaptation; rgbf adapt_to_light(const rgbf& light) { - const float influence=0.0001; + const float influence=0.0001f; const float max_adapt=1; const float min_adapt=0; float intensity=(light.r+light.g+light.b)/3.0; @@ -136,6 +136,7 @@ public: virtual void calculate()=0; virtual void updateWindow()=0; + virtual void preRender()=0; virtual void loadSettings()=0; virtual void clear()=0; @@ -225,7 +226,8 @@ public: tthread::mutex unprocessedMutex; std::stack unprocessed; //stack of parts of map where lighting is not finished std::vector& occlusion; - + int& num_diffusion; + tthread::mutex writeLock; //mutex for lightMap std::vector& lightMap; @@ -257,7 +259,7 @@ public: void run(); private: void doLight(int x,int y); - void doRay(const rgbf& power,int cx,int cy,int tx,int ty); + void doRay(const rgbf& power,int cx,int cy,int tx,int ty,int num_diffuse); rgbf lightUpCell(rgbf power,int dx,int dy,int tx,int ty); }; class lightingEngineViewscreen:public lightingEngine @@ -269,13 +271,13 @@ public: void calculate(); void updateWindow(); - + void preRender(); void loadSettings(); void clear(); void debug(bool enable){doDebug=enable;}; private: - + void fixAdvMode(int mode); df::coord2d worldToViewportCoord(const df::coord2d& in,const DFHack::rect2d& r,const df::coord2d& window2d) ; @@ -313,6 +315,7 @@ private: std::vector lights; //Threading stuff + int num_diffuse; //under same lock as ocupancy lightThreadDispatch threading; //misc void setHour(float h){dayHour=h;}; @@ -346,6 +349,7 @@ private: matLightDef matWater; matLightDef matCitizen; float levelDim; + int adv_mode; //materials std::unordered_map,matLightDef> matDefs; //buildings diff --git a/plugins/rendermax/rendermax.cpp b/plugins/rendermax/rendermax.cpp index 0a15807af..dcfee0092 100644 --- a/plugins/rendermax/rendermax.cpp +++ b/plugins/rendermax/rendermax.cpp @@ -61,6 +61,7 @@ struct dwarmode_render_hook : viewscreen_dwarfmodest{ DEFINE_VMETHOD_INTERPOSE(void,render,()) { CoreSuspendClaimer suspend; + engine->preRender(); INTERPOSE_NEXT(render)(); engine->calculate(); engine->updateWindow(); @@ -73,6 +74,7 @@ struct dungeon_render_hook : viewscreen_dungeonmodest{ DEFINE_VMETHOD_INTERPOSE(void,render,()) { CoreSuspendClaimer suspend; + engine->preRender(); INTERPOSE_NEXT(render)(); engine->calculate(); engine->updateWindow(); diff --git a/plugins/rendermax/rendermax.lua b/plugins/rendermax/rendermax.lua index 24b5c7fc8..d26b3d2c1 100644 --- a/plugins/rendermax/rendermax.lua +++ b/plugins/rendermax/rendermax.lua @@ -189,6 +189,9 @@ special.dayColors={ {0,0,0}, --dark at 0 hours {0.5,0.5,0.5}, {0,0,0}} --dark at 24 hours special.daySpeed=1 -- 1->1200 cur_year_ticks per day. 2->600 ticks +special.diffusionCount=1 -- split beam max 1 times to mimic diffuse lights +special.advMode=0 -- 1 or 0 different modes for adv mode. 0-> use df vision system, + -- 1(does not work)->everything visible, let rendermax light do the work --TODO dragonfire --materials @@ -219,4 +222,4 @@ addCreature("ELEMENTMAN_MAGMA",{0.8,0.2,0.2},{0.8,0.2,0.2},5) --items addItem("GEM",nil,nil,{"useMaterial","onGround"}) addItem("ROUGH",nil,nil,{"useMaterial","onGround"}) -addItem("SMALLGEM",nil,nil,{"useMaterial","onGround"}) \ No newline at end of file +addItem("SMALLGEM",nil,nil,{"useMaterial","onGround"}) From 4cbab2b014fc3c67d6c8f0038cbe787e2e0a72fa Mon Sep 17 00:00:00 2001 From: Warmist Date: Fri, 7 Mar 2014 20:28:07 +0200 Subject: [PATCH 066/676] Small fix that got lost in rebase --- plugins/rendermax/renderer_light.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index 1127c6636..1ab57caa3 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -56,6 +56,7 @@ lightSource::lightSource(rgbf power,int radius):power(power),flicker(false) this->radius = radius; else { + float levelDim = 0.2f;//TODO this is not correct if you change config float totalPower = power.r; if(totalPower < power.g)totalPower = power.g; if(totalPower < power.b)totalPower = power.b; From b9ed7a5cc2aee922c50d0ec135df39df79fafcdc Mon Sep 17 00:00:00 2001 From: Eric Wald Date: Fri, 24 May 2013 12:43:40 -0600 Subject: [PATCH 067/676] Stockflow plugin v1.0 --- Readme.html | 26 + Readme.rst | 23 + dfhack.init-example | 3 + plugins/CMakeLists.txt | 1 + plugins/lua/stockflow.lua | 1099 +++++++++++++++++++++++++++++++++++++ plugins/stockflow.cpp | 374 +++++++++++++ 6 files changed, 1526 insertions(+) create mode 100644 plugins/lua/stockflow.lua create mode 100644 plugins/stockflow.cpp diff --git a/Readme.html b/Readme.html index e606d7137..ba89a442e 100644 --- a/Readme.html +++ b/Readme.html @@ -462,6 +462,7 @@ access DF memory and allow for easier development of new tools.

  • job
  • job-material
  • job-duplicate
  • +
  • stockflow
  • workflow
    • Function
    • Constraint format
    • @@ -2122,6 +2123,31 @@ instantly duplicates the job. +
      +

      stockflow

      +

      Allows the fortress bookkeeper to queue jobs through the manager.

      +

      Usage:

      +
      +
      +
      stockflow enable
      +
      Enable the plugin.
      +
      stockflow disable
      +
      Disable the plugin.
      +
      stockflow list
      +
      List any work order settings for your stockpiles.
      +
      stockflow status
      +
      Display whether the plugin is enabled.
      +
      +
      +
      +
      While enabled, the 'q' menu of each stockpile will have two new options:
      +
      j: Select a job to order, from an interface like the manager's screen. +J: Cycle between several options for how many such jobs to order.
      +
      +

      Whenever the bookkeeper updates stockpile records, new work orders will +be placed on the manager's queue for each such selection, reduced by the +number of identical orders already in the queue.

      +

      workflow

      Manage control of repeat jobs.

      diff --git a/Readme.rst b/Readme.rst index cd9101aab..dbef32f26 100644 --- a/Readme.rst +++ b/Readme.rst @@ -1317,6 +1317,29 @@ Duplicate the selected job in a workshop: * In 'q' mode, when a job is highlighted within a workshop or furnace building, instantly duplicates the job. +stockflow +--------- +Allows the fortress bookkeeper to queue jobs through the manager. + +Usage: + + ``stockflow enable`` + Enable the plugin. + ``stockflow disable`` + Disable the plugin. + ``stockflow list`` + List any work order settings for your stockpiles. + ``stockflow status`` + Display whether the plugin is enabled. + +While enabled, the 'q' menu of each stockpile will have two new options: + j: Select a job to order, from an interface like the manager's screen. + J: Cycle between several options for how many such jobs to order. + +Whenever the bookkeeper updates stockpile records, new work orders will +be placed on the manager's queue for each such selection, reduced by the +number of identical orders already in the queue. + workflow -------- Manage control of repeat jobs. diff --git a/dfhack.init-example b/dfhack.init-example index 22d2c2972..8525896d4 100644 --- a/dfhack.init-example +++ b/dfhack.init-example @@ -150,6 +150,9 @@ tweak military-training # enable autoSyndrome autoSyndrome enable +# allow the fortress bookkeeper to queue jobs through the manager +stockflow enable + ########### # Scripts # ########### diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 638fed3fd..29c39f1bb 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -112,6 +112,7 @@ if (BUILD_SUPPORTED) DFHACK_PLUGIN(rename rename.cpp LINK_LIBRARIES lua PROTOBUFS rename) DFHACK_PLUGIN(jobutils jobutils.cpp) DFHACK_PLUGIN(workflow workflow.cpp LINK_LIBRARIES lua) + DFHACK_PLUGIN(stockflow stockflow.cpp LINK_LIBRARIES lua) DFHACK_PLUGIN(showmood showmood.cpp) DFHACK_PLUGIN(fixveins fixveins.cpp) DFHACK_PLUGIN(fixpositions fixpositions.cpp) diff --git a/plugins/lua/stockflow.lua b/plugins/lua/stockflow.lua new file mode 100644 index 000000000..510298a06 --- /dev/null +++ b/plugins/lua/stockflow.lua @@ -0,0 +1,1099 @@ +local _ENV = mkmodule('plugins.stockflow') + +gui = require "gui" +utils = require "utils" + +reaction_list = {} +saved_orders = {} +jobs_to_create = {} + +triggers = { + {filled = false, divisor = 1, name = "Per empty space"}, + {filled = true, divisor = 1, name = "Per stored item"}, + {filled = false, divisor = 2, name = "Per two empty spaces"}, + {filled = true, divisor = 2, name = "Per two stored items"}, + {filled = false, divisor = 3, name = "Per three empty spaces"}, + {filled = true, divisor = 3, name = "Per three stored items"}, + {filled = false, divisor = 4, name = "Per four empty spaces"}, + {filled = true, divisor = 4, name = "Per four stored items"}, + {name = "Never"}, +} + +-- Populate the reaction and stockpile order lists. +-- To be called whenever a world is loaded. +function initialize_world() + reaction_list = collect_reactions() + saved_orders = collect_orders() + jobs_to_create = {} +end + +-- Clear all caches. +-- Called when a world is loaded, or when the plugin is disabled. +function clear_caches() + reaction_list = {} + saved_orders = {} + jobs_to_create = {} +end + +function trigger_name(cache) + local trigger = triggers[cache.entry.ints[3]] + return trigger and trigger.name or "Never" +end + +function list_orders() + local listed = false + for _, spec in pairs(saved_orders) do + local num = spec.stockpile.stockpile_number + local name = spec.entry.value + local trigger = trigger_name(spec) + print("Stockpile #"..num, name, trigger) + listed = true + end + + if not listed then + print("No manager jobs have been set for your stockpiles.") + print("Use j in a stockpile menu to create one...") + end +end + +-- Save the stockpile jobs for later creation. +-- Called when the bookkeeper starts updating stockpile records. +function start_bookkeeping() + result = {} + for reaction_id, quantity in pairs(check_stockpiles()) do + local amount = order_quantity(reaction_list[reaction_id].order, quantity) + if amount > 0 then + result[reaction_id] = amount + end + end + + jobs_to_create = result +end + +-- Insert any saved jobs. +-- Called when the bookkeeper finishes updating stockpile records. +function finish_bookkeeping() + for reaction, amount in pairs(jobs_to_create) do + create_orders(reaction_list[reaction].order, amount) + end + + jobs_to_create = {} +end + +function stockpile_settings(sp) + local order = saved_orders[sp.id] + if not order then + return "No job selected", "" + end + + return order.entry.value, trigger_name(order) +end + +-- Toggle the trigger condition for a stockpile. +function toggle_trigger(sp) + local saved = saved_orders[sp.id] + if saved then + saved.entry.ints[3] = (saved.entry.ints[3] % #triggers) + 1 + saved.entry:save() + end +end + +function collect_orders() + local result = {} + local entries = dfhack.persistent.get_all("stockflow/entry", true) + if entries then + local stockpiles = df.global.world.buildings.other.STOCKPILE + for _, entry in ipairs(entries) do + local spid = entry.ints[1] + local stockpile = utils.binsearch(stockpiles, spid, "id") + if stockpile then + -- Todo: What if entry.value ~= reaction_list[order_number].name? + result[spid] = { + stockpile = stockpile, + entry = entry, + } + end + end + end + + return result +end + +-- Choose an order that the stockpile should add to the manager queue. +function select_order(stockpile) + screen:reset(stockpile) + screen:show() +end + +function reaction_entry(job_type, values, name) + local order = df.manager_order:new() + -- These defaults differ from the newly created order's. + order:assign{ + job_type = job_type, + unk_2 = -1, + item_subtype = -1, + mat_type = -1, + mat_index = -1, + } + + if values then + -- Override default attributes. + order:assign(values) + end + + return { + name = name or df.job_type.attrs[job_type].caption, + order = order, + } +end + +function resource_reactions(reactions, job_type, mat_info, keys, items, options) + local values = {} + for key, value in pairs(mat_info.management) do + values[key] = value + end + + for _, itemid in ipairs(keys) do + local itemdef = items[itemid] + local start = options.verb or mat_info.verb or "Make" + if options.adjective then + start = start.." "..itemdef.adjective + end + + if (not options.permissible) or options.permissible(itemdef) then + local item_name = " "..itemdef[options.name_field or "name"] + if options.capitalize then + item_name = string.gsub(item_name, " .", string.upper) + end + + values.item_subtype = itemid + table.insert(reactions, reaction_entry(job_type, values, start.." "..mat_info.adjective..item_name)) + end + end +end + +function material_reactions(reactions, itemtypes, mat_info) + -- Expects a list of {job_type, verb, item_name} tuples. + for _, row in ipairs(itemtypes) do + local line = row[2].." "..mat_info.adjective + if row[3] then + line = line.." "..row[3] + end + + table.insert(reactions, reaction_entry(row[1], mat_info.management, line)) + end +end + +function clothing_reactions(reactions, mat_info, filter) + local resources = df.global.world.entities.all[df.global.ui.civ_id].resources + local itemdefs = df.global.world.raws.itemdefs + resource_reactions(reactions, 101, mat_info, resources.armor_type, itemdefs.armor, {permissible = filter}) + resource_reactions(reactions, 103, mat_info, resources.pants_type, itemdefs.pants, {permissible = filter}) + resource_reactions(reactions, 117, mat_info, resources.gloves_type, itemdefs.gloves, {permissible = filter}) + resource_reactions(reactions, 102, mat_info, resources.helm_type, itemdefs.helms, {permissible = filter}) + resource_reactions(reactions, 118, mat_info, resources.shoes_type, itemdefs.shoes, {permissible = filter}) +end + +-- Find the reaction types that should be listed in the management interface. +function collect_reactions() + -- The sequence here tries to match the native manager screen. + -- It should also be possible to collect the sequence from somewhere native, + -- but I currently can only find it while the job selection screen is active. + -- Even that list doesn't seem to include their names. + local result = {} + + local materials = { + rock = { + adjective = "rock", + management = {mat_type = 0}, + }, + } + + for _, name in ipairs{"wood", "cloth", "leather", "silk", "yarn", "bone", "shell", "tooth", "horn", "pearl"} do + materials[name] = { + adjective = name, + management = {material_category = {[name] = true}}, + } + end + + materials.wood.adjective = "wooden" + materials.tooth.adjective = "ivory/tooth" + materials.leather.clothing_flag = "LEATHER" + + local jobs = {12, 22, 219, 31, 32} + for _, job_id in ipairs(jobs) do + table.insert(result, reaction_entry(job_id)) + end + + -- Cutting, encrusting, and metal extraction. + local rock_types = df.global.world.raws.inorganics + for rock_id = #rock_types-1, 0, -1 do + local material = rock_types[rock_id].material + local rock_name = material.state_adj.Solid + if material.flags.IS_STONE or material.flags.IS_GEM then + table.insert(result, reaction_entry(85, {mat_type = 0, mat_index = rock_id}, "Cut "..rock_name)) + + table.insert(result, reaction_entry(87, { + mat_type = 0, + mat_index = rock_id, + item_category = {finished_goods = true}, + }, "Encrust Finished Goods With "..rock_name)) + + table.insert(result, reaction_entry(87, { + mat_type = 0, + mat_index = rock_id, + item_category = {furniture = true}, + }, "Encrust Furniture With "..rock_name)) + + table.insert(result, reaction_entry(87, { + mat_type = 0, + mat_index = rock_id, + item_category = {ammo = true}, + }, "Encrust Ammo With "..rock_name)) + end + + if #rock_types[rock_id].metal_ore.mat_index > 0 then + table.insert(result, reaction_entry(90, {mat_type = 0, mat_index = rock_id}, "Smelt "..rock_name.." Ore")) + end + + if #rock_types[rock_id].thread_metal.mat_index > 0 then + table.insert(result, reaction_entry(92, {mat_type = 0, mat_index = rock_id})) + end + end + + -- Glass cutting and encrusting, with different job numbers. + -- We could search the entire table, but glass is less subject to raws. + local glass_types = df.global.world.raws.mat_table.builtin + local glasses = {} + for glass_id = 3, 5 do + local material = glass_types[glass_id] + local glass_name = material.state_adj.Solid + if material.flags.IS_GLASS then + -- For future use. + table.insert(glasses, { + adjective = glass_name, + management = {mat_type = glass_id}, + }) + + table.insert(result, reaction_entry(86, {mat_type = glass_id}, "Cut "..glass_name)) + + table.insert(result, reaction_entry(88, { + mat_type = glass_id, + item_category = {finished_goods = true}, + }, "Encrust Finished Goods With "..glass_name)) + + table.insert(result, reaction_entry(88, { + mat_type = glass_id, + item_category = {furniture = true}, + }, "Encrust Furniture With "..glass_name)) + + table.insert(result, reaction_entry(88, { + mat_type = glass_id, + item_category = {ammo = true}, + }, "Encrust Ammo With "..glass_name)) + end + end + + -- Dyeing + table.insert(result, reaction_entry(192)) + table.insert(result, reaction_entry(193)) + + -- Sew Image + cloth_mats = {materials.cloth, materials.silk, materials.yarn, materials.leather} + for _, material in ipairs(cloth_mats) do + material_reactions(result, {{194, "Sew", "Image"}}, material) + end + + for _, spec in ipairs{materials.bone, materials.shell, materials.tooth, materials.horn, materials.pearl} do + material_reactions(result, {{132, "Decorate With"}}, spec) + end + + -- Make Totem + table.insert(result, reaction_entry(130)) + -- Butcher an Animal + table.insert(result, reaction_entry(105)) + -- Mill Plants + table.insert(result, reaction_entry(107)) + -- Make Potash From Lye + table.insert(result, reaction_entry(189)) + -- Make Potash From Ash + table.insert(result, reaction_entry(191)) + + -- Kitchen + table.insert(result, reaction_entry(115, {mat_type = 2}, "Prepare Easy Meal")) + table.insert(result, reaction_entry(115, {mat_type = 3}, "Prepare Fine Meal")) + table.insert(result, reaction_entry(115, {mat_type = 4}, "Prepare Lavish Meal")) + + -- Brew Drink + table.insert(result, reaction_entry(150)) + + -- Weaving + table.insert(result, reaction_entry(116, {material_category = {plant = true}}, "Weave Thread into Cloth")) + table.insert(result, reaction_entry(116, {material_category = {silk = true}}, "Weave Thread into Silk")) + table.insert(result, reaction_entry(116, {material_category = {yarn = true}}, "Weave Yarn into Cloth")) + + -- Extracts, farmer's workshop, and wood burning + local jobs = {151, 152, 153, 106, 110, 109, 214, 215, 188, 111, 112, 113, 114, 186, 187} + for _, job_id in ipairs(jobs) do + table.insert(result, reaction_entry(job_id)) + end + + -- Reactions defined in the raws. + -- Not all reactions are allowed to the civilization. + -- That includes "Make sharp rock" by default. + local entity = df.global.world.entities.all[df.global.ui.civ_id] + for _, reaction_id in ipairs(entity.entity_raw.workshops.permitted_reaction_id) do + local reaction = df.global.world.raws.reactions[reaction_id] + local name = string.gsub(reaction.name, "^.", string.upper) + table.insert(result, reaction_entry(211, {reaction_name = reaction.code}, name)) + end + + -- Metal forging + local itemdefs = df.global.world.raws.itemdefs + for rock_id = 0, #rock_types - 1 do + local material = rock_types[rock_id].material + local rock_name = material.state_adj.Solid + local mat_flags = { + adjective = rock_name, + management = {mat_type = 0, mat_index = rock_id}, + verb = "Forge", + } + + if material.flags.IS_METAL then + table.insert(result, reaction_entry(104, mat_flags.management, "Stud With "..rock_name)) + + if material.flags.ITEMS_WEAPON then + -- Todo: Are these really the right flags to check? + resource_reactions(result, 97, mat_flags, entity.resources.weapon_type, itemdefs.weapons, { + permissible = (function(itemdef) return itemdef.skill_ranged == -1 end), + }) + + -- Is this entirely disconnected from the entity? + material_reactions(result, {{135, "Forge", "Ballista Arrow Head"}}, mat_flags) + + resource_reactions(result, 142, mat_flags, entity.resources.trapcomp_type, itemdefs.trapcomps, { + adjective = true, + }) + + resource_reactions(result, 136, mat_flags, entity.resources.siegeammo_type, itemdefs.siege_ammo, { + verb = "Assemble", + }) + end + + if material.flags.ITEMS_WEAPON_RANGED then + resource_reactions(result, 97, mat_flags, entity.resources.weapon_type, itemdefs.weapons, { + permissible = (function(itemdef) return itemdef.skill_ranged >= 0 end), + }) + end + + if material.flags.ITEMS_DIGGER then + -- Todo: Ranged or training digging weapons? + resource_reactions(result, 97, mat_flags, entity.resources.digger_type, itemdefs.weapons, { + }) + end + + if material.flags.ITEMS_AMMO then + resource_reactions(result, 131, mat_flags, entity.resources.ammo_type, itemdefs.ammo, { + name_field = "name_plural", + }) + end + + if material.flags.ITEMS_ANVIL then + material_reactions(result, {{98, "Forge", "Anvil"}}, mat_flags) + end + + if material.flags.ITEMS_ARMOR then + local metalclothing = (function(itemdef) return itemdef.props.flags.METAL end) + clothing_reactions(result, mat_flags, metalclothing) + resource_reactions(result, 119, mat_flags, entity.resources.shield_type, itemdefs.shields, { + }) + end + + if material.flags.ITEMS_SOFT then + local metalclothing = (function(itemdef) return itemdef.props.flags.SOFT and not itemdef.props.flags.METAL end) + clothing_reactions(result, mat_flags, metalclothing) + end + + if material.flags.ITEMS_HARD then + resource_reactions(result, 218, mat_flags, entity.resources.tool_type, itemdefs.tools, { + permissible = (function(itemdef) return itemdef.flags.HARD_MAT end), + capitalize = true, + }) + end + + if material.flags.ITEMS_METAL then + resource_reactions(result, 218, mat_flags, entity.resources.tool_type, itemdefs.tools, { + permissible = (function(itemdef) return itemdef.flags.METAL_MAT end), + capitalize = true, + }) + end + + if material.flags.ITEMS_HARD then + material_reactions(result, { + {69, "Construct", "Door"}, + {70, "Construct", "Floodgate"}, + {200, "Construct", "Hatch Cover"}, + {201, "Construct", "Grate"}, + {72, "Construct", "Throne"}, + {73, "Construct", "Sarcophagus"}, + {74, "Construct", "Table"}, + {205, "Construct", "Splint"}, + {206, "Construct", "Crutch"}, + {77, "Construct", "Armor Stand"}, + {78, "Construct", "Weapon Rack"}, + {79, "Construct", "Cabinet"}, + {123, "Forge", "Goblet"}, + {124, "Forge", "Instrument"}, + {125, "Forge", "Toy"}, + {80, "Construct", "Statue"}, + {81, "Construct", "Blocks"}, + {126, "Forge", "Animal Trap"}, + {127, "Forge", "Barrel"}, + {128, "Forge", "Bucket"}, + {76, "Construct", "Bin"}, + {195, "Forge", "Pipe Section"}, + {120, "Forge", "Cage"}, + {84, "Mint", "Coins"}, + {75, "Construct", "Chest"}, + {122, "Forge", "Flask"}, + {121, "Forge", "Chain"}, + {83, "Make", "Crafts"}, + }, mat_flags) + end + + if material.flags.ITEMS_SOFT then + material_reactions(result, { + {133, "Make", "Backpack"}, + {134, "Make", "Quiver"}, + {99, "Construct", "Catapult Parts"}, + {100, "Construct", "Ballista Parts"}, + }, mat_flags) + end + end + end + + -- Traction Bench + table.insert(result, reaction_entry(207)) + + -- Non-metal weapons + resource_reactions(result, 97, materials.wood, entity.resources.weapon_type, itemdefs.weapons, { + permissible = (function(itemdef) return itemdef.skill_ranged >= 0 end), + }) + + resource_reactions(result, 97, materials.wood, entity.resources.training_weapon_type, itemdefs.weapons, { + }) + + resource_reactions(result, 97, materials.bone, entity.resources.weapon_type, itemdefs.weapons, { + permissible = (function(itemdef) return itemdef.skill_ranged >= 0 end), + }) + + resource_reactions(result, 97, materials.rock, entity.resources.weapon_type, itemdefs.weapons, { + permissible = (function(itemdef) return itemdef.flags.CAN_STONE end), + }) + + -- Wooden items + -- Closely related to the ITEMS_HARD list. + material_reactions(result, { + {69, "Construct", "Door"}, + {70, "Construct", "Floodgate"}, + {200, "Construct", "Hatch Cover"}, + {201, "Construct", "Grate"}, + {72, "Construct", "Chair"}, + {73, "Construct", "Casket"}, + {74, "Construct", "Table"}, + {77, "Construct", "Armor Stand"}, + {78, "Construct", "Weapon Rack"}, + {79, "Construct", "Cabinet"}, + {123, "Make", "Cup"}, + {124, "Make", "Instrument"}, + }, materials.wood) + + resource_reactions(result, 218, materials.wood, entity.resources.tool_type, itemdefs.tools, { + -- permissible = (function(itemdef) return itemdef.flags.WOOD_MAT end), + capitalize = true, + }) + + material_reactions(result, { + {125, "Make", "Toy"}, + {81, "Construct", "Blocks"}, + {205, "Construct", "Splint"}, + {206, "Construct", "Crutch"}, + {126, "Make", "Animal Trap"}, + {127, "Make", "Barrel"}, + {128, "Make", "Bucket"}, + {76, "Construct", "Bin"}, + {120, "Make", "Cage"}, + {195, "Make", "Pipe Section"}, + }, materials.wood) + + resource_reactions(result, 142, materials.wood, entity.resources.trapcomp_type, itemdefs.trapcomps, { + permissible = (function(itemdef) return itemdef.flags.WOOD end), + adjective = true, + }) + + -- Rock items + material_reactions(result, { + {69, "Construct", "Door"}, + {70, "Construct", "Floodgate"}, + {200, "Construct", "Hatch Cover"}, + {201, "Construct", "Grate"}, + {72, "Construct", "Throne"}, + {73, "Construct", "Coffin"}, + {74, "Construct", "Table"}, + {77, "Construct", "Armor Stand"}, + {78, "Construct", "Weapon Rack"}, + {79, "Construct", "Cabinet"}, + {123, "Make", "Mug"}, + {124, "Make", "Instrument"}, + }, materials.rock) + + resource_reactions(result, 218, materials.rock, entity.resources.tool_type, itemdefs.tools, { + permissible = (function(itemdef) return itemdef.flags.HARD_MAT end), + capitalize = true, + }) + + material_reactions(result, { + {125, "Make", "Toy"}, + {203, "Construct", "Quern"}, + {204, "Construct", "Millstone"}, + {212, "Construct", "Slab"}, + {80, "Construct", "Statue"}, + {81, "Construct", "Blocks"}, + }, materials.rock) + + -- Glass items + for _, mat_info in ipairs(glasses) do + material_reactions(result, { + {69, "Construct", "Portal"}, + {70, "Construct", "Floodgate"}, + {200, "Construct", "Hatch Cover"}, + {201, "Construct", "Grate"}, + {72, "Construct", "Throne"}, + {73, "Construct", "Coffin"}, + {74, "Construct", "Table"}, + {77, "Construct", "Armor Stand"}, + {78, "Construct", "Weapon Rack"}, + {79, "Construct", "Cabinet"}, + {123, "Make", "Goblet"}, + {124, "Make", "Instrument"}, + }, mat_info) + + resource_reactions(result, 218, mat_info, entity.resources.tool_type, itemdefs.tools, { + permissible = (function(itemdef) return itemdef.flags.HARD_MAT end), + capitalize = true, + }) + + material_reactions(result, { + {125, "Make", "Toy"}, + {80, "Construct", "Statue"}, + {81, "Construct", "Blocks"}, + {120, "Make", "Terrarium"}, + {195, "Make", "Tube"}, + }, mat_info) + + resource_reactions(result, 142, mat_info, entity.resources.trapcomp_type, itemdefs.trapcomps, { + adjective = true, + }) + end + + -- Bed, specified as wooden. + table.insert(result, reaction_entry(71, materials.wood.management)) + + -- Windows + for _, mat_info in ipairs(glasses) do + material_reactions(result, { + {129, "Make", "Window"}, + }, mat_info) + end + + -- Rock Mechanisms + table.insert(result, reaction_entry(141, materials.rock.management)) + + resource_reactions(result, 136, materials.wood, entity.resources.siegeammo_type, itemdefs.siege_ammo, { + verb = "Assemble", + }) + + for _, mat_info in ipairs(glasses) do + material_reactions(result, { + {82, "Make Raw", nil}, + }, mat_info) + end + + material_reactions(result, { + {133, "Make", "Backpack"}, + {134, "Make", "Quiver"}, + }, materials.leather) + + for _, material in ipairs(cloth_mats) do + clothing_reactions(result, material, (function(itemdef) return itemdef.props.flags[material.clothing_flag or "SOFT"] end)) + end + + -- Boxes, Bags, and Ropes + boxmats = { + {mats = {materials.wood}, box = "Chest"}, + {mats = {materials.rock}, box = "Coffer"}, + {mats = glasses, box = "Box", flask = "Vial"}, + {mats = {materials.cloth}, box = "Bag", chain = "Rope"}, + {mats = {materials.leather}, box = "Bag", flask = "Waterskin"}, + {mats = {materials.silk, materials.yarn}, box = "Bag", chain = "Rope"}, + } + for _, boxmat in ipairs(boxmats) do + for _, mat in ipairs(boxmat.mats) do + material_reactions(result, {{75, "Construct", boxmat.box}}, mat) + if boxmat.chain then + material_reactions(result, {{121, "Make", boxmat.chain}}, mat) + end + if boxmat.flask then + material_reactions(result, {{122, "Make", boxmat.flask}}, mat) + end + end + end + + for _, mat in ipairs{ + materials.wood, + materials.rock, + materials.cloth, + materials.leather, + materials.shell, + materials.bone, + materials.silk, + materials.tooth, + materials.horn, + materials.pearl, + materials.yarn, + } do + material_reactions(result, {{83, "Make", "Crafts"}}, mat) + end + + -- Siege engine parts + table.insert(result, reaction_entry(99, materials.wood.management)) + table.insert(result, reaction_entry(100, materials.wood.management)) + + for _, mat in ipairs{materials.wood, materials.bone} do + resource_reactions(result, 131, mat, entity.resources.ammo_type, itemdefs.ammo, { + name_field = "name_plural", + }) + end + + -- BARRED and SCALED as flag names don't quite seem to fit, here. + clothing_reactions(result, materials.bone, (function(itemdef) return itemdef.props.flags[3] end)) + clothing_reactions(result, materials.shell, (function(itemdef) return itemdef.props.flags[4] end)) + + for _, mat in ipairs{materials.wood, materials.leather} do + resource_reactions(result, 119, mat, entity.resources.shield_type, itemdefs.shields, {}) + end + + -- Melt a Metal Object + table.insert(result, reaction_entry(91)) + + return result +end + +screen = gui.FramedScreen { + frame_title = "Select Stockpile Order", +} + +PageSize = 16 +function screen:onRenderBody(dc) + -- Emulates the built-in manager screen. + dc:seek(1, 1):string("Type in parts of the name to narrow your search. ", COLOR_WHITE) + dc:string(gui.getKeyDisplay("LEAVESCREEN"), COLOR_LIGHTGREEN) + dc:string(" to abort.", COLOR_WHITE) + dc:seek(1, PageSize + 5):string(self.search_string, COLOR_LIGHTCYAN) + for _, item in ipairs(self.displayed) do + dc:seek(item.x, item.y):string(item.name, item.color) + end +end + +function screen:onInput(keys) + if keys.LEAVESCREEN then + self:dismiss() + elseif keys.SELECT then + self:dismiss() + local selected = self.reactions[self.position].index + store_order(self.stockpile, selected) + elseif keys.STANDARDSCROLL_UP then + self.position = self.position - 1 + elseif keys.STANDARDSCROLL_DOWN then + self.position = self.position + 1 + elseif keys.STANDARDSCROLL_LEFT then + self.position = self.position - PageSize + elseif keys.STANDARDSCROLL_RIGHT then + self.position = self.position + PageSize + elseif keys.STANDARDSCROLL_PAGEUP then + -- Moves to the first item displayed on the new page, for some reason. + self.position = self.position - PageSize*2 - ((self.position-1) % (PageSize*2)) + elseif keys.STANDARDSCROLL_PAGEDOWN then + -- Moves to the first item displayed on the new page, for some reason. + self.position = self.position + PageSize*2 - ((self.position-1) % (PageSize*2)) + elseif keys.STRING_A000 then + -- This seems like an odd way to check for Backspace. + self.search_string = string.sub(self.search_string, 1, -2) + elseif keys._STRING and keys._STRING >= 32 then + -- This interface only accepts letters and spaces. + local char = string.char(keys._STRING) + if char == " " or string.find(char, "^%a") then + self.search_string = self.search_string .. string.upper(char) + end + end + + self:refilter() +end + +function screen:reset(stockpile) + self.stockpile = stockpile + self.search_string = "" + self.position = 1 + self:refilter() +end + +function matchall(haystack, needles) + for _, needle in ipairs(needles) do + if not string.find(haystack, needle) then + return false + end + end + + return true +end + +function splitstring(full, pattern) + local last = string.len(full) + local result = {} + local n = 1 + while n <= last do + local start, stop = string.find(full, pattern, n) + if not start then + result[#result+1] = string.sub(full, n) + break + elseif start > n then + result[#result+1] = string.sub(full, n, start - 1) + end + + if stop < n then + -- The pattern matches an empty string. + -- Avoid an infinite loop. + break + end + + n = stop + 1 + end + + return result +end + +function screen:refilter() + local filtered = {} + local needles = splitstring(self.search_string, " ") + for key, value in ipairs(reaction_list) do + if matchall(string.upper(value.name), needles) then + filtered[#filtered+1] = { + index = key, + name = value.name + } + end + end + + if self.position < 1 then + self.position = #filtered + elseif self.position > #filtered then + self.position = 1 + end + + local start = 1 + while self.position >= start + PageSize*2 do + start = start + PageSize*2 + end + + local displayed = {} + for n = 0, PageSize*2 - 1 do + local item = filtered[start + n] + if not item then + break + end + + local x = 1 + local y = 4 + n + if n >= PageSize then + x = 39 + y = y - PageSize + end + + local color = COLOR_CYAN + if start + n == self.position then + color = COLOR_LIGHTCYAN + end + + displayed[n + 1] = { + x = x, + y = y, + name = item.name, + color = color, + } + end + + self.reactions = filtered + self.displayed = displayed +end + +function store_order(stockpile, order_number) + local name = reaction_list[order_number].name + -- print("Setting stockpile #"..stockpile.stockpile_number.." to "..name.." (#"..order_number..")") + local saved = saved_orders[stockpile.id] + if saved then + saved.entry.value = name + saved.entry.ints[2] = order_number + saved.entry:save() + else + saved_orders[stockpile.id] = { + stockpile = stockpile, + entry = dfhack.persistent.save{ + key = "stockflow/entry/"..stockpile.id, + value = name, + ints = { + stockpile.id, + order_number, + 1, + }, + }, + } + end +end + +-- Compare the job specification of two orders. +function orders_match(a, b) + local fields = { + "job_type", + "item_subtype", + "reaction_name", + "mat_type", + "mat_index", + } + + for _, fieldname in ipairs(fields) do + if a[fieldname] ~= b[fieldname] then + return false + end + end + + local subtables = { + "item_category", + "material_category", + } + + for _, fieldname in ipairs(subtables) do + local aa = a[fieldname] + local bb = b[fieldname] + for key, value in ipairs(aa) do + if bb[key] ~= value then + return false + end + end + end + + return true +end + +-- Reduce the quantity by the number of matching orders in the queue. +function order_quantity(order, quantity) + local amount = quantity + for _, managed in ipairs(df.global.world.manager_orders) do + if orders_match(order, managed) then + amount = amount - managed.amount_left + if amount < 0 then + return 0 + end + end + end + + if amount > 30 then + -- Respect the quantity limit. + -- With this many in the queue, we can wait for the next cycle. + return 30 + end + + return amount +end + +-- Place a new copy of the order onto the manager's queue. +function create_orders(order, amount) + local new_order = order:new() + new_order.amount_left = amount + new_order.amount_total = amount + -- Todo: Create in a validated state if the fortress is small enough? + new_order.is_validated = 0 + df.global.world.manager_orders:insert('#', new_order) +end + +function findItemsAtTile(x, y, z) + -- There should be a faster and easier way to do this... + local found = {} + for _, item in ipairs(df.global.world.items.all) do + -- local ix, iy, iz = dfhack.items.getPosition(item) + if item.pos.x == x and item.pos.y == y and + item.pos.z == z and item.flags.on_ground then + found[#found+1] = item + end + end + + return found +end + +function countContents(container, settings) + local total = 0 + local blocking = false + for _, item in ipairs(dfhack.items.getContainedItems(container)) do + if item.flags.container then + -- Recursively count the total of items contained. + -- Not likely to go more than two levels deep. + local subtotal, subblocked = countContents(item, settings) + if subtotal > 0 then + -- Ignore the inner container itself; + -- generally, only the contained items matter. + total = total + subtotal + elseif subblocked then + blocking = true + elseif matches_stockpile(item, settings) then + -- The container may or may not be empty, + -- but is stockpiled as a container itself. + total = total + 1 + else + blocking = true + end + elseif matches_stockpile(item, settings) then + total = total + 1 + else + blocking = true + end + end + + return total, blocking +end + +function check_stockpiles(verbose) + local result = {} + for _, spec in pairs(saved_orders) do + local trigger = triggers[spec.entry.ints[3]] + if trigger and trigger.divisor then + local reaction = spec.entry.ints[2] + local filled, empty = check_pile(spec.stockpile, verbose) + local amount = trigger.filled and filled or empty + amount = (amount - (amount % trigger.divisor)) / trigger.divisor + result[reaction] = (result[reaction] or 0) + amount + end + end + + return result +end + +function check_pile(sp, verbose) + local numspaces = 0 + local filled = 0 + local empty = 0 + for y = sp.y1, sp.y2 do + for x = sp.x1, sp.x2 do + -- Sadly, the obvious check currently fails when y == sp.y2 + if dfhack.buildings.containsTile(sp, x, y) or + (y == sp.y2 and dfhack.buildings.findAtTile(x, y, sp.z) == sp) then + numspaces = numspaces + 1 + local designation, occupancy = dfhack.maps.getTileFlags(x, y, sp.z) + if not designation.liquid_type then + if not occupancy.item then + empty = empty + 1 + else + local item_count, blocked = count_pile_items(sp, x, y) + if item_count > 0 then + filled = filled + item_count + elseif not blocked then + empty = empty + 1 + end + end + end + end + end + end + + if verbose then + print("Stockpile #"..sp.stockpile_number, + string.format("%3d spaces", numspaces), + string.format("%4d items", filled), + string.format("%4d empty spaces", empty)) + end + + return filled, empty +end + +function count_pile_items(sp, x, y) + local item_count = 0 + local blocked = false + for _, item in ipairs(findItemsAtTile(x, y, sp.z)) do + if item:isAssignedToThisStockpile(sp.id) then + -- This is a bin or barrel associated with the stockpile. + -- If it's empty, it doesn't count as blocking the stockpile space. + -- Oddly, when empty, item.flags.container might be false. + local subtotal, subblocked = countContents(item, sp.settings) + item_count = item_count + subtotal + if subblocked then + blocked = true + end + elseif matches_stockpile(item, sp.settings) then + item_count = item_count + 1 + else + blocked = true + end + end + + return item_count, blocked +end + +function matches_stockpile(item, settings) + -- Check whether the item matches the stockpile. + -- FIXME: This is starting to look like a whole lot of work. + if df.item_barst:is_instance(item) then + return settings.flags.bars_blocks + elseif df.item_blocksst:is_instance(item) then + return settings.flags.bars_blocks + elseif df.item_smallgemst:is_instance(item) then + return settings.flags.gems + elseif df.item_boulderst:is_instance(item) then + return settings.flags.stone + elseif df.item_woodst:is_instance(item) then + return settings.flags.wood + elseif df.item_seedsst:is_instance(item) then + return settings.flags.food + elseif df.item_meatst:is_instance(item) then + return settings.flags.food + elseif df.item_plantst:is_instance(item) then + return settings.flags.food + elseif df.item_leavesst:is_instance(item) then + return settings.flags.food + elseif df.item_cheesest:is_instance(item) then + return settings.flags.food + elseif df.item_globst:is_instance(item) then + return settings.flags.food + elseif df.item_fishst:is_instance(item) then + return settings.flags.food + elseif df.item_fish_rawst:is_instance(item) then + return settings.flags.food + elseif df.item_foodst:is_instance(item) then + return settings.flags.food + elseif df.item_drinkst:is_instance(item) then + return settings.flags.food + elseif df.item_eggst:is_instance(item) then + return settings.flags.food + elseif df.item_skin_tannedst:is_instance(item) then + return settings.flags.leather + elseif df.item_remainsst:is_instance(item) then + return settings.flags.refuse + elseif df.item_verminst:is_instance(item) then + return settings.flags.animals + elseif df.item_petst:is_instance(item) then + return settings.flags.animals + elseif df.item_threadst:is_instance(item) then + return settings.flags.cloth + end + + return true +end + +return _ENV diff --git a/plugins/stockflow.cpp b/plugins/stockflow.cpp new file mode 100644 index 000000000..26bc3f9e8 --- /dev/null +++ b/plugins/stockflow.cpp @@ -0,0 +1,374 @@ +/* + * Stockflow plugin. + * For best effect, place "stockflow enable" in your dfhack.init configuration. + */ + +#include "uicommon.h" +#include "LuaTools.h" + +#include "df/building_stockpilest.h" +#include "df/job.h" +#include "df/viewscreen_dwarfmodest.h" + +#include "modules/Gui.h" +#include "modules/Maps.h" +#include "modules/World.h" + +using namespace DFHack; +using namespace std; + +using df::global::world; +using df::global::ui; +using df::building_stockpilest; + +DFHACK_PLUGIN("stockflow"); + +bool enabled = false; +const char *tagline = "Allows the fortress bookkeeper to queue jobs through the manager."; +const char *usage = ( + " stockflow enable\n" + " Enable the plugin.\n" + " stockflow disable\n" + " Disable the plugin.\n" + " stockflow list\n" + " List any work order settings for your stockpiles.\n" + " stockflow status\n" + " Display whether the plugin is enabled.\n" + "\n" + "While enabled, the 'q' menu of each stockpile will have two new options:\n" + " j: Select a job to order, from an interface like the manager's screen.\n" + " J: Cycle between several options for how many such jobs to order.\n" + "\n" + "Whenever the bookkeeper updates stockpile records, new work orders will\n" + "be placed on the manager's queue for each such selection, reduced by the\n" + "number of identical orders already in the queue.\n" +); + +/* + * Stockpile Access + */ +static building_stockpilest *get_selected_stockpile() { + if (!Gui::dwarfmode_hotkey(Core::getTopViewscreen()) || + ui->main.mode != ui_sidebar_mode::QueryBuilding) + { + return nullptr; + } + + return virtual_cast(world->selected_building); +} + + +/* + * Lua interface. + * Currently calls out to Lua functions, but never back in. + */ +class LuaHelper { +public: + void cycle(color_ostream &out) { + bool found = false; + for (df::job_list_link* link = &df::global::world->job_list; link != NULL; link = link->next) { + if (link->item == NULL) continue; + if (link->item->job_type == job_type::UpdateStockpileRecords) { + found = true; + break; + } + } + + if (found && !bookkeeping) { + command_method("start_bookkeeping", out); + bookkeeping = true; + } else if (bookkeeping && !found) { + command_method("finish_bookkeeping", out); + bookkeeping = false; + } + } + + void init() { + stockpile_id = -1; + initialized = false; + bookkeeping = false; + } + + bool reset(color_ostream &out, bool load) { + stockpile_id = -1; + bookkeeping = false; + if (load) { + return initialized = command_method("initialize_world", out); + } else if (initialized) { + initialized = false; + return command_method("clear_caches", out); + } + + return true; + } + + bool command_method(const char *method, color_ostream &out) { + // Calls a lua function with no parameters. + + // Suspension is required for "stockflow enable" from the command line, + // but may be overkill for other situations. + CoreSuspender suspend; + + auto L = Lua::Core::State; + Lua::StackUnwinder top(L); + + if (!lua_checkstack(L, 1)) + return false; + + if (!Lua::PushModulePublic(out, L, "plugins.stockflow", method)) + return false; + + if (!Lua::SafeCall(out, L, 0, 0)) + return false; + + return true; + } + + bool stockpile_method(const char *method, building_stockpilest *sp) { + // Combines the select_order and toggle_trigger method calls, + // because they share the same signature. + + // Suspension is necessary for toggle_trigger, + // but may be overkill for select_order. + // Both are used from hooks, so CoreSuspender is prohibited. + CoreSuspendClaimer suspend; + + auto L = Lua::Core::State; + color_ostream_proxy out(Core::getInstance().getConsole()); + + Lua::StackUnwinder top(L); + + if (!lua_checkstack(L, 2)) + return false; + + if (!Lua::PushModulePublic(out, L, "plugins.stockflow", method)) + return false; + + Lua::Push(L, sp); + + if (!Lua::SafeCall(out, L, 1, 0)) + return false; + + // Invalidate the string cache. + stockpile_id = -1; + + return true; + } + + bool collect_settings(building_stockpilest *sp) { + // Find strings representing the job to order, and the trigger condition. + // There might be a memory leak here; C++ is odd like that. + auto L = Lua::Core::State; + color_ostream_proxy out(Core::getInstance().getConsole()); + + Lua::StackUnwinder top(L); + + if (!lua_checkstack(L, 2)) + return false; + + if (!Lua::PushModulePublic(out, L, "plugins.stockflow", "stockpile_settings")) + return false; + + Lua::Push(L, sp); + + if (!Lua::SafeCall(out, L, 1, 2)) + return false; + + if (!lua_isstring(L, -1)) + return false; + + current_trigger = lua_tostring(L, -1); + lua_pop(L, 1); + + if (!lua_isstring(L, -1)) + return false; + + current_job = lua_tostring(L, -1); + lua_pop(L, 1); + + stockpile_id = sp->id; + + return true; + } + + void draw(building_stockpilest *sp) { + if (sp->id != stockpile_id) { + if (!collect_settings(sp)) { + Core::printerr("Stockflow job collection failed!\n"); + return; + } + } + + auto dims = Gui::getDwarfmodeViewDims(); + int left_margin = dims.menu_x1 + 1; + int x = left_margin; + int y = 14; + + OutputHotkeyString(x, y, current_job, "j", true, left_margin, COLOR_WHITE, COLOR_LIGHTRED); + if (*current_trigger) + OutputHotkeyString(x, y, current_trigger, " J", true, left_margin, COLOR_WHITE, COLOR_LIGHTRED); + } + + +private: + long stockpile_id; + bool initialized; + bool bookkeeping; + const char *current_job; + const char *current_trigger; +}; + +static LuaHelper helper; + +#define DELTA_TICKS 600 + +DFhackCExport command_result plugin_onupdate(color_ostream &out) { + if (!enabled) + return CR_OK; + + if (!Maps::IsValid()) + return CR_OK; + + static decltype(world->frame_counter) last_frame_count = 0; + + if (DFHack::World::ReadPauseState()) + return CR_OK; + + if (world->frame_counter - last_frame_count < DELTA_TICKS) + return CR_OK; + + last_frame_count = world->frame_counter; + + helper.cycle(out); + + return CR_OK; +} + + +/* + * Interface hooks + */ +struct stockflow_hook : public df::viewscreen_dwarfmodest { + typedef df::viewscreen_dwarfmodest interpose_base; + + bool handleInput(set *input) { + building_stockpilest *sp = get_selected_stockpile(); + if (!sp) + return false; + + if (input->count(interface_key::CUSTOM_J)) { + // Select a new order for this stockpile. + if (!helper.stockpile_method("select_order", sp)) { + Core::printerr("Stockflow order selection failed!\n"); + } + + return true; + } else if (input->count(interface_key::CUSTOM_SHIFT_J)) { + // Toggle the order trigger for this stockpile. + if (!helper.stockpile_method("toggle_trigger", sp)) { + Core::printerr("Stockflow trigger toggle failed!\n"); + } + + 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)(); + + building_stockpilest *sp = get_selected_stockpile(); + if (sp) + helper.draw(sp); + } +}; + +IMPLEMENT_VMETHOD_INTERPOSE(stockflow_hook, feed); +IMPLEMENT_VMETHOD_INTERPOSE(stockflow_hook, render); + + +static command_result stockflow_cmd(color_ostream &out, vector & parameters) { + bool desired = enabled; + if (parameters.size() == 1) { + if (parameters[0] == "enable" || parameters[0] == "on" || parameters[0] == "1") { + desired = true; + } else if (parameters[0] == "disable" || parameters[0] == "off" || parameters[0] == "0") { + desired = false; + } else if (parameters[0] == "usage" || parameters[0] == "help" || parameters[0] == "?") { + out.print("%s: %s\nUsage:\n%s", name, tagline, usage); + return CR_OK; + } else if (parameters[0] == "list") { + if (!enabled) { + out.printerr("Stockflow is not currently enabled.\n"); + return CR_FAILURE; + } + + if (!Maps::IsValid()) { + out.printerr("You haven't loaded a map yet.\n"); + return CR_FAILURE; + } + + // Tell Lua to list any saved stockpile orders. + return helper.command_method("list_orders", out)? CR_OK: CR_FAILURE; + } else if (parameters[0] != "status") { + return CR_WRONG_USAGE; + } + } else if (parameters.size() > 1) { + return CR_WRONG_USAGE; + } + + if (desired != enabled) { + if (desired && !gps) { + out.printerr("Stockflow needs graphics.\n"); + return CR_FAILURE; + } + + if (!INTERPOSE_HOOK(stockflow_hook, feed).apply(desired) || !INTERPOSE_HOOK(stockflow_hook, render).apply(desired)) { + out.printerr("Could not %s stockflow hooks!\n", desired? "insert": "remove"); + return CR_FAILURE; + } + + if (!helper.reset(out, desired && Maps::IsValid())) { + out.printerr("Could not reset stockflow world data!\n"); + return CR_FAILURE; + } + } + + out.print("Stockflow is %s %s.\n", (desired == enabled)? "currently": "now", desired? "enabled": "disabled"); + enabled = desired; + return CR_OK; +} + + +DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) { + if (event == DFHack::SC_MAP_LOADED) { + if (!helper.reset(out, enabled)) { + out.printerr("Could not load stockflow world data!\n"); + return CR_FAILURE; + } + } else if (event == DFHack::SC_MAP_UNLOADED) { + if (!helper.reset(out, false)) { + out.printerr("Could not unload stockflow world data!\n"); + return CR_FAILURE; + } + } + + return CR_OK; +} + +DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) { + helper.init(); + commands.push_back(PluginCommand(name, tagline, stockflow_cmd, false, usage)); + + return CR_OK; +} + +DFhackCExport command_result plugin_shutdown(color_ostream &out) { + return CR_OK; +} From 0a99bb0cae9fda8dc6eb131763a18329a06677ce Mon Sep 17 00:00:00 2001 From: Eric Wald Date: Fri, 31 May 2013 15:30:57 -0600 Subject: [PATCH 068/676] Now works when enabled is true by default. --- plugins/stockflow.cpp | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/plugins/stockflow.cpp b/plugins/stockflow.cpp index 26bc3f9e8..72e804507 100644 --- a/plugins/stockflow.cpp +++ b/plugins/stockflow.cpp @@ -1,6 +1,7 @@ /* * Stockflow plugin. - * For best effect, place "stockflow enable" in your dfhack.init configuration. + * For best effect, place "stockflow enable" in your dfhack.init configuration, + * or set `enabled` to true by default. */ #include "uicommon.h" @@ -293,6 +294,25 @@ IMPLEMENT_VMETHOD_INTERPOSE(stockflow_hook, feed); IMPLEMENT_VMETHOD_INTERPOSE(stockflow_hook, render); +static bool apply_hooks(color_ostream &out, bool enabling) { + if (enabling && !gps) { + out.printerr("Stockflow needs graphics.\n"); + return false; + } + + if (!INTERPOSE_HOOK(stockflow_hook, feed).apply(enabling) || !INTERPOSE_HOOK(stockflow_hook, render).apply(enabling)) { + out.printerr("Could not %s stockflow hooks!\n", enabling? "insert": "remove"); + return false; + } + + if (!helper.reset(out, enabling && Maps::IsValid())) { + out.printerr("Could not reset stockflow world data!\n"); + return false; + } + + return true; +} + static command_result stockflow_cmd(color_ostream &out, vector & parameters) { bool desired = enabled; if (parameters.size() == 1) { @@ -324,18 +344,7 @@ static command_result stockflow_cmd(color_ostream &out, vector & parame } if (desired != enabled) { - if (desired && !gps) { - out.printerr("Stockflow needs graphics.\n"); - return CR_FAILURE; - } - - if (!INTERPOSE_HOOK(stockflow_hook, feed).apply(desired) || !INTERPOSE_HOOK(stockflow_hook, render).apply(desired)) { - out.printerr("Could not %s stockflow hooks!\n", desired? "insert": "remove"); - return CR_FAILURE; - } - - if (!helper.reset(out, desired && Maps::IsValid())) { - out.printerr("Could not reset stockflow world data!\n"); + if (!apply_hooks(out, desired)) { return CR_FAILURE; } } @@ -364,8 +373,8 @@ DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_chan DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) { helper.init(); + if (enabled) apply_hooks(out, true); commands.push_back(PluginCommand(name, tagline, stockflow_cmd, false, usage)); - return CR_OK; } From 3288ab8895bd06e16ec47995bad2ec62ad9defed Mon Sep 17 00:00:00 2001 From: Eric Wald Date: Fri, 31 May 2013 15:48:08 -0600 Subject: [PATCH 069/676] Repairing the new menu option list in the Readme files. --- Readme.html | 7 +++++-- Readme.rst | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Readme.html b/Readme.html index ba89a442e..7c00ef047 100644 --- a/Readme.html +++ b/Readme.html @@ -2141,8 +2141,11 @@ instantly duplicates the job.
      While enabled, the 'q' menu of each stockpile will have two new options:
      -
      j: Select a job to order, from an interface like the manager's screen. -J: Cycle between several options for how many such jobs to order.
      +
        +
      • j: Select a job to order, from an interface like the manager's screen.
      • +
      • J: Cycle between several options for how many such jobs to order.
      • +
      +

      Whenever the bookkeeper updates stockpile records, new work orders will be placed on the manager's queue for each such selection, reduced by the diff --git a/Readme.rst b/Readme.rst index dbef32f26..6d39ec845 100644 --- a/Readme.rst +++ b/Readme.rst @@ -1333,8 +1333,8 @@ Usage: Display whether the plugin is enabled. While enabled, the 'q' menu of each stockpile will have two new options: - j: Select a job to order, from an interface like the manager's screen. - J: Cycle between several options for how many such jobs to order. + * j: Select a job to order, from an interface like the manager's screen. + * J: Cycle between several options for how many such jobs to order. Whenever the bookkeeper updates stockpile records, new work orders will be placed on the manager's queue for each such selection, reduced by the From 5fb87c2ea941a8517763ee5cce708afd4d836704 Mon Sep 17 00:00:00 2001 From: Eric Wald Date: Fri, 18 Apr 2014 21:58:20 -0600 Subject: [PATCH 070/676] Now accepts the new enable/disable commands. --- plugins/stockflow.cpp | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/plugins/stockflow.cpp b/plugins/stockflow.cpp index 72e804507..b96faa47f 100644 --- a/plugins/stockflow.cpp +++ b/plugins/stockflow.cpp @@ -1,7 +1,7 @@ /* * Stockflow plugin. * For best effect, place "stockflow enable" in your dfhack.init configuration, - * or set `enabled` to true by default. + * or set AUTOENABLE to true. */ #include "uicommon.h" @@ -24,7 +24,13 @@ using df::building_stockpilest; DFHACK_PLUGIN("stockflow"); +#define AUTOENABLE false +#ifdef DFHACK_PLUGIN_IS_ENABLED +DFHACK_PLUGIN_IS_ENABLED(enabled); +#else bool enabled = false; +#endif + const char *tagline = "Allows the fortress bookkeeper to queue jobs through the manager."; const char *usage = ( " stockflow enable\n" @@ -371,13 +377,34 @@ DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_chan return CR_OK; } +DFhackCExport command_result plugin_enable(color_ostream& out, bool enable) { + /* Accept the "enable stockflow"/"disable stockflow" syntax, where available. */ + /* Same as "stockflow enable"/"stockflow disable" except without the status line. */ + if (enable != enabled) { + if (!apply_hooks(out, enable)) { + return CR_FAILURE; + } + + enabled = enable; + } + + return CR_OK; +} + DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) { helper.init(); - if (enabled) apply_hooks(out, true); + if (AUTOENABLE) { + if (!apply_hooks(out, true)) { + return CR_FAILURE; + } + + enabled = true; + } + commands.push_back(PluginCommand(name, tagline, stockflow_cmd, false, usage)); return CR_OK; } DFhackCExport command_result plugin_shutdown(color_ostream &out) { - return CR_OK; + return plugin_enable(out, false); } From a14f31fbfe0b61a6a91fbc3d8aec44ff3ae2ee50 Mon Sep 17 00:00:00 2001 From: Anuradha Dissanayake Date: Sat, 3 May 2014 11:44:35 +1200 Subject: [PATCH 071/676] Show box select dimensions in Automaterial --- plugins/automaterial.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/plugins/automaterial.cpp b/plugins/automaterial.cpp index 825e69b01..b8021aaf2 100644 --- a/plugins/automaterial.cpp +++ b/plugins/automaterial.cpp @@ -735,7 +735,7 @@ struct jobutils_hook : public df::viewscreen_dwarfmodest MaterialDescriptor material = get_material_in_list(ui_build_selector->sel_index); if (material.valid) { - if (input->count(interface_key::SELECT) || input->count(interface_key::SEC_SELECT)) + 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 @@ -749,7 +749,7 @@ struct jobutils_hook : public df::viewscreen_dwarfmodest 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::SEC_SELECT) && ui_build_selector->is_grouped) ? -1 : 1)); + 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); @@ -1162,6 +1162,15 @@ struct jobutils_hook : public df::viewscreen_dwarfmodest 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); + int cx = box_first.x; int cy = box_first.y; OutputString(COLOR_BROWN, cx, cy, "X"); From 5e52bb60f04658933fe59256849f9dfd4a25770e Mon Sep 17 00:00:00 2001 From: Anuradha Dissanayake Date: Sat, 3 May 2014 21:19:46 +1200 Subject: [PATCH 072/676] Update uicommon.h with new common display functions --- plugins/uicommon.h | 315 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 305 insertions(+), 10 deletions(-) diff --git a/plugins/uicommon.h b/plugins/uicommon.h index 47ab4d330..0ea0da861 100644 --- a/plugins/uicommon.h +++ b/plugins/uicommon.h @@ -1,4 +1,7 @@ #include +#include +#include +#include #include #include #include @@ -10,9 +13,17 @@ #include #include +#include "modules/Items.h" #include "modules/Screen.h" +#include "modules/World.h" +#include "df/building_stockpilest.h" +#include "df/caravan_state.h" +#include "df/dfhack_material_category.h" #include "df/enabler.h" +#include "df/item_quality.h" +#include "df/ui.h" +#include "df/world.h" using namespace std; using std::string; @@ -32,11 +43,34 @@ using df::global::gps; #define nullptr 0L #endif -#define COLOR_TITLE COLOR_BLUE +#define COLOR_TITLE COLOR_BROWN #define COLOR_UNSELECTED COLOR_GREY #define COLOR_SELECTED COLOR_WHITE #define COLOR_HIGHLIGHTED COLOR_GREEN +struct coord32_t +{ + int32_t x, y, z; + + coord32_t() + { + x = -30000; + y = -30000; + z = -30000; + } + + coord32_t(df::coord& other) + { + x = other.x; + y = other.y; + z = other.z; + } + + df::coord get_coord16() const + { + return df::coord(x, y, z); + } +}; template static void for_each_(vector &v, Fn func) @@ -80,6 +114,17 @@ void OutputHotkeyString(int &x, int &y, const char *text, const char *hotkey, bo OutputString(text_color, x, y, display, newline, left_margin); } +void OutputLabelString(int &x, int &y, const char *text, const char *hotkey, const string &label, bool newline = false, + int left_margin = 0, int8_t text_color = COLOR_WHITE, int8_t hotkey_color = COLOR_LIGHTGREEN) +{ + OutputString(hotkey_color, x, y, hotkey); + string display(": "); + display.append(text); + display.append(": "); + OutputString(text_color, x, y, display); + OutputString(hotkey_color, x, y, label, newline, left_margin); +} + void OutputFilterString(int &x, int &y, const char *text, const char *hotkey, bool state, bool newline = false, int left_margin = 0, int8_t hotkey_color = COLOR_LIGHTGREEN) { @@ -93,9 +138,9 @@ void OutputToggleString(int &x, int &y, const char *text, const char *hotkey, bo OutputHotkeyString(x, y, text, hotkey); OutputString(COLOR_WHITE, x, y, ": "); if (state) - OutputString(COLOR_GREEN, x, y, "Enabled", newline, left_margin); + OutputString(COLOR_GREEN, x, y, "On", newline, left_margin); else - OutputString(COLOR_GREY, x, y, "Disabled", newline, left_margin); + OutputString(COLOR_GREY, x, y, "Off", newline, left_margin); } const int ascii_to_enum_offset = interface_key::STRING_A048 - '0'; @@ -113,6 +158,22 @@ static void set_to_limit(int &value, const int maximum, const int min = 0) value = maximum; } +// trim from start +static inline std::string <rim(std::string &s) { + s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun(std::isspace)))); + return s; +} + +// trim from end +static inline std::string &rtrim(std::string &s) { + s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun(std::isspace))).base(), s.end()); + return s; +} + +// trim from both ends +static inline std::string &trim(std::string &s) { + return ltrim(rtrim(s)); +} inline void paint_text(const UIColor color, const int &x, const int &y, const std::string &text, const UIColor background = 0) { @@ -145,6 +206,215 @@ static string pad_string(string text, const int size, const bool front = true, c } +/* + * Utility Functions + */ + +static df::building_stockpilest *get_selected_stockpile() +{ + if (!Gui::dwarfmode_hotkey(Core::getTopViewscreen()) || + df::global::ui->main.mode != ui_sidebar_mode::QueryBuilding) + { + return nullptr; + } + + return virtual_cast(df::global::world->selected_building); +} + +static bool can_trade() +{ + if (df::global::ui->caravans.size() == 0) + return false; + + for (auto it = df::global::ui->caravans.begin(); it != df::global::ui->caravans.end(); it++) + { + auto caravan = *it; + auto trade_state = caravan->trade_state; + auto time_remaining = caravan->time_remaining; + if ((trade_state != 1 && trade_state != 2) || time_remaining == 0) + return false; + } + + return true; +} + +static bool is_metal_item(df::item *item) +{ + MaterialInfo mat(item); + return (mat.getCraftClass() == craft_material_class::Metal); +} + +bool is_set_to_melt(df::item* item) +{ + return item->flags.bits.melt; +} + +// Copied from Kelly Martin's code +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; + + for (auto g = item->general_refs.begin(); g != item->general_refs.end(); g++) + { + 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: + { + df::item* c = (*g)->getItem(); + for (auto gg = c->general_refs.begin(); gg != c->general_refs.end(); gg++) + { + if ((*gg)->getType() == general_ref_type::UNIT_HOLDER) + return false; + } + } + break; + } + } + + if (item->getQuality() >= item_quality::Masterful) + return false; + + return true; +} + + +/* + * Stockpile Access + */ + +class StockpileInfo { +public: + StockpileInfo() : id(0), sp(nullptr) + { + } + + StockpileInfo(df::building_stockpilest *sp_) : sp(sp_) + { + readBuilding(); + } + + bool inStockpile(df::item *i) + { + df::item *container = Items::getContainer(i); + if (container) + return inStockpile(container); + + if (i->pos.z != z) return false; + if (i->pos.x < x1 || i->pos.x >= x2 || + i->pos.y < y1 || i->pos.y >= y2) return false; + int e = (i->pos.x - x1) + (i->pos.y - y1) * sp->room.width; + return sp->room.extents[e] == 1; + } + + bool isValid() + { + if (!id) + return false; + + auto found = df::building::find(id); + return found && found == sp && found->getType() == building_type::Stockpile; + } + + int32_t getId() + { + return id; + } + + bool matches(df::building_stockpilest* sp) + { + return this->sp == sp; + } + +protected: + int32_t id; + df::building_stockpilest* sp; + + void readBuilding() + { + if (!sp) + return; + + id = sp->id; + z = sp->z; + x1 = sp->room.x; + x2 = sp->room.x + sp->room.width; + y1 = sp->room.y; + y2 = sp->room.y + sp->room.height; + } + +private: + int x1, x2, y1, y2, z; +}; + + +class PersistentStockpileInfo : public StockpileInfo { +public: + PersistentStockpileInfo(df::building_stockpilest *sp, string persistence_key) : + StockpileInfo(sp), persistence_key(persistence_key) + { + } + + PersistentStockpileInfo(PersistentDataItem &config, string persistence_key) : + config(config), persistence_key(persistence_key) + { + id = config.ival(1); + } + + bool load() + { + auto found = df::building::find(id); + if (!found || found->getType() != building_type::Stockpile) + return false; + + sp = virtual_cast(found); + if (!sp) + return false; + + readBuilding(); + + return true; + } + + void save() + { + config = DFHack::World::AddPersistentData(persistence_key); + config.ival(1) = id; + } + + void remove() + { + DFHack::World::DeletePersistentData(config); + } + +private: + PersistentDataItem config; + string persistence_key; +}; + + + /* * List classes */ @@ -155,9 +425,10 @@ public: T elem; string text, keywords; bool selected; + UIColor color; - ListEntry(const string text, const T elem, const string keywords = "") : - elem(elem), text(text), selected(false), keywords(keywords) + ListEntry(const string text, const T elem, const string keywords = "", const UIColor color = COLOR_UNSELECTED) : + elem(elem), text(text), selected(false), keywords(keywords), color(color) { } }; @@ -173,7 +444,6 @@ public: bool multiselect; bool allow_null; bool auto_select; - bool force_sort; bool allow_search; bool feed_changed_highlight; @@ -188,7 +458,6 @@ public: multiselect = false; allow_null = true; auto_select = false; - force_sort = false; allow_search = true; feed_changed_highlight = false; } @@ -198,6 +467,8 @@ public: list.clear(); display_list.clear(); display_start_offset = 0; + if (highlighted_index != -1) + highlighted_index = 0; max_item_width = title.length(); resize(); } @@ -231,6 +502,11 @@ public: it->text = pad_string(it->text, max_item_width, false); } + return getMaxItemWidth(); + } + + int getMaxItemWidth() + { return left_margin + max_item_width; } @@ -245,7 +521,7 @@ public: for (int i = display_start_offset; i < display_list.size() && i < last_index_able_to_display; i++) { ++y; - UIColor fg_color = (display_list[i]->selected) ? COLOR_SELECTED : COLOR_UNSELECTED; + UIColor fg_color = (display_list[i]->selected) ? COLOR_SELECTED : display_list[i]->color; UIColor bg_color = (is_selected_column && i == highlighted_index) ? COLOR_HIGHLIGHTED : COLOR_BLACK; string item_label = display_list[i]->text; @@ -324,6 +600,15 @@ public: } } + void centerSelection() + { + if (display_list.size() == 0) + return; + display_start_offset = highlighted_index - (display_max_rows / 2); + validateDisplayOffset(); + validateHighlight(); + } + void validateHighlight() { set_to_limit(highlighted_index, display_list.size() - 1); @@ -347,10 +632,15 @@ public: highlighted_index += highlight_change + offset_shift * display_max_rows; display_start_offset += offset_shift * display_max_rows; - set_to_limit(display_start_offset, max(0, (int)(display_list.size())-display_max_rows)); + validateDisplayOffset(); validateHighlight(); } + void validateDisplayOffset() + { + set_to_limit(display_start_offset, max(0, (int)(display_list.size())-display_max_rows)); + } + void setHighlight(const int index) { if (!initHighlightChange()) @@ -378,6 +668,9 @@ public: void toggleHighlighted() { + if (display_list.size() == 0) + return; + if (auto_select) return; @@ -505,6 +798,7 @@ public: // Standard character search_string += last_token - ascii_to_enum_offset; filterDisplay(); + centerSelection(); } else if (last_token == interface_key::STRING_A000) { @@ -513,6 +807,7 @@ public: { search_string.erase(search_string.length()-1); filterDisplay(); + centerSelection(); } } else @@ -547,7 +842,7 @@ public: return false; } - void sort() + void sort(bool force_sort = false) { if (force_sort || list.size() < 100) std::sort(list.begin(), list.end(), sort_fn); From de9140336fa9071acc71c175ae04e39b7e59ca17 Mon Sep 17 00:00:00 2001 From: Anuradha Dissanayake Date: Sat, 3 May 2014 22:56:34 +1200 Subject: [PATCH 073/676] autotrade: adds "(Un)mark All" options to both panes of trade screen. --- NEWS | 1 + plugins/autotrade.cpp | 240 +++++++++++++++--------------------------- 2 files changed, 85 insertions(+), 156 deletions(-) diff --git a/NEWS b/NEWS index 165938e2b..6f5c4bd13 100644 --- a/NEWS +++ b/NEWS @@ -20,6 +20,7 @@ DFHack future - digfort: improved csv parsing, add start() comment handling - exterminate: allow specifying a caste (exterminate gob:male) - createitem: in adventure mode it now defaults to the controlled unit as maker. + - autotrade: adds "(Un)mark All" options to both panes of trade screen. Siege engine plugin: - engine quality and distance to target now affect accuracy diff --git a/plugins/autotrade.cpp b/plugins/autotrade.cpp index 0d11af9f0..e31fccdbc 100644 --- a/plugins/autotrade.cpp +++ b/plugins/autotrade.cpp @@ -6,6 +6,7 @@ #include "df/world_raws.h" #include "df/building_def.h" #include "df/viewscreen_dwarfmodest.h" +#include "df/viewscreen_tradegoodsst.h" #include "df/building_stockpilest.h" #include "modules/Items.h" #include "df/building_tradedepotst.h" @@ -14,10 +15,8 @@ #include "df/job_item_ref.h" #include "modules/Job.h" #include "df/ui.h" -#include "df/caravan_state.h" #include "df/mandate.h" #include "modules/Maps.h" -#include "modules/World.h" using df::global::world; using df::global::cursor; @@ -25,127 +24,9 @@ using df::global::ui; using df::building_stockpilest; DFHACK_PLUGIN("autotrade"); -#define PLUGIN_VERSION 0.2 - - -/* - * Stockpile Access - */ - -static building_stockpilest *get_selected_stockpile() -{ - if (!Gui::dwarfmode_hotkey(Core::getTopViewscreen()) || - ui->main.mode != ui_sidebar_mode::QueryBuilding) - { - return nullptr; - } - - return virtual_cast(world->selected_building); -} - -static bool can_trade() -{ - if (df::global::ui->caravans.size() == 0) - return false; - - for (auto it = df::global::ui->caravans.begin(); it != df::global::ui->caravans.end(); it++) - { - auto caravan = *it; - auto trade_state = caravan->trade_state; - auto time_remaining = caravan->time_remaining; - if ((trade_state != 1 && trade_state != 2) || time_remaining == 0) - return false; - } - - return true; -} - -class StockpileInfo { -public: - - StockpileInfo(df::building_stockpilest *sp_) : sp(sp_) - { - readBuilding(); - } - - StockpileInfo(PersistentDataItem &config) - { - this->config = config; - id = config.ival(1); - } - - bool inStockpile(df::item *i) - { - df::item *container = Items::getContainer(i); - if (container) - return inStockpile(container); - - if (i->pos.z != z) return false; - if (i->pos.x < x1 || i->pos.x >= x2 || - i->pos.y < y1 || i->pos.y >= y2) return false; - int e = (i->pos.x - x1) + (i->pos.y - y1) * sp->room.width; - return sp->room.extents[e] == 1; - } - - bool isValid() - { - auto found = df::building::find(id); - return found && found == sp && found->getType() == building_type::Stockpile; - } - - bool load() - { - auto found = df::building::find(id); - if (!found || found->getType() != building_type::Stockpile) - return false; - - sp = virtual_cast(found); - if (!sp) - return false; - - readBuilding(); - - return true; - } - - int32_t getId() - { - return id; - } - - bool matches(df::building_stockpilest* sp) - { - return this->sp == sp; - } - - void save() - { - config = DFHack::World::AddPersistentData("autotrade/stockpiles"); - config.ival(1) = id; - } - - void remove() - { - DFHack::World::DeletePersistentData(config); - } - -private: - PersistentDataItem config; - df::building_stockpilest* sp; - int x1, x2, y1, y2, z; - int32_t id; - - void readBuilding() - { - id = sp->id; - z = sp->z; - x1 = sp->room.x; - x2 = sp->room.x + sp->room.width; - y1 = sp->room.y; - y2 = sp->room.y + sp->room.height; - } -}; +#define PLUGIN_VERSION 0.4 +static const string PERSISTENCE_KEY = "autotrade/stockpiles"; /* * Depot Access @@ -316,15 +197,10 @@ static bool is_valid_item(df::item *item) return true; } -static void mark_all_in_stockpiles(vector &stockpiles, bool announce) +static void mark_all_in_stockpiles(vector &stockpiles) { if (!depot_info.findDepot()) - { - if (announce) - Gui::showAnnouncement("Cannot trade, no valid depot available", COLOR_RED, true); - return; - } std::vector &items = world->items.other[items_other_id::IN_PLAY]; @@ -389,8 +265,6 @@ static void mark_all_in_stockpiles(vector &stockpiles, bool annou if (marked_count) Gui::showAnnouncement("Marked " + int_to_string(marked_count) + " items for trade", COLOR_GREEN, false); - else if (announce) - Gui::showAnnouncement("No more items to mark", COLOR_RED, true); if (error_count >= 5) { @@ -419,10 +293,10 @@ public: void add(df::building_stockpilest *sp) { - auto pile = StockpileInfo(sp); + auto pile = PersistentStockpileInfo(sp, PERSISTENCE_KEY); if (pile.isValid()) { - monitored_stockpiles.push_back(StockpileInfo(sp)); + monitored_stockpiles.push_back(pile); monitored_stockpiles.back().save(); } } @@ -456,20 +330,20 @@ public: ++it; } - mark_all_in_stockpiles(monitored_stockpiles, false); + mark_all_in_stockpiles(monitored_stockpiles); } void reset() { monitored_stockpiles.clear(); std::vector items; - DFHack::World::GetPersistentData(&items, "autotrade/stockpiles"); + DFHack::World::GetPersistentData(&items, PERSISTENCE_KEY); for (auto i = items.begin(); i != items.end(); i++) { - auto pile = StockpileInfo(*i); + auto pile = PersistentStockpileInfo(*i, PERSISTENCE_KEY); if (pile.load()) - monitored_stockpiles.push_back(StockpileInfo(pile)); + monitored_stockpiles.push_back(pile); else pile.remove(); } @@ -477,7 +351,7 @@ public: private: - vector monitored_stockpiles; + vector monitored_stockpiles; }; static StockpileMonitor monitor; @@ -519,18 +393,7 @@ struct trade_hook : public df::viewscreen_dwarfmodest if (!sp) return false; - if (input->count(interface_key::CUSTOM_M)) - { - if (!can_trade()) - return false; - - vector wrapper; - wrapper.push_back(StockpileInfo(sp)); - mark_all_in_stockpiles(wrapper, true); - - return true; - } - else if (input->count(interface_key::CUSTOM_U)) + if (input->count(interface_key::CUSTOM_SHIFT_T)) { if (monitor.isMonitored(sp)) monitor.remove(sp); @@ -558,18 +421,81 @@ struct trade_hook : public df::viewscreen_dwarfmodest auto dims = Gui::getDwarfmodeViewDims(); int left_margin = dims.menu_x1 + 1; int x = left_margin; - int y = 23; - - if (can_trade()) - OutputHotkeyString(x, y, "Mark all for trade", "m", true, left_margin); - - OutputToggleString(x, y, "Auto trade", "u", monitor.isMonitored(sp), true, left_margin); + int y = 24; + OutputToggleString(x, y, "Auto trade", "Shift-T", monitor.isMonitored(sp), true, left_margin); } }; IMPLEMENT_VMETHOD_INTERPOSE(trade_hook, feed); IMPLEMENT_VMETHOD_INTERPOSE(trade_hook, render); +struct tradeview_hook : public df::viewscreen_tradegoodsst +{ + typedef df::viewscreen_tradegoodsst interpose_base; + + bool handleInput(set *input) + { + if (input->count(interface_key::CUSTOM_M)) + { + for (int i = 0; i < trader_selected.size(); i++) + { + trader_selected[i] = 1; + } + } + else if (input->count(interface_key::CUSTOM_U)) + { + for (int i = 0; i < trader_selected.size(); i++) + { + trader_selected[i] = 0; + } + } + else if (input->count(interface_key::CUSTOM_SHIFT_M)) + { + for (int i = 0; i < broker_selected.size(); i++) + { + broker_selected[i] = 1; + } + } + else if (input->count(interface_key::CUSTOM_SHIFT_U)) + { + for (int i = 0; i < broker_selected.size(); i++) + { + broker_selected[i] = 0; + } + } + else + { + return false; + } + + return true; + } + + DEFINE_VMETHOD_INTERPOSE(void, feed, (set *input)) + { + if (!handleInput(input)) + INTERPOSE_NEXT(feed)(input); + } + + DEFINE_VMETHOD_INTERPOSE(void, render, ()) + { + INTERPOSE_NEXT(render)(); + int x = 2; + int y = 27; + OutputHotkeyString(x, y, "Mark all", "m", true, 2); + OutputHotkeyString(x, y, "Unmark all", "u"); + + x = 42; + y = 27; + OutputHotkeyString(x, y, "Mark all", "Shift-m", true, 42); + OutputHotkeyString(x, y, "Unmark all", "Shift-u"); + } +}; + +IMPLEMENT_VMETHOD_INTERPOSE(tradeview_hook, feed); +IMPLEMENT_VMETHOD_INTERPOSE(tradeview_hook, render); + + static command_result autotrade_cmd(color_ostream &out, vector & parameters) { if (!parameters.empty()) @@ -612,7 +538,9 @@ DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) monitor.reset(); if (!INTERPOSE_HOOK(trade_hook, feed).apply(enable) || - !INTERPOSE_HOOK(trade_hook, render).apply(enable)) + !INTERPOSE_HOOK(trade_hook, render).apply(enable) || + !INTERPOSE_HOOK(tradeview_hook, feed).apply(enable) || + !INTERPOSE_HOOK(tradeview_hook, render).apply(enable)) return CR_FAILURE; is_enabled = enable; From 7fd7d3d48c99e27d878d1f2352c6ec5325da4bb7 Mon Sep 17 00:00:00 2001 From: Anuradha Dissanayake Date: Sun, 4 May 2014 21:55:58 +1200 Subject: [PATCH 074/676] mousequery: several usability improvements. mousequery: show live overlay (in menu area) of what's on the tile under the mouse cursor. --- NEWS | 2 + plugins/mousequery.cpp | 899 +++++++++++++++++++++++++++++++++-------- 2 files changed, 734 insertions(+), 167 deletions(-) diff --git a/NEWS b/NEWS index 6f5c4bd13..8be141a4f 100644 --- a/NEWS +++ b/NEWS @@ -21,6 +21,8 @@ DFHack future - exterminate: allow specifying a caste (exterminate gob:male) - createitem: in adventure mode it now defaults to the controlled unit as maker. - autotrade: adds "(Un)mark All" options to both panes of trade screen. + - mousequery: several usability improvements. + - mousequery: show live overlay (in menu area) of what's on the tile under the mouse cursor. Siege engine plugin: - engine quality and distance to target now affect accuracy diff --git a/plugins/mousequery.cpp b/plugins/mousequery.cpp index 97e703752..9cc6bb783 100644 --- a/plugins/mousequery.cpp +++ b/plugins/mousequery.cpp @@ -1,244 +1,792 @@ -#include - -#include "Core.h" -#include -#include -#include #include -#include "DataDefs.h" - #include "df/building.h" -#include "df/enabler.h" #include "df/item.h" -#include "df/ui.h" #include "df/unit.h" #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 "modules/Gui.h" -#include "modules/Screen.h" - - -using std::set; -using std::string; -using std::ostringstream; +#include "modules/World.h" +#include "modules/Maps.h" +#include "modules/Buildings.h" +#include "modules/Items.h" +#include "modules/Units.h" +#include "modules/Translation.h" -using namespace DFHack; -using namespace df::enums; +#include "uicommon.h" +#include "TileTypes.h" +#include "DataFuncs.h" -using df::global::enabler; -using df::global::gps; using df::global::world; using df::global::ui; +using namespace df::enums::ui_sidebar_mode; + +DFHACK_PLUGIN("mousequery"); + +#define PLUGIN_VERSION 0.17 + +static int32_t last_clicked_x, last_clicked_y, last_clicked_z; +static int32_t last_pos_x, last_pos_y, last_pos_z; +static df::coord last_move_pos; +static size_t max_list_size = 300000; // Avoid iterating over huge lists -static int32_t last_x, last_y, last_z; -static size_t max_list_size = 100000; // Avoid iterating over huge lists +static bool plugin_enabled = true; +static bool rbutton_enabled = true; +static bool tracking_enabled = false; +static bool active_scrolling = false; +static bool box_designation_enabled = false; +static bool live_view = true; +static bool skip_tracking_once = false; +static bool mouse_moved = false; + +static int scroll_delay = 100; + +static df::coord get_mouse_pos(int32_t &mx, int32_t &my) +{ + df::coord pos; + pos.x = -30000; + + if (!enabler->tracking_on) + return pos; + + if (!Gui::getMousePos(mx, my)) + return pos; + + int32_t vx, vy, vz; + if (!Gui::getViewCoords(vx, vy, vz)) + return pos; + + pos.x = vx + mx - 1; + pos.y = vy + my - 1; + pos.z = vz; + + return pos; +} + +static bool is_valid_pos(const df::coord pos) +{ + auto designation = Maps::getTileDesignation(pos); + if (!designation) + return false; + + if (designation->bits.hidden) + return false; // Items in parts of the map not yet revealed + + return true; +} + +static vector get_units_at(const df::coord pos, bool only_one) +{ + vector list; + + auto count = world->units.active.size(); + if (count > max_list_size) + return list; + + df::unit_flags1 bad_flags; + bad_flags.whole = 0; + bad_flags.bits.dead = true; + bad_flags.bits.hidden_ambusher = true; + bad_flags.bits.hidden_in_ambush = true; + + for (size_t i = 0; i < count; i++) + { + df::unit *unit = world->units.active[i]; + + if(unit->pos.x == pos.x && unit->pos.y == pos.y && unit->pos.z == pos.z && + !(unit->flags1.whole & bad_flags.whole) && + unit->profession != profession::THIEF && unit->profession != profession::MASTER_THIEF) + { + list.push_back(unit); + if (only_one) + break; + } + } + + return list; +} + +static vector get_items_at(const df::coord pos, bool only_one) +{ + vector list; + auto count = world->items.other[items_other_id::IN_PLAY].size(); + if (count > max_list_size) + return list; + + df::item_flags bad_flags; + bad_flags.whole = 0; + bad_flags.bits.in_building = true; + bad_flags.bits.garbage_collect = true; + bad_flags.bits.removed = true; + bad_flags.bits.dead_dwarf = true; + bad_flags.bits.murder = true; + bad_flags.bits.construction = true; + bad_flags.bits.in_inventory = true; + bad_flags.bits.in_chest = true; + + for (size_t i = 0; i < count; i++) + { + df::item *item = world->items.other[items_other_id::IN_PLAY][i]; + if (item->flags.whole & bad_flags.whole) + continue; + + if (pos.z == item->pos.z && pos.x == item->pos.x && pos.y == item->pos.y) + list.push_back(item); + } + + return list; +} + +static df::interface_key get_default_query_mode(const df::coord pos) +{ + if (!is_valid_pos(pos)) + return df::interface_key::D_LOOK; + + bool fallback_to_building_query = false; + + // Check for unit under cursor + auto ulist = get_units_at(pos, true); + if (ulist.size() > 0) + return df::interface_key::D_VIEWUNIT; + + // Check for building under cursor + auto bld = Buildings::findAtTile(pos); + if (bld) + { + df::building_type type = bld->getType(); + + if (type == building_type::Stockpile) + { + fallback_to_building_query = true; + } + else + { + // For containers use item view, for everything else, query view + return (type == building_type::Box || type == building_type::Cabinet || + type == building_type::Weaponrack || type == building_type::Armorstand) + ? df::interface_key::D_BUILDITEM : df::interface_key::D_BUILDJOB; + } + } + + // Check for items under cursor + auto ilist = get_items_at(pos, true); + if (ilist.size() > 0) + return df::interface_key::D_LOOK; + + return (fallback_to_building_query) ? df::interface_key::D_BUILDJOB : df::interface_key::D_LOOK; +} struct mousequery_hook : public df::viewscreen_dwarfmodest { typedef df::viewscreen_dwarfmodest interpose_base; - void send_key(const df::interface_key &key) + void sendKey(const df::interface_key &key) { set tmp; tmp.insert(key); - //INTERPOSE_NEXT(feed)(&tmp); - this->feed(&tmp); + INTERPOSE_NEXT(feed)(&tmp); + } + + bool isInDesignationMenu() + { + switch (ui->main.mode) + { + case DesignateMine: + case DesignateRemoveRamps: + case DesignateUpStair: + case DesignateDownStair: + case DesignateUpDownStair: + case DesignateUpRamp: + case DesignateChannel: + case DesignateGatherPlants: + case DesignateRemoveDesignation: + case DesignateSmooth: + case DesignateCarveTrack: + case DesignateEngrave: + case DesignateCarveFortification: + case DesignateChopTrees: + case DesignateToggleEngravings: + case DesignateRemoveConstruction: + case DesignateTrafficHigh: + case DesignateTrafficNormal: + case DesignateTrafficLow: + case DesignateTrafficRestricted: + return true; + + case Burrows: + return ui->burrows.in_define_mode; + }; + + return false; } - df::interface_key get_default_query_mode(const int32_t &x, const int32_t &y, const int32_t &z) + bool isInTrackableMode() { - bool fallback_to_building_query = false; + if (isInDesignationMenu()) + return box_designation_enabled; - // Check for unit under cursor - size_t count = world->units.all.size(); - if (count <= max_list_size) + switch (ui->main.mode) { - for(size_t i = 0; i < count; i++) - { - df::unit *unit = world->units.all[i]; + case DesignateItemsClaim: + case DesignateItemsForbid: + case DesignateItemsMelt: + case DesignateItemsUnmelt: + case DesignateItemsDump: + case DesignateItemsUndump: + case DesignateItemsHide: + case DesignateItemsUnhide: + case DesignateTrafficHigh: + case DesignateTrafficNormal: + case DesignateTrafficLow: + case DesignateTrafficRestricted: + case Stockpiles: + case Squads: + case NotesPoints: + case NotesRoutes: + case Zones: + return true; + + case Build: + return inBuildPlacement(); + + case QueryBuilding: + case BuildingItems: + case ViewUnits: + case LookAround: + return !enabler->mouse_lbut; + + default: + return false; + }; + } - if(unit->pos.x == x && unit->pos.y == y && unit->pos.z == z) - return df::interface_key::D_VIEWUNIT; - } - } - else + bool isInAreaSelectionMode() + { + bool selectableMode = + isInDesignationMenu() || + ui->main.mode == Stockpiles || + ui->main.mode == Zones; + + if (selectableMode) { - fallback_to_building_query = true; + int32_t x, y, z; + return Gui::getDesignationCoords(x, y, z); } - // Check for building under cursor - count = world->buildings.all.size(); - if (count <= max_list_size) + return false; + } + + bool handleMouse(const set *input) + { + int32_t mx, my; + auto mpos = get_mouse_pos(mx, my); + if (mpos.x == -30000) + return false; + + auto dims = Gui::getDwarfmodeViewDims(); + if (enabler->mouse_lbut) { - for(size_t i = 0; i < count; i++) - { - df::building *bld = world->buildings.all[i]; + bool cursor_still_here = (last_clicked_x == mpos.x && last_clicked_y == mpos.y && last_clicked_z == mpos.z); + last_clicked_x = mpos.x; + last_clicked_y = mpos.y; + last_clicked_z = mpos.z; - if (z == bld->z && - x >= bld->x1 && x <= bld->x2 && - y >= bld->y1 && y <= bld->y2) - { - df::building_type type = bld->getType(); + df::interface_key key = interface_key::NONE; + bool designationMode = false; + bool skipRefresh = false; - if (type == building_type::Stockpile) + if (isInTrackableMode()) + { + designationMode = true; + key = df::interface_key::SELECT; + } + else + { + switch (ui->main.mode) + { + case QueryBuilding: + if (cursor_still_here) + key = df::interface_key::D_BUILDITEM; + break; + + case BuildingItems: + if (cursor_still_here) + key = df::interface_key::D_VIEWUNIT; + break; + + case ViewUnits: + if (cursor_still_here) + key = df::interface_key::D_LOOK; + break; + + case LookAround: + if (cursor_still_here) + key = df::interface_key::D_BUILDJOB; + break; + + case Build: + if (df::global::ui_build_selector) { - fallback_to_building_query = true; - break; // Check for items in stockpile first + if (df::global::ui_build_selector->stage < 2) + { + designationMode = true; + key = df::interface_key::SELECT; + } + else + { + designationMode = true; + skipRefresh = true; + key = df::interface_key::SELECT_ALL; + } } + break; + + case Default: + break; - // For containers use item view, fir everything else, query view - return (type == building_type::Box || type == building_type::Cabinet || - type == building_type::Weaponrack || type == building_type::Armorstand) - ? df::interface_key::D_BUILDITEM : df::interface_key::D_BUILDJOB; + default: + return false; } } - } - else - { - fallback_to_building_query = true; - } + enabler->mouse_lbut = 0; - // Check for items under cursor - count = world->items.all.size(); - if (count <= max_list_size) - { - for(size_t i = 0; i < count; i++) + // Can't check limits earlier as we must be sure we are in query or default mode + // (so we can clear the button down flag) + int right_bound = (dims.menu_x1 > 0) ? dims.menu_x1 - 2 : gps->dimx - 2; + if (mx < 1 || mx > right_bound || my < 1 || my > gps->dimy - 2) + return false; + + if (ui->main.mode == df::ui_sidebar_mode::Zones || + ui->main.mode == df::ui_sidebar_mode::Stockpiles) { - df::item *item = world->items.all[i]; - if (z == item->pos.z && x == item->pos.x && y == item->pos.y && - !item->flags.bits.in_building && !item->flags.bits.hidden && - !item->flags.bits.in_job && !item->flags.bits.in_chest && - !item->flags.bits.in_inventory) + int32_t x, y, z; + if (Gui::getDesignationCoords(x, y, z)) { - return df::interface_key::D_LOOK; + auto dX = abs(x - mpos.x); + if (dX > 30) + return false; + + auto dY = abs(y - mpos.y); + if (dY > 30) + return false; } } + + if (!designationMode) + { + while (ui->main.mode != Default) + { + sendKey(df::interface_key::LEAVESCREEN); + } + + if (key == interface_key::NONE) + key = get_default_query_mode(mpos); + + sendKey(key); + } + + if (!skipRefresh) + { + // Force UI refresh + moveCursor(mpos, true); + } + + if (designationMode) + sendKey(key); + + return true; + } + else if (rbutton_enabled && enabler->mouse_rbut) + { + if (isInDesignationMenu() && !box_designation_enabled) + return false; + + // Escape out of query mode + enabler->mouse_rbut_down = 0; + 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) || + (isInTrackableMode() && tracking_enabled)) + { + sendKey(df::interface_key::LEAVESCREEN); + } + else + { + int scroll_trigger_x = dims.menu_x1 / 3; + int scroll_trigger_y = gps->dimy / 3; + if (mx < scroll_trigger_x) + sendKey(interface_key::CURSOR_LEFT_FAST); + + if (mx > ((dims.menu_x1 > 0) ? dims.menu_x1 : gps->dimx) - scroll_trigger_x) + sendKey(interface_key::CURSOR_RIGHT_FAST); + + if (my < scroll_trigger_y) + sendKey(interface_key::CURSOR_UP_FAST); + + if (my > gps->dimy - scroll_trigger_y) + sendKey(interface_key::CURSOR_DOWN_FAST); + } + } + else if (input->count(interface_key::CUSTOM_M) && isInDesignationMenu()) + { + box_designation_enabled = !box_designation_enabled; } else { - fallback_to_building_query = true; + if (input->count(interface_key::CURSOR_UP) || + input->count(interface_key::CURSOR_DOWN) || + input->count(interface_key::CURSOR_LEFT) || + input->count(interface_key::CURSOR_RIGHT) || + input->count(interface_key::CURSOR_UPLEFT) || + input->count(interface_key::CURSOR_UPRIGHT) || + input->count(interface_key::CURSOR_DOWNLEFT) || + input->count(interface_key::CURSOR_DOWNRIGHT) || + input->count(interface_key::CURSOR_UP_FAST) || + input->count(interface_key::CURSOR_DOWN_FAST) || + input->count(interface_key::CURSOR_LEFT_FAST) || + input->count(interface_key::CURSOR_RIGHT_FAST) || + input->count(interface_key::CURSOR_UPLEFT_FAST) || + input->count(interface_key::CURSOR_UPRIGHT_FAST) || + input->count(interface_key::CURSOR_DOWNLEFT_FAST) || + input->count(interface_key::CURSOR_DOWNRIGHT_FAST) || + input->count(interface_key::CURSOR_UP_Z) || + input->count(interface_key::CURSOR_DOWN_Z) || + input->count(interface_key::CURSOR_UP_Z_AUX) || + input->count(interface_key::CURSOR_DOWN_Z_AUX)) + { + mouse_moved = false; + if (shouldTrack()) + skip_tracking_once = true; + } } - return (fallback_to_building_query) ? df::interface_key::D_BUILDJOB : df::interface_key::D_LOOK; + return false; } - bool handle_mouse(const set *input) + void moveCursor(df::coord &mpos, bool forced) { - int32_t cx, cy, vz; - if (enabler->tracking_on) + bool should_skip_tracking = skip_tracking_once; + skip_tracking_once = false; + if (!forced) + { + if (mpos.x == last_pos_x && mpos.y == last_pos_y && mpos.z == last_pos_z) + return; + } + + last_pos_x = mpos.x; + last_pos_y = mpos.y; + last_pos_z = mpos.z; + + if (!forced && should_skip_tracking) { - if (enabler->mouse_lbut) + return; + } + + int32_t x, y, z; + Gui::getCursorCoords(x, y, z); + if (mpos.x == x && mpos.y == y && mpos.z == z) + return; + + Gui::setCursorCoords(mpos.x, mpos.y, mpos.z); + sendKey(interface_key::CURSOR_DOWN_Z); + sendKey(interface_key::CURSOR_UP_Z); + } + + bool inBuildPlacement() + { + return df::global::ui_build_selector && + df::global::ui_build_selector->building_type != -1 && + df::global::ui_build_selector->stage == 1; + } + + bool shouldTrack() + { + if (!tracking_enabled) + return false; + + return isInTrackableMode(); + } + + DEFINE_VMETHOD_INTERPOSE(void, feed, (set *input)) + { + if (!plugin_enabled || !handleMouse(input)) + INTERPOSE_NEXT(feed)(input); + } + + DEFINE_VMETHOD_INTERPOSE(void, render, ()) + { + INTERPOSE_NEXT(render)(); + + if (!plugin_enabled) + return; + + static decltype(enabler->clock) last_t = 0; + + auto dims = Gui::getDwarfmodeViewDims(); + auto right_margin = (dims.menu_x1 > 0) ? dims.menu_x1 : gps->dimx; + + int32_t mx, my; + auto mpos = get_mouse_pos(mx, my); + bool mpos_valid = mpos.x != -30000 && mpos.y != -30000 && mpos.z != -30000; + if (mx < 1 || mx > right_margin - 2 || my < 1 || my > gps->dimy - 2) + mpos_valid = false; + + if (mpos_valid) + { + if (mpos.x != last_move_pos.x || mpos.y != last_move_pos.y || mpos.z != last_move_pos.z) { - int32_t mx, my; - if (Gui::getMousePos(mx, my)) - { - int32_t vx, vy; - if (Gui::getViewCoords(vx, vy, vz)) - { - cx = vx + mx - 1; - cy = vy + my - 1; + mouse_moved = true; + last_move_pos = mpos; + } + } - using namespace df::enums::ui_sidebar_mode; - df::interface_key key = interface_key::NONE; - bool cursor_still_here = (last_x == cx && last_y == cy && last_z == vz); - switch(ui->main.mode) - { - case QueryBuilding: - if (cursor_still_here) - key = df::interface_key::D_BUILDITEM; - break; - - case BuildingItems: - if (cursor_still_here) - key = df::interface_key::D_VIEWUNIT; - break; - - case ViewUnits: - if (cursor_still_here) - key = df::interface_key::D_LOOK; - break; - - case LookAround: - if (cursor_still_here) - key = df::interface_key::D_BUILDJOB; - break; - - case Default: - break; - - default: - return false; - } + int left_margin = dims.menu_x1 + 1; + int look_width = dims.menu_x2 - dims.menu_x1 - 1; + int disp_x = left_margin; - enabler->mouse_lbut = 0; + if (isInDesignationMenu()) + { + int x = left_margin; + int y = 24; + OutputString(COLOR_BROWN, x, y, "DFHack MouseQuery", true, left_margin); + OutputToggleString(x, y, "Box Select", "m", box_designation_enabled, true, left_margin); + } - // Can't check limits earlier as we must be sure we are in query or default mode we can clear the button flag - // Otherwise the feed gets stuck in a loop - uint8_t menu_width, area_map_width; - Gui::getMenuWidth(menu_width, area_map_width); - int32_t w = gps->dimx; - if (menu_width == 1) w -= 57; //Menu is open doubly wide - else if (menu_width == 2 && area_map_width == 3) w -= 33; //Just the menu is open - else if (menu_width == 2 && area_map_width == 2) w -= 26; //Just the area map is open + //Display selection dimensions + bool showing_dimensions = false; + if (isInAreaSelectionMode()) + { + showing_dimensions = true; + int32_t x, y, z; + Gui::getDesignationCoords(x, y, z); + coord32_t curr_pos; - if (mx < 1 || mx > w || my < 1 || my > gps->dimy - 2) - return false; + if (!tracking_enabled && mouse_moved && mpos_valid && + (!isInDesignationMenu() || box_designation_enabled)) + { + curr_pos = mpos; + } + else + { + Gui::getCursorCoords(curr_pos.x, curr_pos.y, curr_pos.z); + } + auto dX = abs(x - curr_pos.x) + 1; + auto dY = abs(y - curr_pos.y) + 1; + auto dZ = abs(z - curr_pos.z) + 1; + + int disp_y = gps->dimy - 3; + stringstream label; + label << "Selection: " << dX << "x" << dY << "x" << dZ; + OutputString(COLOR_WHITE, disp_x, disp_y, label.str()); + } + else + { + mouse_moved = false; + } - while (ui->main.mode != Default) - { - send_key(df::interface_key::LEAVESCREEN); - } + if (!mpos_valid) + return; - if (key == interface_key::NONE) - key = get_default_query_mode(cx, cy, vz); + int scroll_buffer = 6; + auto delta_t = enabler->clock - last_t; + if (active_scrolling && !isInTrackableMode() && delta_t > scroll_delay) + { + last_t = enabler->clock; + if (mx < scroll_buffer) + { + sendKey(interface_key::CURSOR_LEFT); + return; + } - send_key(key); + if (mx > right_margin - scroll_buffer) + { + sendKey(interface_key::CURSOR_RIGHT); + return; + } - // Force UI refresh - Gui::setCursorCoords(cx, cy, vz); - send_key(interface_key::CURSOR_DOWN_Z); - send_key(interface_key::CURSOR_UP_Z); - last_x = cx; - last_y = cy; - last_z = vz; + if (my < scroll_buffer) + { + sendKey(interface_key::CURSOR_UP); + return; + } - return true; - } - } + if (my > gps->dimy - scroll_buffer) + { + sendKey(interface_key::CURSOR_DOWN); + return; } - else if (enabler->mouse_rbut) + } + + if (!live_view && !isInTrackableMode() && !DFHack::World::ReadPauseState()) + return; + + if (!tracking_enabled && isInTrackableMode()) + { + UIColor color = COLOR_GREEN; + int32_t x, y, z; + if (Gui::getDesignationCoords(x, y, z)) { - // Escape out of query mode - using namespace df::enums::ui_sidebar_mode; - if (ui->main.mode == QueryBuilding || ui->main.mode == BuildingItems || - ui->main.mode == ViewUnits || ui->main.mode == LookAround) + color = COLOR_WHITE; + if (ui->main.mode == df::ui_sidebar_mode::Zones || + ui->main.mode == df::ui_sidebar_mode::Stockpiles) { - while (ui->main.mode != Default) - { - enabler->mouse_rbut = 0; - send_key(df::interface_key::LEAVESCREEN); - } + auto dX = abs(x - mpos.x); + if (dX > 30) + color = COLOR_RED; + + auto dY = abs(y - mpos.y); + if (dY > 30) + color = COLOR_RED; } } + + OutputString(color, mx, my, "X"); + return; + } + + if (shouldTrack()) + { + if (delta_t <= scroll_delay && (mx < scroll_buffer || + mx > dims.menu_x1 - scroll_buffer || + my < scroll_buffer || + my > gps->dimy - scroll_buffer)) + { + return; + } + + last_t = enabler->clock; + moveCursor(mpos, false); } - return false; + if (dims.menu_x1 <= 0) + return; // No menu displayed + + if (!is_valid_pos(mpos) || isInTrackableMode()) + return; + + if (showing_dimensions) + return; + + // Display live query + auto ulist = get_units_at(mpos, false); + auto bld = Buildings::findAtTile(mpos); + auto ilist = get_items_at(mpos, false); + + int look_list = ulist.size() + ((bld) ? 1 : 0) + ilist.size() + 1; + set_to_limit(look_list, 8); + int disp_y = gps->dimy - look_list - 2; + + int c = 0; + for (auto it = ulist.begin(); it != ulist.end() && c < 8; it++, c++) + { + string label; + auto name = Units::getVisibleName(*it); + if (name->has_name) + label = Translation::TranslateName(name, false); + if (label.length() > 0) + label += ", "; + + label += Units::getProfessionName(*it); // Check animal type too + label = pad_string(label, look_width, false, true); + + OutputString(COLOR_WHITE, disp_x, disp_y, label, true, left_margin); + } + + for (auto it = ilist.begin(); it != ilist.end() && c < 8; it++, c++) + { + auto label = Items::getDescription(*it, 0, false); + label = pad_string(label, look_width, false, true); + OutputString(COLOR_YELLOW, disp_x, disp_y, label, true, left_margin); + } + + if (c > 7) + return; + + if (bld) + { + string label; + bld->getName(&label); + label = pad_string(label, look_width, false, true); + OutputString(COLOR_CYAN, disp_x, disp_y, label, true, left_margin); + } + + if (c > 7) + return; + + auto tt = Maps::getTileType(mpos); + OutputString(COLOR_BLUE, disp_x, disp_y, tileName(*tt), true, left_margin); } +}; - DEFINE_VMETHOD_INTERPOSE(void, feed, (set *input)) +IMPLEMENT_VMETHOD_INTERPOSE_PRIO(mousequery_hook, feed, 100); +IMPLEMENT_VMETHOD_INTERPOSE_PRIO(mousequery_hook, render, 100); + +static command_result mousequery_cmd(color_ostream &out, vector & parameters) +{ + bool show_help = false; + if (parameters.size() < 1) { - if (!handle_mouse(input)) - INTERPOSE_NEXT(feed)(input); + show_help = true; + } + else + { + auto cmd = toLower(parameters[0]); + auto state = (parameters.size() == 2) ? toLower(parameters[1]) : "-1"; + if (cmd[0] == 'v') + { + out << "MouseQuery" << endl << "Version: " << PLUGIN_VERSION << endl; + } + else if (cmd[0] == 'p') + { + plugin_enabled = (state == "enable"); + } + else if (cmd[0] == 'r') + { + rbutton_enabled = (state == "enable"); + } + else if (cmd[0] == 't') + { + tracking_enabled = (state == "enable"); + if (!tracking_enabled) + active_scrolling = false; + } + else if (cmd[0] == 'e') + { + active_scrolling = (state == "enable"); + if (active_scrolling) + tracking_enabled = true; + } + else if (cmd[0] == 'l') + { + live_view = (state == "enable"); + } + else if (cmd[0] == 'd') + { + auto l = atoi(state.c_str()); + if (l > 0 || state == "0") + scroll_delay = l; + else + out << "Current delay: " << scroll_delay << endl; + } + else + { + show_help = true; + } } -}; -IMPLEMENT_VMETHOD_INTERPOSE(mousequery_hook, feed); + if (show_help) + return CR_WRONG_USAGE; -DFHACK_PLUGIN("mousequery"); + return CR_OK; +} DFHACK_PLUGIN_IS_ENABLED(is_enabled); DFhackCExport command_result plugin_enable ( color_ostream &out, bool enable) @@ -248,9 +796,12 @@ DFhackCExport command_result plugin_enable ( color_ostream &out, bool enable) if (is_enabled != enable) { - last_x = last_y = last_z = -1; + last_clicked_x = last_clicked_y = last_clicked_z = -1; + last_pos_x = last_pos_y = last_pos_z = -1; + last_move_pos.x = last_move_pos.y = last_move_pos.z = -1; - if (!INTERPOSE_HOOK(mousequery_hook, feed).apply(enable)) + if (!INTERPOSE_HOOK(mousequery_hook, feed).apply(enable) || + !INTERPOSE_HOOK(mousequery_hook, render).apply(enable)) return CR_FAILURE; is_enabled = enable; @@ -261,7 +812,19 @@ DFhackCExport command_result plugin_enable ( color_ostream &out, bool enable) DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { - last_x = last_y = last_z = -1; + commands.push_back( + PluginCommand( + "mousequery", "Add mouse functionality to Dwarf Fortress", + mousequery_cmd, false, + "mousequery [plugin|rbutton|track|edge|live] [enabled|disabled]\n" + " plugin: enable/disable the entire plugin\n" + " rbutton: enable/disable right mouse button\n" + " track: enable/disable moving cursor in build and designation mode\n" + " edge: enable/disable active edge scrolling (when on, will also enable tracking)\n" + " live: enable/disable query view when unpaused\n\n" + "mousequery delay \n" + " Set delay when edge scrolling in tracking mode. Omit amount to display current setting.\n" + )); return CR_OK; } @@ -270,7 +833,9 @@ DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_chan { switch (event) { case SC_MAP_LOADED: - last_x = last_y = last_z = -1; + last_clicked_x = last_clicked_y = last_clicked_z = -1; + last_pos_x = last_pos_y = last_pos_z = -1; + last_move_pos.x = last_move_pos.y = last_move_pos.z = -1; break; default: break; From cba143c4478c2a0ee2b89c83a04f1c58ad65ab11 Mon Sep 17 00:00:00 2001 From: Anuradha Dissanayake Date: Sun, 4 May 2014 22:04:03 +1200 Subject: [PATCH 075/676] Add workshop profile search. --- NEWS | 1 + plugins/search.cpp | 48 ++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/NEWS b/NEWS index 8be141a4f..961708dc3 100644 --- a/NEWS +++ b/NEWS @@ -23,6 +23,7 @@ DFHack future - autotrade: adds "(Un)mark All" options to both panes of trade screen. - mousequery: several usability improvements. - mousequery: show live overlay (in menu area) of what's on the tile under the mouse cursor. + - search: workshop profile search added. Siege engine plugin: - engine quality and distance to target now affect accuracy diff --git a/plugins/search.cpp b/plugins/search.cpp index 0f519414b..e0213bf06 100644 --- a/plugins/search.cpp +++ b/plugins/search.cpp @@ -11,6 +11,7 @@ #include "df/viewscreen_layer_stockpilest.h" #include "df/viewscreen_layer_militaryst.h" #include "df/viewscreen_layer_noblelistst.h" +#include "df/viewscreen_layer_workshop_profilest.h" #include "df/viewscreen_tradegoodsst.h" #include "df/viewscreen_unitlistst.h" #include "df/viewscreen_buildinglistst.h" @@ -423,7 +424,7 @@ protected: virtual bool can_init(S *screen) { auto list = getLayerList(screen); - if (!is_list_valid(screen) || !list->active) + if (!is_list_valid(screen) || !list || !list->active) return false; return true; @@ -699,8 +700,8 @@ template V generic_search_hook ::module; #define IMPLEMENT_HOOKS_PRIO(screen, module, prio) \ typedef generic_search_hook module##_hook; \ - template<> IMPLEMENT_VMETHOD_INTERPOSE_PRIO(module##_hook, feed, 100); \ - template<> IMPLEMENT_VMETHOD_INTERPOSE_PRIO(module##_hook, render, 100) + template<> IMPLEMENT_VMETHOD_INTERPOSE_PRIO(module##_hook, feed, prio); \ + template<> IMPLEMENT_VMETHOD_INTERPOSE_PRIO(module##_hook, render, prio) // // END: Generic Search functionality @@ -920,7 +921,7 @@ private: }; -IMPLEMENT_HOOKS(df::viewscreen_storesst, stocks_search); +IMPLEMENT_HOOKS_PRIO(df::viewscreen_storesst, stocks_search, 100); // // END: Stocks screen search @@ -1048,7 +1049,9 @@ private: { // Block the keys if were searching if (!search_string.empty()) + { input->clear(); + } return false; } @@ -1081,6 +1084,9 @@ public: { make_text_dim(2, 37, 22); make_text_dim(42, gps->dimx-2, 22); + int32_t x = 2; + int32_t y = gps->dimy - 3; + OutputString(COLOR_YELLOW, x, y, "Note: Clear search to trade"); } } @@ -1120,6 +1126,9 @@ public: { make_text_dim(2, 37, 22); make_text_dim(42, gps->dimx-2, 22); + int32_t x = 42; + int32_t y = gps->dimy - 3; + OutputString(COLOR_YELLOW, x, y, "Note: Clear search to trade"); } } @@ -1432,6 +1441,36 @@ IMPLEMENT_HOOKS(df::viewscreen_layer_noblelistst, nobles_search); // END: Nobles search list // +// +// START: Workshop profiles search list +// +typedef layered_search profiles_search_base; +class profiles_search : public profiles_search_base +{ +public: + + string get_element_description(df::unit *element) const + { + return get_unit_description(element); + } + + void render() const + { + print_search_option(2, 23); + } + + vector *get_primary_list() + { + return &viewscreen->workers; + } +}; + +IMPLEMENT_HOOKS(df::viewscreen_layer_workshop_profilest, profiles_search); + +// +// END: Workshop profiles search list +// + // // START: Job list search @@ -1621,6 +1660,7 @@ DFHACK_PLUGIN_IS_ENABLED(is_enabled); HOOK_ACTION(pets_search_hook) \ HOOK_ACTION(military_search_hook) \ HOOK_ACTION(nobles_search_hook) \ + HOOK_ACTION(profiles_search_hook) \ HOOK_ACTION(annoucnement_search_hook) \ HOOK_ACTION(joblist_search_hook) \ HOOK_ACTION(burrow_search_hook) \ From 5a3065fa80fcf71a56207a3f68e731f8ab53be65 Mon Sep 17 00:00:00 2001 From: Anuradha Dissanayake Date: Sun, 4 May 2014 22:23:10 +1200 Subject: [PATCH 076/676] Use shifted keys for buildingplan so as not to interfere with QuickFort. --- plugins/buildingplan.cpp | 49 +++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/plugins/buildingplan.cpp b/plugins/buildingplan.cpp index 6bc1c5525..8b228b49e 100644 --- a/plugins/buildingplan.cpp +++ b/plugins/buildingplan.cpp @@ -38,7 +38,7 @@ using df::global::ui_build_selector; using df::global::world; DFHACK_PLUGIN("buildingplan"); -#define PLUGIN_VERSION 0.9 +#define PLUGIN_VERSION 0.12 struct MaterialDescriptor { @@ -58,16 +58,6 @@ struct MaterialDescriptor } }; -struct coord32_t -{ - int32_t x, y, z; - - df::coord get_coord16() const - { - return df::coord(x, y, z); - } -}; - DFhackCExport command_result plugin_shutdown ( color_ostream &out ) { return CR_OK; @@ -78,6 +68,7 @@ DFhackCExport command_result plugin_shutdown ( color_ostream &out ) #define SIDEBAR_WIDTH 30 static bool show_debugging = false; +static bool show_help = false; static void debug(const string &msg) { @@ -943,7 +934,7 @@ struct buildingplan_hook : public df::viewscreen_dwarfmodest if (isInPlannedBuildingPlacementMode()) { auto type = ui_build_selector->building_type; - if (input->count(interface_key::CUSTOM_P)) + if (input->count(interface_key::CUSTOM_SHIFT_P)) { planmode_enabled[type] = !planmode_enabled[type]; if (!planmode_enabled[type]) @@ -954,6 +945,14 @@ struct buildingplan_hook : public df::viewscreen_dwarfmodest } return true; } + else if (input->count(interface_key::CUSTOM_P) || + input->count(interface_key::CUSTOM_F) || + input->count(interface_key::CUSTOM_Q) || + input->count(interface_key::CUSTOM_D) || + input->count(interface_key::CUSTOM_N)) + { + show_help = true; + } if (is_planmode_enabled(type)) { @@ -983,7 +982,7 @@ struct buildingplan_hook : public df::viewscreen_dwarfmodest return true; } - else if (input->count(interface_key::CUSTOM_F)) + else if (input->count(interface_key::CUSTOM_SHIFT_F)) { if (!planner.inQuickFortMode()) { @@ -994,15 +993,15 @@ struct buildingplan_hook : public df::viewscreen_dwarfmodest planner.disableQuickfortMode(); } } - else if (input->count(interface_key::CUSTOM_M)) + else if (input->count(interface_key::CUSTOM_SHIFT_M)) { Screen::show(new ViewscreenChooseMaterial(planner.getDefaultItemFilterForType(type))); } - else if (input->count(interface_key::CUSTOM_Q)) + else if (input->count(interface_key::CUSTOM_SHIFT_Q)) { planner.cycleDefaultQuality(type); } - else if (input->count(interface_key::CUSTOM_D)) + else if (input->count(interface_key::CUSTOM_SHIFT_D)) { planner.getDefaultItemFilterForType(type)->decorated_only = !planner.getDefaultItemFilterForType(type)->decorated_only; @@ -1080,22 +1079,25 @@ struct buildingplan_hook : public df::viewscreen_dwarfmodest { int y = 23; - OutputToggleString(x, y, "Planning Mode", "p", is_planmode_enabled(type), true, left_margin); + if (show_help) + { + OutputString(COLOR_BROWN, x, y, "Note: "); + OutputString(COLOR_WHITE, x, y, "Use Shift-Keys here", true, left_margin); + } + OutputToggleString(x, y, "Planning Mode", "P", is_planmode_enabled(type), true, left_margin); if (is_planmode_enabled(type)) { - OutputToggleString(x, y, "Quickfort Mode", "f", planner.inQuickFortMode(), true, left_margin); + OutputToggleString(x, y, "Quickfort Mode", "F", planner.inQuickFortMode(), true, left_margin); auto filter = planner.getDefaultItemFilterForType(type); - OutputHotkeyString(x, y, "Min Quality: ", "q"); + OutputHotkeyString(x, y, "Min Quality: ", "Q"); OutputString(COLOR_BROWN, x, y, filter->getMinQuality(), true, left_margin); - OutputHotkeyString(x, y, "Decorated Only: ", "d"); - OutputString(COLOR_BROWN, x, y, - (filter->decorated_only) ? "Yes" : "No", true, left_margin); + OutputToggleString(x, y, "Decorated Only: ", "D", filter->decorated_only, true, left_margin); - OutputHotkeyString(x, y, "Material Filter:", "m", true, left_margin); + OutputHotkeyString(x, y, "Material Filter:", "M", true, left_margin); auto filter_descriptions = filter->getMaterialFilterAsVector(); for (auto it = filter_descriptions.begin(); it != filter_descriptions.end(); ++it) OutputString(COLOR_BROWN, x, y, " *" + *it, true, left_margin); @@ -1131,6 +1133,7 @@ struct buildingplan_hook : public df::viewscreen_dwarfmodest else { planner.in_dummmy_screen = false; + show_help = false; } } }; From e9c7d502e2f0904da6a25701cde25efffee8e7e0 Mon Sep 17 00:00:00 2001 From: Anuradha Dissanayake Date: Sun, 4 May 2014 22:45:48 +1200 Subject: [PATCH 077/676] dwarfmonitor: add screen to summarise preferences of fortress dwarfs. --- NEWS | 1 + plugins/dwarfmonitor.cpp | 607 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 580 insertions(+), 28 deletions(-) diff --git a/NEWS b/NEWS index 961708dc3..2b6470787 100644 --- a/NEWS +++ b/NEWS @@ -24,6 +24,7 @@ DFHack future - mousequery: several usability improvements. - mousequery: show live overlay (in menu area) of what's on the tile under the mouse cursor. - search: workshop profile search added. + - dwarfmonitor: add screen to summarise preferences of fortress dwarfs. Siege engine plugin: - engine quality and distance to target now affect accuracy diff --git a/plugins/dwarfmonitor.cpp b/plugins/dwarfmonitor.cpp index 2776227c5..be271750a 100644 --- a/plugins/dwarfmonitor.cpp +++ b/plugins/dwarfmonitor.cpp @@ -17,6 +17,29 @@ #include "modules/Maps.h" #include "df/activity_event.h" #include "df/activity_entry.h" +#include "df/unit_preference.h" +#include "df/unit_soul.h" +#include "df/item_type.h" + +#include "df/itemdef_weaponst.h" +#include "df/itemdef_trapcompst.h" +#include "df/itemdef_toyst.h" +#include "df/itemdef_toolst.h" +#include "df/itemdef_instrumentst.h" +#include "df/itemdef_armorst.h" +#include "df/itemdef_ammost.h" +#include "df/itemdef_siegeammost.h" +#include "df/itemdef_glovesst.h" +#include "df/itemdef_shoesst.h" +#include "df/itemdef_shieldst.h" +#include "df/itemdef_helmst.h" +#include "df/itemdef_pantsst.h" +#include "df/itemdef_foodst.h" +#include "df/trapcomp_flags.h" +#include "df/creature_raw.h" +#include "df/world_raws.h" +#include "df/descriptor_shape.h" +#include "df/descriptor_color.h" using std::deque; @@ -25,7 +48,7 @@ using df::global::ui; typedef int16_t activity_type; -#define PLUGIN_VERSION 0.5 +#define PLUGIN_VERSION 0.8 #define DAY_TICKS 1200 #define DELTA_TICKS 100 @@ -48,6 +71,36 @@ static map> work_history; static int misery[] = { 0, 0, 0, 0, 0, 0, 0 }; static bool misery_upto_date = false; +static color_value monitor_colors[] = +{ + COLOR_LIGHTRED, + COLOR_RED, + COLOR_YELLOW, + COLOR_WHITE, + COLOR_CYAN, + COLOR_LIGHTBLUE, + COLOR_LIGHTGREEN +}; + +static int get_happiness_cat(df::unit *unit) +{ + int happy = unit->status.happiness; + if (happy == 0) // miserable + return 0; + else if (happy <= 25) // very unhappy + return 1; + else if (happy <= 50) // unhappy + return 2; + else if (happy <= 75) // fine + return 3; + else if (happy <= 125) // quite content + return 4; + else if (happy <= 150) // happy + return 5; + else // ecstatic + return 6; +} + static int get_max_history() { return ticks_per_day * max_history_days; @@ -129,6 +182,7 @@ static string getActivityLabel(const activity_type activity) return label; } + class ViewscreenDwarfStats : public dfhack_viewscreen { public: @@ -981,6 +1035,520 @@ private: } }; + +struct preference_map +{ + df::unit_preference pref; + vector dwarves; + string label; + + string getItemLabel() + { + df::world_raws::T_itemdefs &defs = df::global::world->raws.itemdefs; + label = ENUM_ATTR_STR(item_type, caption, pref.item_type); + switch (pref.item_type) + { + case (df::item_type::WEAPON): + label = defs.weapons[pref.item_subtype]->name_plural; + break; + case (df::item_type::TRAPCOMP): + label = defs.trapcomps[pref.item_subtype]->name_plural; + break; + case (df::item_type::TOY): + label = defs.toys[pref.item_subtype]->name_plural; + break; + case (df::item_type::TOOL): + label = defs.tools[pref.item_subtype]->name_plural; + break; + case (df::item_type::INSTRUMENT): + label = defs.instruments[pref.item_subtype]->name_plural; + break; + case (df::item_type::ARMOR): + label = defs.armor[pref.item_subtype]->name_plural; + break; + case (df::item_type::AMMO): + label = defs.ammo[pref.item_subtype]->name_plural; + break; + case (df::item_type::SIEGEAMMO): + label = defs.siege_ammo[pref.item_subtype]->name_plural; + break; + case (df::item_type::GLOVES): + label = defs.gloves[pref.item_subtype]->name_plural; + break; + case (df::item_type::SHOES): + label = defs.shoes[pref.item_subtype]->name_plural; + break; + case (df::item_type::SHIELD): + label = defs.shields[pref.item_subtype]->name_plural; + break; + case (df::item_type::HELM): + label = defs.helms[pref.item_subtype]->name_plural; + break; + case (df::item_type::PANTS): + label = defs.pants[pref.item_subtype]->name_plural; + break; + case (df::item_type::FOOD): + label = defs.food[pref.item_subtype]->name; + break; + + default: + break; + } + + return label; + } + + void makeLabel() + { + label = ""; + + typedef df::unit_preference::T_type T_type; + df::world_raws &raws = world->raws; + switch (pref.type) + { + case (T_type::LikeCreature): + { + label = "Creature :"; + auto creature = df::creature_raw::find(pref.creature_id); + if (creature) + label += creature->name[1]; + break; + } + + case (T_type::HateCreature): + { + label = "Hates :"; + auto creature = df::creature_raw::find(pref.creature_id); + if (creature) + label += creature->name[1]; + break; + } + + case (T_type::LikeItem): + label = "Item :" + getItemLabel(); + break; + + case (T_type::LikeFood): + { + label = "Food :"; + if (pref.matindex < 0 || pref.item_type == item_type::MEAT) + { + auto index = (pref.item_type == item_type::FISH) ? pref.mattype : pref.matindex; + if (index > 0) + { + auto creature = df::creature_raw::find(index); + if (creature) + label += creature->name[0]; + } + else + { + label += "Invalid"; + } + + break; + } + } + + case (T_type::LikeMaterial): + { + if (label.length() == 0) + label += "Material :"; + MaterialInfo matinfo(pref.mattype, pref.matindex); + if (pref.type == T_type::LikeFood && pref.item_type == item_type::PLANT) + { + label += matinfo.material->prefix; + } + else + label += matinfo.toString(); + + break; + } + + case (T_type::LikePlant): + { + df::plant_raw *p = raws.plants.all[pref.plant_id]; + label += "Plant :" + p->name_plural; + break; + } + + case (T_type::LikeShape): + label += "Shape :" + raws.language.shapes[pref.shape_id]->name_plural; + break; + + case (T_type::LikeTree): + { + df::plant_raw *p = raws.plants.all[pref.plant_id]; + label += "Tree :" + p->name_plural; + break; + } + + case (T_type::LikeColor): + label += "Color :" + raws.language.colors[pref.color_id]->name; + break; + } + } +}; + + +class ViewscreenPreferences : public dfhack_viewscreen +{ +public: + ViewscreenPreferences() + { + preferences_column.multiselect = false; + preferences_column.auto_select = true; + preferences_column.setTitle("Preference"); + preferences_column.bottom_margin = 3; + preferences_column.search_margin = 35; + + dwarf_column.multiselect = false; + dwarf_column.auto_select = true; + dwarf_column.allow_null = true; + dwarf_column.setTitle("Units with Preference"); + dwarf_column.bottom_margin = 3; + dwarf_column.search_margin = 35; + + populatePreferencesColumn(); + } + + void populatePreferencesColumn() + { + selected_column = 0; + + auto last_selected_index = preferences_column.highlighted_index; + preferences_column.clear(); + preference_totals.clear(); + + for (auto iter = world->units.active.begin(); iter != world->units.active.end(); iter++) + { + df::unit* unit = *iter; + if (!Units::isCitizen(unit)) + continue; + + if (DFHack::Units::isDead(unit)) + continue; + + if (!unit->status.current_soul) + continue; + + for (auto it = unit->status.current_soul->preferences.begin(); + it != unit->status.current_soul->preferences.end(); + it++) + { + auto pref = *it; + if (!pref->active) + continue; + bool foundInStore = false; + for (size_t pref_index = 0; pref_index < preferences_store.size(); pref_index++) + { + if (isMatchingPreference(preferences_store[pref_index].pref, *pref)) + { + foundInStore = true; + preferences_store[pref_index].dwarves.push_back(unit); + } + } + + if (!foundInStore) + { + size_t pref_index = preferences_store.size(); + preferences_store.resize(pref_index + 1); + preferences_store[pref_index].pref = *pref; + preferences_store[pref_index].dwarves.push_back(unit); + } + } + } + + for (size_t i = 0; i < preferences_store.size(); i++) + { + preference_totals[i] = preferences_store[i].dwarves.size(); + } + + vector> rev_vec(preference_totals.begin(), preference_totals.end()); + sort(rev_vec.begin(), rev_vec.end(), less_second()); + + for (auto rev_it = rev_vec.begin(); rev_it != rev_vec.end(); rev_it++) + { + auto pref_index = rev_it->first; + preferences_store[pref_index].makeLabel(); + + string label = pad_string(int_to_string(rev_it->second), 3); + label += " "; + label += preferences_store[pref_index].label; + ListEntry elem(label, pref_index, "", getItemColor(preferences_store[pref_index].pref.type)); + preferences_column.add(elem); + } + + dwarf_column.left_margin = preferences_column.fixWidth() + 2; + preferences_column.filterDisplay(); + preferences_column.setHighlight(last_selected_index); + populateDwarfColumn(); + } + + bool isMatchingPreference(df::unit_preference &lhs, df::unit_preference &rhs) + { + if (lhs.type != rhs.type) + return false; + + typedef df::unit_preference::T_type T_type; + switch (lhs.type) + { + case (T_type::LikeCreature): + if (lhs.creature_id != rhs.creature_id) + return false; + break; + + case (T_type::HateCreature): + if (lhs.creature_id != rhs.creature_id) + return false; + break; + + case (T_type::LikeFood): + if (lhs.item_type != rhs.item_type) + return false; + if (lhs.mattype != rhs.mattype || lhs.matindex != rhs.matindex) + return false; + break; + + case (T_type::LikeItem): + if (lhs.item_type != rhs.item_type || lhs.item_subtype != rhs.item_subtype) + return false; + break; + + case (T_type::LikeMaterial): + if (lhs.mattype != rhs.mattype || lhs.matindex != rhs.matindex) + return false; + break; + + case (T_type::LikePlant): + if (lhs.plant_id != rhs.plant_id) + return false; + break; + + case (T_type::LikeShape): + if (lhs.shape_id != rhs.shape_id) + return false; + break; + + case (T_type::LikeTree): + if (lhs.item_type != rhs.item_type) + return false; + break; + + case (T_type::LikeColor): + if (lhs.color_id != rhs.color_id) + return false; + break; + + default: + return false; + } + + return true; + } + + UIColor getItemColor(const df::unit_preference::T_type &type) const + { + typedef df::unit_preference::T_type T_type; + switch (type) + { + case (T_type::LikeCreature): + return COLOR_WHITE; + + case (T_type::HateCreature): + return COLOR_LIGHTRED; + + case (T_type::LikeFood): + return COLOR_GREEN; + + case (T_type::LikeItem): + return COLOR_YELLOW; + + case (T_type::LikeMaterial): + return COLOR_CYAN; + + case (T_type::LikePlant): + return COLOR_BROWN; + + case (T_type::LikeShape): + return COLOR_BLUE; + + case (T_type::LikeTree): + return COLOR_BROWN; + + case (T_type::LikeColor): + return COLOR_BLUE; + + default: + return false; + } + + return true; + } + + void populateDwarfColumn() + { + dwarf_column.clear(); + if (preferences_column.getDisplayListSize() > 0) + { + auto selected_preference = preferences_column.getFirstSelectedElem(); + for (auto dfit = preferences_store[selected_preference].dwarves.begin(); + dfit != preferences_store[selected_preference].dwarves.end(); + dfit++) + { + string label = getUnitName(*dfit); + auto happy = get_happiness_cat(*dfit); + UIColor color = monitor_colors[happy]; + switch (happy) + { + case 0: + label += " (miserable)"; + break; + + case 1: + label += " (very unhappy)"; + break; + + case 2: + label += " (unhappy)"; + break; + + case 3: + label += " (fine)"; + break; + + case 4: + label += " (quite content)"; + break; + + case 5: + label += " (happy)"; + break; + + case 6: + label += " (ecstatic)"; + break; + } + + ListEntry elem(label, *dfit, "", color); + dwarf_column.add(elem); + } + } + + dwarf_column.clearSearch(); + dwarf_column.setHighlight(0); + } + + void feed(set *input) + { + bool key_processed = false; + switch (selected_column) + { + case 0: + key_processed = preferences_column.feed(input); + break; + case 1: + key_processed = dwarf_column.feed(input); + break; + } + + if (key_processed) + { + if (selected_column == 0 && preferences_column.feed_changed_highlight) + { + populateDwarfColumn(); + } + + return; + } + + if (input->count(interface_key::LEAVESCREEN)) + { + input->clear(); + Screen::dismiss(this); + return; + } + else if (input->count(interface_key::CUSTOM_SHIFT_Z)) + { + df::unit *selected_unit = (selected_column == 1) ? dwarf_column.getFirstSelectedElem() : nullptr; + if (selected_unit) + { + input->clear(); + Screen::dismiss(this); + Gui::resetDwarfmodeView(true); + send_key(interface_key::D_VIEWUNIT); + move_cursor(selected_unit->pos); + } + } + else if (input->count(interface_key::CURSOR_LEFT)) + { + --selected_column; + validateColumn(); + } + else if (input->count(interface_key::CURSOR_RIGHT)) + { + ++selected_column; + validateColumn(); + } + else if (enabler->tracking_on && enabler->mouse_lbut) + { + if (preferences_column.setHighlightByMouse()) + { + selected_column = 0; + populateDwarfColumn(); + } + else if (dwarf_column.setHighlightByMouse()) + selected_column = 1; + + enabler->mouse_lbut = enabler->mouse_rbut = 0; + } + } + + void render() + { + if (Screen::isDismissed(this)) + return; + + dfhack_viewscreen::render(); + + Screen::clear(); + Screen::drawBorder(" Dwarf Preferences "); + + preferences_column.display(selected_column == 0); + dwarf_column.display(selected_column == 1); + + int32_t y = gps->dimy - 3; + int32_t x = 2; + OutputHotkeyString(x, y, "Leave", "Esc"); + + x += 2; + OutputHotkeyString(x, y, "Zoom Unit", "Shift-Z"); + } + + std::string getFocusString() { return "dwarfmonitor_preferences"; } + +private: + ListColumn preferences_column; + ListColumn dwarf_column; + int selected_column; + + map preference_totals; + + vector preferences_store; + + void validateColumn() + { + set_to_limit(selected_column, 1); + } + + void resize(int32_t x, int32_t y) + { + dfhack_viewscreen::resize(x, y); + preferences_column.resize(); + dwarf_column.resize(); + } +}; + + static void open_stats_srceen() { Screen::show(new ViewscreenFortStats()); @@ -1045,21 +1613,7 @@ static void update_dwarf_stats(bool is_paused) if (monitor_misery) { - int happy = unit->status.happiness; - if (happy == 0) // miserable - misery[0]++; - else if (happy <= 25) // very unhappy - misery[1]++; - else if (happy <= 50) // unhappy - misery[2]++; - else if (happy <= 75) // fine - misery[3]++; - else if (happy <= 125) // quite content - misery[4]++; - else if (happy <= 150) // happy - misery[5]++; - else // ecstatic - misery[6]++; + misery[get_happiness_cat(unit)]++; } if (!monitor_jobs || is_paused) @@ -1094,6 +1648,7 @@ static void update_dwarf_stats(bool is_paused) } } + DFhackCExport command_result plugin_onupdate (color_ostream &out) { if (!monitor_jobs && !monitor_misery) @@ -1125,17 +1680,6 @@ DFhackCExport command_result plugin_onupdate (color_ostream &out) return CR_OK; } -static color_value monitor_colors[] = -{ - COLOR_LIGHTRED, - COLOR_RED, - COLOR_YELLOW, - COLOR_WHITE, - COLOR_CYAN, - COLOR_LIGHTBLUE, - COLOR_LIGHTGREEN -}; - struct dwarf_monitor_hook : public df::viewscreen_dwarfmodest { typedef df::viewscreen_dwarfmodest interpose_base; @@ -1265,6 +1809,11 @@ static command_result dwarfmonitor_cmd(color_ostream &out, vector & par if(Maps::IsValid()) Screen::show(new ViewscreenFortStats()); } + else if (cmd == 'p' || cmd == 'P') + { + if(Maps::IsValid()) + Screen::show(new ViewscreenPreferences()); + } else { show_help = true; @@ -1307,7 +1856,9 @@ DFhackCExport command_result plugin_init(color_ostream &out, std::vector \n" " as above\n\n" "dwarfmonitor stats\n" - " Show statistics summary\n\n" + " Show statistics summary\n" + "dwarfmonitor prefs\n" + " Show dwarf preferences summary\n\n" )); return CR_OK; From 1f93b8e144ce2941d66afc1706401a4c2aeb6bf9 Mon Sep 17 00:00:00 2001 From: Anuradha Dissanayake Date: Sun, 4 May 2014 23:43:52 +1200 Subject: [PATCH 078/676] getplants: add autochop function to automate woodcutting. --- NEWS | 1 + plugins/getplants.cpp | 684 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 682 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS index 2b6470787..4c0ec2797 100644 --- a/NEWS +++ b/NEWS @@ -25,6 +25,7 @@ DFHack future - mousequery: show live overlay (in menu area) of what's on the tile under the mouse cursor. - search: workshop profile search added. - dwarfmonitor: add screen to summarise preferences of fortress dwarfs. + - getplants: add autochop function to automate woodcutting. Siege engine plugin: - engine quality and distance to target now affect accuracy diff --git a/plugins/getplants.cpp b/plugins/getplants.cpp index eaa8077f2..5a5337f45 100644 --- a/plugins/getplants.cpp +++ b/plugins/getplants.cpp @@ -1,17 +1,32 @@ // (un)designate matching plants for gathering/cutting +#include "uicommon.h" + #include "Core.h" #include "Console.h" #include "Export.h" #include "PluginManager.h" - #include "DataDefs.h" #include "TileTypes.h" + #include "df/world.h" #include "df/map_block.h" #include "df/tile_dig_designation.h" #include "df/plant_raw.h" #include "df/plant.h" +#include "df/ui.h" +#include "df/burrow.h" +#include "df/item_flags.h" +#include "df/item.h" +#include "df/items_other_id.h" +#include "df/viewscreen_dwarfmodest.h" + +#include "modules/Screen.h" +#include "modules/Maps.h" +#include "modules/Burrows.h" +#include "modules/World.h" +#include "modules/MapCache.h" +#include "modules/Gui.h" #include @@ -22,6 +37,601 @@ using namespace DFHack; using namespace df::enums; using df::global::world; +using df::global::ui; + +#define PLUGIN_VERSION 0.3 +DFHACK_PLUGIN("getplants"); + + +static bool autochop_enabled = false; +static int min_logs, max_logs; +static bool wait_for_threshold; + +static PersistentDataItem config_autochop; + +struct WatchedBurrow +{ + int32_t id; + df::burrow *burrow; + + WatchedBurrow(df::burrow *burrow) : burrow(burrow) + { + id = burrow->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; + } + + return burrow_ids.str(); + } + + void clear() + { + burrows.clear(); + } + + void add(const int32_t id) + { + if (!isValidBurrow(id)) + return; + + WatchedBurrow wb(getBurrow(id)); + burrows.push_back(wb); + } + + void add(const string burrow_ids) + { + istringstream iss(burrow_ids); + int id; + while (iss >> id) + { + add(id); + } + } + + bool isValidPos(const df::coord &plant_pos) + { + validate(); + if (!burrows.size()) + return true; + + for (auto it = burrows.begin(); it != burrows.end(); it++) + { + df::burrow *burrow = it->burrow; + if (Burrows::isAssignedTile(burrow, plant_pos)) + return true; + } + + return false; + } + + bool isBurrowWatched(const df::burrow *burrow) + { + validate(); + for (auto it = burrows.begin(); it != burrows.end(); it++) + { + if (it->burrow == burrow) + return true; + } + + return false; + } + +private: + static bool isValidBurrow(const int32_t id) + { + return getBurrow(id); + } + + static df::burrow *getBurrow(const int32_t id) + { + return df::burrow::find(id); + } + + void validate() + { + for (auto it = burrows.begin(); it != burrows.end();) + { + if (!isValidBurrow(it->id)) + it = burrows.erase(it); + else + ++it; + } + } + + vector burrows; +}; + +static WatchedBurrows watchedBurrows; + +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; +} + +static void initialize() +{ + watchedBurrows.clear(); + autochop_enabled = false; + min_logs = 80; + max_logs = 100; + wait_for_threshold = false; + + 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); + } + else + { + config_autochop = World::AddPersistentData("autochop/config"); + if (config_autochop.isValid()) + save_config(); + } +} + +static int do_chop_designation(bool chop, bool count_only) +{ + int count = 0; + for (size_t i = 0; i < world->map.map_blocks.size(); i++) + { + df::map_block *cur = world->map.map_blocks[i]; + for (size_t j = 0; j < cur->plants.size(); j++) + { + const df::plant *plant = cur->plants[j]; + int x = plant->pos.x % 16; + int y = plant->pos.y % 16; + + if (plant->flags.bits.is_shrub) + continue; + if (cur->designation[x][y].bits.hidden) + continue; + + df::tiletype_shape shape = tileShape(cur->tiletype[x][y]); + if (shape != tiletype_shape::TREE) + continue; + + if (!count_only && !watchedBurrows.isValidPos(plant->pos)) + continue; + + bool dirty = false; + if (chop && cur->designation[x][y].bits.dig == tile_dig_designation::No) + { + if (count_only) + { + ++count; + } + else + { + cur->designation[x][y].bits.dig = tile_dig_designation::Default; + dirty = true; + } + } + + if (!chop && cur->designation[x][y].bits.dig == tile_dig_designation::Default) + { + if (count_only) + { + ++count; + } + else + { + cur->designation[x][y].bits.dig = tile_dig_designation::No; + dirty = true; + } + } + + if (dirty) + { + cur->flags.bits.designated = true; + ++count; + } + } + } + + return count; +} + +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()) + { + case general_ref_type::CONTAINED_IN_ITEM: + return false; + + case general_ref_type::UNIT_HOLDER: + return false; + + case general_ref_type::BUILDING_HOLDER: + return false; + + default: + break; + } + } + + 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) + { + // Ignore any items assigned to a job + return false; + } + } + + return true; +} + +static int get_log_count() +{ + 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; + +#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++) + { + df::item *item = items[i]; + + if (item->getType() != item_type::WOOD) + continue; + + if (item->flags.whole & bad_flags.whole) + continue; + + if (!is_valid_item(item)) + 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); + } + else + { + do_chop_designation(true, false); + } + } +} + +class ViewscreenAutochop : public dfhack_viewscreen +{ +public: + ViewscreenAutochop() + { + burrows_column.multiselect = true; + burrows_column.setTitle("Burrows"); + burrows_column.bottom_margin = 3; + burrows_column.allow_search = false; + burrows_column.text_clip_at = 30; + + populateBurrowsColumn(); + message.clear(); + } + + void populateBurrowsColumn() + { + selected_column = 0; + + auto last_selected_index = burrows_column.highlighted_index; + burrows_column.clear(); + + for (auto iter = ui->burrows.list.begin(); iter != ui->burrows.list.end(); iter++) + { + df::burrow* burrow = *iter; + auto elem = ListEntry(burrow->name, burrow); + elem.selected = watchedBurrows.isBurrowWatched(burrow); + burrows_column.add(elem); + } + + burrows_column.fixWidth(); + burrows_column.filterDisplay(); + + current_log_count = get_log_count(); + marked_tree_count = do_chop_designation(false, true); + } + + void change_min_logs(int delta) + { + if (!autochop_enabled) + return; + + min_logs += delta; + if (min_logs < 0) + min_logs = 0; + if (min_logs > max_logs) + max_logs = min_logs; + } + + void change_max_logs(int delta) + { + if (!autochop_enabled) + return; + + max_logs += delta; + if (max_logs < min_logs) + min_logs = max_logs; + } + + void feed(set *input) + { + 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); + } + 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); + } + 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 (enabler->tracking_on && enabler->mouse_lbut) + { + if (burrows_column.setHighlightByMouse()) + { + selected_column = 0; + } + + enabler->mouse_lbut = enabler->mouse_rbut = 0; + } + } + + 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); + } + 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; + OutputToggleString(x, y, "Autochop", "a", autochop_enabled, true, left_margin); + OutputHotkeyString(x, y, "Designate Now", "d", true, left_margin); + OutputHotkeyString(x, y, "Undesignate Now", "u", true, left_margin); + OutputHotkeyString(x, y, "Toggle Burrow", "Enter", true, left_margin); + if (autochop_enabled) + { + OutputLabelString(x, y, "Min Logs", "hjHJ", int_to_string(min_logs), true, left_margin); + OutputLabelString(x, y, "Max Logs", "klKL", int_to_string(max_logs), true, left_margin); + } + + ++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; + MapExtras::MapCache mcache; + string message; + + void validateColumn() + { + set_to_limit(selected_column, 0); + } + + 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; + + bool isInDesignationMenu() + { + using namespace df::enums::ui_sidebar_mode; + return (ui->main.mode == DesignateChopTrees); + } + + void sendKey(const df::interface_key &key) + { + set tmp; + tmp.insert(key); + INTERPOSE_NEXT(feed)(&tmp); + } + + DEFINE_VMETHOD_INTERPOSE(void, feed, (set *input)) + { + if (isInDesignationMenu() && input->count(interface_key::CUSTOM_C)) + { + sendKey(interface_key::LEAVESCREEN); + Screen::show(new ViewscreenAutochop()); + } + else + { + INTERPOSE_NEXT(feed)(input); + } + } + + DEFINE_VMETHOD_INTERPOSE(void, render, ()) + { + INTERPOSE_NEXT(render)(); + + auto dims = Gui::getDwarfmodeViewDims(); + if (dims.menu_x1 <= 0) + return; + + df::ui_sidebar_mode d = ui->main.mode; + if (!isInDesignationMenu()) + return; + + int left_margin = dims.menu_x1 + 1; + int x = left_margin; + int y = 26; + OutputHotkeyString(x, y, "Autochop Dashboard", "c"); + } +}; + +IMPLEMENT_VMETHOD_INTERPOSE_PRIO(autochop_hook, feed, 100); +IMPLEMENT_VMETHOD_INTERPOSE_PRIO(autochop_hook, render, 100); + command_result df_getplants (color_ostream &out, vector & parameters) { @@ -45,6 +655,17 @@ command_result df_getplants (color_ostream &out, vector & parameters) exclude = true; else if(parameters[i] == "-a") all = true; + else if(parameters[i] == "debug") + { + save_config(); + } + else if(parameters[i] == "autochop") + { + if(Maps::IsValid()) + Screen::show(new ViewscreenAutochop()); + + return CR_OK; + } else plantNames.insert(parameters[i]); } @@ -148,7 +769,48 @@ command_result df_getplants (color_ostream &out, vector & parameters) return CR_OK; } -DFHACK_PLUGIN("getplants"); +DFhackCExport command_result plugin_onupdate (color_ostream &out) +{ + if (!autochop_enabled) + return CR_OK; + + if(!Maps::IsValid()) + return CR_OK; + + static decltype(world->frame_counter) last_frame_count = 0; + + if (DFHack::World::ReadPauseState()) + return CR_OK; + + if (world->frame_counter - last_frame_count < 1200) // Check every day + return CR_OK; + + last_frame_count = world->frame_counter; + + do_autochop(); + + return CR_OK; +} + +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(autochop_hook, feed).apply(enable) || + !INTERPOSE_HOOK(autochop_hook, render).apply(enable)) + return CR_FAILURE; + + is_enabled = enable; + initialize(); + } + + return CR_OK; +} DFhackCExport command_result plugin_init ( color_ostream &out, vector &commands) { @@ -164,8 +826,11 @@ DFhackCExport command_result plugin_init ( color_ostream &out, vector Date: Sun, 4 May 2014 21:00:16 +0300 Subject: [PATCH 079/676] Updated readme, NEWS and changed where rendermax looks for it's config. --- NEWS | 2 ++ Readme.rst | 20 ++++++++++++++++++++ images/rendermax.png | Bin 0 -> 10672 bytes plugins/rendermax/CMakeLists.txt | 2 +- plugins/rendermax/renderer_light.cpp | 13 +++++++++++-- 5 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 images/rendermax.png diff --git a/NEWS b/NEWS index 2a3bbb39d..e53e1aa0c 100644 --- a/NEWS +++ b/NEWS @@ -10,6 +10,8 @@ DFHack future Misc improvements: - digfort: improved csv parsing, add start() comment handling - exterminate: allow specifying a caste (exterminate gob:male) + New plugins: + - rendermax: replace the renderer with something else. Most interesting is "rendermax light"- a lighting engine for df. DFHack v0.34.11-r4 diff --git a/Readme.rst b/Readme.rst index 29f0a71fa..9bc3583b9 100644 --- a/Readme.rst +++ b/Readme.rst @@ -329,6 +329,26 @@ Options: The building must be one of stockpile, workshop, furnace, trap, siege engine or an activity zone. +rendermax +--------- +A collection of renderer replacing/enhancing filters. For better effect try changing the +black color in palette to non totally black. For more info see thread in forums: +http://www.bay12forums.com/smf/index.php?topic=128487.0 + +Options: + + :rendermax trippy: Randomizes each tiles color. Used for fun mainly. + :rendermax light: Enable lighting engine. + :rendermax light reload: Reload the settings file. + :rendermax light sun |cycle: Set time to (in hours) or set it to df time cycle. + :rendermax occlusionON|occlusionOFF: Show debug occlusion info. + :rendermax disable: Disable any filter that is enabled. + +An image showing lava and dragon breath. Not pictured here: sunlight, shining items/plants, +materials that color the light etc... + +.. image:: images/rendermax.png + Adventure mode ============== diff --git a/images/rendermax.png b/images/rendermax.png new file mode 100644 index 0000000000000000000000000000000000000000..942f4cc28aaa8547eb016c36bebe676a3212b701 GIT binary patch literal 10672 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 literal 0 HcmV?d00001 diff --git a/plugins/rendermax/CMakeLists.txt b/plugins/rendermax/CMakeLists.txt index ef6621dc1..ba589732d 100644 --- a/plugins/rendermax/CMakeLists.txt +++ b/plugins/rendermax/CMakeLists.txt @@ -38,4 +38,4 @@ ENDIF(UNIX) # this makes sure all the stuff is put in proper places and linked to dfhack DFHACK_PLUGIN(rendermax ${PROJECT_SRCS} LINK_LIBRARIES ${PROJECT_LIBS}) install(FILES rendermax.lua - DESTINATION ${DFHACK_DATA_DESTINATION}) + DESTINATION ${DFHACK_DATA_DESTINATION}/raw) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index 1ab57caa3..395e12c14 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -1184,8 +1184,17 @@ void lightingEngineViewscreen::defaultSettings() } void lightingEngineViewscreen::loadSettings() { - - const std::string settingsfile="rendermax.lua"; + std::string rawFolder; + if(df::global::world->cur_savegame.save_dir!="") + { + rawFolder= "data/save/" + (df::global::world->cur_savegame.save_dir) + "/raw/"; + } + else + { + rawFolder= "raw/"; + } + const std::string settingsfile=rawFolder+"rendermax.lua"; + CoreSuspender lock; color_ostream_proxy out(Core::getInstance().getConsole()); From 723cfcf28e7427dd7218397c8b8189d0b69170e7 Mon Sep 17 00:00:00 2001 From: Eric Wald Date: Sun, 4 May 2014 12:35:10 -0600 Subject: [PATCH 080/676] Attempting to repair an order object leak. Now frees manager_order objects in the reaction_list when the plugin is disabled or the world unloaded. However, there may still be orders leaked elsewhere. --- plugins/lua/stockflow.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/lua/stockflow.lua b/plugins/lua/stockflow.lua index 510298a06..d2add16c9 100644 --- a/plugins/lua/stockflow.lua +++ b/plugins/lua/stockflow.lua @@ -30,6 +30,10 @@ end -- Clear all caches. -- Called when a world is loaded, or when the plugin is disabled. function clear_caches() + -- Free the C++ objects in the reaction list. + for _, value in ipairs(reaction_list) do + value.order:delete() + end reaction_list = {} saved_orders = {} jobs_to_create = {} From 2d5f7e40220bfe4b88c8f7de18a2260c20da8665 Mon Sep 17 00:00:00 2001 From: Eric Wald Date: Sun, 4 May 2014 12:52:59 -0600 Subject: [PATCH 081/676] Magic number reduction: Persistent storage Replaces explicit indices into the ints field of Stockflow persistent storage objects with enumerated values. --- plugins/lua/stockflow.lua | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/plugins/lua/stockflow.lua b/plugins/lua/stockflow.lua index d2add16c9..c18138ced 100644 --- a/plugins/lua/stockflow.lua +++ b/plugins/lua/stockflow.lua @@ -19,6 +19,12 @@ triggers = { {name = "Never"}, } +entry_ints = { + stockpile_id = 1, + order_number = 2, + trigger_number = 3, +} + -- Populate the reaction and stockpile order lists. -- To be called whenever a world is loaded. function initialize_world() @@ -40,7 +46,7 @@ function clear_caches() end function trigger_name(cache) - local trigger = triggers[cache.entry.ints[3]] + local trigger = triggers[cache.entry.ints[entry_ints.trigger_number]] return trigger and trigger.name or "Never" end @@ -97,7 +103,7 @@ end function toggle_trigger(sp) local saved = saved_orders[sp.id] if saved then - saved.entry.ints[3] = (saved.entry.ints[3] % #triggers) + 1 + saved.entry.ints[entry_ints.trigger_number] = (saved.entry.ints[entry_ints.trigger_number] % #triggers) + 1 saved.entry:save() end end @@ -108,7 +114,7 @@ function collect_orders() if entries then local stockpiles = df.global.world.buildings.other.STOCKPILE for _, entry in ipairs(entries) do - local spid = entry.ints[1] + local spid = entry.ints[entry_ints.stockpile_id] local stockpile = utils.binsearch(stockpiles, spid, "id") if stockpile then -- Todo: What if entry.value ~= reaction_list[order_number].name? @@ -846,7 +852,7 @@ function store_order(stockpile, order_number) local saved = saved_orders[stockpile.id] if saved then saved.entry.value = name - saved.entry.ints[2] = order_number + saved.entry.ints[entry_ints.order_number] = order_number saved.entry:save() else saved_orders[stockpile.id] = { @@ -977,9 +983,9 @@ end function check_stockpiles(verbose) local result = {} for _, spec in pairs(saved_orders) do - local trigger = triggers[spec.entry.ints[3]] + local trigger = triggers[spec.entry.ints[entry_ints.trigger_number]] if trigger and trigger.divisor then - local reaction = spec.entry.ints[2] + local reaction = spec.entry.ints[entry_ints.order_number] local filled, empty = check_pile(spec.stockpile, verbose) local amount = trigger.filled and filled or empty amount = (amount - (amount % trigger.divisor)) / trigger.divisor From bdb22235af87be0f8561c23ac3cea6711e943ee6 Mon Sep 17 00:00:00 2001 From: Eric Wald Date: Sun, 4 May 2014 15:02:12 -0600 Subject: [PATCH 082/676] Magic number reduction: Job types --- plugins/lua/stockflow.lua | 361 ++++++++++++++++++++------------------ 1 file changed, 189 insertions(+), 172 deletions(-) diff --git a/plugins/lua/stockflow.lua b/plugins/lua/stockflow.lua index c18138ced..6e893a660 100644 --- a/plugins/lua/stockflow.lua +++ b/plugins/lua/stockflow.lua @@ -19,6 +19,12 @@ triggers = { {name = "Never"}, } +-- There must be a better way get the value of an enum. +job_types = {} +for key, value in ipairs(df.job_type) do + job_types[value] = key +end + entry_ints = { stockpile_id = 1, order_number = 2, @@ -197,11 +203,11 @@ end function clothing_reactions(reactions, mat_info, filter) local resources = df.global.world.entities.all[df.global.ui.civ_id].resources local itemdefs = df.global.world.raws.itemdefs - resource_reactions(reactions, 101, mat_info, resources.armor_type, itemdefs.armor, {permissible = filter}) - resource_reactions(reactions, 103, mat_info, resources.pants_type, itemdefs.pants, {permissible = filter}) - resource_reactions(reactions, 117, mat_info, resources.gloves_type, itemdefs.gloves, {permissible = filter}) - resource_reactions(reactions, 102, mat_info, resources.helm_type, itemdefs.helms, {permissible = filter}) - resource_reactions(reactions, 118, mat_info, resources.shoes_type, itemdefs.shoes, {permissible = filter}) + resource_reactions(reactions, job_types.MakeArmor, mat_info, resources.armor_type, itemdefs.armor, {permissible = filter}) + resource_reactions(reactions, job_types.MakePants, mat_info, resources.pants_type, itemdefs.pants, {permissible = filter}) + resource_reactions(reactions, job_types.MakeGloves, mat_info, resources.gloves_type, itemdefs.gloves, {permissible = filter}) + resource_reactions(reactions, job_types.MakeHelm, mat_info, resources.helm_type, itemdefs.helms, {permissible = filter}) + resource_reactions(reactions, job_types.MakeShoes, mat_info, resources.shoes_type, itemdefs.shoes, {permissible = filter}) end -- Find the reaction types that should be listed in the management interface. @@ -230,10 +236,12 @@ function collect_reactions() materials.tooth.adjective = "ivory/tooth" materials.leather.clothing_flag = "LEATHER" - local jobs = {12, 22, 219, 31, 32} - for _, job_id in ipairs(jobs) do - table.insert(result, reaction_entry(job_id)) - end + -- Collection and Entrapment + table.insert(result, reaction_entry(job_types.CollectWebs)) + table.insert(result, reaction_entry(job_types.CollectSand)) + table.insert(result, reaction_entry(job_types.CollectClay)) + table.insert(result, reaction_entry(job_types.CatchLiveLandAnimal)) + table.insert(result, reaction_entry(job_types.CatchLiveFish)) -- Cutting, encrusting, and metal extraction. local rock_types = df.global.world.raws.inorganics @@ -241,21 +249,24 @@ function collect_reactions() local material = rock_types[rock_id].material local rock_name = material.state_adj.Solid if material.flags.IS_STONE or material.flags.IS_GEM then - table.insert(result, reaction_entry(85, {mat_type = 0, mat_index = rock_id}, "Cut "..rock_name)) + table.insert(result, reaction_entry(job_types.CutGems, { + mat_type = 0, + mat_index = rock_id, + }, "Cut "..rock_name)) - table.insert(result, reaction_entry(87, { + table.insert(result, reaction_entry(job_types.EncrustWithGems, { mat_type = 0, mat_index = rock_id, item_category = {finished_goods = true}, }, "Encrust Finished Goods With "..rock_name)) - table.insert(result, reaction_entry(87, { + table.insert(result, reaction_entry(job_types.EncrustWithGems, { mat_type = 0, mat_index = rock_id, item_category = {furniture = true}, }, "Encrust Furniture With "..rock_name)) - table.insert(result, reaction_entry(87, { + table.insert(result, reaction_entry(job_types.EncrustWithGems, { mat_type = 0, mat_index = rock_id, item_category = {ammo = true}, @@ -263,11 +274,11 @@ function collect_reactions() end if #rock_types[rock_id].metal_ore.mat_index > 0 then - table.insert(result, reaction_entry(90, {mat_type = 0, mat_index = rock_id}, "Smelt "..rock_name.." Ore")) + table.insert(result, reaction_entry(job_types.SmeltOre, {mat_type = 0, mat_index = rock_id}, "Smelt "..rock_name.." Ore")) end if #rock_types[rock_id].thread_metal.mat_index > 0 then - table.insert(result, reaction_entry(92, {mat_type = 0, mat_index = rock_id})) + table.insert(result, reaction_entry(job_types.ExtractMetalStrands, {mat_type = 0, mat_index = rock_id})) end end @@ -285,19 +296,19 @@ function collect_reactions() management = {mat_type = glass_id}, }) - table.insert(result, reaction_entry(86, {mat_type = glass_id}, "Cut "..glass_name)) + table.insert(result, reaction_entry(job_types.CutGlass, {mat_type = glass_id}, "Cut "..glass_name)) - table.insert(result, reaction_entry(88, { + table.insert(result, reaction_entry(job_types.EncrustWithGlass, { mat_type = glass_id, item_category = {finished_goods = true}, }, "Encrust Finished Goods With "..glass_name)) - table.insert(result, reaction_entry(88, { + table.insert(result, reaction_entry(job_types.EncrustWithGlass, { mat_type = glass_id, item_category = {furniture = true}, }, "Encrust Furniture With "..glass_name)) - table.insert(result, reaction_entry(88, { + table.insert(result, reaction_entry(job_types.EncrustWithGlass, { mat_type = glass_id, item_category = {ammo = true}, }, "Encrust Ammo With "..glass_name)) @@ -305,48 +316,54 @@ function collect_reactions() end -- Dyeing - table.insert(result, reaction_entry(192)) - table.insert(result, reaction_entry(193)) + table.insert(result, reaction_entry(job_types.DyeThread)) + table.insert(result, reaction_entry(job_types.DyeCloth)) -- Sew Image cloth_mats = {materials.cloth, materials.silk, materials.yarn, materials.leather} for _, material in ipairs(cloth_mats) do - material_reactions(result, {{194, "Sew", "Image"}}, material) + material_reactions(result, {{job_types.SewImage, "Sew", "Image"}}, material) end for _, spec in ipairs{materials.bone, materials.shell, materials.tooth, materials.horn, materials.pearl} do - material_reactions(result, {{132, "Decorate With"}}, spec) + material_reactions(result, {{job_types.DecorateWith, "Decorate With"}}, spec) end - -- Make Totem - table.insert(result, reaction_entry(130)) - -- Butcher an Animal - table.insert(result, reaction_entry(105)) - -- Mill Plants - table.insert(result, reaction_entry(107)) - -- Make Potash From Lye - table.insert(result, reaction_entry(189)) - -- Make Potash From Ash - table.insert(result, reaction_entry(191)) + table.insert(result, reaction_entry(job_types.MakeTotem)) + table.insert(result, reaction_entry(job_types.ButcherAnimal)) + table.insert(result, reaction_entry(job_types.MillPlants)) + table.insert(result, reaction_entry(job_types.MakePotashFromLye)) + table.insert(result, reaction_entry(job_types.MakePotashFromAsh)) -- Kitchen - table.insert(result, reaction_entry(115, {mat_type = 2}, "Prepare Easy Meal")) - table.insert(result, reaction_entry(115, {mat_type = 3}, "Prepare Fine Meal")) - table.insert(result, reaction_entry(115, {mat_type = 4}, "Prepare Lavish Meal")) + table.insert(result, reaction_entry(job_types.PrepareMeal, {mat_type = 2}, "Prepare Easy Meal")) + table.insert(result, reaction_entry(job_types.PrepareMeal, {mat_type = 3}, "Prepare Fine Meal")) + table.insert(result, reaction_entry(job_types.PrepareMeal, {mat_type = 4}, "Prepare Lavish Meal")) -- Brew Drink - table.insert(result, reaction_entry(150)) + table.insert(result, reaction_entry(job_types.BrewDrink)) -- Weaving - table.insert(result, reaction_entry(116, {material_category = {plant = true}}, "Weave Thread into Cloth")) - table.insert(result, reaction_entry(116, {material_category = {silk = true}}, "Weave Thread into Silk")) - table.insert(result, reaction_entry(116, {material_category = {yarn = true}}, "Weave Yarn into Cloth")) + table.insert(result, reaction_entry(job_types.WeaveCloth, {material_category = {plant = true}}, "Weave Thread into Cloth")) + table.insert(result, reaction_entry(job_types.WeaveCloth, {material_category = {silk = true}}, "Weave Thread into Silk")) + table.insert(result, reaction_entry(job_types.WeaveCloth, {material_category = {yarn = true}}, "Weave Yarn into Cloth")) -- Extracts, farmer's workshop, and wood burning - local jobs = {151, 152, 153, 106, 110, 109, 214, 215, 188, 111, 112, 113, 114, 186, 187} - for _, job_id in ipairs(jobs) do - table.insert(result, reaction_entry(job_id)) - end + table.insert(result, reaction_entry(job_types.ExtractFromPlants)) + table.insert(result, reaction_entry(job_types.ExtractFromRawFish)) + table.insert(result, reaction_entry(job_types.ExtractFromLandAnimal)) + table.insert(result, reaction_entry(job_types.PrepareRawFish)) + table.insert(result, reaction_entry(job_types.MakeCheese)) + table.insert(result, reaction_entry(job_types.MilkCreature)) + table.insert(result, reaction_entry(job_types.ShearCreature)) + table.insert(result, reaction_entry(job_types.SpinThread)) + table.insert(result, reaction_entry(job_types.MakeLye)) + table.insert(result, reaction_entry(job_types.ProcessPlants)) + table.insert(result, reaction_entry(job_types.ProcessPlantsBag)) + table.insert(result, reaction_entry(job_types.ProcessPlantsVial)) + table.insert(result, reaction_entry(job_types.ProcessPlantsBarrel)) + table.insert(result, reaction_entry(job_types.MakeCharcoal)) + table.insert(result, reaction_entry(job_types.MakeAsh)) -- Reactions defined in the raws. -- Not all reactions are allowed to the civilization. @@ -355,7 +372,7 @@ function collect_reactions() for _, reaction_id in ipairs(entity.entity_raw.workshops.permitted_reaction_id) do local reaction = df.global.world.raws.reactions[reaction_id] local name = string.gsub(reaction.name, "^.", string.upper) - table.insert(result, reaction_entry(211, {reaction_name = reaction.code}, name)) + table.insert(result, reaction_entry(job_types.CustomReaction, {reaction_name = reaction.code}, name)) end -- Metal forging @@ -370,52 +387,52 @@ function collect_reactions() } if material.flags.IS_METAL then - table.insert(result, reaction_entry(104, mat_flags.management, "Stud With "..rock_name)) + table.insert(result, reaction_entry(job_types.StudWith, mat_flags.management, "Stud With "..rock_name)) if material.flags.ITEMS_WEAPON then -- Todo: Are these really the right flags to check? - resource_reactions(result, 97, mat_flags, entity.resources.weapon_type, itemdefs.weapons, { + resource_reactions(result, job_types.MakeWeapon, mat_flags, entity.resources.weapon_type, itemdefs.weapons, { permissible = (function(itemdef) return itemdef.skill_ranged == -1 end), }) -- Is this entirely disconnected from the entity? - material_reactions(result, {{135, "Forge", "Ballista Arrow Head"}}, mat_flags) + material_reactions(result, {{MakeBallistaArrowHead, "Forge", "Ballista Arrow Head"}}, mat_flags) - resource_reactions(result, 142, mat_flags, entity.resources.trapcomp_type, itemdefs.trapcomps, { + resource_reactions(result, job_types.MakeTrapComponent, mat_flags, entity.resources.trapcomp_type, itemdefs.trapcomps, { adjective = true, }) - resource_reactions(result, 136, mat_flags, entity.resources.siegeammo_type, itemdefs.siege_ammo, { + resource_reactions(result, job_types.AssembleSiegeAmmo, mat_flags, entity.resources.siegeammo_type, itemdefs.siege_ammo, { verb = "Assemble", }) end if material.flags.ITEMS_WEAPON_RANGED then - resource_reactions(result, 97, mat_flags, entity.resources.weapon_type, itemdefs.weapons, { + resource_reactions(result, job_types.MakeWeapon, mat_flags, entity.resources.weapon_type, itemdefs.weapons, { permissible = (function(itemdef) return itemdef.skill_ranged >= 0 end), }) end if material.flags.ITEMS_DIGGER then -- Todo: Ranged or training digging weapons? - resource_reactions(result, 97, mat_flags, entity.resources.digger_type, itemdefs.weapons, { + resource_reactions(result, job_types.MakeWeapon, mat_flags, entity.resources.digger_type, itemdefs.weapons, { }) end if material.flags.ITEMS_AMMO then - resource_reactions(result, 131, mat_flags, entity.resources.ammo_type, itemdefs.ammo, { + resource_reactions(result, job_types.MakeAmmo, mat_flags, entity.resources.ammo_type, itemdefs.ammo, { name_field = "name_plural", }) end if material.flags.ITEMS_ANVIL then - material_reactions(result, {{98, "Forge", "Anvil"}}, mat_flags) + material_reactions(result, {{job_types.ForgeAnvil, "Forge", "Anvil"}}, mat_flags) end if material.flags.ITEMS_ARMOR then local metalclothing = (function(itemdef) return itemdef.props.flags.METAL end) clothing_reactions(result, mat_flags, metalclothing) - resource_reactions(result, 119, mat_flags, entity.resources.shield_type, itemdefs.shields, { + resource_reactions(result, job_types.MakeShield, mat_flags, entity.resources.shield_type, itemdefs.shields, { }) end @@ -425,14 +442,14 @@ function collect_reactions() end if material.flags.ITEMS_HARD then - resource_reactions(result, 218, mat_flags, entity.resources.tool_type, itemdefs.tools, { + resource_reactions(result, job_types.MakeTool, mat_flags, entity.resources.tool_type, itemdefs.tools, { permissible = (function(itemdef) return itemdef.flags.HARD_MAT end), capitalize = true, }) end if material.flags.ITEMS_METAL then - resource_reactions(result, 218, mat_flags, entity.resources.tool_type, itemdefs.tools, { + resource_reactions(result, job_types.MakeTool, mat_flags, entity.resources.tool_type, itemdefs.tools, { permissible = (function(itemdef) return itemdef.flags.METAL_MAT end), capitalize = true, }) @@ -440,198 +457,198 @@ function collect_reactions() if material.flags.ITEMS_HARD then material_reactions(result, { - {69, "Construct", "Door"}, - {70, "Construct", "Floodgate"}, - {200, "Construct", "Hatch Cover"}, - {201, "Construct", "Grate"}, - {72, "Construct", "Throne"}, - {73, "Construct", "Sarcophagus"}, - {74, "Construct", "Table"}, - {205, "Construct", "Splint"}, - {206, "Construct", "Crutch"}, - {77, "Construct", "Armor Stand"}, - {78, "Construct", "Weapon Rack"}, - {79, "Construct", "Cabinet"}, - {123, "Forge", "Goblet"}, - {124, "Forge", "Instrument"}, - {125, "Forge", "Toy"}, - {80, "Construct", "Statue"}, - {81, "Construct", "Blocks"}, - {126, "Forge", "Animal Trap"}, - {127, "Forge", "Barrel"}, - {128, "Forge", "Bucket"}, - {76, "Construct", "Bin"}, - {195, "Forge", "Pipe Section"}, - {120, "Forge", "Cage"}, - {84, "Mint", "Coins"}, - {75, "Construct", "Chest"}, - {122, "Forge", "Flask"}, - {121, "Forge", "Chain"}, - {83, "Make", "Crafts"}, + {job_types.ConstructDoor, "Construct", "Door"}, + {job_types.ConstructFloodgate, "Construct", "Floodgate"}, + {job_types.ConstructHatchCover, "Construct", "Hatch Cover"}, + {job_types.ConstructGrate, "Construct", "Grate"}, + {job_types.ConstructThrone, "Construct", "Throne"}, + {job_types.ConstructCoffin, "Construct", "Sarcophagus"}, + {job_types.ConstructTable, "Construct", "Table"}, + {job_types.ConstructSplint, "Construct", "Splint"}, + {job_types.ConstructCrutch, "Construct", "Crutch"}, + {job_types.ConstructArmorStand, "Construct", "Armor Stand"}, + {job_types.ConstructWeaponRack, "Construct", "Weapon Rack"}, + {job_types.ConstructCabinet, "Construct", "Cabinet"}, + {job_types.MakeGoblet, "Forge", "Goblet"}, + {job_types.MakeInstrument, "Forge", "Instrument"}, + {job_types.MakeToy, "Forge", "Toy"}, + {job_types.ConstructStatue, "Construct", "Statue"}, + {job_types.ConstructBlocks, "Construct", "Blocks"}, + {job_types.MakeAnimalTrap, "Forge", "Animal Trap"}, + {job_types.MakeBarrel, "Forge", "Barrel"}, + {job_types.MakeBucket, "Forge", "Bucket"}, + {job_types.ConstructBin, "Construct", "Bin"}, + {job_types.MakePipeSection, "Forge", "Pipe Section"}, + {job_types.MakeCage, "Forge", "Cage"}, + {job_types.MintCoins, "Mint", "Coins"}, + {job_types.ConstructChest, "Construct", "Chest"}, + {job_types.MakeFlask, "Forge", "Flask"}, + {job_types.MakeChain, "Forge", "Chain"}, + {job_types.MakeCrafts, "Make", "Crafts"}, }, mat_flags) end if material.flags.ITEMS_SOFT then material_reactions(result, { - {133, "Make", "Backpack"}, - {134, "Make", "Quiver"}, - {99, "Construct", "Catapult Parts"}, - {100, "Construct", "Ballista Parts"}, + {job_types.MakeBackpack, "Make", "Backpack"}, + {job_types.MakeQuiver, "Make", "Quiver"}, + {job_types.ConstructCatapultParts, "Construct", "Catapult Parts"}, + {job_types.ConstructBallistaParts, "Construct", "Ballista Parts"}, }, mat_flags) end end end -- Traction Bench - table.insert(result, reaction_entry(207)) + table.insert(result, reaction_entry(job_types.ConstructTractionBench)) -- Non-metal weapons - resource_reactions(result, 97, materials.wood, entity.resources.weapon_type, itemdefs.weapons, { + resource_reactions(result, job_types.MakeWeapon, materials.wood, entity.resources.weapon_type, itemdefs.weapons, { permissible = (function(itemdef) return itemdef.skill_ranged >= 0 end), }) - resource_reactions(result, 97, materials.wood, entity.resources.training_weapon_type, itemdefs.weapons, { + resource_reactions(result, job_types.MakeWeapon, materials.wood, entity.resources.training_weapon_type, itemdefs.weapons, { }) - resource_reactions(result, 97, materials.bone, entity.resources.weapon_type, itemdefs.weapons, { + resource_reactions(result, job_types.MakeWeapon, materials.bone, entity.resources.weapon_type, itemdefs.weapons, { permissible = (function(itemdef) return itemdef.skill_ranged >= 0 end), }) - resource_reactions(result, 97, materials.rock, entity.resources.weapon_type, itemdefs.weapons, { + resource_reactions(result, job_types.MakeWeapon, materials.rock, entity.resources.weapon_type, itemdefs.weapons, { permissible = (function(itemdef) return itemdef.flags.CAN_STONE end), }) -- Wooden items -- Closely related to the ITEMS_HARD list. material_reactions(result, { - {69, "Construct", "Door"}, - {70, "Construct", "Floodgate"}, - {200, "Construct", "Hatch Cover"}, - {201, "Construct", "Grate"}, - {72, "Construct", "Chair"}, - {73, "Construct", "Casket"}, - {74, "Construct", "Table"}, - {77, "Construct", "Armor Stand"}, - {78, "Construct", "Weapon Rack"}, - {79, "Construct", "Cabinet"}, - {123, "Make", "Cup"}, - {124, "Make", "Instrument"}, + {job_types.ConstructDoor, "Construct", "Door"}, + {job_types.ConstructFloodgate, "Construct", "Floodgate"}, + {job_types.ConstructHatchCover, "Construct", "Hatch Cover"}, + {job_types.ConstructGrate, "Construct", "Grate"}, + {job_types.ConstructThrone, "Construct", "Chair"}, + {job_types.ConstructCoffin, "Construct", "Casket"}, + {job_types.ConstructTable, "Construct", "Table"}, + {job_types.ConstructArmorStand, "Construct", "Armor Stand"}, + {job_types.ConstructWeaponRack, "Construct", "Weapon Rack"}, + {job_types.ConstructCabinet, "Construct", "Cabinet"}, + {job_types.MakeGoblet, "Make", "Cup"}, + {job_types.MakeInstrument, "Make", "Instrument"}, }, materials.wood) - resource_reactions(result, 218, materials.wood, entity.resources.tool_type, itemdefs.tools, { + resource_reactions(result, job_types.MakeTool, materials.wood, entity.resources.tool_type, itemdefs.tools, { -- permissible = (function(itemdef) return itemdef.flags.WOOD_MAT end), capitalize = true, }) material_reactions(result, { - {125, "Make", "Toy"}, - {81, "Construct", "Blocks"}, - {205, "Construct", "Splint"}, - {206, "Construct", "Crutch"}, - {126, "Make", "Animal Trap"}, - {127, "Make", "Barrel"}, - {128, "Make", "Bucket"}, - {76, "Construct", "Bin"}, - {120, "Make", "Cage"}, - {195, "Make", "Pipe Section"}, + {job_types.MakeToy, "Make", "Toy"}, + {job_types.ConstructBlocks, "Construct", "Blocks"}, + {job_types.ConstructSplint, "Construct", "Splint"}, + {job_types.ConstructCrutch, "Construct", "Crutch"}, + {job_types.MakeAnimalTrap, "Make", "Animal Trap"}, + {job_types.MakeBarrel, "Make", "Barrel"}, + {job_types.MakeBucket, "Make", "Bucket"}, + {job_types.ConstructBin, "Construct", "Bin"}, + {job_types.MakeCage, "Make", "Cage"}, + {job_types.MakePipeSection, "Make", "Pipe Section"}, }, materials.wood) - resource_reactions(result, 142, materials.wood, entity.resources.trapcomp_type, itemdefs.trapcomps, { + resource_reactions(result, job_types.MakeTrapComponent, materials.wood, entity.resources.trapcomp_type, itemdefs.trapcomps, { permissible = (function(itemdef) return itemdef.flags.WOOD end), adjective = true, }) -- Rock items material_reactions(result, { - {69, "Construct", "Door"}, - {70, "Construct", "Floodgate"}, - {200, "Construct", "Hatch Cover"}, - {201, "Construct", "Grate"}, - {72, "Construct", "Throne"}, - {73, "Construct", "Coffin"}, - {74, "Construct", "Table"}, - {77, "Construct", "Armor Stand"}, - {78, "Construct", "Weapon Rack"}, - {79, "Construct", "Cabinet"}, - {123, "Make", "Mug"}, - {124, "Make", "Instrument"}, + {job_types.ConstructDoor, "Construct", "Door"}, + {job_types.ConstructFloodgate, "Construct", "Floodgate"}, + {job_types.ConstructHatchCover, "Construct", "Hatch Cover"}, + {job_types.ConstructGrate, "Construct", "Grate"}, + {job_types.ConstructThrone, "Construct", "Throne"}, + {job_types.ConstructCoffin, "Construct", "Coffin"}, + {job_types.ConstructTable, "Construct", "Table"}, + {job_types.ConstructArmorStand, "Construct", "Armor Stand"}, + {job_types.ConstructWeaponRack, "Construct", "Weapon Rack"}, + {job_types.ConstructCabinet, "Construct", "Cabinet"}, + {job_types.MakeGoblet, "Make", "Mug"}, + {job_types.MakeInstrument, "Make", "Instrument"}, }, materials.rock) - resource_reactions(result, 218, materials.rock, entity.resources.tool_type, itemdefs.tools, { + resource_reactions(result, job_types.MakeTool, materials.rock, entity.resources.tool_type, itemdefs.tools, { permissible = (function(itemdef) return itemdef.flags.HARD_MAT end), capitalize = true, }) material_reactions(result, { - {125, "Make", "Toy"}, - {203, "Construct", "Quern"}, - {204, "Construct", "Millstone"}, - {212, "Construct", "Slab"}, - {80, "Construct", "Statue"}, - {81, "Construct", "Blocks"}, + {job_types.MakeToy, "Make", "Toy"}, + {job_types.ConstructQuern, "Construct", "Quern"}, + {job_types.ConstructMillstone, "Construct", "Millstone"}, + {job_types.ConstructSlab, "Construct", "Slab"}, + {job_types.ConstructStatue, "Construct", "Statue"}, + {job_types.ConstructBlocks, "Construct", "Blocks"}, }, materials.rock) -- Glass items for _, mat_info in ipairs(glasses) do material_reactions(result, { - {69, "Construct", "Portal"}, - {70, "Construct", "Floodgate"}, - {200, "Construct", "Hatch Cover"}, - {201, "Construct", "Grate"}, - {72, "Construct", "Throne"}, - {73, "Construct", "Coffin"}, - {74, "Construct", "Table"}, - {77, "Construct", "Armor Stand"}, - {78, "Construct", "Weapon Rack"}, - {79, "Construct", "Cabinet"}, - {123, "Make", "Goblet"}, - {124, "Make", "Instrument"}, + {job_types.ConstructDoor, "Construct", "Portal"}, + {job_types.ConstructFloodgate, "Construct", "Floodgate"}, + {job_types.ConstructHatchCover, "Construct", "Hatch Cover"}, + {job_types.ConstructGrate, "Construct", "Grate"}, + {job_types.ConstructThrone, "Construct", "Throne"}, + {job_types.ConstructCoffin, "Construct", "Coffin"}, + {job_types.ConstructTable, "Construct", "Table"}, + {job_types.ConstructArmorStand, "Construct", "Armor Stand"}, + {job_types.ConstructWeaponRack, "Construct", "Weapon Rack"}, + {job_types.ConstructCabinet, "Construct", "Cabinet"}, + {job_types.MakeGoblet, "Make", "Goblet"}, + {job_types.MakeInstrument, "Make", "Instrument"}, }, mat_info) - resource_reactions(result, 218, mat_info, entity.resources.tool_type, itemdefs.tools, { + resource_reactions(result, job_types.MakeTool, mat_info, entity.resources.tool_type, itemdefs.tools, { permissible = (function(itemdef) return itemdef.flags.HARD_MAT end), capitalize = true, }) material_reactions(result, { - {125, "Make", "Toy"}, - {80, "Construct", "Statue"}, - {81, "Construct", "Blocks"}, - {120, "Make", "Terrarium"}, - {195, "Make", "Tube"}, + {job_types.MakeToy, "Make", "Toy"}, + {job_types.ConstructStatue, "Construct", "Statue"}, + {job_types.ConstructBlocks, "Construct", "Blocks"}, + {job_types.MakeCage, "Make", "Terrarium"}, + {job_types.MakePipeSection, "Make", "Tube"}, }, mat_info) - resource_reactions(result, 142, mat_info, entity.resources.trapcomp_type, itemdefs.trapcomps, { + resource_reactions(result, job_types.MakeTrapComponent, mat_info, entity.resources.trapcomp_type, itemdefs.trapcomps, { adjective = true, }) end -- Bed, specified as wooden. - table.insert(result, reaction_entry(71, materials.wood.management)) + table.insert(result, reaction_entry(job_types.ConstructBed, materials.wood.management)) -- Windows for _, mat_info in ipairs(glasses) do material_reactions(result, { - {129, "Make", "Window"}, + {job_types.MakeWindow, "Make", "Window"}, }, mat_info) end -- Rock Mechanisms - table.insert(result, reaction_entry(141, materials.rock.management)) + table.insert(result, reaction_entry(job_types.ConstructMechanisms, materials.rock.management)) - resource_reactions(result, 136, materials.wood, entity.resources.siegeammo_type, itemdefs.siege_ammo, { + resource_reactions(result, job_types.AssembleSiegeAmmo, materials.wood, entity.resources.siegeammo_type, itemdefs.siege_ammo, { verb = "Assemble", }) for _, mat_info in ipairs(glasses) do material_reactions(result, { - {82, "Make Raw", nil}, + {job_types.MakeRawGlass, "Make Raw", nil}, }, mat_info) end material_reactions(result, { - {133, "Make", "Backpack"}, - {134, "Make", "Quiver"}, + {job_types.MakeBackpack, "Make", "Backpack"}, + {job_types.MakeQuiver, "Make", "Quiver"}, }, materials.leather) for _, material in ipairs(cloth_mats) do @@ -649,12 +666,12 @@ function collect_reactions() } for _, boxmat in ipairs(boxmats) do for _, mat in ipairs(boxmat.mats) do - material_reactions(result, {{75, "Construct", boxmat.box}}, mat) + material_reactions(result, {{job_types.ConstructChest, "Construct", boxmat.box}}, mat) if boxmat.chain then - material_reactions(result, {{121, "Make", boxmat.chain}}, mat) + material_reactions(result, {{job_types.MakeChain, "Make", boxmat.chain}}, mat) end if boxmat.flask then - material_reactions(result, {{122, "Make", boxmat.flask}}, mat) + material_reactions(result, {{job_types.MakeFlask, "Make", boxmat.flask}}, mat) end end end @@ -672,15 +689,15 @@ function collect_reactions() materials.pearl, materials.yarn, } do - material_reactions(result, {{83, "Make", "Crafts"}}, mat) + material_reactions(result, {{job_types.MakeCrafts, "Make", "Crafts"}}, mat) end -- Siege engine parts - table.insert(result, reaction_entry(99, materials.wood.management)) - table.insert(result, reaction_entry(100, materials.wood.management)) + table.insert(result, reaction_entry(job_types.ConstructCatapultParts, materials.wood.management)) + table.insert(result, reaction_entry(job_types.ConstructBallistaParts, materials.wood.management)) for _, mat in ipairs{materials.wood, materials.bone} do - resource_reactions(result, 131, mat, entity.resources.ammo_type, itemdefs.ammo, { + resource_reactions(result, job_types.MakeAmmo, mat, entity.resources.ammo_type, itemdefs.ammo, { name_field = "name_plural", }) end @@ -690,11 +707,11 @@ function collect_reactions() clothing_reactions(result, materials.shell, (function(itemdef) return itemdef.props.flags[4] end)) for _, mat in ipairs{materials.wood, materials.leather} do - resource_reactions(result, 119, mat, entity.resources.shield_type, itemdefs.shields, {}) + resource_reactions(result, job_types.MakeShield, mat, entity.resources.shield_type, itemdefs.shields, {}) end -- Melt a Metal Object - table.insert(result, reaction_entry(91)) + table.insert(result, reaction_entry(job_types.MeltMetalObject)) return result end From 767badbfb9bd36017eaf56f7f91f16bcb970d4ea Mon Sep 17 00:00:00 2001 From: Eric Wald Date: Sun, 4 May 2014 15:13:25 -0600 Subject: [PATCH 083/676] Magic number reduction: Clothing flags Bone and shell are used to construct BARRED and SCALED armor/clothing items, for some reason. --- plugins/lua/stockflow.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/lua/stockflow.lua b/plugins/lua/stockflow.lua index 6e893a660..a9390bef7 100644 --- a/plugins/lua/stockflow.lua +++ b/plugins/lua/stockflow.lua @@ -703,8 +703,8 @@ function collect_reactions() end -- BARRED and SCALED as flag names don't quite seem to fit, here. - clothing_reactions(result, materials.bone, (function(itemdef) return itemdef.props.flags[3] end)) - clothing_reactions(result, materials.shell, (function(itemdef) return itemdef.props.flags[4] end)) + clothing_reactions(result, materials.bone, (function(itemdef) return itemdef.props.flags.BARRED end)) + clothing_reactions(result, materials.shell, (function(itemdef) return itemdef.props.flags.SCALED end)) for _, mat in ipairs{materials.wood, materials.leather} do resource_reactions(result, job_types.MakeShield, mat, entity.resources.shield_type, itemdefs.shields, {}) From 4fc850445d55acbc646723110be9165361fc49b5 Mon Sep 17 00:00:00 2001 From: Eric Wald Date: Sun, 4 May 2014 15:28:53 -0600 Subject: [PATCH 084/676] Magic Number Reduction: Display placement constants --- plugins/lua/stockflow.lua | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/plugins/lua/stockflow.lua b/plugins/lua/stockflow.lua index a9390bef7..e8ac9db9a 100644 --- a/plugins/lua/stockflow.lua +++ b/plugins/lua/stockflow.lua @@ -31,6 +31,10 @@ entry_ints = { trigger_number = 3, } +PageSize = 16 +FirstRow = 4 +CenterCol = 39 + -- Populate the reaction and stockpile order lists. -- To be called whenever a world is loaded. function initialize_world() @@ -720,7 +724,6 @@ screen = gui.FramedScreen { frame_title = "Select Stockpile Order", } -PageSize = 16 function screen:onRenderBody(dc) -- Emulates the built-in manager screen. dc:seek(1, 1):string("Type in parts of the name to narrow your search. ", COLOR_WHITE) @@ -840,9 +843,9 @@ function screen:refilter() end local x = 1 - local y = 4 + n + local y = FirstRow + n if n >= PageSize then - x = 39 + x = CenterCol y = y - PageSize end From 144eff4cf2db2ae5d87a1aef304217f457cd6475 Mon Sep 17 00:00:00 2001 From: Eric Wald Date: Sun, 4 May 2014 15:36:42 -0600 Subject: [PATCH 085/676] Clearing the center column. The official order selection screen has an extra space to the left of the orders on the right-hand side. --- plugins/lua/stockflow.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/lua/stockflow.lua b/plugins/lua/stockflow.lua index e8ac9db9a..ec52c3deb 100644 --- a/plugins/lua/stockflow.lua +++ b/plugins/lua/stockflow.lua @@ -33,7 +33,7 @@ entry_ints = { PageSize = 16 FirstRow = 4 -CenterCol = 39 +CenterCol = 38 -- Populate the reaction and stockpile order lists. -- To be called whenever a world is loaded. @@ -838,6 +838,7 @@ function screen:refilter() local displayed = {} for n = 0, PageSize*2 - 1 do local item = filtered[start + n] + local name = item.name if not item then break end @@ -847,6 +848,7 @@ function screen:refilter() if n >= PageSize then x = CenterCol y = y - PageSize + name = " "..name end local color = COLOR_CYAN @@ -857,7 +859,7 @@ function screen:refilter() displayed[n + 1] = { x = x, y = y, - name = item.name, + name = name, color = color, } end From f824e7f2944165fc1b2046260562fc2c6332358a Mon Sep 17 00:00:00 2001 From: Anuradha Dissanayake Date: Mon, 5 May 2014 16:56:40 +1200 Subject: [PATCH 086/676] stocks plugin: added more filtering and display options. --- NEWS | 1 + plugins/stocks.cpp | 812 ++++++++++++++++++++++++++++++++++----------- 2 files changed, 621 insertions(+), 192 deletions(-) diff --git a/NEWS b/NEWS index 4c0ec2797..463b978b9 100644 --- a/NEWS +++ b/NEWS @@ -26,6 +26,7 @@ DFHack future - search: workshop profile search added. - dwarfmonitor: add screen to summarise preferences of fortress dwarfs. - getplants: add autochop function to automate woodcutting. + - stocks: added more filtering and display options. Siege engine plugin: - engine quality and distance to target now affect accuracy diff --git a/plugins/stocks.cpp b/plugins/stocks.cpp index 7873595ff..3b9c0317d 100644 --- a/plugins/stocks.cpp +++ b/plugins/stocks.cpp @@ -8,6 +8,7 @@ #include "df/item.h" #include "df/viewscreen_dwarfmodest.h" +#include "df/viewscreen_storesst.h" #include "df/items_other_id.h" #include "df/job.h" #include "df/unit.h" @@ -23,11 +24,14 @@ #include "modules/World.h" #include "modules/Screen.h" #include "modules/Maps.h" +#include "modules/Units.h" +#include "df/building_cagest.h" +#include "df/ui_advmode.h" using df::global::world; DFHACK_PLUGIN("stocks"); -#define PLUGIN_VERSION 0.2 +#define PLUGIN_VERSION 0.12 DFhackCExport command_result plugin_shutdown ( color_ostream &out ) { @@ -61,36 +65,23 @@ static string get_quality_name(const df::item_quality quality) return ENUM_KEY_STR(item_quality, quality); } - -/* - * Trade - */ - -static df::job *get_item_job(df::item *item) -{ - auto ref = Items::getSpecificRef(item, specific_ref_type::JOB); - if (ref && ref->job) - return ref->job; - - return nullptr; -} - static df::item *get_container_of(df::item *item) { auto container = Items::getContainer(item); return (container) ? container : item; } -static bool is_marked_for_trade(df::item *item, df::item *container = nullptr) +static df::item *get_container_of(df::unit *unit) { - item = (container) ? container : get_container_of(item); - auto job = get_item_job(item); - if (!job) - return false; - - return job->job_type == job_type::BringItemToDepot; + auto ref = Units::getGeneralRef(unit, general_ref_type::CONTAINED_IN_ITEM); + return (ref) ? ref->getItem() : nullptr; } + +/* + * Trade Info + */ + static bool check_mandates(df::item *item) { for (auto it = world->mandates.begin(); it != world->mandates.end(); it++) @@ -173,12 +164,6 @@ static bool can_trade_item_and_container(df::item *item) return true; } -static bool is_in_inventory(df::item *item) -{ - item = get_container_of(item); - return item->flags.bits.in_inventory; -} - class TradeDepotInfo { @@ -188,7 +173,7 @@ public: reset(); } - void prepareTradeVarables() + void prepareTradeVariables() { reset(); for(auto bld_it = world->buildings.all.begin(); bld_it != world->buildings.all.end(); bld_it++) @@ -199,43 +184,47 @@ public: depot = bld; id = depot->id; - trade_possible = caravansAvailable(); + trade_possible = can_trade(); break; } } - bool assignItem(df::item *item) + bool assignItem(vector &entries) { - item = get_container_of(item); - if (!can_trade_item_and_container(item)) - return false; + for (auto it = entries.begin(); it != entries.end(); it++) + { + auto item = *it; + item = get_container_of(item); + if (!can_trade_item_and_container(item)) + return false; - auto href = df::allocate(); - if (!href) - return false; + auto href = df::allocate(); + if (!href) + return false; - auto job = new df::job(); + auto job = new df::job(); - df::coord tpos(depot->centerx, depot->centery, depot->z); - job->pos = tpos; + df::coord tpos(depot->centerx, depot->centery, depot->z); + job->pos = tpos; - job->job_type = job_type::BringItemToDepot; + job->job_type = job_type::BringItemToDepot; - // job <-> item link - if (!Job::attachJobItem(job, item, df::job_item_ref::Hauled)) - { - delete job; - delete href; - return false; - } + // job <-> item link + if (!Job::attachJobItem(job, item, df::job_item_ref::Hauled)) + { + delete job; + delete href; + return false; + } - // job <-> building link - href->building_id = id; - depot->jobs.push_back(job); - job->general_refs.push_back(href); + // job <-> building link + href->building_id = id; + depot->jobs.push_back(job); + job->general_refs.push_back(href); - // add to job list - Job::linkIntoWorld(job); + // add to job list + Job::linkIntoWorld(job); + } return true; } @@ -269,27 +258,46 @@ private: return true; } +}; - bool caravansAvailable() - { - if (df::global::ui->caravans.size() == 0) - return false; +static TradeDepotInfo depot_info; - for (auto it = df::global::ui->caravans.begin(); it != df::global::ui->caravans.end(); it++) - { - auto caravan = *it; - auto trade_state = caravan->trade_state; - auto time_remaining = caravan->time_remaining; - if ((trade_state != 1 && trade_state != 2) || time_remaining == 0) - return false; - } - return true; - } -}; +/* + * Item manipulation + */ -static TradeDepotInfo depot_info; +static map items_in_cages; +static df::job *get_item_job(df::item *item) +{ + auto ref = Items::getSpecificRef(item, specific_ref_type::JOB); + if (ref && ref->job) + return ref->job; + + return nullptr; +} + +static bool is_marked_for_trade(df::item *item, df::item *container = nullptr) +{ + item = (container) ? container : get_container_of(item); + auto job = get_item_job(item); + if (!job) + return false; + + return job->job_type == job_type::BringItemToDepot; +} + +static bool is_in_inventory(df::item *item) +{ + item = get_container_of(item); + return item->flags.bits.in_inventory; +} + +static bool is_item_in_cage_cache(df::item *item) +{ + return items_in_cages.find(item) != items_in_cages.end(); +} static string get_keywords(df::item *item) { @@ -301,9 +309,6 @@ static string get_keywords(df::item *item) if (item->flags.bits.rotten) keywords += "rotten "; - if (item->flags.bits.foreign) - keywords += "foreign "; - if (item->flags.bits.owned) keywords += "owned "; @@ -319,6 +324,9 @@ static string get_keywords(df::item *item) if (item->flags.bits.melt) keywords += "melt "; + if (is_item_in_cage_cache(item)) + keywords += "caged "; + if (is_in_inventory(item)) keywords += "inventory "; @@ -331,11 +339,178 @@ static string get_keywords(df::item *item) return keywords; } +static string get_item_label(df::item *item, bool trim = false) +{ + auto label = Items::getDescription(item, 0, false); + if (trim && item->getType() == item_type::BIN) + { + auto pos = label.find("<#"); + if (pos != string::npos) + { + label = label.substr(0, pos-1); + } + } + + auto wear = item->getWear(); + if (wear > 0) + { + string wearX; + switch (wear) + { + case 1: + wearX = "x"; + break; + + case 2: + wearX = "X"; + break; + + case 3: + wearX = "xX"; + break; + + default: + wearX = "XX"; + break; + + } + + label = wearX + label + wearX; + } + + label = pad_string(label, MAX_NAME, false, true); + + return label; +} + +struct item_grouped_entry +{ + std::vector entries; + + string getLabel(bool grouped) const + { + if (entries.size() == 0) + return ""; + + return get_item_label(entries[0], grouped); + } + + string getKeywords() const + { + return get_keywords(entries[0]); + } + + df::item *getFirstItem() const + { + if (entries.size() == 0) + return nullptr; + + return entries[0]; + } + + bool canMelt() const + { + // TODO: Fix melting + return false; + + df::item *item = getFirstItem(); + if (!item) + return false; + + return can_melt(item); + } + + bool isSetToMelt() const + { + df::item *item = getFirstItem(); + if (!item) + return false; + + return is_set_to_melt(item); + } + + bool contains(df::item *item) const + { + return std::find(entries.begin(), entries.end(), item) != entries.end(); + } + + void setFlags(const df::item_flags flags, const bool state) + { + for (auto it = entries.begin(); it != entries.end(); it++) + { + if (state) + (*it)->flags.whole |= flags.whole; + else + (*it)->flags.whole &= ~flags.whole; + } + } + + bool isSingleItem() + { + return entries.size() == 1; + } +}; + + +struct extra_filters +{ + bool hide_trade_marked, hide_in_inventory, hide_in_cages; + + extra_filters() + { + reset(); + } + + void reset() + { + hide_in_inventory = false; + hide_trade_marked = false; + } +}; + +static bool cages_populated = false; +static vector cages; + +static void find_cages() +{ + if (cages_populated) + return; + + for (size_t b=0; b < world->buildings.all.size(); b++) + { + df::building* building = world->buildings.all[b]; + if (building->getType() == building_type::Cage) + { + cages.push_back(static_cast(building)); + } + } + + cages_populated = true; +} + +static df::building_cagest *is_in_cage(df::unit *unit) +{ + find_cages(); + for (auto it = cages.begin(); it != cages.end(); it++) + { + auto cage = *it; + for (size_t c = 0; c < cage->assigned_units.size(); c++) + { + if(cage->assigned_units[c] == unit->id) + return cage; + } + } + + return nullptr; +} + + template class StockListColumn : public ListColumn { - virtual void display_extras(const T &item, int32_t &x, int32_t &y) const + virtual void display_extras(const T &item_group, int32_t &x, int32_t &y) const { + auto item = item_group->getFirstItem(); if (item->flags.bits.in_job) OutputString(COLOR_LIGHTBLUE, x, y, "J"); else @@ -346,11 +521,6 @@ class StockListColumn : public ListColumn else OutputString(COLOR_LIGHTBLUE, x, y, " "); - if (item->flags.bits.foreign) - OutputString(COLOR_BROWN, x, y, "G"); - else - OutputString(COLOR_LIGHTBLUE, x, y, " "); - if (item->flags.bits.owned) OutputString(COLOR_GREEN, x, y, "O"); else @@ -365,12 +535,12 @@ class StockListColumn : public ListColumn OutputString(COLOR_LIGHTMAGENTA, x, y, "D"); else OutputString(COLOR_LIGHTBLUE, x, y, " "); - + if (item->flags.bits.on_fire) OutputString(COLOR_LIGHTRED, x, y, "R"); else OutputString(COLOR_LIGHTBLUE, x, y, " "); - + if (item->flags.bits.melt) OutputString(COLOR_BLUE, x, y, "M"); else @@ -381,6 +551,11 @@ class StockListColumn : public ListColumn else OutputString(COLOR_LIGHTBLUE, x, y, " "); + if (is_item_in_cage_cache(item)) + OutputString(COLOR_LIGHTRED, x, y, "C"); + else + OutputString(COLOR_LIGHTBLUE, x, y, " "); + if (depot_info.canTrade()) { if (is_marked_for_trade(item)) @@ -428,21 +603,6 @@ class StockListColumn : public ListColumn } }; -struct extra_filters -{ - bool hide_trade_marked, hide_in_inventory; - - extra_filters() - { - reset(); - } - - void reset() - { - hide_in_inventory = false; - hide_trade_marked = false; - } -}; class ViewscreenStocks : public dfhack_viewscreen { @@ -450,10 +610,10 @@ public: static df::item_flags hide_flags; static extra_filters extra_hide_flags; - ViewscreenStocks() + ViewscreenStocks(df::building_stockpilest *sp = NULL) : sp(sp) { + is_grouped = true; selected_column = 0; - items_column.setTitle("Item"); items_column.multiselect = false; items_column.auto_select = true; items_column.allow_search = true; @@ -468,7 +628,6 @@ public: checked_flags.bits.in_job = true; checked_flags.bits.rotten = true; - checked_flags.bits.foreign = true; checked_flags.bits.owned = true; checked_flags.bits.forbid = true; checked_flags.bits.dump = true; @@ -479,6 +638,11 @@ public: min_quality = item_quality::Ordinary; max_quality = item_quality::Artifact; min_wear = 0; + cages.clear(); + items_in_cages.clear(); + cages_populated = false; + + last_selected_item = nullptr; populateItems(); @@ -512,12 +676,7 @@ public: return; } - if (input->count(interface_key::CUSTOM_CTRL_G)) - { - hide_flags.bits.foreign = !hide_flags.bits.foreign; - populateItems(); - } - else if (input->count(interface_key::CUSTOM_CTRL_J)) + if (input->count(interface_key::CUSTOM_CTRL_J)) { hide_flags.bits.in_job = !hide_flags.bits.in_job; populateItems(); @@ -557,6 +716,11 @@ public: extra_hide_flags.hide_in_inventory = !extra_hide_flags.hide_in_inventory; populateItems(); } + else if (input->count(interface_key::CUSTOM_CTRL_C)) + { + extra_hide_flags.hide_in_cages = !extra_hide_flags.hide_in_cages; + populateItems(); + } else if (input->count(interface_key::CUSTOM_CTRL_T)) { extra_hide_flags.hide_trade_marked = !extra_hide_flags.hide_trade_marked; @@ -577,6 +741,12 @@ public: setAllFlags(false); populateItems(); } + else if (input->count(interface_key::CHANGETAB)) + { + is_grouped = !is_grouped; + populateItems(); + items_column.centerSelection(); + } else if (input->count(interface_key::SECONDSCROLL_UP)) { if (min_quality > item_quality::Ordinary) @@ -621,18 +791,23 @@ public: else if (input->count(interface_key::CUSTOM_SHIFT_Z)) { input->clear(); - auto item = items_column.getFirstSelectedElem(); - if (!item) + auto item_group = items_column.getFirstSelectedElem(); + if (!item_group) return; + + if (is_grouped && !item_group->isSingleItem()) + return; + + auto item = item_group->getFirstItem(); auto pos = getRealPos(item); - if (!pos) + if (!isRealPos(pos)) return; Screen::dismiss(this); // Could be clever here, if item is in a container, to look inside the container. // But that's different for built containers vs bags/pots in stockpiles. send_key(interface_key::D_LOOK); - move_cursor(*pos); + move_cursor(pos); } else if (input->count(interface_key::CUSTOM_SHIFT_A)) { @@ -642,35 +817,33 @@ public: { df::item_flags flags; flags.bits.dump = true; - applyFlag(flags); + toggleFlag(flags); populateItems(); } else if (input->count(interface_key::CUSTOM_SHIFT_F)) { df::item_flags flags; flags.bits.forbid = true; - applyFlag(flags); + toggleFlag(flags); + populateItems(); + } + else if (input->count(interface_key::CUSTOM_SHIFT_M)) + { + //TODO: Fix melting + return; + + toggleMelt(); populateItems(); } else if (input->count(interface_key::CUSTOM_SHIFT_T)) { - if (apply_to_all) + if (depot_info.canTrade()) { - auto &list = items_column.getDisplayList(); - for (auto iter = list.begin(); iter != list.end(); iter++) + auto selected = getSelectedItems(); + for (auto it = selected.begin(); it != selected.end(); it++) { - auto item = (*iter)->elem; - if (item) - depot_info.assignItem(item); + depot_info.assignItem((*it)->entries); } - - populateItems(); - } - else - { - auto item = items_column.getFirstSelectedElem(); - if (item && depot_info.assignItem(item)) - populateItems(); } } @@ -731,21 +904,22 @@ public: y = 2; x = left_margin; OutputString(COLOR_BROWN, x, y, "Filters", true, left_margin); - OutputString(COLOR_LIGHTRED, x, y, "Press Ctrl-Hotkey to Toggle", true, left_margin); + OutputString(COLOR_LIGHTRED, x, y, "Press Ctrl-Hotkey to toggle", true, left_margin); OutputFilterString(x, y, "In Job", "J", !hide_flags.bits.in_job, true, left_margin, COLOR_LIGHTBLUE); OutputFilterString(x, y, "Rotten", "X", !hide_flags.bits.rotten, true, left_margin, COLOR_CYAN); - OutputFilterString(x, y, "Foreign Made", "G", !hide_flags.bits.foreign, true, left_margin, COLOR_BROWN); OutputFilterString(x, y, "Owned", "O", !hide_flags.bits.owned, true, left_margin, COLOR_GREEN); OutputFilterString(x, y, "Forbidden", "F", !hide_flags.bits.forbid, true, left_margin, COLOR_RED); OutputFilterString(x, y, "Dump", "D", !hide_flags.bits.dump, true, left_margin, COLOR_LIGHTMAGENTA); OutputFilterString(x, y, "On Fire", "R", !hide_flags.bits.on_fire, true, left_margin, COLOR_LIGHTRED); OutputFilterString(x, y, "Melt", "M", !hide_flags.bits.melt, true, left_margin, COLOR_BLUE); OutputFilterString(x, y, "In Inventory", "I", !extra_hide_flags.hide_in_inventory, true, left_margin, COLOR_WHITE); + OutputFilterString(x, y, "Caged", "C", !extra_hide_flags.hide_in_cages, true, left_margin, COLOR_LIGHTRED); OutputFilterString(x, y, "Trade", "T", !extra_hide_flags.hide_trade_marked, true, left_margin, COLOR_LIGHTGREEN); OutputFilterString(x, y, "No Flags", "N", !hide_unflagged, true, left_margin, COLOR_GREY); ++y; OutputHotkeyString(x, y, "Clear All", "Shift-C", true, left_margin); OutputHotkeyString(x, y, "Enable All", "Shift-E", true, left_margin); + OutputHotkeyString(x, y, "Toggle Grouping", "TAB", true, left_margin); ++y; OutputHotkeyString(x, y, "Min Qual: ", "-+"); OutputString(COLOR_BROWN, x, y, get_quality_name(min_quality), true, left_margin); @@ -765,21 +939,43 @@ public: OutputString(COLOR_BROWN, x, y, (apply_to_all) ? "Listed" : "Selected", true, left_margin); OutputHotkeyString(x, y, "Dump", "Shift-D", true, left_margin); OutputHotkeyString(x, y, "Forbid", "Shift-F", true, left_margin); - OutputHotkeyString(x, y, "Mark for Trade", "Shift-T", true, left_margin); + + //TODO: Fix melting + //OutputHotkeyString(x, y, "Melt", "Shift-M", true, left_margin); + + if (depot_info.canTrade()) + OutputHotkeyString(x, y, "Mark for Trade", "Shift-T", true, left_margin); + + y = gps->dimy - 6; + OutputString(COLOR_LIGHTRED, x, y, "Flag names can also", true, left_margin); + OutputString(COLOR_LIGHTRED, x, y, "be searched for", true, left_margin); } std::string getFocusString() { return "stocks_view"; } private: - StockListColumn items_column; + StockListColumn items_column; int selected_column; bool apply_to_all, hide_unflagged; df::item_flags checked_flags; df::item_quality min_quality, max_quality; int16_t min_wear; + bool is_grouped; + std::list grouped_items_store; + df::item *last_selected_item; + string last_selected_hash; + int last_display_offset; + df::building_stockpilest *sp; + + static bool isRealPos(const df::coord pos) + { + return pos.x != -30000; + } - static df::coord *getRealPos(df::item *item) + static df::coord getRealPos(df::item *item) { + df::coord pos; + pos.x = -30000; item = get_container_of(item); if (item->flags.bits.in_inventory) { @@ -789,61 +985,144 @@ private: if (ref && ref->job) { if (ref->job->job_type == job_type::Eat || ref->job->job_type == job_type::Drink) - return nullptr; + return pos; auto unit = Job::getWorker(ref->job); if (unit) - return &unit->pos; + return unit->pos; } - return nullptr; + return pos; } else { auto unit = Items::getHolderUnit(item); if (unit) - return &unit->pos; + { + if (!Units::isCitizen(unit)) + { + auto cage_item = get_container_of(unit); + if (cage_item) + { + items_in_cages[item] = true; + return cage_item->pos; + } + + auto cage_building = is_in_cage(unit); + if (cage_building) + { + items_in_cages[item] = true; + pos.x = cage_building->centerx; + pos.y = cage_building->centery; + pos.z = cage_building->z; + } + + return pos; + } + + return unit->pos; + } - return nullptr; + return pos; } } - return &item->pos; + return item->pos; } - void applyFlag(const df::item_flags flags) + void toggleMelt() { - if (apply_to_all) + //TODO: Fix melting + return; + + int set_to_melt = -1; + auto selected = getSelectedItems(); + vector items; + for (auto it = selected.begin(); it != selected.end(); it++) { - int state_to_apply = -1; - for (auto iter = items_column.getDisplayList().begin(); iter != items_column.getDisplayList().end(); iter++) + auto item_group = *it; + + if (set_to_melt == -1) + set_to_melt = (item_group->isSetToMelt()) ? 0 : 1; + + if (set_to_melt) { - auto item = (*iter)->elem; - if (item) + if (!item_group->canMelt() || item_group->isSetToMelt()) + continue; + } + else if (!item_group->isSetToMelt()) + { + continue; + } + + items.insert(items.end(), item_group->entries.begin(), item_group->entries.end()); + } + + auto &melting_items = world->items.other[items_other_id::ANY_MELT_DESIGNATED]; + for (auto it = items.begin(); it != items.end(); it++) + { + auto item = *it; + if (set_to_melt) + { + insert_into_vector(melting_items, &df::item::id, item); + item->flags.bits.melt = true; + } + else + { + for (auto mit = melting_items.begin(); mit != melting_items.end(); mit++) { - // Set all flags based on state of first item in list - if (state_to_apply == -1) - state_to_apply = (item->flags.whole & flags.whole) ? 0 : 1; - - if (state_to_apply) - item->flags.whole |= flags.whole; - else - item->flags.whole &= ~flags.whole; + if (item != *mit) + continue; + + melting_items.erase(mit); + item->flags.bits.melt = false; + break; } } } + } + + void toggleFlag(const df::item_flags flags) + { + int state_to_apply = -1; + auto selected = getSelectedItems(); + for (auto it = selected.begin(); it != selected.end(); it++) + { + auto grouped_entry = (*it); + auto item = grouped_entry->getFirstItem(); + if (state_to_apply == -1) + state_to_apply = (item->flags.whole & flags.whole) ? 0 : 1; + + grouped_entry->setFlags(flags.whole, state_to_apply); + } + } + + vector getSelectedItems() + { + vector result; + if (apply_to_all) + { + for (auto it = items_column.getDisplayList().begin(); it != items_column.getDisplayList().end(); it++) + { + auto item_group = (*it)->elem; + if (!item_group) + continue; + result.push_back(item_group); + } + } else { - auto item = items_column.getFirstSelectedElem(); - if (item) - item->flags.whole ^= flags.whole; + auto item_group = items_column.getFirstSelectedElem(); + if (item_group) + result.push_back(item_group); } + + return result; } void setAllFlags(bool state) { hide_flags.bits.in_job = state; hide_flags.bits.rotten = state; - hide_flags.bits.foreign = state; hide_flags.bits.owned = state; hide_flags.bits.forbid = state; hide_flags.bits.dump = state; @@ -853,10 +1132,13 @@ private: hide_unflagged = state; extra_hide_flags.hide_trade_marked = state; extra_hide_flags.hide_in_inventory = state; + extra_hide_flags.hide_in_cages = state; } void populateItems() { + items_column.setTitle((is_grouped) ? "Item (count)" : "Item"); + preserveLastSelected(); items_column.clear(); df::item_flags bad_flags; @@ -865,15 +1147,20 @@ private: bad_flags.bits.trader = true; bad_flags.bits.in_building = true; bad_flags.bits.garbage_collect = true; - bad_flags.bits.hostile = true; bad_flags.bits.removed = true; bad_flags.bits.dead_dwarf = true; bad_flags.bits.murder = true; bad_flags.bits.construction = true; - depot_info.prepareTradeVarables(); + depot_info.prepareTradeVariables(); - std::vector &items = world->items.other[items_other_id::IN_PLAY]; + std::vector &items = world->items.other[items_other_id::IN_PLAY]; + std::map grouped_items; + grouped_items_store.clear(); + item_grouped_entry *next_selected_group = nullptr; + StockpileInfo spInfo; + if (sp) + spInfo = StockpileInfo(sp); for (size_t i = 0; i < items.size(); i++) { @@ -887,13 +1174,10 @@ private: continue; auto pos = getRealPos(item); - if (!pos) + if (!isRealPos(pos)) continue; - if (pos->x == -30000) - continue; - - auto designation = Maps::getTileDesignation(*pos); + auto designation = Maps::getTileDesignation(pos); if (!designation) continue; @@ -904,11 +1188,15 @@ private: if (extra_hide_flags.hide_trade_marked && trade_marked) continue; + bool caged = is_item_in_cage_cache(item); + if (extra_hide_flags.hide_in_cages && caged) + continue; + if (extra_hide_flags.hide_in_inventory && container->flags.bits.in_inventory) continue; if (hide_unflagged && (!(item->flags.whole & checked_flags.whole) && - !trade_marked && !container->flags.bits.in_inventory)) + !trade_marked && !caged && !container->flags.bits.in_inventory)) { continue; } @@ -921,40 +1209,89 @@ private: if (wear < min_wear) continue; - auto label = Items::getDescription(item, 0, false); - if (wear > 0) + if (spInfo.isValid() && !spInfo.inStockpile(item)) + continue; + + if (is_grouped) { - string wearX; - switch (wear) + auto hash = getItemHash(item); + if (grouped_items.find(hash) == grouped_items.end()) { - case 1: - wearX = "x"; - break; - - case 2: - wearX = "X"; - break; - - case 3: - wearX = "xX"; - break; + grouped_items_store.push_back(item_grouped_entry()); + grouped_items[hash] = &grouped_items_store.back(); + } + grouped_items[hash]->entries.push_back(item); + if (last_selected_item && + !next_selected_group && + hash == last_selected_hash) + { + next_selected_group = grouped_items[hash]; + } + } + else + { + grouped_items_store.push_back(item_grouped_entry()); + auto item_group = &grouped_items_store.back(); + item_group->entries.push_back(item); - default: - wearX = "XX"; - break; + auto label = get_item_label(item); + auto entry = ListEntry(label, item_group, item_group->getKeywords()); + items_column.add(entry); + if (last_selected_item && + !next_selected_group && + item == last_selected_item) + { + next_selected_group = item_group; } + } + } - label = wearX + label + wearX; + if (is_grouped) + { + for (auto groups_iter = grouped_items.begin(); groups_iter != grouped_items.end(); groups_iter++) + { + auto item_group = groups_iter->second; + stringstream label; + label << item_group->getLabel(is_grouped); + if (!item_group->isSingleItem()) + label << " (" << item_group->entries.size() << ")"; + auto entry = ListEntry(label.str(), item_group, item_group->getKeywords()); + items_column.add(entry); } + } - label = pad_string(label, MAX_NAME, false, true); + items_column.fixWidth(); + items_column.filterDisplay(); - auto entry = ListEntry(label, item, get_keywords(item)); - items_column.add(entry); + if (next_selected_group) + { + items_column.selectItem(next_selected_group); + items_column.display_start_offset = last_display_offset; } + } + + string getItemHash(df::item *item) + { + auto label = get_item_label(item, true); + auto quality = static_cast(item->getQuality()); + auto quality_enum = static_cast(quality); + auto quality_string = ENUM_KEY_STR(item_quality, quality_enum); + auto hash = label + quality_string + int_to_string(item->flags.whole & checked_flags.whole) + " " + + int_to_string(item->hasImprovements()); - items_column.filterDisplay(); + return hash; + } + + void preserveLastSelected() + { + last_selected_item = nullptr; + auto selected_entry = items_column.getFirstSelectedElem(); + if (!selected_entry) + return; + last_selected_item = selected_entry->getFirstItem(); + last_selected_hash = (is_grouped && last_selected_item) ? getItemHash(last_selected_item) : ""; + last_display_offset = items_column.display_start_offset; } void validateColumn() @@ -973,6 +1310,102 @@ private: df::item_flags ViewscreenStocks::hide_flags; extra_filters ViewscreenStocks::extra_hide_flags; +struct stocks_hook : public df::viewscreen_storesst +{ + typedef df::viewscreen_storesst interpose_base; + + DEFINE_VMETHOD_INTERPOSE(void, feed, (set *input)) + { + if (input->count(interface_key::CUSTOM_E)) + { + Screen::dismiss(this); + Screen::dismiss(Gui::getCurViewscreen(true)); + Screen::show(new ViewscreenStocks()); + return; + } + INTERPOSE_NEXT(feed)(input); + } + + DEFINE_VMETHOD_INTERPOSE(void, render, ()) + { + INTERPOSE_NEXT(render)(); + auto dim = Screen::getWindowSize(); + int x = 40; + int y = dim.y - 2; + OutputHotkeyString(x, y, "Enhanced View", "e"); + } +}; + +IMPLEMENT_VMETHOD_INTERPOSE(stocks_hook, feed); +IMPLEMENT_VMETHOD_INTERPOSE(stocks_hook, render); + + +struct stocks_stockpile_hook : public df::viewscreen_dwarfmodest +{ + typedef df::viewscreen_dwarfmodest interpose_base; + + bool handleInput(set *input) + { + df::building_stockpilest *sp = get_selected_stockpile(); + if (!sp) + return false; + + if (input->count(interface_key::CUSTOM_I)) + { + Screen::show(new ViewscreenStocks(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 = 23; + + OutputHotkeyString(x, y, "Show Inventory", "i", true, left_margin); + } +}; + +IMPLEMENT_VMETHOD_INTERPOSE(stocks_stockpile_hook, feed); +IMPLEMENT_VMETHOD_INTERPOSE(stocks_stockpile_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(stocks_hook, feed).apply(enable) || + !INTERPOSE_HOOK(stocks_hook, render).apply(enable) || + !INTERPOSE_HOOK(stocks_stockpile_hook, feed).apply(enable) || + !INTERPOSE_HOOK(stocks_stockpile_hook, render).apply(enable)) + return CR_FAILURE; + + is_enabled = enable; + } + + return CR_OK; +} static command_result stocks_cmd(color_ostream &out, vector & parameters) { @@ -993,12 +1426,8 @@ static command_result stocks_cmd(color_ostream &out, vector & parameter return CR_WRONG_USAGE; } - -DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) +DFhackCExport command_result plugin_init (color_ostream &out, std::vector &commands) { - if (!gps) - out.printerr("Could not insert stocks plugin hooks!\n"); - commands.push_back( PluginCommand( "stocks", "An improved stocks display screen", @@ -1009,7 +1438,6 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector Date: Mon, 5 May 2014 14:59:59 +1000 Subject: [PATCH 087/676] added two scripts Added scripts `exportlegends` and `log-region`, including init and readme entries. The init file now has a section for legends-mode keybindings. --- Readme.rst | 14 ++++++++ dfhack.init-example | 11 +++++++ scripts/exportlegends.lua | 68 +++++++++++++++++++++++++++++++++++++++ scripts/log-region.lua | 35 ++++++++++++++++++++ 4 files changed, 128 insertions(+) create mode 100644 scripts/exportlegends.lua create mode 100644 scripts/log-region.lua diff --git a/Readme.rst b/Readme.rst index 499920cab..d3d344767 100644 --- a/Readme.rst +++ b/Readme.rst @@ -1501,6 +1501,15 @@ dwarfexport ----------- Export dwarves to RuneSmith-compatible XML. +exportlegends +------------- +Exports data from legends mode; allowing a set-and-forget export of large worlds. + +Options: + + :maps: Exports all fifteen detailed maps + :all: first exports the world/gen info, then the XML, then all detailed maps + Job management ============== @@ -1999,6 +2008,11 @@ Options: Known limitations: if the selected unit is currently performing a job, the mood will not be started. +log-region +---------- +When enabled in dfhack.init, each time a fort is loaded identifying information will be written to the gamelog. Assists in parsing the file if you switch between forts, and adds information for story-building. + + ======= Scripts ======= diff --git a/dfhack.init-example b/dfhack.init-example index 3470bf36b..7cbc495a3 100644 --- a/dfhack.init-example +++ b/dfhack.init-example @@ -41,6 +41,14 @@ keybinding add Ctrl-Shift-P command-prompt keybinding add Ctrl-B adv-bodyswap keybinding add Ctrl-Shift-B "adv-bodyswap force" +############################## +# Generic legends bindings # +############################## + +# export all information, or just the detailed maps (doesn't handle site maps) +keybinding add Ctrl-A@legends "exportlegends all" +keybinding add Ctrl-D@legends "exportlegends maps" + ############################# # Context-specific bindings # ############################# @@ -182,6 +190,9 @@ enable automaterial # write the correct season to gamelog on world load soundsense-season +# write identifying information about the fort to gamelog on world load +log-region + # patch the material objects in memory to fix cloth stockpiles fix/cloth-stockpile enable diff --git a/scripts/exportlegends.lua b/scripts/exportlegends.lua new file mode 100644 index 000000000..df65da7b2 --- /dev/null +++ b/scripts/exportlegends.lua @@ -0,0 +1,68 @@ +-- Export everything from legends mode +-- Based on the script 'exportmaps' +-- Suggested keybinding: keybinding add Shift-A@legends "exportlegends all" + +gui = require 'gui' + +local args = {...} +local vs = dfhack.gui.getCurViewscreen() +local i = 0 + +if args[1] then print(' A script which exports data from legends mode. Valid arguments are "maps" (detailed maps) or "all" (detailed maps, world/gen info, XML).') end + + +local MAPS = { + [0] = "Standard biome+site map", + "Elevations including lake and ocean floors", + "Elevations respecting water level", + "Biome", + "Hydrosphere", + "Temperature", + "Rainfall", + "Drainage", + "Savagery", + "Volcanism", + "Current vegetation", + "Evil", + "Salinity", + "Structures/fields/roads/etc.", + "Trade", +} +function wait_for_legends_vs() + vs = dfhack.gui.getCurViewscreen() + if i < 15 then + if df.viewscreen_legendsst:is_instance(vs) then + gui.simulateInput(vs, 'LEGENDS_EXPORT_DETAILED_MAP') -- "d" on screen some number internally + dfhack.timeout(10,'frames',wait_for_export_maps_vs) + else + dfhack.timeout(10,'frames',wait_for_legends_vs) + end + end +end +function wait_for_export_maps_vs() + vs = dfhack.gui.getCurViewscreen() + if df.viewscreen_export_graphical_mapst:is_instance(vs) then + vs.anon_13 = i -- anon_13 appears to be the selection cursor for this viewscreen + print('Exporting: '..MAPS[i]) + i = i + 1 + gui.simulateInput(vs, 'SELECT') -- 1 internally, enter on screen + dfhack.timeout(10,'frames',wait_for_legends_vs) + else + dfhack.timeout(10,'frames',wait_for_export_maps_vs) + end +end + +if df.viewscreen_legendsst:is_instance( vs ) then -- dfhack.gui.getCurFocus() == "legends" + if args[1] == "all" then + gui.simulateInput(vs, df.interface_key.LEGENDS_EXPORT_MAP) + print('Exporting: world map/gen info') + gui.simulateInput(vs, df.interface_key.LEGENDS_EXPORT_XML) + print('Exporting: legends xml') + wait_for_legends_vs() + elseif args[1] == "maps" then wait_for_legends_vs() + end +elseif df.viewscreen_export_graphical_mapst:is_instance(vs) then + if args[1] == "maps" or "all" then wait_for_export_maps_vs() end +else + dfhack.printerr('Not in legends view') +end diff --git a/scripts/log-region.lua b/scripts/log-region.lua new file mode 100644 index 000000000..490662ef3 --- /dev/null +++ b/scripts/log-region.lua @@ -0,0 +1,35 @@ +-- On map load writes information about the loaded region to gamelog.txt +-- By Kurik Amudnil and Warmist (http://www.bay12forums.com/smf/index.php?topic=91166.msg4467072#msg4467072) + +local function write_gamelog(msg) + local log = io.open('gamelog.txt', 'a') + log:write(msg.."\n") + log:close() +end + +local args = {...} +if args[1] == 'disable' then + dfhack.onStateChange[_ENV] = nil +else + dfhack.onStateChange[_ENV] = function(op) + if op == SC_WORLD_LOADED then + if df.world_site.find(df.global.ui.site_id) ~= nil then -- added this check, now only attempts write in fort mode + local site = df.world_site.find(df.global.ui.site_id) + local fort_ent = df.global.ui.main.fortress_entity + local civ_ent = df.historical_entity.find(df.global.ui.civ_id) + local worldname = df.global.world.world_data.name + -- site positions + -- site .pos.x .pos.y + -- site .rgn_min_x .rgn_min_y .rgn_max_x .rgn_max.y + -- site .global_min_x .global_min_y .global_max_x .global_max_y + --site.name + --fort_ent.name + --civ_ent.name + + write_gamelog('Loaded '..df.global.world.cur_savegame.save_dir..', '..dfhack.TranslateName(worldname)..' ('..dfhack.TranslateName(worldname ,true)..') at coordinates ('..site.pos.x..','..site.pos.y..')'..NEWLINE.. + 'Loaded the fortress '..dfhack.TranslateName(site.name)..' ('..dfhack.TranslateName(site.name, true)..'), colonized by the group '..dfhack.TranslateName(fort_ent.name)..' ('..dfhack.TranslateName(fort_ent.name,true).. + ') of the civilization '..dfhack.TranslateName(civ_ent.name)..' ('..dfhack.TranslateName(civ_ent.name,true)..').'..NEWLINE) + end + end + end +end From 09620b65fbe65f6b04a4544b5e3876981c6c0208 Mon Sep 17 00:00:00 2001 From: PeridexisErrant Date: Mon, 5 May 2014 15:13:32 +1000 Subject: [PATCH 088/676] delete obsolete plugins, add notes +new dfstatus The new dfstatus script needs some work on magic numbers before it moves to the scripts folder; the notes file contains my notes on further things to delete. --- needs_porting/_notes.txt | 72 ++++++++ needs_porting/dfstatus.cpp | 206 --------------------- needs_porting/dfstatus.lua | 110 +++++++++++ needs_porting/digger.cpp | 363 ------------------------------------- needs_porting/digger2.cpp | 180 ------------------ needs_porting/fix-3708.cpp | 217 ---------------------- needs_porting/lair.cpp | 74 -------- needs_porting/reveal.py | 93 ---------- needs_porting/veinswap.cpp | 229 ----------------------- 9 files changed, 182 insertions(+), 1362 deletions(-) create mode 100644 needs_porting/_notes.txt delete mode 100644 needs_porting/dfstatus.cpp create mode 100644 needs_porting/dfstatus.lua delete mode 100644 needs_porting/digger.cpp delete mode 100644 needs_porting/digger2.cpp delete mode 100644 needs_porting/fix-3708.cpp delete mode 100644 needs_porting/lair.cpp delete mode 100644 needs_porting/reveal.py delete mode 100644 needs_porting/veinswap.cpp diff --git a/needs_porting/_notes.txt b/needs_porting/_notes.txt new file mode 100644 index 000000000..04e123228 --- /dev/null +++ b/needs_porting/_notes.txt @@ -0,0 +1,72 @@ +Notes by PeridexisErrant, on the needs_porting scripts and plugins: + +I've deleted "reveal", "lair", and "dfstatus.cpp" - they've been replaced by other more recent versions. (I added the dfstatus script). +Also deleted "digger", less useful than "digger2" and generally replaced by autochop. "digger2" is replaced by digfort. +And now "veinswap"; replaced by "changevein". "fix-3708.cpp" deals with a bug that the Mantis tracker marks as resolved. + + +Need confirmation that they're obsolete before deleting (notes below): +attachtest.py, dfbauxtite.cpp, digpattern.cpp, drawtile.cpp, treedump.py, and veinlook.cpp + +To port: +copypaste.cpp, hellhole.cpp, hotkeynotedump.py, itemdesignator.cpp (partial), position.py + + +All undeleted plugins: + +attachtest.py +- checks if dfhack is working with DF (i think) +- definitely replaced on windows, pretty sure also on other platforms + +copypaste.cpp +- a proof-of-concept plugin to allow copy-pasting in DF +- if I read it right, it does both terrain and buildings/constructions +- would be awesome if someone ported it! + +creaturemanager.cpp +- Display creatures; modify skills and labors of creatures; kill creatures; etc +- looks pretty impressive if incomplete (at 1500 lines I only looked over the comments) +- could maybe do with porting, but it's unfinished and I suspect that most functions are available elsewhere + +dfbauxtite.cpp +- as far as I can tell, this plugin changes the material of all mechanisms (printed string 'mechanisms', class 'item_trapparts') to Bauxtite; presumably from when this was the only magma-safe stone. +- I don't know of a newer alternative, but it's not really neccessary anymore + +digpattern.cpp +- I think all this one does is allow multi-z designations - no longer required. + +drawtile.cpp +- draws changes to tiles +- I think this is replaced by tiletypes, but not sure + +hellhole.cpp +- digs a hole to HFS instantly +- no known equivilant, needs porting + +hotkeynotedump.py +- outputs a list of hotkeys with names +- less useful with propper dfhack keybindings available, but still useful +- trivial to redo in lua (it's 20 lines, could be 10) + +incrementalsearch.cpp +- linux-only memory voodoo; leaving this alone + +itemdesignator.cpp +- applies flags to categories of items (eg item_microcline; forbid/dump/melt/on fire) +- appart from fire, replaced by Falconne's enhanced stocks screen +- port the fire part? + +position.py +- prints information about time (year/month/day/tick) and place (embark location/cursor pos) and window size +- the wider context can be written to gamelog on world load by "log-region.lua"; this doesn't seem to be included yet + +SegementedFinder.h +- no idea what's happening here + +treedump.py +- looks like it outputs a list of trees in a given volume +- overlaps with 'prospect' and 'autochop'; probably does not need porting + +veinlook.cpp +- I'm not sure what this does that "digv" and "reveal" don't but there must be something since it was around at the same time as reveal. + diff --git a/needs_porting/dfstatus.cpp b/needs_porting/dfstatus.cpp deleted file mode 100644 index 91c6f6589..000000000 --- a/needs_porting/dfstatus.cpp +++ /dev/null @@ -1,206 +0,0 @@ -/* - * dfstatus.cpp -*/ - -#include - -#ifndef LINUX_BUILD - #include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -//#include //to break on keyboard input -#include -#include -#include -using namespace std; -#include -#include -#include - -WINDOW *create_newwin(int height, int width, int starty, int startx); - - int32_t drinkCount = 0; - int32_t mealsCount = 0; - int32_t plantCount = 0; - int32_t fishCount = 0; - int32_t meatCount = 0; - int32_t logsCount = 0; - int32_t barCount = 0; - int32_t clothCount = 0; - int32_t ironBars = 0; - int32_t pigIronBars = 0; - int32_t goldBars = 0; - int32_t silverBars = 0; - int32_t copperBars = 0; - int32_t steelBars = 0; - int32_t fuel = 0; - uint64_t start_time = 0; - uint64_t end_time = 0; - uint64_t total_time = 0; - -WINDOW *create_newwin(int height, int width, int starty, int startx){ - WINDOW *local_win; - - local_win = newwin(height, width, starty, startx); - box(local_win, 0, 0); /* 0, 0 gives default characters - * for the vertical and horizontal - * lines */ - //first row - mvwprintw(local_win,2 ,2,"Drinks : %d", drinkCount); - mvwprintw(local_win,4 ,2,"Meals : %d", mealsCount); - mvwprintw(local_win,6 ,2,"Plants : %d", plantCount); - mvwprintw(local_win,7 ,2,"Fish : %d", fishCount); - mvwprintw(local_win,8 ,2,"Meat : %d", meatCount); - mvwprintw(local_win,10,2,"Logs : %d", logsCount); - mvwprintw(local_win,12,2,"Cloth : %d", clothCount); - //second row - mvwprintw(local_win,2,22,"Iron Bars : %d", ironBars); - mvwprintw(local_win,3,22,"Gold Bars : %d", goldBars); - mvwprintw(local_win,4,22,"Silver Bars : %d", silverBars); - mvwprintw(local_win,5,22,"Copper Bars : %d", copperBars); - mvwprintw(local_win,6,22,"Steel Bars : %d", steelBars); - mvwprintw(local_win,7,22,"Pig iron Bars : %d", pigIronBars); - mvwprintw(local_win,9,22,"Fuel : %d", fuel); - total_time += end_time - start_time; - mvwprintw(local_win,14,2,"Time: %d ms last update, %d ms total", end_time - start_time, total_time); - - wrefresh(local_win); // paint the screen and all components. - - return local_win; -} - -int main() -{ - WINDOW *my_win; - int startx, starty, width, height; - - DFHack::Process * p; - DFHack::ContextManager DFMgr("Memory.xml"); - DFHack::Context * DF; - DFHack::Materials * Materials; - try{ //is DF running? - DF = DFMgr.getSingleContext(); - DF->Attach(); - Materials = DF->getMaterials(); - Materials->ReadAllMaterials(); - DF->Resume(); - } - catch (exception& e){ - cerr << e.what() << endl; - return 1; - } - //init and Attach - ofstream file("dfstatus_errors.txt"); - streambuf* strm_buffer = cerr.rdbuf(); // save cerr's output buffer - cerr.rdbuf (file.rdbuf()); // redirect output into the file - - initscr(); //start curses. - nonl(); - intrflush(stdscr, FALSE); - keypad(stdscr, TRUE); - do{ - drinkCount = 0; - mealsCount = 0; - plantCount = 0; - fishCount = 0; - meatCount = 0; - logsCount = 0; - barCount = 0; - clothCount = 0; - ironBars = 0; - pigIronBars = 0; - goldBars = 0; - silverBars = 0; - copperBars = 0; - steelBars = 0; - fuel = 0; - - //FILE * pFile; - //pFile = fopen("dump.txt","w"); - start_time = GetTimeMs64(); - if(!DF->Suspend()) - { - break; - } - - //DFHack::Gui * Gui = DF->getGui(); - - DFHack::Items * Items = DF->getItems(); - Items->Start(); - - DFHack::VersionInfo * mem = DF->getMemoryInfo(); - p = DF->getProcess(); - - DFHack::OffsetGroup* itemGroup = mem->getGroup("Items"); - DFHack::DfVector p_items (p, itemGroup->getAddress("items_vector")); - uint32_t size = p_items.size(); - - DFHack::dfh_item itm; //declare itm - //memset(&itm, 0, sizeof(DFHack::dfh_item)); //seems to set every value in itm to 0 - - for(unsigned int idx = 0; idx < size; idx++) //fill our item variables with this loop - { - Items->readItem(p_items[idx], itm); - - if (itm.base.flags.owned) //only count what we actually own. - continue; - - string s0 = Items->getItemClass(itm.matdesc.itemType).c_str(); - string s1 = Items->getItemDescription(itm, Materials).c_str(); - - if( s0 == "drink" ) {drinkCount += itm.quantity;} - else if(s0 == "food"){mealsCount += itm.quantity;} - else if(s0 == "plant"){plantCount += itm.quantity;} - else if(s0 == "fish"){fishCount += itm.quantity;} - else if(s0 == "meat"){meatCount += itm.quantity;} - else if(s0 == "wood"){logsCount += itm.quantity;} - else if(s0 == "cloth"){clothCount += itm.quantity;} - else if(s0 == "bar") //need to break it down by ItemDescription to get the different types of bars. - { - barCount = barCount + itm.quantity; - if(s1.find("PIG_IRON")!=string::npos){pigIronBars++;} - else if(s1.find("IRON")!=string::npos){ironBars++;} - else if(s1.find("GOLD")!=string::npos){goldBars++;} - else if(s1.find("SILVER")!=string::npos){silverBars++;} - else if(s1.find("COPPER")!=string::npos){copperBars++;} - else if(s1.find("STEEL")!=string::npos){steelBars++;} - else if(s1.find("COAL")!=string::npos){fuel++;} - } - /*if(s0 != "boulder" && s0 != "thread"){ - fprintf(pFile,"%5d: %12s - %64s - [%d]\n", idx, Items->getItemClass(itm.matdesc.itemType).c_str(), Items->getItemDescription(itm, Materials).c_str(), itm.quantity); - }*/ - } - DF->Resume(); - end_time = GetTimeMs64(); - //printf("%d - %d\n", (clock()/CLOCKS_PER_SEC),(clock()/CLOCKS_PER_SEC)%60); - height = LINES; - width = COLS; - starty = (LINES - height) / 2; - startx = (COLS - width) / 2; - - my_win = create_newwin(height, width, starty, startx); - -#ifdef LINUX_BUILD - sleep(10); -#else - Sleep(10000); -#endif - - } while(true); - - endwin(); /* End curses mode */ - cerr.rdbuf (strm_buffer); // restore old output buffer - file.close(); - - return 0; -} \ No newline at end of file diff --git a/needs_porting/dfstatus.lua b/needs_porting/dfstatus.lua new file mode 100644 index 000000000..180d2afa3 --- /dev/null +++ b/needs_porting/dfstatus.lua @@ -0,0 +1,110 @@ +-- dfstatus 1.04 - a quick access status screen. +-- written by enjia2000@gmail.com + +local gui = require 'gui' + +function draw() + screen2 = gui.FramedScreen{ + frame_style = gui.GREY_LINE_FRAME, + frame_title = 'dfstatus', + frame_width = 16, + frame_height = 17, + frame_inset = 1, + } +end + +if (not shown) then + draw() + screen2:show() + shown = true +else + shown = nil + screen2:dismiss() +end + +function screen2:onRenderBody(dc) + local drink = 0 + local wood = 0 + --local meat = 0 + --local raw_fish = 0 + --local plants = 0 + local prepared_meals = 0 + + local fuel = 0 + local pigiron = 0 + local iron = 0 + local steel = 0 + + local silver = 0 + local copper = 0 + local gold = 0 + + local tannedhides = 0 + local cloth = 0 + + for _,item in ipairs(df.global.world.items.all) do + if(not item.flags.rotten and not item.flags.dump and not item.flags.forbid and not item.flags.in_building and not item.flags.trader) then + if (item:getType() == 5) then wood = wood + item:getStackSize() + elseif (item:getType() == 68) then drink = drink + item:getStackSize() + elseif (item:getType() == 54) then tannedhides = tannedhides + item:getStackSize() + elseif (item:getType() == 57) then cloth = cloth + item:getStackSize() + --elseif (item:getType() == 47) then meat = meat + item:getStackSize() + --elseif (item:getType() == 49) then raw_fish = raw_fish + item:getStackSize() + --elseif (item:getType() == 53) then plants = plants + item:getStackSize() + elseif (item:getType() == 71) then prepared_meals = prepared_meals + item:getStackSize() + elseif (item:getType() == 0) then + for token in string.gmatch(dfhack.items.getDescription(item,0),"[^%s]+") do + if (token == "silver") then silver = silver + item:getStackSize() + elseif (token == "charcoal" or token == "coke") then fuel = fuel + item:getStackSize() + elseif (token == "iron") then iron = iron + item:getStackSize() + elseif (token == "pig") then pigiron = pigiron + item:getStackSize() + elseif (token == "copper") then copper = copper + item:getStackSize() + elseif (token == "gold") then gold = gold + item:getStackSize() + elseif (token == "steel") then steel = steel + item:getStackSize() + end + break -- only need to look at the 1st token of each item. + end + end + end + end + dc:string("Drinks: ".. drink, COLOR_LIGHTGREEN) + dc:newline(0) + dc:string("Meals: ".. prepared_meals, COLOR_LIGHTGREEN) + dc:newline(0) + dc:newline(0) + dc:string("Wood: ".. wood, COLOR_LIGHTGREEN) + dc:newline(0) + dc:newline(0) + dc:string("Hides: ".. tannedhides, COLOR_LIGHTGREEN) + dc:newline(0) + dc:string("Cloth: ".. cloth, COLOR_LIGHTGREEN) + dc:newline(0) + --dc:string("Raw Fish: ".. raw_fish, COLOR_LIGHTGREEN) + --dc:newline(0) + --dc:string("Plants: ".. plants, COLOR_LIGHTGREEN) + --dc:newline(0) + dc:newline(0) + dc:string("Bars:", COLOR_LIGHTGREEN) + dc:newline(1) + dc:string("Fuel: ".. fuel, COLOR_LIGHTGREEN) + dc:newline(1) + dc:string("Pig Iron: ".. pigiron, COLOR_LIGHTGREEN) + dc:newline(1) + dc:string("Steel: ".. steel, COLOR_LIGHTGREEN) + dc:newline(1) + dc:string("Iron: ".. iron, COLOR_LIGHTGREEN) + dc:newline(1) + dc:newline(1) + dc:string("Copper: ".. copper, COLOR_LIGHTGREEN) + dc:newline(1) + dc:string("Silver: ".. silver, COLOR_LIGHTGREEN) + dc:newline(1) + dc:string("Gold: ".. gold, COLOR_LIGHTGREEN) +end + +function screen2:onInput(keys) + if keys.LEAVESCREEN or keys.SELECT then + shown = nil + self:dismiss() + end +end diff --git a/needs_porting/digger.cpp b/needs_porting/digger.cpp deleted file mode 100644 index 86a9b84b2..000000000 --- a/needs_porting/digger.cpp +++ /dev/null @@ -1,363 +0,0 @@ -// digger.cpp - -// NOTE currently only works with trees - -// TODO add a sort of "sub-target" to dig() to make it able to designate stone as well - -#include -#include -#include -#include -#include -#include -using namespace std; - -#include -#include -#include - -// counts the occurances of a certain element in a vector -// used to determine of a given tile is a target -int vec_count(vector& vec, uint16_t t) -{ - int count = 0; - for (uint32_t i = 0; i < vec.size(); ++i) - { - if (vec[i] == t) - ++count; - } - return count; -} - -// splits a string on a certain char -// -// src is the string to split -// delim is the delimiter to split the string around -// tokens is filled with every occurance between delims -void string_split(vector& tokens, const std::string& src, const std::string& delim) -{ - std::string::size_type start = 0; - std::string::size_type end; - while (true) - { - end = src.find(delim, start); - tokens.push_back(src.substr(start, end - start)); - if (end == std::string::npos) // last token handled - break; - start = end + delim.size(); // skip next delim - } -} - -// this is used to parse the command line options -void parse_int_csv(vector& targets, const std::string& src) -{ - std::string::size_type start = 0; - std::string::size_type end; - while (true) - { - end = src.find(",", start); - targets.push_back(atoi(src.substr(start, end - start).c_str())); - if (end == std::string::npos) // last token handled - break; - start = end + 1; // skip next delim - } -} - -struct DigTarget -{ - DigTarget() : - source_distance(0), - grid_x(0), grid_y(0), - local_x(0), local_y(0), - real_x(0), real_y(0), z(0) - { - } - - DigTarget( - int realx, int realy, int _z, - int sourcex, int sourcey, int sourcez) : - real_x(realx), real_y(realy), z(_z) - { - grid_x = realx/16; - grid_y = realy/16; - - local_x = realx%16; - local_y = realy%16; - - source_distance = manhattan_distance( - real_x, real_y, z, - sourcex, sourcey, sourcez); - } - - DigTarget( - int gridx, int gridy, int _z, - int localx, int localy, - int sourcex, int sourcey, int sourcez) : - grid_x(gridx), grid_y(gridy), - local_x(localx), local_y(localy), - z(_z) - { - real_x = (grid_x*16)+local_x; - real_y = (grid_y*16)+local_y; - - source_distance = manhattan_distance( - real_x, real_y, z, - sourcex, sourcey, sourcez); - } - - int source_distance; // the distance to the source coords, used for sorting - - int grid_x, grid_y; // what grid the target is in - int local_x, local_y; // on what coord in the grid the target is in (0-16) - int real_x, real_y; // real coordinates for target, thats grid*16+local - int z; // z position for target, stored plain since there arent z grids - - bool operator<(const DigTarget& o) const { return source_distance < o.source_distance; } - -private: - // calculates the manhattan distance between two coords - int manhattan_distance(int x, int y, int z, int xx, int yy, int zz) - { - return abs(x-xx)+abs(y-yy)+abs(z-zz); - } -}; - -int dig(DFHack::Maps* Maps, - vector& targets, - int num = -1, - const int x_source = 0, - const int y_source = 0, - const int z_source = 0, - bool verbose = false) -{ - if (num == 0) - return 0; // max limit of 0, nothing to do - - uint32_t x_max,y_max,z_max; - DFHack::designations40d designations; - DFHack::tiletypes40d tiles; - Maps->getSize(x_max,y_max,z_max); - - // every tile found, will later be sorted by distance to source - vector candidates; - - if (verbose) - cout << "source is " << x_source << " " << y_source << " " << z_source << endl; - - // walk the map - for(uint32_t x = 0; x < x_max; x++) - { - for(uint32_t y = 0; y < y_max; y++) - { - for(uint32_t z = 0; z < z_max; z++) - { - if(Maps->isValidBlock(x,y,z)) - { - // read block designations and tiletype - Maps->ReadDesignations(x,y,z, &designations); - Maps->ReadTileTypes(x,y,z, &tiles); - - // search all tiles for dig targets: - // visible, not yet marked for dig and matching tile type - for(uint32_t lx = 0; lx < 16; lx++) - { - for(uint32_t ly = 0; ly < 16; ly++) - { - if (/*designations[lx][ly].bits.hidden == 0 && */ - designations[lx][ly].bits.dig == 0 && - vec_count(targets, DFHack::tileShape(tiles[lx][ly])) > 0) - { - DigTarget dt( - x, y, z, - lx, ly, - x_source, y_source, z_source); - candidates.push_back(dt); - - if (verbose) - { - cout << "target found at " << dt.real_x << " " << dt.real_y << " " << dt.z; - cout << ", " << dt.source_distance << " tiles to source" << endl; - } - } - } // local y - } // local x - } - } - } - } - - // if we found more tiles than was requested, sort them by distance to source, - // keep the front 'num' elements and drop the rest - if (num != -1 && candidates.size() > (unsigned int)num) - { - sort(candidates.begin(), candidates.end()); - candidates.resize(num); - } - num = candidates.size(); - - if (verbose) - cout << "=== proceeding to designating targets ===" << endl; - - // mark the tiles for actual digging - for (vector::const_iterator i = candidates.begin(); i != candidates.end(); ++i) - { - if (verbose) - { - cout << "designating at " << (*i).real_x << " " << (*i).real_y << " " << (*i).z; - cout << ", " << (*i).source_distance << " tiles to source" << endl; - } - - // TODO this could probably be made much better, theres a big chance the trees are on the same grid - Maps->ReadDesignations((*i).grid_x, (*i).grid_y, (*i).z, &designations); - designations[(*i).local_x][(*i).local_y].bits.dig = DFHack::designation_default; - Maps->WriteDesignations((*i).grid_x, (*i).grid_y, (*i).z, &designations); - - // Mark as dirty so the jobs are properly picked up by the dwarves - Maps->WriteDirtyBit((*i).grid_x, (*i).grid_y, (*i).z, true); - } - - return num; -} - -void test() -{ - ////////////////////////// - // DigTarget - { - DigTarget dt( - 20, 35, 16, - 10, 12, 14); - - assert(dt.grid_x == 1); - assert(dt.grid_y == 2); - - assert(dt.local_x == 4); - assert(dt.local_y == 3); - - assert(dt.real_x == 20); - assert(dt.real_y == 35); - - assert(dt.z == 16); - assert(dt.source_distance == 35); - } - { - DigTarget dt( - 2, 4, 16, - 5, 10, - 10, 12, 14); - - assert(dt.grid_x == 2); - assert(dt.grid_y == 4); - - assert(dt.local_x == 5); - assert(dt.local_y == 10); - - assert(dt.real_x == 37); - assert(dt.real_y == 74); - - assert(dt.z == 16); - assert(dt.source_distance == 91); - } - - ////////////////////////// - // string splitter - { - vector tokens; - string src = "10,9,11"; - string delim = ","; - string_split(tokens, src, delim); - - assert(tokens.size() == 3); - assert(tokens[0] == "10"); - assert(tokens[1] == "9"); - assert(tokens[2] == "11"); - } - { - vector tokens; - string src = "10"; - string delim = ","; - string_split(tokens, src, delim); - - assert(tokens.size() == 1); - assert(tokens[0] == "10"); - } - { - vector targets; - parse_int_csv(targets, "9,10"); - assert(targets[0] == 9); - assert(targets[1] == 10); - } -} - -int main (int argc, char** argv) -{ - //test(); - - // Command line options - string s_targets; - string s_origin; - bool verbose; - int max = 10; - argstream as(argc,argv); - - as >>option('v',"verbose",verbose,"Active verbose mode") - >>parameter('o',"origin",s_origin,"Close to where we should designate targets, format: x,y,z") - >>parameter('t',"targets",s_targets,"What kinds of tile we should designate, format: type1,type2") - >>parameter('m',"max",max,"The maximum limit of designated targets") - >>help(); - - // some commands need extra care - vector targets; - parse_int_csv(targets, s_targets); - - vector origin; - parse_int_csv(origin, s_origin); - - // sane check - if (!as.isOk()) - { - cout << as.errorLog(); - } - else if (targets.size() == 0 || origin.size() != 3) - { - cout << as.usage(); - } - else - { - DFHack::ContextManager DFMgr("Memory.xml"); - DFHack::Context *DF = DFMgr.getSingleContext(); - try - { - DF->Attach(); - } - catch (exception& e) - { - cerr << e.what() << endl; - #ifndef LINUX_BUILD - cin.ignore(); - #endif - return 1; - } - DFHack::Maps *Maps = DF->getMaps(); - if (Maps && Maps->Start()) - { - int count = dig(Maps, targets, max, origin[0],origin[1],origin[2], verbose); - cout << count << " targets designated" << endl; - Maps->Finish(); - - if (!DF->Detach()) - { - cerr << "Unable to detach DF process" << endl; - } - } - else - { - cerr << "Unable to init map" << endl; - } - } - #ifndef LINUX_BUILD - cout << "Done. Press any key to continue" << endl; - cin.ignore(); - #endif - return 0; -} diff --git a/needs_porting/digger2.cpp b/needs_porting/digger2.cpp deleted file mode 100644 index ea34b8c2c..000000000 --- a/needs_porting/digger2.cpp +++ /dev/null @@ -1,180 +0,0 @@ -/** - * @file digger2.cpp - * @author rOut - * - * Improved digger tool. - * - * Takes a text file as first an only argument. - * The text file is read as a grid, and every character represents a designation for a tile. - * Allowed characters are 'd' for dig, 'u' for up stairs, 'j' for down stairs, 'i' for up and down stairs, 'h' for channel, 'r' for upward ramp and 'x' to remove designation. - * Other characters don't do anything and can be used for padding. - * The designation pattern is the wrote in game memory, centered on the current cursor position. Thus, the game needs to be in designation mode or, perhaps, any other mode that have a cursor. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -using namespace std; - -#include -#include -#define BLOCK_SIZE 16 - - -void dig(DFHack::Maps* layers, DFHack::Gui* Gui, ::std::vector< ::std::string >& dig_map, bool verbose = false) -{ - int32_t x_cent; - int32_t y_cent; - int32_t z_cent; - Gui->getCursorCoords(x_cent, y_cent, z_cent); - -// ::std::cout << "x_cent: " << x_cent << " y_cent: " << y_cent << " z_cent: " << z_cent << ::std::endl; - - int32_t z_from = z_cent; - int32_t z = 0; - - uint32_t x_max; - uint32_t y_max; - uint32_t z_max; - layers->getSize(x_max, y_max, z_max); - -// ::std::cout << "x_max: " << x_max << " y_max: " << y_max << " z_max: " << z_max << ::std::endl; - - int32_t dig_height = dig_map.size(); - int32_t y_from = y_cent - (dig_height / 2); -// ::std::cout << "dig_height: " << dig_height << ::std::endl; -// ::std::cout << "y_from: " << y_from << ::std::endl; - - int32_t y = 0; - DFHack::designations40d designations; - DFHack::tiletypes40d tiles; - ::std::vector< ::std::string >::iterator str_it; - for (str_it = dig_map.begin(); str_it != dig_map.end(); ++str_it) { - int32_t dig_width = str_it->size(); - int32_t x_from = x_cent - (dig_width / 2); - -// ::std::cout << "x_cent: " << x_cent << " y_cent: " << y_cent << " z_cent: " << z_cent << ::std::endl; -// ::std::cout << "dig_width: " << dig_width << ::std::endl; -// ::std::cout << "x_from: " << x_from << ::std::endl; - - int32_t x = 0; - ::std::string::iterator chr_it; - for (chr_it = str_it->begin(); chr_it != str_it ->end(); ++chr_it) - { - int32_t x_grid = (x_from + x) / BLOCK_SIZE; - int32_t y_grid = (y_from + y) / BLOCK_SIZE; - int32_t z_grid = z_from + z; - int32_t x_locl = (x_from + x) - x_grid * BLOCK_SIZE; - int32_t y_locl = (y_from + y) - y_grid * BLOCK_SIZE; - int32_t z_locl = 0; - - if (x_grid >= 0 && y_grid >= 0 && x_grid < x_max && y_grid < y_max) - { - // TODO this could probably be made much better, theres a big chance the trees are on the same grid - layers->ReadDesignations(x_grid, y_grid, z_grid, &designations); - layers->ReadTileTypes(x_grid, y_grid, z_grid, &tiles); - -// ::std::cout << ::std::hex << "designations: " << designations[x_locl][y_locl].bits.dig << ::std::dec << ::std::endl; - DFHack::naked_designation & des = designations[x_locl][y_locl].bits; - if ( DFHack::tileShape(tiles[x_locl][y_locl]) == DFHack::WALL) - { - switch ((char) *chr_it) - { - case 'd': - des.dig = DFHack::designation_default; - break; - case 'u': - des.dig = DFHack::designation_u_stair; - break; - case 'j': - des.dig = DFHack::designation_d_stair; - break; - case 'i': - des.dig = DFHack::designation_ud_stair; - break; - case 'h': - des.dig = DFHack::designation_channel; - break; - case 'r': - des.dig = DFHack::designation_ramp; - break; - case 'x': - des.dig = DFHack::designation_no; - break; - } - - if (verbose) - { - ::std::cout << "designating " << (char) *chr_it << " at " << x_from + x << " " << y_from + y << " " << z_from + z << ::std::endl; - } - - layers->WriteDesignations(x_grid, y_grid, z_grid, &designations); - - // Mark as dirty so the jobs are properly picked up by the dwarves - layers->WriteDirtyBit(x_grid, y_grid, z_grid, true); - } - } - - ++x; - } - ++y; - } -} - -int main(int argc, char** argv) { - if(argc < 2) { - ::std::cout << "gimme a file!" << ::std::endl; - return 1; - } - - ::std::ifstream map_in(argv[1]); - - ::std::vector< ::std::string > dig_map; - while (map_in.good() && !map_in.eof() && !map_in.bad()) { - ::std::string line; - map_in >> line; - - dig_map.push_back(line); - } - dig_map.resize(dig_map.size() - 1); - - DFHack::ContextManager DFMgr("Memory.xml"); - DFHack::Context * DF = DFMgr.getSingleContext(); - - try { - DF->Attach(); - } catch (::std::exception& e) { - ::std::cerr << e.what() << ::std::endl; -#ifndef LINUX_BUILD - ::std::cin.ignore(); -#endif - return 1; - } - - DFHack::Maps *layers = DF->getMaps(); - if (layers && layers->Start()) { - - dig(layers, DF->getGui(), dig_map, true); - - ::std::cout << "Finished digging" << ::std::endl; - layers->Finish(); - - if (!DF->Detach()) { - ::std::cerr << "Unable to detach DF process" << ::std::endl; - } - - } else { - ::std::cerr << "Unable to init map" << ::std::endl; - } - -#ifndef LINUX_BUILD - ::std::cout << "Done. Press any key to continue" << ::std::endl; - ::std::cin.ignore(); -#endif - return 0; -} diff --git a/needs_porting/fix-3708.cpp b/needs_porting/fix-3708.cpp deleted file mode 100644 index 2cfaed912..000000000 --- a/needs_porting/fix-3708.cpp +++ /dev/null @@ -1,217 +0,0 @@ -/* Fixes bug 3708 (Ghosts that can't be engraved on a slab). - - Cause of the bug: - - In order to be engraved on a slab, the creature must be - a historical figure, i.e. be in the historical figure list - of the Legends mode. It seems that caravan guards are not - added to that list until they do something notable, e.g. - kill a goblin. Unfortunately, their own death doesn't - trigger this sometimes. - - Solution: - - Steal a historical figure entry from a dead goblin, by - replacing the IDs in the structures; also overwrite his - name, race and profession to make the menus make slightly - more sense. - - Downsides: - - - Obviously, this is an ugly hack. - - The Legends mode still lists the guard as belonging to - the goblin civilization, and killed by whoever killed the - original goblin. There might be other inconsistencies. - - Positive sides: - - - Avoids messing with tricky creature control code, - by allowing the ghost to be removed naturally. - */ - -#include -#include -#include -#include -#include -#include -using namespace std; - -#define DFHACK_WANT_MISCUTILS -#include - -enum likeType -{ - FAIL = 0, - MATERIAL = 1, - ITEM = 2, - FOOD = 3 -}; - -DFHack::Materials * Materials; -DFHack::VersionInfo *mem; -DFHack::Creatures * Creatures = NULL; - -void printCreature(DFHack::Context * DF, const DFHack::t_creature & creature) -{ - cout << "Address: " << hex << creature.origin << dec << ", creature race: " << Materials->raceEx[creature.race].rawname - << ", position: " << creature.x << "x " << creature.y << "y "<< creature.z << "z" << endl - << "Name: " << creature.name.first_name; - - if (creature.name.nickname[0]) - cout << " `" << creature.name.nickname << "'"; - - DFHack::Translation * Tran = DF->getTranslation(); - - cout << " " << Tran->TranslateName(creature.name,false) - << " (" << Tran->TranslateName(creature.name,true) << ")" << endl; - - cout << "Profession: " << mem->getProfession(creature.profession); - - if(creature.custom_profession[0]) - cout << ", custom: " << creature.custom_profession; - - uint32_t dayoflife = creature.birth_year*12*28 + creature.birth_time/1200; - cout << endl - << "Born on the year " << creature.birth_year - << ", month " << (creature.birth_time/1200/28) - << ", day " << ((creature.birth_time/1200) % 28 + 1) - << ", " << dayoflife << " days lived." << endl << endl; -} - - -int main (int numargs, char ** args) -{ - DFHack::World * World; - DFHack::ContextManager DFMgr("Memory.xml"); - DFHack::Context* DF; - try - { - DF = DFMgr.getSingleContext(); - DF->Attach(); - } - catch (exception& e) - { - cerr << e.what() << endl; - #ifndef LINUX_BUILD - cin.ignore(); - #endif - return 1; - } - - Creatures = DF->getCreatures(); - Materials = DF->getMaterials(); - World = DF->getWorld(); - DFHack::Translation * Tran = DF->getTranslation(); - - uint32_t numCreatures; - if(!Creatures->Start(numCreatures)) - { - cerr << "Can't get creatures" << endl; - #ifndef LINUX_BUILD - cin.ignore(); - #endif - return 1; - } - - Materials->ReadCreatureTypes(); - Materials->ReadCreatureTypesEx(); - - mem = DF->getMemoryInfo(); - DFHack::Process *p = DF->getProcess(); - - if(!Tran->Start()) - { - cerr << "Can't get name tables" << endl; - return 1; - } - - DFHack::OffsetGroup *ogc = mem->getGroup("Creatures")->getGroup("creature"); - uint32_t o_flags3 = ogc->getOffset("flags3"); - uint32_t o_c_hfid = ogc->getGroup("advanced")->getOffset("hist_figure_id"); - - std::list goblins; - std::list ghosts; - - for(uint32_t i = 0; i < numCreatures; i++) - { - DFHack::t_creature temp; - Creatures->ReadCreature(i,temp); - - int32_t hfid = p->readDWord(temp.origin + o_c_hfid); - - if (hfid > 0) { - if (temp.flags1.bits.dead) { - std::string name = Materials->raceEx[temp.race].rawname; - if (name == "GOBLIN") - goblins.push_back(i); - } - } else { - uint32_t flags3 = p->readDWord(temp.origin + o_flags3); - if (!(flags3 & 0x1000)) - continue; - - ghosts.push_back(i); - } - } - - if (goblins.size() >= ghosts.size() && ghosts.size() > 0) - { - DFHack::OffsetGroup *grp_figures = mem->getGroup("Legends")->getGroup("figures"); - uint32_t f_vector = p->readDWord(grp_figures->getAddress("vector")); - uint32_t f_id = grp_figures->getOffset("figure_id"); - uint32_t f_unit = grp_figures->getOffset("unit_id"); - uint32_t f_name = grp_figures->getOffset("name"); - uint32_t f_race = grp_figures->getOffset("race"); - uint32_t f_profession = grp_figures->getOffset("profession"); - - for (std::list::iterator it = ghosts.begin(); it != ghosts.end(); ++it) - { - int i = *it; - DFHack::t_creature ghost; - Creatures->ReadCreature(i,ghost); - - printCreature(DF,ghost); - - int igoblin = goblins.front(); - goblins.pop_front(); - DFHack::t_creature goblin; - Creatures->ReadCreature(igoblin,goblin); - - printCreature(DF,goblin); - - int32_t hfid = p->readDWord(goblin.origin + o_c_hfid); - uint32_t fptr = p->readDWord(f_vector + 4*hfid); - - if (p->readDWord(fptr + f_id) != hfid || - p->readDWord(fptr + f_unit) != goblin.id || - p->readWord(fptr + f_race) != goblin.race) - { - cout << "Data structure inconsistency detected, aborting."; - break; - } - - if (1) { - p->writeDWord(goblin.origin + o_c_hfid, -1); - p->writeDWord(ghost.origin + o_c_hfid, hfid); - p->writeDWord(fptr + f_unit, ghost.id); - p->writeWord(fptr + f_race, ghost.race); - p->writeWord(fptr + f_profession, ghost.profession); - Creatures->CopyNameTo(ghost, fptr + f_name); - cout << "Pair succesfully patched." << endl << endl; - } - } - } - else - { - cout << "No suitable ghosts, or not enough goblins." << endl; - } - - Creatures->Finish(); - DF->Detach(); - #ifndef LINUX_BUILD - cout << "Done. Press any key to continue" << endl; - cin.ignore(); - #endif - return 0; -} diff --git a/needs_porting/lair.cpp b/needs_porting/lair.cpp deleted file mode 100644 index 7bf3ddc28..000000000 --- a/needs_porting/lair.cpp +++ /dev/null @@ -1,74 +0,0 @@ -// This is a simple bit writer... it marks the whole map as a creature lair, preventing item scatter. - -#include -#include -#include -using namespace std; -#include -#include - -int main (void) -{ - bool temporary_terminal = TemporaryTerminal(); - uint32_t x_max,y_max,z_max; - DFHack::occupancies40d occupancies; - - DFHack::ContextManager DFMgr("Memory.xml"); - DFHack::Context *DF; - try - { - DF = DFMgr.getSingleContext(); - DF->Attach(); - } - catch (exception& e) - { - cerr << e.what() << endl; - if(temporary_terminal) - cin.ignore(); - return 1; - } - - DFHack::Maps *Maps =DF->getMaps(); - - // init the map - if(!Maps->Start()) - { - cerr << "Can't init map." << endl; - if(temporary_terminal) - cin.ignore(); - return 1; - } - - cout << "Designating, please wait..." << endl; - - Maps->getSize(x_max,y_max,z_max); - for(uint32_t x = 0; x< x_max;x++) - { - for(uint32_t y = 0; y< y_max;y++) - { - for(uint32_t z = 0; z< z_max;z++) - { - if(Maps->isValidBlock(x,y,z)) - { - // read block designations - Maps->ReadOccupancy(x,y,z, &occupancies); - //Maps->ReadTileTypes(x,y,z, &tiles); - // change the monster_lair flag to 1 - for (uint32_t i = 0; i < 16;i++) for (uint32_t j = 0; j < 16;j++) - { - // add tile type chack here - occupancies[i][j].bits.monster_lair = 1; - } - // write the designations back - Maps->WriteOccupancy(x,y,z, &occupancies); - } - } - } - } - if(temporary_terminal) - { - cout << "The map has been marked as a creature lair. Items shouldn't scatter." << endl; - cin.ignore(); - } - return 0; -} diff --git a/needs_porting/reveal.py b/needs_porting/reveal.py deleted file mode 100644 index 268e1a3a8..000000000 --- a/needs_porting/reveal.py +++ /dev/null @@ -1,93 +0,0 @@ -import time -from context import ContextManager - -class HideBlock(object): - __slots__ = [ "x", "y", "z", "hiddens" ] - - def __init__(self, *args, **kwargs): - self.x = 0 - self.y = 0 - self.z = 0 - self.hiddens = [[0 for i in xrange(16)] for j in xrange(16)] - -df_cm = ContextManager("Memory.xml") -df = df_cm.get_single_context() - -df.attach() - -m = df.maps -w = df.world - -print "Pausing..." - -w.start() - -#this mimics the hack in the C++ reveal tool that attempts to ensure that DF isn't in the middle of -#a frame update -w.set_pause_state(True) -df.resume() -time.sleep(1) -df.suspend() - -w.finish() - -m.start() - -print "Revealing, please wait..." - -m_x, m_y, m_z = m.size -hide_blocks = [] - -for x in xrange(m_x): - for y in xrange(m_y): - for z in xrange(m_z): - if m.is_valid_block(x, y, z): - hb = HideBlock() - - hb.x = x - hb.y = y - hb.z = z - - d = m.read_designations(x, y, z) - - for k_i, i in enumerate(d): - for k_j, j in enumerate(i): - hb.hiddens[k_i][k_j] = j.bits.hidden - - j.bits.hidden = 0 - - hide_blocks.append(hb) - - m.write_designations(x, y, z, d) - -m.finish() -df.detach() - -print "Map revealed. The game has been paused for you." -print "Unpausing can unleash the forces of hell!" -print "Press any key to unreveal." -print "Close to keep the map revealed !!FOREVER!!" - -raw_input() - -print "Unrevealing...please wait" - -df.attach() -m = df.maps -m.start() - -for h in hide_blocks: - d = m.read_designations(h.x, h.y, h.z) - - for k_i, i in enumerate(h.hiddens): - for k_j, j in enumerate(i): - d[k_i][k_j].bits.hidden = j - - m.write_designations(h.x, h.y, h.z, d) - -m.finish() - -print "Done. Press any key to continue" -raw_input() - -df.detach() \ No newline at end of file diff --git a/needs_porting/veinswap.cpp b/needs_porting/veinswap.cpp deleted file mode 100644 index 668386100..000000000 --- a/needs_porting/veinswap.cpp +++ /dev/null @@ -1,229 +0,0 @@ -#include -#include -#include -#include -#include -#include -using namespace std; - -#include - -#include -#include -#include - - -//Globals -DFHack::Context *DF; -DFHack::Maps *maps; -DFHack::Gui *gui; -DFHack::Materials *mats; - - - -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// -int main (void) -{ - int32_t - cx,cy,z, //cursor coords - tx,ty, //tile coords within block - bx,by; //block coords - //DFHack::designations40d designations; - //DFHack::tiletypes40d tiles; - //DFHack::t_temperatures temp1,temp2; - uint32_t x_max,y_max,z_max; - - DFHack::ContextManager DFMgr("Memory.xml"); - try - { - - DF=DFMgr.getSingleContext(); - DF->Attach(); - maps = DF->getMaps(); - maps->Start(); - maps->getSize(x_max,y_max,z_max); - gui = DF->getGui(); - mats = DF->getMaterials(); - mats->ReadAllMaterials(); - if(!mats->ReadInorganicMaterials()) - { - printf("Error: Could not load materials!\n"); - #ifndef LINUX_BUILD - cin.ignore(); - #endif - return 1; - } - } - catch (exception& e) - { - cerr << e.what() << endl; - #ifndef LINUX_BUILD - cin.ignore(); - #endif - return 1; - } - - - - bool end = false; - cout << "Welcome to the Vein Swap tool.\nType 'help' or ? for a list of available commands, 'q' to quit" << endl; - string mode = ""; - string command = ""; - - while(!end) - { - DF->Resume(); - - cout << endl << ":"; - getline(cin, command); - int ch = command[0]; - if(command.length()<=0) ch=0; - if( ((int)command.find("help")) >=0 ) ch='?'; //under windows, find was casting unsigned! - - //Process user command. - switch(ch) - { - case 'h': - case '?': - cout << "" << endl - << "Commands:" << endl - << "p - print vein at cursor" << endl - << "m - print all inorganic material types" << endl - << "r MatTo - replace the vein at cursor with MatTo" << endl - << "R Percent MatFrom MatTo - replace Percent of MatFrom veins with MatTo veins" << endl - << "help OR ? - print this list of commands" << endl - << "q - quit" << endl - << endl - << "Usage:\n\t" << endl; - break; - case 'p': - case 10: - case 13: - case 0: - { - //Print current cursor - mats->ReadAllMaterials(); - - if(!maps->Start()) - { - cout << "Can't see any DF map loaded." << endl; - break; - } - if(!gui->getCursorCoords(cx,cy,z)) - { - cout << "Can't get cursor coords! Make sure you have a cursor active in DF." << endl; - break; - } - //cout << "cursor coords: " << x << "/" << y << "/" << z << endl; - tx=cx%16; ty=cy%16; - bx=cx/16; by=cy/16; - DFHack::DFCoord xyz(cx,cy,z); - - printf("Cursor[%d,%d,%d] Block(%d,%d) Tile(%d,%d)\n", cx,cy,z, bx,by, tx,ty ); - - if(!maps->isValidBlock(bx,by,z)) - { - cout << "Invalid block." << endl; - break; - } - - vector veinVector; - int found=0; - maps->ReadVeins(bx,by,z,&veinVector); - printf("Veins in block (%d):\n",veinVector.size()); - for(unsigned long i=0;iinorganic[veinVector[i].type].id - ); - } - printf("Cursor is%s in vein.\n", (found?"":" not") ); - - maps->Finish(); - DF->Resume(); - } - break; - case 'v': - break; - case 'm': - break; - case 'R': - break; - case 'q': - end = true; - cout << "Bye!" << endl; - break; - case 'r': - DF->Suspend(); - do{ - //Process parameters - long matto = atol( command.c_str()+1 ); - if( matto < 0 || matto >= (long)mats->inorganic.size() ){ - cout << "Invalid material: " << matto << endl; - break; - } - - if(!maps->Start()) - { - cout << "Can't see any DF map loaded." << endl; - break; - } - if(!gui->getCursorCoords(cx,cy,z)) - { - cout << "Can't get cursor coords! Make sure you have a cursor active in DF." << endl; - break; - } - tx=cx%16; ty=cy%16; - bx=cx/16; by=cy/16; - printf("Cursor[%d,%d,%d] Block(%d,%d) Tile(%d,%d)\n", cx,cy,z, bx,by, tx,ty ); - - if(!maps->isValidBlock(bx,by,z)) - { - cout << "Invalid block." << endl; - break; - } - - //MapExtras::MapCache MC(maps); - //MapExtras::Block B(maps,DFHack::DFCoord(bx,by,z)); - - vector veinVector; - int v=-1; //the vector pointed to by the cursor - - mats->ReadAllMaterials(); - - maps->ReadVeins(bx,by,z,&veinVector); - for(unsigned long i=0 ; v<0 && iinorganic[veinVector[v].type].id, matto, mats->inorganic[matto].id ); -printf("%X\n",veinVector[v].vtable); - vector veinTable; - - veinVector[v].type = matto; - maps->WriteVein( &veinVector[v] ); - - - maps->Finish(); - - cout << endl << "Finished." << endl; - }while(0); - DF->Resume(); - break; - default: - cout << "Unknown command: " << command << endl; - }//end switch - - }//end while - - DF->Detach(); - //#ifndef LINUX_BUILD - //cout << "Done. Press any key to continue" << endl; - //cin.ignore(); - //#endif - return 0; -} From e011c390f10cabe3d1c67a96038a3de5a8ffcdf1 Mon Sep 17 00:00:00 2001 From: PeridexisErrant Date: Mon, 5 May 2014 15:52:21 +1000 Subject: [PATCH 089/676] formatted notes, deleted more went through `blame` and `history` to work out value of remaining stuff, formatted notes for other people to read and deleted a few more plugins --- needs_porting/_notes.txt | 92 +-- needs_porting/attachtest.py | 53 -- needs_porting/drawtile.cpp | 315 ---------- needs_porting/treedump.py | 52 -- needs_porting/veinlook.cpp | 1117 ----------------------------------- 5 files changed, 25 insertions(+), 1604 deletions(-) delete mode 100644 needs_porting/attachtest.py delete mode 100644 needs_porting/drawtile.cpp delete mode 100644 needs_porting/treedump.py delete mode 100644 needs_porting/veinlook.cpp diff --git a/needs_porting/_notes.txt b/needs_porting/_notes.txt index 04e123228..a653a7b56 100644 --- a/needs_porting/_notes.txt +++ b/needs_porting/_notes.txt @@ -1,72 +1,30 @@ Notes by PeridexisErrant, on the needs_porting scripts and plugins: -I've deleted "reveal", "lair", and "dfstatus.cpp" - they've been replaced by other more recent versions. (I added the dfstatus script). -Also deleted "digger", less useful than "digger2" and generally replaced by autochop. "digger2" is replaced by digfort. -And now "veinswap"; replaced by "changevein". "fix-3708.cpp" deals with a bug that the Mantis tracker marks as resolved. +I deleted: + attachtest.py obsolete + digger.cpp less useful than digger2, replaced by autochop + digger2.cpp replaced by digfort + dfstatus.cpp replaced by dfstatus.lua (which needs a little work too) + drawtile.cpp replaced by tiletypes + fix-3708.cpp obsolete, bug fixed in vanilla + lair.cpp replaced by lair + reveal.py replaced by reveal + treedump.py replaced by prospect & autochop + veinlook.cpp replaced by prospect + veinswap.cpp replaced by changevein + + +To investigate further: + creaturemanager.cpp modify skills and labors of creatures, kill creatures, etc; impressive but I suspect most functions implemented elsewhere + digpattern.cpp allows multi-Z designations. Obsolete, or is there more here? + incrementalsearch.cpp linux-only memory stuff; unqualified to judge + SegementedFinder.h more memory stuff -Need confirmation that they're obsolete before deleting (notes below): -attachtest.py, dfbauxtite.cpp, digpattern.cpp, drawtile.cpp, treedump.py, and veinlook.cpp - To port: -copypaste.cpp, hellhole.cpp, hotkeynotedump.py, itemdesignator.cpp (partial), position.py - - -All undeleted plugins: - -attachtest.py -- checks if dfhack is working with DF (i think) -- definitely replaced on windows, pretty sure also on other platforms - -copypaste.cpp -- a proof-of-concept plugin to allow copy-pasting in DF -- if I read it right, it does both terrain and buildings/constructions -- would be awesome if someone ported it! - -creaturemanager.cpp -- Display creatures; modify skills and labors of creatures; kill creatures; etc -- looks pretty impressive if incomplete (at 1500 lines I only looked over the comments) -- could maybe do with porting, but it's unfinished and I suspect that most functions are available elsewhere - -dfbauxtite.cpp -- as far as I can tell, this plugin changes the material of all mechanisms (printed string 'mechanisms', class 'item_trapparts') to Bauxtite; presumably from when this was the only magma-safe stone. -- I don't know of a newer alternative, but it's not really neccessary anymore - -digpattern.cpp -- I think all this one does is allow multi-z designations - no longer required. - -drawtile.cpp -- draws changes to tiles -- I think this is replaced by tiletypes, but not sure - -hellhole.cpp -- digs a hole to HFS instantly -- no known equivilant, needs porting - -hotkeynotedump.py -- outputs a list of hotkeys with names -- less useful with propper dfhack keybindings available, but still useful -- trivial to redo in lua (it's 20 lines, could be 10) - -incrementalsearch.cpp -- linux-only memory voodoo; leaving this alone - -itemdesignator.cpp -- applies flags to categories of items (eg item_microcline; forbid/dump/melt/on fire) -- appart from fire, replaced by Falconne's enhanced stocks screen -- port the fire part? - -position.py -- prints information about time (year/month/day/tick) and place (embark location/cursor pos) and window size -- the wider context can be written to gamelog on world load by "log-region.lua"; this doesn't seem to be included yet - -SegementedFinder.h -- no idea what's happening here - -treedump.py -- looks like it outputs a list of trees in a given volume -- overlaps with 'prospect' and 'autochop'; probably does not need porting - -veinlook.cpp -- I'm not sure what this does that "digv" and "reveal" don't but there must be something since it was around at the same time as reveal. - + copypaste.cpp high value target - a proof of concept plugin to allow copy-pasting in DF; does both terrain and buildings/constructions + dfbauxtite.cpp changes material of mechanisms to bauxtite (ie magma-safe) + hellhole.cpp instantly creates a hole to the HFS + hotkeynotedump.py outputs a list of hotkeys and names; most useful before keybindings were possible. Trival to port but low value. + itemdesignator.cpp mostly replaced by Falconne's enhanced stocks, but could port the interesting 'set fire to things' function + position.py outputs very detailed time& place info diff --git a/needs_porting/attachtest.py b/needs_porting/attachtest.py deleted file mode 100644 index e734e1ba1..000000000 --- a/needs_porting/attachtest.py +++ /dev/null @@ -1,53 +0,0 @@ -import time -from pydfhack import ContextManager - -df_cm = ContextManager("Memory.xml") -df = None - -def test_attach(): - global df - - if not df: - df = df_cm.get_single_context() - - if not df.attach(): - print "Unable to attach!" - return False - elif not df.detach(): - print "Unabled to detach!" - return False - else: - return True - -def suspend_test(): - global df - - if not df: - df = df_cm.get_single_context() - - print "Testing suspend/resume" - - df.attach() - - t1 = time.time() - - for i in xrange(1000): - df.suspend() - - if i % 10 == 0: - print "%i%%" % (i / 10.0,) - - df.resume() - - t2 = time.time() - - df.detach() - - print "suspend test done in $0.9f seconds" % (t2 - t1) - -if __name__ == "__main__": - if test_attach(): - suspend_test() - - print "Done. Press any key to continue" - raw_input() \ No newline at end of file diff --git a/needs_porting/drawtile.cpp b/needs_porting/drawtile.cpp deleted file mode 100644 index 72f2beb0f..000000000 --- a/needs_porting/drawtile.cpp +++ /dev/null @@ -1,315 +0,0 @@ -// - -#include -#include -#include -#include -#include -using namespace std; - -#include - - -#include -#include - -//Avoid including Windows.h because it causes name clashes -extern "C" __declspec(dllimport) void __stdcall Sleep(unsigned long milliseconds); - -//Trim -#define WHITESPACE " \t\r\n" -inline string trimr(const string & s, const string & t = WHITESPACE) -{ - string d (s); - string::size_type i (d.find_last_not_of (t)); - if (i == string::npos) - return ""; - else - return d.erase (d.find_last_not_of (t) + 1) ; -} -inline string triml(const string & s, const string & t = WHITESPACE) -{ - string d (s); - return d.erase (0, s.find_first_not_of (t)) ; -} -inline string trim(const string & s, const string & t = WHITESPACE) -{ - string d (s); - return triml(trimr(d, t), t); -} - -void printtiletype( int i ){ - printf("%s\n%4i ; %-13s ; %-11s ; %c ; %-12s ; %s\n", - ( DFHack::tileTypeTable[i].name ? DFHack::tileTypeTable[i].name : "[invalid tile]" ), - i, - ( DFHack::tileTypeTable[i].name ? DFHack::TileShapeString[ DFHack::tileTypeTable[i].shape ] : "" ), - ( DFHack::tileTypeTable[i].name ? DFHack::TileMaterialString[ DFHack::tileTypeTable[i].material ] : "" ), - ( DFHack::tileTypeTable[i].variant ? '0'+ DFHack::tileTypeTable[i].variant : ' ' ), - ( DFHack::tileTypeTable[i].special ? DFHack::TileSpecialString[ DFHack::tileTypeTable[i].special ] : "" ), - ( DFHack::tileTypeTable[i].direction.whole ? DFHack::tileTypeTable[i].direction.getStr() : "" ), - 0 - ); -} - - -int main (void) -{ - int32_t x,y,z,tx,ty; - //DFHack::designations40d designations; - DFHack::tiletypes40d tiles; - //DFHack::t_temperatures temp1,temp2; - uint32_t x_max,y_max,z_max; - int32_t oldT, newT; - int count, dirty; - - //Brush defaults - DFHack::TileShape BrushClass = DFHack::WALL; - DFHack::TileMaterial BrushMat = DFHack::tilematerial_invalid; - int BrushType = -1; - - DFHack::ContextManager DFMgr("Memory.xml"); - DFHack::Context *DF; - DFHack::Maps * Maps; - DFHack::Gui * Gui; - try - { - DF=DFMgr.getSingleContext(); - DF->Attach(); - Maps = DF->getMaps(); - Maps->Start(); - Maps->getSize(x_max,y_max,z_max); - Gui = DF->getGui(); - } - catch (exception& e) - { - cerr << e.what() << endl; - #ifndef LINUX_BUILD - cin.ignore(); - #endif - return 1; - } - bool end = false; - cout << "Welcome to the Tile Drawing tool.\nType 'help' or ? for a list of available commands, 'q' to quit" << endl; - string mode = "wall"; - string command = ""; - - while(!end) - { - DF->Resume(); - - cout << endl << ":"; - getline(cin, command); - int ch = command[0]; - if(command.length()<=0) ch=0; - if( ((int)command.find("help")) >=0 ) ch='?'; //under windows, find was casting unsigned! - switch(ch) - { - case '?': - cout << "Modes:" << endl - << "O - draw Open Space" << endl - << "M - draw material only (shape unchanged)" << endl - << "m number - use Material value entered" << endl - << "r - use Rock/stone material" << endl - << "l - use Soil material" << endl - << "v - use Vein material" << endl - << "H - draw tile shape only (material unchanged)" << endl - << "h number - draw Tile Shape value entered" << endl - << "w - draw Wall tiles" << endl - << "f - draw Floor tiles" << endl - << "t number - draw exact tile type entered" << endl - << "Commands:" << endl - << "p - print tile shapes and materials, and current brush" << endl - << "P - print all tile types" << endl - << "q - quit" << endl - << "help OR ? - print this list of commands" << endl - << "d - being drawing" << endl - << endl - << "Usage:\nChoose a mode (default is walls), then enter 'd' to being drawing.\nMove the cursor in DF wherever you want to draw.\nPress any key to pause drawing." << endl; - break; - case 'p': - //Classes - printf("\nTile Type Classes:\n"); - for(int i=0;iSuspend(); - kbhit(); //throw away, just to be sure. - for(;;) - { - if(!Maps->Start()) - { - cout << "Can't see any DF map loaded." << endl; - break; - } - if(!Gui->getCursorCoords(x,y,z)) - { - cout << "Can't get cursor coords! Make sure you have a cursor active in DF." << endl; - break; - } - //cout << "cursor coords: " << x << "/" << y << "/" << z << endl; - tx=x%16; ty=y%16; - - if(!Maps->isValidBlock(x/16,y/16,z)) - { - cout << "Invalid block." << endl; - break; - } - - //Read the tiles. - dirty=0; - Maps->ReadTileTypes((x/16),(y/16),z, &tiles); - oldT = tiles[tx][ty]; - - newT = -1; - if( 0=0 && BrushClass>=0 && ( BrushClass != DFHack::tileTypeTable[oldT].shape || BrushMat != DFHack::tileTypeTable[oldT].material) ){ - //Set tile material and class - newT = DFHack::findTileType(BrushClass,BrushMat, DFHack::tileTypeTable[oldT].variant , DFHack::tileTypeTable[oldT].special , DFHack::tileTypeTable[oldT].direction ); - if(newT<0) newT = DFHack::findTileType(BrushClass,BrushMat, DFHack::tilevariant_invalid, DFHack::tileTypeTable[oldT].special , DFHack::tileTypeTable[oldT].direction ); - if(newT<0) newT = DFHack::findTileType(BrushClass,BrushMat, DFHack::tilevariant_invalid , DFHack::tileTypeTable[oldT].special , (uint32_t)0 ); - }else if( BrushMat<0 && BrushClass>=0 && BrushClass != DFHack::tileTypeTable[oldT].shape ){ - //Set current tile class only, as accurately as can be expected - newT = DFHack::findSimilarTileType(oldT,BrushClass); - }else if( BrushClass<0 && BrushMat>=0 && BrushMat != DFHack::tileTypeTable[oldT].material ){ - //Set current tile material only - newT = DFHack::findTileType(DFHack::tileTypeTable[oldT].shape,BrushMat, DFHack::tileTypeTable[oldT].variant , DFHack::tileTypeTable[oldT].special , DFHack::tileTypeTable[oldT].direction ); - if(newT<0) newT = DFHack::findTileType(DFHack::tileTypeTable[oldT].shape,BrushMat, DFHack::tilevariant_invalid , DFHack::tileTypeTable[oldT].special , DFHack::tileTypeTable[oldT].direction ); - if(newT<0) newT = DFHack::findTileType(DFHack::tileTypeTable[oldT].shape,BrushMat, DFHack::tilevariant_invalid , DFHack::tileTypeTable[oldT].special , (uint32_t)0 ); - } - //If no change, skip it (couldn't find a good tile type, or already what we want) - if ( newT > 0 && oldT != newT ){ - //Set new tile type - tiles[tx][ty] = newT; - dirty=-1; - } - //If anything was changed, write it all. - if (dirty) - { - //Maps->WriteDesignations(x/16,y/16,z/16, &designations); - Maps->WriteTileTypes(x/16,y/16,z, &tiles); - printf("(%4d,%4d,%4d)",x,y,z); - ++count; - } - - Maps->Finish(); - - Sleep(10); - if( kbhit() ) break; - } - cin.clear(); - cout << endl << count << " tiles were drawn." << endl << "Drawing halted. Entering command mode." << endl; - } - continue; - break; - default: - cout << "Unknown command: " << command << endl; - } - - } - DF->Detach(); - #ifndef LINUX_BUILD - cout << "Done. Press any key to continue" << endl; - cin.ignore(); - #endif - return 0; -} diff --git a/needs_porting/treedump.py b/needs_porting/treedump.py deleted file mode 100644 index f6017c94b..000000000 --- a/needs_porting/treedump.py +++ /dev/null @@ -1,52 +0,0 @@ -from context import Context, ContextManager - -cm = ContextManager("Memory.xml") -df = cm.get_single_context() - -df.attach() - -gui = df.gui -veg = df.vegetation -mps = df.maps -mat = df.materials - -x, y, z = gui.get_cursor_coords() - -num_veg = veg.start() - -if x == -30000: - print "----==== Trees ====----" - - for i in xrange(num_veg): - t = veg.read(i) - - print "%d/%d/%d, %d:%d" % (t.x, t.y, t.z, t.type, t.material) -else: - #new method, gets the list of trees in a block. can show farm plants - if mps.start(): - pos_tuple = (x, y, z) - trees = mps.read_vegetation(x / 16, y / 16, z) - - if trees is not None: - for t in trees: - if (t.x, t.y, t.z) == pos_tuple: - print "----==== Tree at %d/%d/%d ====----" % pos_tuple - print str(t) - break - mps.finish() - - #old method, get the tree from the global vegetation vector. can't show farm plants - for i in xrange(num_veg): - t = veg.read(i) - - if (t.x, t.y, t.z) == pos_tuple: - print "----==== Tree at %d/%d/%d ====----" % pos_tuple - print str(t) - break - -veg.finish() - -df.detach() - -print "Done. Press any key to continue" -raw_input() \ No newline at end of file diff --git a/needs_porting/veinlook.cpp b/needs_porting/veinlook.cpp deleted file mode 100644 index c5837a25c..000000000 --- a/needs_porting/veinlook.cpp +++ /dev/null @@ -1,1117 +0,0 @@ -#include // for memset -#include -#include -#include -#include -#include -#include -#include -using namespace std; - -#include -// the header name comes from the build system. -#include -#include -#include -#include -#include - -#define DFHACK_WANT_MISCUTILS -#define DFHACK_WANT_TILETYPES -#include - -using namespace DFHack; - - -string error; -Context * pDF = 0; - - -struct t_tempz -{ - int32_t limit; - int character; -}; - -t_tempz temp_limits[]= -{ - {50, '.'}, - {100, '+'}, - {500, '*'}, - {1000, '#'}, - {2000, '!'} -}; -#define NUM_LIMITS 5 - -static void finish(int sig); - -int gotoxy(int x, int y) -{ - wmove(stdscr, y , x ); - return 0; -} - -void putch(int x, int y, int znak, int color) -{ - attron(COLOR_PAIR(color)); - mvwaddch(stdscr, y, x, znak); - attroff(COLOR_PAIR(color)); -} -void putwch(int x, int y, int znak, int color) -{ - attron(COLOR_PAIR(color)); - mvwaddch(stdscr, y, x, znak); - attroff(COLOR_PAIR(color)); -} -/* - enum TileClass - { - EMPTY, - - WALL, - PILLAR, - FORTIFICATION, - - STAIR_UP, - STAIR_DOWN, - STAIR_UPDOWN, - - RAMP, - - FLOOR, - TREE_DEAD, - TREE_OK, - SAPLING_DEAD, - SAPLING_OK, - SHRUB_DEAD, - SHRUB_OK, - BOULDER, - PEBBLES - };*/ - -void puttile(int x, int y, int tiletype, int color) -{ - unsigned int znak; - switch(tileShape(tiletype)) - { - case EMPTY: - znak = ' '; - break; - case PILLAR: - case WALL: - attron(COLOR_PAIR(color)); - mvwaddwstr(stdscr, y, x, L"\u2593"); - attroff(COLOR_PAIR(color)); - //znak = ; - return; - case FORTIFICATION: - znak = '#'; - break; - case STAIR_DOWN: - znak = '>'; - break; - case STAIR_UP: - znak = '<'; - break; - case STAIR_UPDOWN: - znak = '='; - break; - case RAMP: - attron(COLOR_PAIR(color)); - mvwaddwstr(stdscr, y, x, L"\u25B2"); - attroff(COLOR_PAIR(color)); - return; - case RAMP_TOP: - attron(COLOR_PAIR(color)); - mvwaddwstr(stdscr, y, x, L"\u25BC"); - attroff(COLOR_PAIR(color)); - return; - case FLOOR: - znak = '.'; - break; - case TREE_DEAD: - case TREE_OK: - attron(COLOR_PAIR(color)); - mvwaddwstr(stdscr, y, x, L"\u2663"); - attroff(COLOR_PAIR(color)); - return; - case SAPLING_DEAD: - case SAPLING_OK: - attron(COLOR_PAIR(color)); - mvwaddwstr(stdscr, y, x, L"\u03C4"); - attroff(COLOR_PAIR(color)); - return; - case SHRUB_DEAD: - case SHRUB_OK: - attron(COLOR_PAIR(color)); - mvwaddwstr(stdscr, y, x, L"\u2666"); - attroff(COLOR_PAIR(color)); - return; - case BOULDER: - case PEBBLES: - znak= '*'; - break; - } - attron(COLOR_PAIR(color)); - mvwaddch(stdscr, y, x, znak); - attroff(COLOR_PAIR(color)); -} - -int cprintf(const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - int i = vwprintw(stdscr,fmt, ap); - va_end(ap); - return i; -} - -void clrscr() -{ - wbkgd(stdscr, COLOR_PAIR(COLOR_BLACK)); - wclear(stdscr); -} - -/* - enum TileMaterial - { - AIR, - SOIL, - STONE, - FEATSTONE, // whatever it is - OBSIDIAN, - - VEIN, - ICE, - GRASS, - GRASS2, - GRASS_DEAD, - GRASS_DRY, - DRIFTWOOD, - HFS, - MAGMA, - CAMPFIRE, - FIRE, - ASHES, - CONSTRUCTED - }; -*/ -int pickColor(int tiletype) -{ - switch(tileMaterial(tiletype)) - { - case AIR: - return COLOR_BLACK; - case STONE: - case FEATSTONE: - case OBSIDIAN: - case CONSTRUCTED: - case ASHES: - default: - return COLOR_WHITE; - case SOIL: - case GRASS_DEAD: - case GRASS_DRY: - case DRIFTWOOD: - return COLOR_YELLOW; - case ICE: - return COLOR_CYAN; - case VEIN: - return COLOR_MAGENTA; - case GRASS: - case GRASS2: - return COLOR_GREEN; - case HFS: - case MAGMA: - case CAMPFIRE: - case FIRE: - return COLOR_RED; - } - -} - -/* -address = absolute address of dump start -length = length in bytes -*/ -void hexdump (DFHack::Context* DF, uint32_t address, uint32_t length, int filenum) -{ - uint32_t reallength; - uint32_t lines; - lines = (length / 16) + 1; - reallength = lines * 16; - char *buf = new char[reallength]; - ofstream myfile; - - stringstream ss; - ss << "hexdump" << filenum << ".txt"; - string name = ss.str(); - - myfile.open (name.c_str()); - - DF->ReadRaw(address, reallength, (uint8_t *) buf); - for (size_t i = 0; i < lines; i++) - { - // leading offset - myfile << "0x" << hex << setw(4) << i*16 << " "; - // groups - for(int j = 0; j < 4; j++) - { - // bytes - for(int k = 0; k < 4; k++) - { - int idx = i * 16 + j * 4 + k; - - myfile << hex << setw(2) << int(static_cast(buf[idx])) << " "; - } - myfile << " "; - } - myfile << endl; - } - delete buf; - myfile.close(); -} - -// p = attached process -// blockaddr = address of the block -// blockX, blockY = local map X and Y coords in 16x16 of the block -// printX, printX = where to print stuff on the screen -/* -void do_features(Process* p, uint32_t blockaddr, uint32_t blockX, uint32_t blockY, int printX, int printY, vector &stonetypes) -{ - memory_info* mem = p->getDescriptor(); - uint32_t block_feature1 = mem->getOffset("map_data_feature_local"); - uint32_t block_feature2 = mem->getOffset("map_data_feature_global"); - uint32_t region_x_offset = mem->getAddress("region_x"); - uint32_t region_y_offset = mem->getAddress("region_y"); - uint32_t region_z_offset = mem->getAddress("region_z"); - uint32_t feature1_start_ptr = mem->getAddress("local_feature_start_ptr"); - int32_t regionX, regionY, regionZ; - - // read position of the region inside DF world - p->readDWord (region_x_offset, (uint32_t &)regionX); - p->readDWord (region_y_offset, (uint32_t &)regionY); - p->readDWord (region_z_offset, (uint32_t &)regionZ); - // local feature present ? - int16_t idx = p->readWord(blockaddr + block_feature1); - if(idx != -1) - { - gotoxy(printX,printY); - cprintf("local feature present: %d", idx); - - uint64_t block48_x = blockX / 3 + regionX; - gotoxy(printX,printY+1); - cprintf("blockX: %d, regionX: %d\nbigblock_x: %d\n", blockX, regionX, block48_x); - - // region X coord offset by 8 big blocks (48x48 tiles) - uint16_t region_x_plus8 = ( block48_x + 8 ) / 16; - //uint16_t v12b = block48_x / 16; - //cout << "v12: " << v12 << " : " << v12b << endl; - // plain region Y coord - uint64_t region_y_local = (blockY / 3 + regionY) / 16; - gotoxy(printX,printY+2); - cprintf("region_y_local: %d\n", region_y_local); - - // deref pointer to the humongo-structure - uint32_t base = p->readDWord(feature1_start_ptr); - gotoxy(printX,printY+3); - cprintf("region_y_local: 0x%x\n", base); - - // this is just a few pointers to arrays of 16B (4 DWORD) structs - uint32_t array_elem = p->readDWord(base + (region_x_plus8 / 16) * 4); - gotoxy(printX,printY+4); - cprintf("array_elem: 0x%x\n", array_elem); - - // second element of the struct is a pointer - uint32_t wtf = p->readDWord(array_elem + (16*(region_y_local/16)) + 4); // rounding! - gotoxy(printX,printY+5); - cprintf("wtf : 0x%x @ 0x%x\n", wtf, array_elem + (16*(region_y_local/16)) ); - if(wtf) - { - //v14 = v10 + 24 * ((signed __int16)_tX + 16 * v9 % 16); - uint32_t feat_vector = wtf + 24 * (16 * (region_x_plus8 % 16) + (region_y_local % 16)); - gotoxy(printX,printY+6); - cprintf("local feature vector: 0x%x\n", feat_vector); - DfVector p_features(p, feat_vector); - gotoxy(printX,printY + 7); - cprintf("feature %d addr: 0x%x\n", idx, p_features[idx]); - if(idx >= p_features.size()) - { - gotoxy(printX,printY + 8); - cprintf("ERROR, out of vector bounds."); - } - else - { - string name = p->readClassName(p->readDWord( p_features[idx] )); - bool discovered = p->readDWord( p_features[idx] + 4 ); - gotoxy(printX,printY+8); - cprintf("%s", name.c_str()); - if(discovered) - { - gotoxy(printX,printY+9); - cprintf("You've discovered it already!"); - } - - if(name == "feature_init_deep_special_tubest") - { - int32_t master_type = p->readWord( p_features[idx] + 0x30 ); - int32_t slave_type = p->readDWord( p_features[idx] + 0x34 ); - char * matname = "unknown"; - // is stone? - if(master_type == 0) - { - matname = stonetypes[slave_type].id; - } - gotoxy(printX,printY+10); - cprintf("material %d/%d : %s", master_type, slave_type, matname); - - } - } - } - } - // global feature present - idx = p->readWord(blockaddr + block_feature2); - if(idx != -1) - { - gotoxy(printX,printY+11); - cprintf( "global feature present: %d\n", idx); - DfVector p_features (p,mem->getAddress("global_feature_vector")); - if(idx < p_features.size()) - { - uint32_t feat_ptr = p->readDWord(p_features[idx] + mem->getOffset("global_feature_funcptr_")); - gotoxy(printX,printY+12); - cprintf("feature descriptor?: 0x%x\n", feat_ptr); - string name = p->readClassName(p->readDWord( feat_ptr)); - bool discovered = p->readDWord( feat_ptr + 4 ); - gotoxy(printX,printY+13); - cprintf("%s", name.c_str()); - if(discovered) - { - gotoxy(printX,printY+14); - cout << "You've discovered it already!" << endl; - } - if(name == "feature_init_underworld_from_layerst") - { - int16_t master_type = p->readWord( feat_ptr + 0x34 ); - int32_t slave_type = p->readDWord( feat_ptr + 0x38 ); - char * matname = "unknown"; - // is stone? - if(master_type == 0) - { - matname = stonetypes[slave_type].id; - } - gotoxy(printX,printY+15); - cprintf("material %d/%d : %s", master_type, slave_type, matname); - } - } - } -} -*/ -void do_features(Context* DF, mapblock40d * block, uint32_t blockX, uint32_t blockY, int printX, int printY, vector &stonetypes) -{ - Maps * Maps = DF->getMaps(); - Process * p = DF->getProcess(); - if(!Maps) - return; - vector global_features; - std::map > local_features; - if(!Maps->ReadGlobalFeatures(global_features)) - return; - if(!Maps->ReadLocalFeatures(local_features)) - return; - - DFCoord pc(blockX, blockY); - int16_t idx =block->global_feature; - if(idx != -1) - { - t_feature &ftr =global_features[idx]; - gotoxy(printX,printY); - cprintf( "global feature present: %d @ 0x%x\n", idx, ftr.origin); - if(ftr.discovered ) - { - gotoxy(printX,printY+1); - cprintf("You've discovered it already!"); - } - - char * matname = (char *) "unknown"; - // is stone? - if(ftr.main_material == 0) - { - matname = stonetypes[ftr.sub_material].id; - } - gotoxy(printX,printY+2); - cprintf("%d:%s, material %d/%d : %s", ftr.type, sa_feature(ftr.type), ftr.main_material, ftr.sub_material, matname); - { - gotoxy(printX,printY+3); - string name = p->readClassName(p->readDWord( ftr.origin )); - cprintf("%s", name.c_str()); - } - } - idx =block->local_feature; - if(idx != -1) - { - vector &ftrv = local_features[pc]; - if(idx < ftrv.size()) - { - t_feature & ftr = *ftrv[idx]; - gotoxy(printX,printY + 4); - cprintf( "local feature present: %d @ 0x%x\n", idx, ftr.origin); - if(ftr.discovered ) - { - gotoxy(printX,printY+ 5); - cprintf("You've discovered it already!"); - } - char * matname = (char *) "unknown"; - // is stone? - if(ftr.main_material == 0) - { - matname = stonetypes[ftr.sub_material].id; - } - gotoxy(printX,printY+6); - cprintf("%d:%s, material %d/%d : %s", ftr.type, sa_feature(ftr.type), ftr.main_material, ftr.sub_material, matname); - - gotoxy(printX,printY+7); - string name = p->readClassName(p->readDWord( ftr.origin )); - cprintf("%s", name.c_str()); - } - else - { - gotoxy(printX,printY + 4); - cprintf( "local feature vector overflow: %d", idx); - } - } -} - -int main(int argc, char *argv[]) -{ - /* initialize your non-curses data structures here */ - - signal(SIGINT, finish); /* arrange interrupts to terminate */ - setlocale(LC_ALL,""); - initscr(); /* initialize the curses library */ - keypad(stdscr, TRUE); /* enable keyboard mapping */ - nonl(); /* tell curses not to do NL->CR/NL on output */ - cbreak(); /* take input chars one at a time, no wait for \n */ - noecho(); /* don't echo input */ - //nodelay(stdscr, true); - - keypad(stdscr, TRUE); - scrollok(stdscr, TRUE); - - if (has_colors()) - { - start_color(); - - /* - * Simple color assignment, often all we need. - */ - init_pair(COLOR_BLACK, COLOR_BLACK, COLOR_BLACK); - init_pair(COLOR_GREEN, COLOR_GREEN, COLOR_BLACK); - init_pair(COLOR_RED, COLOR_RED, COLOR_BLACK); - init_pair(COLOR_BLUE, COLOR_BLUE, COLOR_BLACK); - init_pair(COLOR_YELLOW, COLOR_YELLOW, COLOR_BLACK); - - init_color(COLOR_CYAN, 700, 700, 700); // lt grey - init_color(COLOR_MAGENTA, 500, 500, 500); // dk grey - init_pair(COLOR_WHITE, COLOR_WHITE, COLOR_BLACK); - init_pair(COLOR_CYAN, COLOR_CYAN, COLOR_BLACK); - init_pair(COLOR_MAGENTA, COLOR_MAGENTA, COLOR_BLACK); - } - - int x_max,y_max,z_max; - uint32_t x_max_a,y_max_a,z_max_a; - /* - uint16_t tiletypes[16][16]; - DFHack::t_designation designations[16][16]; - uint8_t regionoffsets[16]; - */ - map materials; - materials.clear(); - mapblock40d blocks[3][3]; - vector effects; - vector< vector > layerassign; - vector veinVector; - vector IceVeinVector; - vector splatter; - vector grass; - vector wconstructs; - t_temperatures b_temp1; - t_temperatures b_temp2; - - DFHack::Materials * Mats = 0; - DFHack::Maps * Maps = 0; - - - DFHack::ContextManager DFMgr("Memory.xml"); - DFHack::Context* DF; - try - { - pDF = DF = DFMgr.getSingleContext(); - DF->Attach(); - Maps = DF->getMaps(); - } - catch (exception& e) - { - cerr << e.what() << endl; - #ifndef LINUX_BUILD - cin.ignore(); - #endif - finish(0); - } - bool hasmats = true; - try - { - Mats = DF->getMaterials(); - } - catch (exception&) - { - hasmats = false; - } - - // init the map - if(!Maps->Start()) - { - error = "Can't find a map to look at."; - finish(0); - } - - Maps->getSize(x_max_a,y_max_a,z_max_a); - x_max = x_max_a; - y_max = y_max_a; - z_max = z_max_a; - - bool hasInorgMats = false; - bool hasPlantMats = false; - bool hasCreatureMats = false; - - if(hasmats) - { - // get stone matgloss mapping - if(Mats->ReadInorganicMaterials()) - { - hasInorgMats = true; - } - if(Mats->ReadCreatureTypes()) - { - hasCreatureMats = true; - } - if(Mats->ReadOrganicMaterials()) - { - hasPlantMats = true; - } - } -/* - // get region geology - if(!DF.ReadGeology( layerassign )) - { - error = "Can't read local geology."; - pDF = 0; - finish(0); - } -*/ - // FIXME: could fail on small forts - int cursorX = x_max/2 - 1; - int cursorY = y_max/2 - 1; - int cursorZ = z_max/2 - 1; - - - bool dig = false; - bool dump = false; - bool digbit = false; - bool dotwiddle; - unsigned char twiddle = 0; - int vein = 0; - int filenum = 0; - bool dirtybit = false; - uint32_t blockaddr = 0; - uint32_t blockaddr2 = 0; - t_blockflags bflags; - bflags.whole = 0; - enum e_tempmode - { - TEMP_NO, - TEMP_1, - TEMP_2, - WATER_SALT, - WATER_STAGNANT - }; - e_tempmode temperature = TEMP_NO; - - // resume so we don't block DF while we wait for input - DF->Resume(); - - for (;;) - { - dig = false; - dump = false; - dotwiddle = false; - digbit = false; - - int c = getch(); /* refresh, accept single keystroke of input */ - flushinp(); - clrscr(); - /* process the command keystroke */ - switch(c) - { - case KEY_DOWN: - cursorY ++; - break; - case KEY_UP: - cursorY --; - break; - case KEY_LEFT: - cursorX --; - break; - case KEY_RIGHT: - cursorX ++; - break; - case KEY_NPAGE: - cursorZ --; - break; - case KEY_PPAGE: - cursorZ ++; - break; - case '+': - vein ++; - break; - case 'd': - dig = true; - break; - case 'o': - dump = true; - break; - case '-': - vein --; - break; - case 'z': - digbit = true; - break; - case '/': - if(twiddle != 0) twiddle--; - break; - case '*': - twiddle++; - break; - case 't': - dotwiddle = true; - break; - case 'b': - temperature = TEMP_NO; - break; - case 'n': - temperature = TEMP_1; - break; - case 'm': - temperature = TEMP_2; - break; - case 'c': - temperature = WATER_SALT; - break; - case 'v': - temperature = WATER_STAGNANT; - break; - case 27: // escape key - DF->Detach(); - return 0; - break; - default: - break; - } - cursorX = max(cursorX, 0); - cursorY = max(cursorY, 0); - cursorZ = max(cursorZ, 0); - - cursorX = min(cursorX, x_max - 1); - cursorY = min(cursorY, y_max - 1); - cursorZ = min(cursorZ, z_max - 1); - - if(twiddle > 31) - twiddle = 31; - - // clear data before we suspend - memset(blocks,0,sizeof(blocks)); - veinVector.clear(); - IceVeinVector.clear(); - effects.clear(); - splatter.clear(); - grass.clear(); - dirtybit = 0; - - // Supend, read/write data - DF->Suspend(); - // restart cleared modules - Maps->Start(); - if(hasmats) - { - Mats->Start(); - if(hasInorgMats) - { - Mats->ReadInorganicMaterials(); - } - if(hasPlantMats) - { - Mats->ReadOrganicMaterials(); - } - if(hasCreatureMats) - { - Mats->ReadCreatureTypes(); - } - } - /* - if(DF.InitReadEffects(effectnum)) - { - for(uint32_t i = 0; i < effectnum;i++) - { - t_effect_df40d effect; - DF.ReadEffect(i,effect); - effects.push_back(effect); - } - } - */ - for(int i = -1; i <= 1; i++) for(int j = -1; j <= 1; j++) - { - mapblock40d * Block = &blocks[i+1][j+1]; - if(Maps->isValidBlock(cursorX+i,cursorY+j,cursorZ)) - { - Maps->ReadBlock40d(cursorX+i,cursorY+j,cursorZ, Block); - // extra processing of the block in the middle - if(i == 0 && j == 0) - { - if(hasInorgMats) - do_features(DF, Block, cursorX, cursorY, 50,10, Mats->inorganic); - // read veins - Maps->ReadVeins(cursorX+i,cursorY+j,cursorZ,&veinVector,&IceVeinVector,&splatter,&grass, &wconstructs); - - // get pointer to block - blockaddr = Maps->getBlockPtr(cursorX+i,cursorY+j,cursorZ); - blockaddr2 = Block->origin; - - // dig all veins and trees - if(dig) - { - for(int x = 0; x < 16; x++) for(int y = 0; y < 16; y++) - { - int16_t tiletype = Block->tiletypes[x][y]; - TileShape tc = tileShape(tiletype); - TileMaterial tm = tileMaterial(tiletype); - if( tc == WALL && tm == VEIN || tc == TREE_OK || tc == TREE_DEAD) - { - Block->designation[x][y].bits.dig = designation_default; - } - } - Maps->WriteDesignations(cursorX+i,cursorY+j,cursorZ, &(Block->designation)); - } - - // read temperature data - Maps->ReadTemperatures(cursorX+i,cursorY+j,cursorZ,&b_temp1, &b_temp2 ); - if(dotwiddle) - { - bitset<32> bs ((int)Block->designation[0][0].whole); - bs.flip(twiddle); - Block->designation[0][0].whole = bs.to_ulong(); - Maps->WriteDesignations(cursorX+i,cursorY+j,cursorZ, &(Block->designation)); - dotwiddle = false; - } - - // do a dump of the block data - if(dump) - { - hexdump(DF,blockaddr,0x1E00,filenum); - filenum++; - } - // read/write dirty bit of the block - Maps->ReadDirtyBit(cursorX+i,cursorY+j,cursorZ,dirtybit); - Maps->ReadBlockFlags(cursorX+i,cursorY+j,cursorZ,bflags); - if(digbit) - { - dirtybit = !dirtybit; - Maps->WriteDirtyBit(cursorX+i,cursorY+j,cursorZ,dirtybit); - } - } - } - } - // Resume, print stuff to the terminal - DF->Resume(); - for(int i = -1; i <= 1; i++) for(int j = -1; j <= 1; j++) - { - mapblock40d * Block = &blocks[i+1][j+1]; - for(int x = 0; x < 16; x++) for(int y = 0; y < 16; y++) - { - int color = COLOR_BLACK; - color = pickColor(Block->tiletypes[x][y]); - /* - if(!Block->designation[x][y].bits.hidden) - { - puttile(x+(i+1)*16,y+(j+1)*16,Block->tiletypes[x][y], color); - } - else*/ - { - - attron(A_STANDOUT); - puttile(x+(i+1)*16,y+(j+1)*16,Block->tiletypes[x][y], color); - attroff(A_STANDOUT); - - } - } - // print effects for the center tile - /* - if(i == 0 && j == 0) - { - for(uint zz = 0; zz < effects.size();zz++) - { - if(effects[zz].z == cursorZ && !effects[zz].isHidden) - { - // block coords to tile coords - uint16_t x = effects[zz].x - (cursorX * 16); - uint16_t y = effects[zz].y - (cursorY * 16); - if(x < 16 && y < 16) - { - putch(x + 16,y + 16,'@',COLOR_WHITE); - } - } - } - } - */ - } - gotoxy(50,0); - cprintf("arrow keys, PGUP, PGDN = navigate"); - gotoxy(50,1); - cprintf("+,- = switch vein"); - gotoxy(50,2); - uint32_t mineralsize = veinVector.size(); - uint32_t icesize = IceVeinVector.size(); - uint32_t splattersize = splatter.size(); - uint32_t grasssize = grass.size(); - uint32_t wconstrsize = wconstructs.size(); - uint32_t totalVeinSize = mineralsize+ icesize + splattersize + grasssize + wconstrsize; - if(vein == totalVeinSize) vein = totalVeinSize - 1; - if(vein < -1) vein = -1; - cprintf("X %d/%d, Y %d/%d, Z %d/%d. Vein %d of %d",cursorX+1,x_max,cursorY+1,y_max,cursorZ,z_max,vein+1,totalVeinSize); - if(!veinVector.empty() || !IceVeinVector.empty() || !splatter.empty() || !grass.empty() || !wconstructs.empty()) - { - if(vein != -1 && vein < totalVeinSize) - { - uint32_t realvein = 0; - if(vein < mineralsize) - { - realvein = vein; - //iterate through vein rows - for(uint32_t j = 0;j<16;j++) - { - //iterate through the bits - for (uint32_t k = 0; k< 16;k++) - { - // and the bit array with a one-bit mask, check if the bit is set - bool set = !!(((1 << k) & veinVector[realvein].assignment[j]) >> k); - if(set) - { - putch(k+16,j+16,'$',COLOR_RED); - } - } - } - if(hasInorgMats) - { - gotoxy(50,3); - cprintf("Mineral: %s",Mats->inorganic[veinVector[vein].type].id); - } - } - else if (vein < mineralsize + icesize) - { - realvein = vein - mineralsize; - t_frozenliquidvein &frozen = IceVeinVector[realvein]; - for(uint32_t i = 0;i<16;i++) - { - for (uint32_t j = 0; j< 16;j++) - { - int color = COLOR_BLACK; - int tile = frozen.tiles[i][j]; - color = pickColor(tile); - - attron(A_STANDOUT); - puttile(i+16,j+16,tile, color); - attroff(A_STANDOUT); - } - } - gotoxy(50,3); - cprintf("ICE"); - } - else if(vein < mineralsize + icesize + splattersize) - { - realvein = vein - mineralsize - icesize; - for(uint32_t yyy = 0; yyy < 16; yyy++) - { - for(uint32_t xxx = 0; xxx < 16; xxx++) - { - uint8_t intensity = splatter[realvein].intensity[xxx][yyy]; - if(intensity) - { - attron(A_STANDOUT); - putch(xxx+16,yyy+16,'*', COLOR_RED); - attroff(A_STANDOUT); - } - } - } - if(hasCreatureMats) - { - gotoxy(50,3); - cprintf("Spatter: %s",PrintSplatterType(splatter[realvein].mat1,splatter[realvein].mat2,Mats->race).c_str()); - } - } - else if(vein < mineralsize + icesize + splattersize + grasssize) - { - realvein = vein - mineralsize - icesize - splattersize; - t_grassvein & grassy =grass[realvein]; - for(uint32_t yyy = 0; yyy < 16; yyy++) - { - for(uint32_t xxx = 0; xxx < 16; xxx++) - { - uint8_t intensity = grassy.intensity[xxx][yyy]; - if(intensity) - { - attron(A_STANDOUT); - putch(xxx+16,yyy+16,'X', COLOR_RED); - attroff(A_STANDOUT); - } - } - } - if(hasPlantMats) - { - gotoxy(50,3); - cprintf("Grass: 0x%x, %s",grassy.address_of, Mats->organic[grassy.material].id); - } - } - else - { - realvein = vein - mineralsize - icesize - splattersize - grasssize; - t_worldconstruction & wconstr=wconstructs[realvein]; - for(uint32_t j = 0; j < 16; j++) - { - for(uint32_t k = 0; k < 16; k++) - { - bool set = !!(((1 << k) & wconstr.assignment[j]) >> k); - if(set) - { - putch(k+16,j+16,'$',COLOR_RED); - } - } - } - if(hasInorgMats) - { - gotoxy(50,3); - cprintf("Road: 0x%x, %d - %s", wconstr.address_of, wconstr.material,Mats->inorganic[wconstr.material].id); - } - } - } - } - mapblock40d * Block = &blocks[1][1]; - t_temperatures * ourtemp; - if(temperature == TEMP_NO) - { - for(int x = 0; x < 16; x++) for(int y = 0; y < 16; y++) - { - if((Block->occupancy[x][y].whole & (1 << twiddle))) - { - putch(x + 16,y + 16,'@',COLOR_WHITE); - } - } - } - else if(temperature == WATER_SALT) - { - for(int x = 0; x < 16; x++) for(int y = 0; y < 16; y++) - { - if(Block->designation[x][y].bits.water_salt) - { - putch(x + 16,y + 16,'@',COLOR_WHITE); - } - } - gotoxy (50,8); - cprintf ("Salt water"); - } - else if(temperature == WATER_STAGNANT) - { - for(int x = 0; x < 16; x++) for(int y = 0; y < 16; y++) - { - if(Block->designation[x][y].bits.water_stagnant) - { - putch(x + 16,y + 16,'@',COLOR_WHITE); - } - } - gotoxy (50,8); - cprintf ("Stagnant water"); - } - else - { - if(temperature == TEMP_1) - ourtemp = &b_temp1; - else if(temperature == TEMP_2) - ourtemp = &b_temp2; - uint64_t sum = 0; - uint16_t min, max; - min = max = (*ourtemp)[0][0]; - for(int x = 0; x < 16; x++) for(int y = 0; y < 16; y++) - { - uint16_t temp = (*ourtemp)[x][y]; - if(temp < min) min = temp; - if(temp > max) max = temp; - sum += temp; - } - uint64_t average = sum/256; - gotoxy (50,8); - if(temperature == TEMP_1) - cprintf ("temperature1 [°U] (min,avg,max): %d,%d,%d", min, average, max); - else if(temperature == TEMP_2) - cprintf ("temperature2 [°U] (min,avg,max): %d,%d,%d", min, average, max); - - for(int x = 0; x < 16; x++) for(int y = 0; y < 16; y++) - { - int32_t temper = (int32_t) (*ourtemp)[x][y]; - temper -= average; - uint32_t abs_temp = abs(temper); - int color; - unsigned char character = ' '; - if(temper >= 0) - color = COLOR_RED; - else - color = COLOR_BLUE; - - for(int i = 0; i < NUM_LIMITS; i++) - { - if(temp_limits[i].limit < abs_temp) - character = temp_limits[i].character; - else break; - } - if( character != ' ') - { - putch(x + 16,y + 16,character,color); - } - } - } - gotoxy (50,4); - cprintf("block address 0x%x, flags 0x%08x",blockaddr, bflags.whole); - gotoxy (50,5); - cprintf("dirty bit: %d, twiddle: %d",dirtybit,twiddle); - gotoxy (50,6); - cprintf ("d - dig veins, o - dump map block, z - toggle dirty bit"); - gotoxy (50,7); - cprintf ("b - no temperature, n - temperature 1, m - temperature 2"); - wrefresh(stdscr); - } - pDF = 0; - finish(0); -} - -static void finish(int sig) -{ - // ugly - if(pDF) - { - pDF->ForceResume(); - pDF->Detach(); - } - endwin(); - if(!error.empty()) - { - cerr << error << endl; - } - exit(0); -} From fbd8727b6b3253d155c04264c248d73d62b2a79e Mon Sep 17 00:00:00 2001 From: Anuradha Dissanayake Date: Mon, 5 May 2014 23:10:12 +1200 Subject: [PATCH 090/676] Add docs for autochop --- Readme.rst | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/Readme.rst b/Readme.rst index 9b8fd07cb..160a1dc7b 100644 --- a/Readme.rst +++ b/Readme.rst @@ -1933,6 +1933,29 @@ another savegame you can use the command list_export: autobutcher.bat +autochop +--------- +Automatically manage tree cutting designation to keep available logs withing given +quotas. + +Open the dashboard by running: +:: + + getplants autochop + +The plugin must be activated (with 'a') before it can be used. You can then set logging quotas +and restrict designations to specific burrows (with 'Enter') if desired. The plugin's activity +cycle runs once every in game day. + +If you add +:: + + enable getplants + +to your dfhack.init there will be a hotkey to open the dashboard from the chop designation +menu. + + autolabor --------- Automatically manage dwarf labors. From fef296110f35a5cc9f502761688b2173d85dadd5 Mon Sep 17 00:00:00 2001 From: Warmist Date: Tue, 6 May 2014 07:17:02 +0300 Subject: [PATCH 091/676] Fixed friend class bug. --- plugins/rendermax/renderer_light.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/rendermax/renderer_light.hpp b/plugins/rendermax/renderer_light.hpp index 83d5da28f..52bfb1f3e 100644 --- a/plugins/rendermax/renderer_light.hpp +++ b/plugins/rendermax/renderer_light.hpp @@ -360,7 +360,7 @@ private: std::unordered_map,itemLightDef> itemDefs; int w,h; DFHack::rect2d mapPort; - friend lightThreadDispatch; + friend class lightThreadDispatch; }; rgbf blend(const rgbf& a,const rgbf& b); rgbf blendMax(const rgbf& a,const rgbf& b); From b6b839c80268911143ca29c40f26a8ffcbc8eb7f Mon Sep 17 00:00:00 2001 From: Warmist Date: Tue, 6 May 2014 21:59:11 +0300 Subject: [PATCH 092/676] Fix for osx to build. --- plugins/rendermax/renderer_opengl.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/rendermax/renderer_opengl.hpp b/plugins/rendermax/renderer_opengl.hpp index ce14f3762..7a1a098c8 100644 --- a/plugins/rendermax/renderer_opengl.hpp +++ b/plugins/rendermax/renderer_opengl.hpp @@ -14,6 +14,7 @@ #include "df/texture_handler.h" #include "df/graphic.h" #include +#include using df::renderer; using df::init; From acdf90c1bacd2555b70105512923aa4383b21de0 Mon Sep 17 00:00:00 2001 From: PeridexisErrant Date: Wed, 7 May 2014 12:26:18 +1000 Subject: [PATCH 093/676] fixed dfstatus script, readme and init entries replaced magic numbers in dfstatus script with `df.item_type.TOKEN`; added dfstatus to readme and init_example; myself to contributors list, updated needs_porting notes and removed script --- Contributors.rst | 1 + Readme.rst | 4 ++ dfhack.init-example | 3 + needs_porting/_notes.txt | 2 +- needs_porting/dfstatus.lua | 110 --------------------------------- scripts/dfstatus.lua | 121 +++++++++++++++++++++++++++++++++++++ 6 files changed, 130 insertions(+), 111 deletions(-) delete mode 100644 needs_porting/dfstatus.lua create mode 100644 scripts/dfstatus.lua diff --git a/Contributors.rst b/Contributors.rst index b10877718..7f09dce73 100644 --- a/Contributors.rst +++ b/Contributors.rst @@ -61,6 +61,7 @@ The following is a list of people who have contributed to **DFHack**. - Caldfir - Antalia - Angus Mezick +- PeridexisErrant And those are the cool people who made **stonesense**. diff --git a/Readme.rst b/Readme.rst index 499920cab..720d06ad1 100644 --- a/Readme.rst +++ b/Readme.rst @@ -2400,6 +2400,10 @@ dfhack commands. Useful for hotkeys. Example:: multicmd locate-ore iron ; digv +dfstatus +======== +Show a quick overview of critical stock quantities, including food, dirnks, wood, and various bars. + ======================= In-game interface tools ======================= diff --git a/dfhack.init-example b/dfhack.init-example index 3470bf36b..83b309001 100644 --- a/dfhack.init-example +++ b/dfhack.init-example @@ -48,6 +48,9 @@ keybinding add Ctrl-Shift-B "adv-bodyswap force" # Stocks plugin keybinding add Ctrl-Shift-Z@dwarfmode/Default "stocks show" +# open an overview window summarising some stocks (dfstatus) +keybinding add Ctrl-Shift-I@dwarfmode/Default dfstatus + # Workflow keybinding add Ctrl-W@dwarfmode/QueryBuilding/Some "gui/workflow" keybinding add Ctrl-I "gui/workflow status" diff --git a/needs_porting/_notes.txt b/needs_porting/_notes.txt index a653a7b56..041e0932d 100644 --- a/needs_porting/_notes.txt +++ b/needs_porting/_notes.txt @@ -4,7 +4,7 @@ I deleted: attachtest.py obsolete digger.cpp less useful than digger2, replaced by autochop digger2.cpp replaced by digfort - dfstatus.cpp replaced by dfstatus.lua (which needs a little work too) + dfstatus.cpp replaced by dfstatus.lua drawtile.cpp replaced by tiletypes fix-3708.cpp obsolete, bug fixed in vanilla lair.cpp replaced by lair diff --git a/needs_porting/dfstatus.lua b/needs_porting/dfstatus.lua deleted file mode 100644 index 180d2afa3..000000000 --- a/needs_porting/dfstatus.lua +++ /dev/null @@ -1,110 +0,0 @@ --- dfstatus 1.04 - a quick access status screen. --- written by enjia2000@gmail.com - -local gui = require 'gui' - -function draw() - screen2 = gui.FramedScreen{ - frame_style = gui.GREY_LINE_FRAME, - frame_title = 'dfstatus', - frame_width = 16, - frame_height = 17, - frame_inset = 1, - } -end - -if (not shown) then - draw() - screen2:show() - shown = true -else - shown = nil - screen2:dismiss() -end - -function screen2:onRenderBody(dc) - local drink = 0 - local wood = 0 - --local meat = 0 - --local raw_fish = 0 - --local plants = 0 - local prepared_meals = 0 - - local fuel = 0 - local pigiron = 0 - local iron = 0 - local steel = 0 - - local silver = 0 - local copper = 0 - local gold = 0 - - local tannedhides = 0 - local cloth = 0 - - for _,item in ipairs(df.global.world.items.all) do - if(not item.flags.rotten and not item.flags.dump and not item.flags.forbid and not item.flags.in_building and not item.flags.trader) then - if (item:getType() == 5) then wood = wood + item:getStackSize() - elseif (item:getType() == 68) then drink = drink + item:getStackSize() - elseif (item:getType() == 54) then tannedhides = tannedhides + item:getStackSize() - elseif (item:getType() == 57) then cloth = cloth + item:getStackSize() - --elseif (item:getType() == 47) then meat = meat + item:getStackSize() - --elseif (item:getType() == 49) then raw_fish = raw_fish + item:getStackSize() - --elseif (item:getType() == 53) then plants = plants + item:getStackSize() - elseif (item:getType() == 71) then prepared_meals = prepared_meals + item:getStackSize() - elseif (item:getType() == 0) then - for token in string.gmatch(dfhack.items.getDescription(item,0),"[^%s]+") do - if (token == "silver") then silver = silver + item:getStackSize() - elseif (token == "charcoal" or token == "coke") then fuel = fuel + item:getStackSize() - elseif (token == "iron") then iron = iron + item:getStackSize() - elseif (token == "pig") then pigiron = pigiron + item:getStackSize() - elseif (token == "copper") then copper = copper + item:getStackSize() - elseif (token == "gold") then gold = gold + item:getStackSize() - elseif (token == "steel") then steel = steel + item:getStackSize() - end - break -- only need to look at the 1st token of each item. - end - end - end - end - dc:string("Drinks: ".. drink, COLOR_LIGHTGREEN) - dc:newline(0) - dc:string("Meals: ".. prepared_meals, COLOR_LIGHTGREEN) - dc:newline(0) - dc:newline(0) - dc:string("Wood: ".. wood, COLOR_LIGHTGREEN) - dc:newline(0) - dc:newline(0) - dc:string("Hides: ".. tannedhides, COLOR_LIGHTGREEN) - dc:newline(0) - dc:string("Cloth: ".. cloth, COLOR_LIGHTGREEN) - dc:newline(0) - --dc:string("Raw Fish: ".. raw_fish, COLOR_LIGHTGREEN) - --dc:newline(0) - --dc:string("Plants: ".. plants, COLOR_LIGHTGREEN) - --dc:newline(0) - dc:newline(0) - dc:string("Bars:", COLOR_LIGHTGREEN) - dc:newline(1) - dc:string("Fuel: ".. fuel, COLOR_LIGHTGREEN) - dc:newline(1) - dc:string("Pig Iron: ".. pigiron, COLOR_LIGHTGREEN) - dc:newline(1) - dc:string("Steel: ".. steel, COLOR_LIGHTGREEN) - dc:newline(1) - dc:string("Iron: ".. iron, COLOR_LIGHTGREEN) - dc:newline(1) - dc:newline(1) - dc:string("Copper: ".. copper, COLOR_LIGHTGREEN) - dc:newline(1) - dc:string("Silver: ".. silver, COLOR_LIGHTGREEN) - dc:newline(1) - dc:string("Gold: ".. gold, COLOR_LIGHTGREEN) -end - -function screen2:onInput(keys) - if keys.LEAVESCREEN or keys.SELECT then - shown = nil - self:dismiss() - end -end diff --git a/scripts/dfstatus.lua b/scripts/dfstatus.lua new file mode 100644 index 000000000..8f494df81 --- /dev/null +++ b/scripts/dfstatus.lua @@ -0,0 +1,121 @@ +-- dfstatus 1.0 - a quick access status screen. +-- written by enjia2000@gmail.com, fixed by Lethosor and PeridexisErrant + + +local gui = require 'gui' + +function draw() + screen2 = gui.FramedScreen{ + frame_style = gui.GREY_LINE_FRAME, + frame_title = 'dfstatus', + frame_width = 16, + frame_height = 17, + frame_inset = 1, + } +end + +if (not shown) then + draw() + screen2:show() + shown = true +else + shown = nil + screen2:dismiss() +end + +function screen2:onRenderBody(dc) + local drink = 0 + local wood = 0 + --local meat = 0 + --local raw_fish = 0 + --local plants = 0 + local prepared_meals = 0 + + local fuel = 0 + local pigiron = 0 + local iron = 0 + local steel = 0 + + local silver = 0 + local copper = 0 + local gold = 0 + + local tannedhides = 0 + local cloth = 0 + + --print("------------------------------") + for _,item in ipairs(df.global.world.items.all) do + if(not item.flags.rotten and not item.flags.dump and not item.flags.forbid) then + if(item:getType() ~= df.item_type.THREAD and item:getType() ~= df.item_type.REMAINS and item:getType() ~= df.item_type.ARMOR and item:getType() ~= df.item_type.SHOES and item:getType() ~= df.item_type.SHIELD and item:getType() ~= df.item_type.HELM and item:getType() ~= df.item_type.GOVES) then + --print(item:getType() .. ":" .. dfhack.items.getDescription(item,0)) + end + if(item:getType() == df.item_type.DRINK)then + --print(item:getType() .. ":" .. dfhack.items.getDescription(item,0)) + end + + if (item:getType() == df.item_type.WOOD) then wood = wood + item:getStackSize() + elseif (item:getType() == df.item_type.DRINK) then drink = drink + item:getStackSize() + elseif (item:getType() == df.item_type.SKIN_TANNED) then tannedhides = tannedhides + item:getStackSize() + elseif (item:getType() == df.item_type.CLOTH) then cloth = cloth + item:getStackSize() + --elseif (item:getType() == df.item_type.MEAT) then meat = meat + item:getStackSize() + --elseif (item:getType() == df.item_type.FISH_RAW) then raw_fish = raw_fish + item:getStackSize() + --elseif (item:getType() == df.item_type.PLANT) then plants = plants + item:getStackSize() + elseif (item:getType() == df.item_type.FOOD) then prepared_meals = prepared_meals + item:getStackSize() + elseif (item:getType() == df.item_type.BAR) then + for token in string.gmatch(dfhack.items.getDescription(item,0),"[^%s]+") do + if (token == "silver") then silver = silver + item:getStackSize() + elseif (token == "charcoal" or token == "coke") then fuel = fuel + item:getStackSize() + elseif (token == "iron") then iron = iron + item:getStackSize() + elseif (token == "pig") then pigiron = pigiron + item:getStackSize() + elseif (token == "copper") then copper = copper + item:getStackSize() + elseif (token == "gold") then gold = gold + item:getStackSize() + elseif (token == "steel") then steel = steel + item:getStackSize() + end + break -- only need to look at the 1st token of each item. + end + end + end + end + --print("------------------------------") + dc:string("Drinks: ".. drink, COLOR_LIGHTGREEN) + dc:newline(0) + dc:string("Meals: ".. prepared_meals, COLOR_LIGHTGREEN) + dc:newline(0) + dc:newline(0) + dc:string("Wood: ".. wood, COLOR_LIGHTGREEN) + dc:newline(0) + dc:newline(0) + dc:string("Hides: ".. tannedhides, COLOR_LIGHTGREEN) + dc:newline(0) + dc:string("Cloth: ".. cloth, COLOR_LIGHTGREEN) + dc:newline(0) +-- dc:string("Raw Fish: ".. raw_fish, COLOR_LIGHTGREEN) +-- dc:newline(0) +-- dc:string("Plants: ".. plants, COLOR_LIGHTGREEN) +-- dc:newline(0) + dc:newline(0) + dc:string("Bars:", COLOR_LIGHTGREEN) + dc:newline(1) + dc:string("Fuel: ".. fuel, COLOR_LIGHTGREEN) + dc:newline(1) + dc:string("Pig Iron: ".. pigiron, COLOR_LIGHTGREEN) + dc:newline(1) + dc:string("Steel: ".. steel, COLOR_LIGHTGREEN) + dc:newline(1) + dc:string("Iron: ".. iron, COLOR_LIGHTGREEN) + dc:newline(1) + dc:newline(1) + dc:string("Copper: ".. copper, COLOR_LIGHTGREEN) + dc:newline(1) + dc:string("Silver: ".. silver, COLOR_LIGHTGREEN) + dc:newline(1) + dc:string("Gold: ".. gold, COLOR_LIGHTGREEN) +end + +function screen2:onInput(keys) + if keys.LEAVESCREEN or keys.SELECT then + shown = nil + self:dismiss() + end +end + From 4b8f68dfa97a9fff3b197a0108f66e4c94aa882b Mon Sep 17 00:00:00 2001 From: Quietust Date: Thu, 8 May 2014 14:22:45 -0500 Subject: [PATCH 094/676] Add a few tweaks - fix wear on cloth/leather items, disable wear on adamantine clothes --- Readme.html | 10 +++- Readme.rst | 5 ++ plugins/tweak.cpp | 119 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 133 insertions(+), 1 deletion(-) diff --git a/Readme.html b/Readme.html index b93dc3bfe..43957d9b1 100644 --- a/Readme.html +++ b/Readme.html @@ -2180,10 +2180,18 @@ unintended inverse dependency of training speed on unit count the units spar more.

      -hive-crash:

      The hive code crashes if there are ungathered products in a hive without bees (bug 6368). +hive-crash:

      The hive code crashes if there are ungathered products in a hive without bees (bug 6368). This tweak prevents it by auto-gathering the products if this happens.

      +craft-age-wear:

      Fixes the behavior of crafted items wearing out over time (bug 6003). +With this tweak, items made from cloth and leather will gain a level of wear every 20 years.

      + + +adamantine-cloth-wear: + 

      Prevents adamantine clothing from wearing out while being worn (bug 6481).

      + +
      diff --git a/Readme.rst b/Readme.rst index 9b8fd07cb..04dd90621 100644 --- a/Readme.rst +++ b/Readme.rst @@ -1371,6 +1371,11 @@ Subcommands that persist until disabled or DF quit: :hive-crash: The hive code crashes if there are ungathered products in a hive without bees (bug 6368). This tweak prevents it by auto-gathering the products if this happens. +:craft-age-wear: Fixes the behavior of crafted items wearing out over time (bug 6003). + With this tweak, items made from cloth and leather will gain a level of wear every 20 years. + +:adamantine-cloth-wear: Prevents adamantine clothing from wearing out while being worn (bug 6481). + fix-armory ---------- diff --git a/plugins/tweak.cpp b/plugins/tweak.cpp index ed5c706ef..d74ef0767 100644 --- a/plugins/tweak.cpp +++ b/plugins/tweak.cpp @@ -11,6 +11,7 @@ #include "modules/Units.h" #include "modules/Items.h" #include "modules/Job.h" +#include "modules/Materials.h" #include "MiscUtils.h" @@ -35,6 +36,12 @@ #include "df/ui_build_selector.h" #include "df/building_trapst.h" #include "df/item_actual.h" +#include "df/item_crafted.h" +#include "df/item_armorst.h" +#include "df/item_helmst.h" +#include "df/item_glovesst.h" +#include "df/item_shoesst.h" +#include "df/item_pantsst.h" #include "df/item_liquid_miscst.h" #include "df/item_powder_miscst.h" #include "df/item_barst.h" @@ -140,6 +147,10 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector 200000000) + age = 200000000; + if (age == orig_age) + return; + + MaterialInfo mat(mat_type, mat_index); + if (!mat.isValid()) + return; + int wear = 0; + + if (mat.material->flags.is_set(material_flags::WOOD)) + wear = 5; + else if (mat.material->flags.is_set(material_flags::LEATHER) || + mat.material->flags.is_set(material_flags::THREAD_PLANT) || + mat.material->flags.is_set(material_flags::SILK) || + mat.material->flags.is_set(material_flags::YARN)) + wear = 1; + else + return; + wear = ((orig_age % wear) + (age - orig_age)) / wear; + if (wear > 0) + addWear(wear, false, false); + } +}; +IMPLEMENT_VMETHOD_INTERPOSE(craft_age_wear_hook, ageItem); + +static bool inc_wear_timer (df::item_constructed *item, int amount) +{ + if (item->flags.bits.artifact) + return false; + + MaterialInfo mat(item->mat_type, item->mat_index); + if (mat.isInorganic() && mat.inorganic->flags.is_set(inorganic_flags::DEEP_SPECIAL)) + return false; + + item->wear_timer += amount; + return (item->wear_timer > 806400); +} + +struct adamantine_cloth_wear_armor_hook : df::item_armorst { + typedef df::item_armorst interpose_base; + + DEFINE_VMETHOD_INTERPOSE(bool, incWearTimer, (int amount)) + { + return inc_wear_timer(this, amount); + } +}; +IMPLEMENT_VMETHOD_INTERPOSE(adamantine_cloth_wear_armor_hook, incWearTimer); + +struct adamantine_cloth_wear_helm_hook : df::item_helmst { + typedef df::item_helmst interpose_base; + + DEFINE_VMETHOD_INTERPOSE(bool, incWearTimer, (int amount)) + { + return inc_wear_timer(this, amount); + } +}; +IMPLEMENT_VMETHOD_INTERPOSE(adamantine_cloth_wear_helm_hook, incWearTimer); + +struct adamantine_cloth_wear_gloves_hook : df::item_glovesst { + typedef df::item_glovesst interpose_base; + + DEFINE_VMETHOD_INTERPOSE(bool, incWearTimer, (int amount)) + { + return inc_wear_timer(this, amount); + } +}; +IMPLEMENT_VMETHOD_INTERPOSE(adamantine_cloth_wear_gloves_hook, incWearTimer); + +struct adamantine_cloth_wear_shoes_hook : df::item_shoesst { + typedef df::item_shoesst interpose_base; + + DEFINE_VMETHOD_INTERPOSE(bool, incWearTimer, (int amount)) + { + return inc_wear_timer(this, amount); + } +}; +IMPLEMENT_VMETHOD_INTERPOSE(adamantine_cloth_wear_shoes_hook, incWearTimer); + +struct adamantine_cloth_wear_pants_hook : df::item_pantsst { + typedef df::item_pantsst interpose_base; + + DEFINE_VMETHOD_INTERPOSE(bool, incWearTimer, (int amount)) + { + return inc_wear_timer(this, amount); + } +}; +IMPLEMENT_VMETHOD_INTERPOSE(adamantine_cloth_wear_pants_hook, incWearTimer); + static void enable_hook(color_ostream &out, VMethodInterposeLinkBase &hook, vector ¶meters) { if (vector_get(parameters, 1) == "disable") @@ -1161,6 +1268,18 @@ static command_result tweak(color_ostream &out, vector ¶meters) { enable_hook(out, INTERPOSE_HOOK(hive_crash_hook, updateAction), parameters); } + else if (cmd == "craft-age-wear") + { + enable_hook(out, INTERPOSE_HOOK(craft_age_wear_hook, ageItem), parameters); + } + else if (cmd == "adamantine-cloth-wear") + { + enable_hook(out, INTERPOSE_HOOK(adamantine_cloth_wear_armor_hook, incWearTimer), parameters); + enable_hook(out, INTERPOSE_HOOK(adamantine_cloth_wear_helm_hook, incWearTimer), parameters); + enable_hook(out, INTERPOSE_HOOK(adamantine_cloth_wear_gloves_hook, incWearTimer), parameters); + enable_hook(out, INTERPOSE_HOOK(adamantine_cloth_wear_shoes_hook, incWearTimer), parameters); + enable_hook(out, INTERPOSE_HOOK(adamantine_cloth_wear_pants_hook, incWearTimer), parameters); + } else return CR_WRONG_USAGE; From 2ec1d841642814417d8599f12131b20fbfbd7f83 Mon Sep 17 00:00:00 2001 From: Lethosor Date: Thu, 8 May 2014 23:15:26 -0400 Subject: [PATCH 095/676] Use DYLD_LIBRARY_PATH on OS X instead of LD_LIBRARY_PATH Fixes DFHack/dfhack#235 --- package/darwin/dfhack-run | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/darwin/dfhack-run b/package/darwin/dfhack-run index cc69db964..e33269d6e 100755 --- a/package/darwin/dfhack-run +++ b/package/darwin/dfhack-run @@ -3,6 +3,6 @@ DF_DIR=$(dirname "$0") cd "${DF_DIR}" -export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:"./stonesense/deplibs":"./hack" +export DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:"./stonesense/deplibs":"./hack" exec hack/dfhack-run "$@" From 709d6e53b5e5f005dd36f36cb93013f78dd955bc Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sun, 11 May 2014 13:19:14 +0400 Subject: [PATCH 096/676] Add new tweaks to NEWS and dfhack.init-example. --- NEWS | 4 ++++ dfhack.init-example | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/NEWS b/NEWS index 165938e2b..28267af11 100644 --- a/NEWS +++ b/NEWS @@ -16,6 +16,10 @@ DFHack future - 'plant create' - spawn a new shrub under the cursor - command-prompt: a dfhack command prompt in df. + New tweaks: + - craft-age-wear: make crafted items wear out with time like in old versions (bug 6003) + - adamantine-cloth-wear: stop adamantine clothing from wearing out (bug 6481) + Misc improvements: - digfort: improved csv parsing, add start() comment handling - exterminate: allow specifying a caste (exterminate gob:male) diff --git a/dfhack.init-example b/dfhack.init-example index 3470bf36b..8a2b24e2b 100644 --- a/dfhack.init-example +++ b/dfhack.init-example @@ -156,6 +156,12 @@ tweak military-training # prevent crash if bees die in a hive with ungathered products by insta-gathering them tweak hive-crash +# make crafted cloth items wear out with time like in old versions (bug 6003) +tweak craft-age-wear + +# stop adamantine clothing from wearing out (bug 6481) +#tweak adamantine-cloth-wear + ########################### # Globally acting plugins # ########################### From fe93aba6122965adfc900862a39c459db21de1c1 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sun, 11 May 2014 13:36:36 +0400 Subject: [PATCH 097/676] Update structures --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index 1558b2373..e0407d8bb 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 1558b2373fcf8fa63928167e007a7f9a709888b6 +Subproject commit e0407d8bbbc965dbb62826d481f27940972ffd66 From a088219b3e78df749e0ad5b229da1a8042bb59ec Mon Sep 17 00:00:00 2001 From: Warmist Date: Sun, 11 May 2014 14:19:02 +0300 Subject: [PATCH 098/676] Fixed ALL the bugs: zooming does not crash and fullscreening also does not crash now. --- plugins/rendermax/renderer_light.hpp | 10 ++++++++++ plugins/rendermax/renderer_opengl.hpp | 12 ++++++++++++ 2 files changed, 22 insertions(+) diff --git a/plugins/rendermax/renderer_light.hpp b/plugins/rendermax/renderer_light.hpp index 52bfb1f3e..357ddaf85 100644 --- a/plugins/rendermax/renderer_light.hpp +++ b/plugins/rendermax/renderer_light.hpp @@ -126,6 +126,16 @@ public: renderer_wrap::resize(w,h); reinitLightGrid(); } + virtual void set_fullscreen() + { + renderer_wrap::set_fullscreen(); + reinitLightGrid(); + } + virtual void zoom(df::zoom_commands z) + { + renderer_wrap::zoom(z); + reinitLightGrid(); + } }; class lightingEngine { diff --git a/plugins/rendermax/renderer_opengl.hpp b/plugins/rendermax/renderer_opengl.hpp index 7a1a098c8..085bdf48a 100644 --- a/plugins/rendermax/renderer_opengl.hpp +++ b/plugins/rendermax/renderer_opengl.hpp @@ -95,10 +95,12 @@ public: virtual void set_fullscreen() { copy_to_inner(); parent->set_fullscreen(); + copy_from_inner(); }; virtual void zoom(df::zoom_commands z) { copy_to_inner(); parent->zoom(z); + copy_from_inner(); }; virtual void resize(int32_t w, int32_t h) { copy_to_inner(); @@ -310,6 +312,16 @@ public: //and then map read //same stuff for all of them i guess... }; + virtual void set_fullscreen() + { + renderer_wrap::set_fullscreen(); + reinitLightGrid(); + } + virtual void zoom(df::zoom_commands z) + { + renderer_wrap::zoom(z); + reinitLightGrid(); + } virtual void grid_resize(int32_t w, int32_t h) { renderer_wrap::grid_resize(w,h); reinitLightGrid(w,h); From e829b2eea44554e99137f7aa14677855910f9308 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sun, 11 May 2014 16:05:00 +0400 Subject: [PATCH 099/676] Fix missing newline and update Readme.html --- Readme.html | 684 +++++++++++++++++--------------- plugins/rendermax/rendermax.cpp | 2 +- 2 files changed, 371 insertions(+), 315 deletions(-) diff --git a/Readme.html b/Readme.html index 61d800aed..de4cea9b3 100644 --- a/Readme.html +++ b/Readme.html @@ -385,207 +385,209 @@ access DF memory and allow for easier development of new tools.

    • copystock
    • rename
    • command-prompt
    • +
    • rendermax
  • -
  • Adventure mode
      -
    • adv-bodyswap
    • -
    • advtools
    • +
    • Adventure mode
    • -
    • Map modification
        -
      • 3dveins
      • -
      • changelayer
      • -
      • changevein
      • -
      • changeitem
      • -
      • colonies
      • -
      • createitem
      • -
      • deramp (by zilpin)
      • -
      • feature
      • -
      • infiniteSky
      • -
      • liquids
      • -
      • liquids-here
      • -
      • tiletypes
      • -
      • tiletypes-commands
      • -
      • tiletypes-here
      • -
      • tiletypes-here-point
      • -
      • tubefill
      • -
      • plant
      • -
      • regrass
      • -
      • weather
      • +
      • Map modification
      • -
      • Map inspection
          -
        • cursecheck
        • -
        • flows
        • -
        • probe
        • -
        • prospect
            -
          • Pre-embark estimate
          • +
          • Map inspection
          • -
          • Mod Interaction
              -
            • autoSyndrome
            • -
            • outsideOnly
            • -
            • syndromeTrigger
                -
              • True Transformation
              • +
              • Mod Interaction
              • -
              • Designations
                  -
                • burrow
                • -
                • digv
                • -
                • digvx
                • -
                • digl
                • -
                • diglx
                • -
                • digexp
                • -
                • digcircle
                • -
                • digtype
                • -
                • digFlood
                • -
                • filltraffic
                • -
                • alltraffic
                • -
                • restrictliquid
                • -
                • restrictice
                • -
                • getplants
                • +
                • Designations
                • -
                • Cleanup and garbage disposal
                    -
                  • clean
                  • -
                  • spotclean
                  • -
                  • autodump
                  • -
                  • autodump-destroy-here
                  • -
                  • autodump-destroy-item
                  • -
                  • cleanowned
                  • +
                  • Cleanup and garbage disposal
                  • -
                  • Bugfixes
                      -
                    • drybuckets
                    • -
                    • fixdiplomats
                    • -
                    • fixmerchants
                    • -
                    • fixveins
                    • -
                    • tweak
                    • -
                    • fix-armory
                    • +
                    • Bugfixes
                    • -
                    • Mode switch and reclaim
                        -
                      • lair
                      • -
                      • mode
                      • +
                      • Mode switch and reclaim
                      • -
                      • Visualizer and data export
                          -
                        • ssense / stonesense
                        • -
                        • mapexport
                        • -
                        • dwarfexport
                        • +
                        • Visualizer and data export
                        • -
                        • Job management
                            -
                          • job
                          • -
                          • job-material
                          • -
                          • job-duplicate
                          • -
                          • workflow
                              -
                            • Function
                            • -
                            • Constraint format
                            • -
                            • Constraint examples
                            • +
                            • Job management
                            • -
                            • Fortress activity management
                                -
                              • seedwatch
                              • -
                              • zone
                                  -
                                • Usage with single units
                                • -
                                • Usage with filters
                                • -
                                • Mass-renaming
                                • -
                                • Cage zones
                                • -
                                • Examples
                                • +
                                • Fortress activity management
                                • -
                                • Other
                                • -
                                • Scripts
                                    -
                                  • fix/*
                                  • -
                                  • gui/*
                                  • -
                                  • binpatch
                                  • -
                                  • quicksave
                                  • -
                                  • setfps
                                  • -
                                  • siren
                                  • -
                                  • growcrops
                                  • -
                                  • removebadthoughts
                                  • -
                                  • exterminate
                                  • -
                                  • source
                                  • -
                                  • masspit
                                  • -
                                  • digfort
                                  • -
                                  • invasion-now
                                  • -
                                  • digmat
                                  • -
                                  • superdwarf
                                  • -
                                  • drainaquifer
                                  • -
                                  • deathcause
                                  • -
                                  • lua
                                  • -
                                  • embark
                                  • -
                                  • lever
                                  • -
                                  • stripcaged
                                  • -
                                  • undump-buildings
                                  • -
                                  • create-items
                                  • -
                                  • locate-ore
                                  • -
                                  • soundsense-season
                                  • -
                                  • multicmd
                                  • +
                                  • Scripts
                                  • -
                                  • In-game interface tools
                                      -
                                    • Dwarf Manipulator
                                    • -
                                    • Search
                                    • -
                                    • AutoMaterial
                                    • -
                                    • gui/liquids
                                    • -
                                    • gui/mechanisms
                                    • -
                                    • gui/rename
                                    • -
                                    • gui/room-list
                                    • -
                                    • gui/choose-weapons
                                    • -
                                    • gui/clone-uniform
                                    • -
                                    • gui/guide-path
                                    • -
                                    • gui/workshop-job
                                    • -
                                    • gui/workflow
                                    • -
                                    • gui/assign-rack
                                    • -
                                    • gui/advfort
                                    • -
                                    • gui/companion-order
                                    • -
                                    • gui/gm-editor
                                    • -
                                    • gui/mod-manager
                                    • +
                                    • In-game interface tools
                                    • -
                                    • Behavior Mods
                                        -
                                      • Siege Engine
                                          -
                                        • Rationale
                                        • -
                                        • Configuration UI
                                        • +
                                        • Behavior Mods @@ -907,11 +909,47 @@ used as a keybinding. Can be called with optional "entry" that will st that pre-filled.

                                          images/command-prompt.png +
                                          +

                                          rendermax

                                          +

                                          A collection of renderer replacing/enhancing filters. For better effect try changing the +black color in palette to non totally black. For more info see thread in forums: +http://www.bay12forums.com/smf/index.php?topic=128487.0

                                          +

                                          Options:

                                          +
                                          + +++ + + + + + + + + + + + + + + + + + + + +
                                          rendermax trippy:
                                           Randomizes each tiles color. Used for fun mainly.
                                          rendermax light:
                                           Enable lighting engine.
                                          rendermax light reload:
                                           Reload the settings file.
                                          rendermax light sun <x>|cycle:
                                           Set time to <x> (in hours) or set it to df time cycle.
                                          rendermax occlusionON|occlusionOFF:
                                           Show debug occlusion info.
                                          rendermax disable:
                                           Disable any filter that is enabled.
                                          +
                                          +

                                          An image showing lava and dragon breath. Not pictured here: sunlight, shining items/plants, +materials that color the light etc...

                                          +images/rendermax.png +
                                          -

                                          Adventure mode

                                          +

                                          Adventure mode

                                          -

                                          adv-bodyswap

                                          +

                                          adv-bodyswap

                                          This allows taking control over your followers and other creatures in adventure mode. For example, you can make them pick up new arms and armor and equip them properly.

                                          @@ -924,7 +962,7 @@ properly.

                                          -

                                          advtools

                                          +

                                          advtools

                                          A package of different adventure mode tools (currently just one)

                                          Usage:

                                          @@ -947,9 +985,9 @@ on item type and being in shop.
                                          -

                                          Map modification

                                          +

                                          Map modification

                                          -

                                          3dveins

                                          +

                                          3dveins

                                          Removes all existing veins from the map and generates new ones using 3D Perlin noise, in order to produce a layout that smoothly flows between Z levels. The vein distribution is based on the world seed, so running @@ -963,7 +1001,7 @@ if vein mass shifts between Z layers.

                                          Note that there is no undo option other than restoring from backup.

                                          -

                                          changelayer

                                          +

                                          changelayer

                                          Changes material of the geology layer under cursor to the specified inorganic RAW material. Can have impact on all surrounding regions, not only your embark! By default changing stone to soil and vice versa is not allowed. By default @@ -1038,7 +1076,7 @@ You did save your game, right?

                                        • -

                                          changevein

                                          +

                                          changevein

                                          Changes material of the vein under cursor to the specified inorganic RAW material. Only affects tiles within the current 16x16 block - for veins and large clusters, you will need to use this command multiple times.

                                          @@ -1051,7 +1089,7 @@ large clusters, you will need to use this command multiple times.

                                          -

                                          changeitem

                                          +

                                          changeitem

                                          Allows changing item material and base quality. By default the item currently selected in the UI will be changed (you can select items in the 'k' list or inside containers/inventory). By default change is only allowed if materials @@ -1091,7 +1129,7 @@ crafters/haulers.

                                          -

                                          colonies

                                          +

                                          colonies

                                          Allows listing all the vermin colonies on the map and optionally turning them into honey bee colonies.

                                          Options:

                                          @@ -1106,7 +1144,7 @@ crafters/haulers.

                                          -

                                          createitem

                                          +

                                          createitem

                                          Allows creating new items of arbitrary types and made of arbitrary materials. By default, items created are spawned at the feet of the selected unit.

                                          Specify the item and material information as you would indicate them in custom reaction raws, with the following differences: @@ -1145,12 +1183,12 @@ By default, items created are spawned at the feet of the selected unit.

                                          -

                                          deramp (by zilpin)

                                          +

                                          deramp (by zilpin)

                                          Removes all ramps designated for removal from the map. This is useful for replicating the old channel digging designation. It also removes any and all 'down ramps' that can remain after a cave-in (you don't have to designate anything for that to happen).

                                          -

                                          feature

                                          +

                                          feature

                                          Enables management of map features.

                                          • Discovering a magma feature (magma pool, volcano, magma sea, or curious @@ -1175,7 +1213,7 @@ that cavern to grow within your fortress.
                                          -

                                          infiniteSky

                                          +

                                          infiniteSky

                                          Automatically allocates new z-levels of sky at the top of the map as you build up, or on request allocates many levels all at once.

                                          Examples:
                                          @@ -1186,7 +1224,7 @@ Enables/disables monitoring of constructions. If you build anything in the secon
                                          -

                                          liquids

                                          +

                                          liquids

                                          Allows adding magma, water and obsidian to the game. It replaces the normal dfhack command line and can't be used from a hotkey. Settings will be remembered as long as dfhack runs. Intended for use in combination with the command @@ -1199,13 +1237,13 @@ temperatures (creating heat traps). You've been warned.

                                          -

                                          liquids-here

                                          +

                                          liquids-here

                                          Run the liquid spawner with the current/last settings made in liquids (if no settings in liquids were made it paints a point of 7/7 magma by default).

                                          Intended to be used as keybinding. Requires an active in-game cursor.

                                          -

                                          tiletypes

                                          +

                                          tiletypes

                                          Can be used for painting map tiles and is an interactive command, much like liquids.

                                          The tool works with two set of options and a brush. The brush determines which @@ -1286,22 +1324,22 @@ up.

                                          For more details, see the 'help' command while using this.

                                          -

                                          tiletypes-commands

                                          +

                                          tiletypes-commands

                                          Runs tiletypes commands, separated by ;. This makes it possible to change tiletypes modes from a hotkey.

                                          -

                                          tiletypes-here

                                          +

                                          tiletypes-here

                                          Apply the current tiletypes options at the in-game cursor position, including the brush. Can be used from a hotkey.

                                          -

                                          tiletypes-here-point

                                          +

                                          tiletypes-here-point

                                          Apply the current tiletypes options at the in-game cursor position to a single tile. Can be used from a hotkey.

                                          -

                                          tubefill

                                          +

                                          tubefill

                                          Fills all the adamantine veins again. Veins that were hollow will be left alone.

                                          @@ -1320,7 +1358,7 @@ alone.

                                          your miner when you dig into the region that used to be hollow.

                                          -

                                          plant

                                          +

                                          plant

                                          A tool for creating shrubs, growing, or getting rid of them.

                                          Subcommands:
                                          @@ -1364,11 +1402,11 @@ Works on all shrubs of the map if the cursor is hidden.

                                          -

                                          regrass

                                          +

                                          regrass

                                          Regrows grass. Not much to it ;)

                                          -

                                          weather

                                          +

                                          weather

                                          Prints the current weather map by default.

                                          Also lets you change the current weather to 'clear sky', 'rainy' or 'snowing'.

                                          Options:

                                          @@ -1389,9 +1427,9 @@ Works on all shrubs of the map if the cursor is hidden.

                                          -

                                          Map inspection

                                          +

                                          Map inspection

                                          -

                                          cursecheck

                                          +

                                          cursecheck

                                          Checks a single map tile or the whole map/world for cursed creatures (ghosts, vampires, necromancers, werebeasts, zombies).

                                          With an active in-game cursor only the selected tile will be observed. @@ -1446,17 +1484,17 @@ of curses, for example.

                                          -

                                          flows

                                          +

                                          flows

                                          A tool for checking how many tiles contain flowing liquids. If you suspect that your magma sea leaks into HFS, you can use this tool to be sure without revealing the map.

                                          -

                                          probe

                                          +

                                          probe

                                          Can be used to determine tile properties like temperature.

                                          -

                                          prospect

                                          +

                                          prospect

                                          Prints a big list of all the present minerals and plants. By default, only the visible part of the map is scanned.

                                          Options:

                                          @@ -1475,7 +1513,7 @@ the visible part of the map is scanned.

                                          -

                                          Pre-embark estimate

                                          +

                                          Pre-embark estimate

                                          If prospect is called during the embark selection screen, it displays an estimate of layer stone availability.

                                          @@ -1500,7 +1538,7 @@ that is actually present.

                                          -

                                          reveal

                                          +

                                          reveal

                                          This reveals the map. By default, HFS will remain hidden so that the demons don't spawn. You can use 'reveal hell' to reveal everything. With hell revealed, you won't be able to unpause until you hide the map again. If you really want @@ -1509,35 +1547,35 @@ to unpause with hell revealed, use 'reveal demons'.

                                          you move. When you use it this way, you don't need to run 'unreveal'.

                                          -

                                          unreveal

                                          +

                                          unreveal

                                          Reverts the effects of 'reveal'.

                                          -

                                          revtoggle

                                          +

                                          revtoggle

                                          Switches between 'reveal' and 'unreveal'.

                                          -

                                          revflood

                                          +

                                          revflood

                                          This command will hide the whole map and then reveal all the tiles that have a path to the in-game cursor.

                                          -

                                          revforget

                                          +

                                          revforget

                                          When you use reveal, it saves information about what was/wasn't visible before revealing everything. Unreveal uses this information to hide things again. This command throws away the information. For example, use in cases where you abandoned with the fort revealed and no longer want the data.

                                          -

                                          showmood

                                          +

                                          showmood

                                          Shows all items needed for the currently active strange mood.

                                          -

                                          Mod Interaction

                                          +

                                          Mod Interaction

                                          This section describes plugins that interact with information in the raw files to add new features that cannot be achieved by only changing raw files.

                                          -

                                          autoSyndrome

                                          +

                                          autoSyndrome

                                          This plugin replaces "boiling rock" syndromes. Without this plugin, it is possible to add a syndrome to a unit by making the unit perform a custom reaction. First, add the syndrome to a rock which boils at room temperature. Make sure that the syndrome is spread by inhaling. Then, add a custom reaction which creates that rock. When the reaction is performed, the rock will be created, then boil. Hopefully, the dwarf will inhale the gas and become afflicted with the syndrome. This has disadvantages.

                                          1. The creating unit might not inhale the gas. This makes it difficult to balance gameplay, as it is hard to measure this probability.
                                          2. @@ -1578,11 +1616,11 @@ you abandoned with the fort revealed and no longer want the data.

                                            Again, note that plugins AND scripts can be executed this way, and arguments will be passed according to the same rules.

                                          -

                                          outsideOnly

                                          +

                                          outsideOnly

                                          This plugin makes it so that buildings whose names begin with OUTSIDE_ONLY cannot be built inside. If the player attempts to do so, the building will automatically be deconstructed.

                                          -

                                          syndromeTrigger

                                          +

                                          syndromeTrigger

                                          This plugin allows DFHack commands to be executed whenever a unit becomes afflicted with a syndrome. This can happen due to a boiling rock, an interaction, autoSyndrome, etc. Regardless of the cause, if the appropriate SYN_CLASS tags are present, the command will execute.

                                          The syntax is very similar to autoSyndrome. If the syndrome has the \COMMAND tag, every subsequent SYN_CLASS tag will be used to create a console command. The following tags are "special":

                                            @@ -1592,7 +1630,7 @@ you abandoned with the fort revealed and no longer want the data.

                                          If there is a [SYN_CLASS:\AUTO_SYNDROME] tag, then the command, if any, will NOT be executed by syndromeTrigger, because it should already have been executed by autoSyndrome.

                                          -

                                          True Transformation

                                          +

                                          True Transformation

                                          The syndromeTrigger plugin also allows true, permanent transformations. In vanilla DF, if syndrome A transforms dwarves into goblins permanently, and syndrome B transforms goblins into dragons permanently, then syndrome B would NOT properly transform goblins that had been transformed from dwarves. True transformations can be achieved with this plugin.

                                          True transformations work differently. First, the unit transforms into a temporary, distinct, intermediate form. While transformed, this plugin overwrites their "original" unit type with the desired type. When the transformation wears off, they will turn "back" into the new unit type. Once truly transformed, units will function as if they had always been the new unit type. Equipment may be dropped on transformation, but relationships and experience should be maintained.

                                          Suppose you want to transform dwarves into goblins. First, make a syndrome that turns dwarves into ducks for 1 tick (start:0:end:1). It should work with END:1, but if it doesn't, try END:5. You MUST use START:0. Setting the end time very high will make the intermediate form take longer, and should have no other influence on the behavior of this plugin. The intermediate form must NOT be the same as the original form, and it must NOT be the same as the final form, or the game will crash. Add the following tags:

                                          @@ -1605,9 +1643,9 @@ you abandoned with the fort revealed and no longer want the data.

                                          -

                                          Designations

                                          +

                                          Designations

                                          -

                                          burrow

                                          +

                                          burrow

                                          Miscellaneous burrow control. Allows manipulating burrows and automated burrow expansion while digging.

                                          Options:

                                          @@ -1655,17 +1693,17 @@ Digging 1-wide corridors with the miner inside the burrow is SLOW.
                                          -

                                          digv

                                          +

                                          digv

                                          Designates a whole vein for digging. Requires an active in-game cursor placed over a vein tile. With the 'x' option, it will traverse z-levels (putting stairs between the same-material tiles).

                                          -

                                          digvx

                                          +

                                          digvx

                                          A permanent alias for 'digv x'.

                                          -

                                          digl

                                          +

                                          digl

                                          Designates layer stone for digging. Requires an active in-game cursor placed over a layer stone tile. With the 'x' option, it will traverse z-levels (putting stairs between the same-material tiles). With the 'undo' option it @@ -1673,11 +1711,11 @@ will remove the dig designation instead (if you realize that digging out a 50 z-level deep layer was not such a good idea after all).

                                          -

                                          diglx

                                          +

                                          diglx

                                          A permanent alias for 'digl x'.

                                          -

                                          digexp

                                          +

                                          digexp

                                          This command can be used for exploratory mining.

                                          See: http://df.magmawiki.com/index.php/DF2010:Exploratory_mining

                                          There are two variables that can be set: pattern and filter.

                                          @@ -1740,7 +1778,7 @@ z-level deep layer was not such a good idea after all).

                                          -

                                          digcircle

                                          +

                                          digcircle

                                          A command for easy designation of filled and hollow circles. It has several types of options.

                                          Shape:

                                          @@ -1803,7 +1841,7 @@ repeats with the last selected parameters.

                                        -

                                        digtype

                                        +

                                        digtype

                                        For every tile on the map of the same vein type as the selected tile, this command designates it to have the same designation as the selected tile. If the selected tile has no designation, they will be dig designated. If an argument is given, the designation of the selected tile is ignored, and all appropriate tiles are set to the specified designation.

                                        Options:

                                        @@ -1831,7 +1869,7 @@ If an argument is given, the designation of the selected tile is ignored, and al
                                        -

                                        digFlood

                                        +

                                        digFlood

                                        Automatically digs out specified veins as they are discovered. It runs once every time a dwarf finishes a dig job. It will only dig out appropriate tiles that are adjacent to the finished dig job. To add a vein type, use digFlood 1 [type]. This will also enable the plugin. To remove a vein type, use digFlood 0 [type] 1 to disable, then remove, then re-enable.

                                        digFlood 0 disable

                                        digFlood 1 enable

                                        @@ -1842,7 +1880,7 @@ If an argument is given, the designation of the selected tile is ignored, and al

                                        See help digFlood for details.

                                        -

                                        filltraffic

                                        +

                                        filltraffic

                                        Set traffic designations using flood-fill starting at the cursor.

                                        Traffic Type Codes:

                                        @@ -1881,7 +1919,7 @@ If an argument is given, the designation of the selected tile is ignored, and al 'filltraffic H' - When used in a room with doors, it will set traffic to HIGH in just that room.
                                        -

                                        alltraffic

                                        +

                                        alltraffic

                                        Set traffic designations for every single tile of the map (useful for resetting traffic designations).

                                        Traffic Type Codes:

                                        @@ -1905,15 +1943,15 @@ If an argument is given, the designation of the selected tile is ignored, and al 'alltraffic N' - Set traffic to 'normal' for all tiles.
                                        -

                                        restrictliquid

                                        +

                                        restrictliquid

                                        Restrict traffic on all visible tiles with liquid.

                                        -

                                        restrictice

                                        +

                                        restrictice

                                        Restrict traffic on all tiles on top of visible ice.

                                        -

                                        getplants

                                        +

                                        getplants

                                        This tool allows plant gathering and tree cutting by RAW ID. Specify the types of trees to cut down and/or shrubs to gather by their plant names, separated by spaces.

                                        @@ -1940,9 +1978,9 @@ all valid plant IDs will be listed.

                                        -

                                        Cleanup and garbage disposal

                                        +

                                        Cleanup and garbage disposal

                                        -

                                        clean

                                        +

                                        clean

                                        Cleans all the splatter that get scattered all over the map, items and creatures. In an old fortress, this can significantly reduce FPS lag. It can also spoil your !!FUN!!, so think before you use it.

                                        @@ -1976,12 +2014,12 @@ also spoil your !!FUN!!, so think before you use it.

                                        -

                                        spotclean

                                        +

                                        spotclean

                                        Works like 'clean map snow mud', but only for the tile under the cursor. Ideal if you want to keep that bloody entrance 'clean map' would clean up.

                                        -

                                        autodump

                                        +

                                        autodump

                                        This utility lets you quickly move all items designated to be dumped. Items are instantly moved to the cursor position, the dump flag is unset, and the forbid flag is set, as if it had been dumped normally. @@ -2008,17 +2046,17 @@ Be aware that any active dump item tasks still point at the item.

                                        -

                                        autodump-destroy-here

                                        +

                                        autodump-destroy-here

                                        Destroy items marked for dumping under cursor. Identical to autodump destroy-here, but intended for use as keybinding.

                                        -

                                        autodump-destroy-item

                                        +

                                        autodump-destroy-item

                                        Destroy the selected item. The item may be selected in the 'k' list, or inside a container. If called again before the game is resumed, cancels destroy.

                                        -

                                        cleanowned

                                        +

                                        cleanowned

                                        Confiscates items owned by dwarfs. By default, owned food on the floor and rotten items are confistacted and dumped.

                                        Options:

                                        @@ -2052,13 +2090,13 @@ worn items with 'X' damage and above.
                                        -

                                        Bugfixes

                                        +

                                        Bugfixes

                                        -

                                        drybuckets

                                        +

                                        drybuckets

                                        This utility removes water from all buckets in your fortress, allowing them to be safely used for making lye.

                                        -

                                        fixdiplomats

                                        +

                                        fixdiplomats

                                        Up to version 0.31.12, Elves only sent Diplomats to your fortress to propose tree cutting quotas due to a bug; once that bug was fixed, Elves stopped caring about excess tree cutting. This command adds a Diplomat position to all Elven @@ -2067,19 +2105,19 @@ to violate them and potentially start wars) in case you haven't already modified your raws accordingly.

                                        -

                                        fixmerchants

                                        +

                                        fixmerchants

                                        This command adds the Guild Representative position to all Human civilizations, allowing them to make trade agreements (just as they did back in 0.28.181.40d and earlier) in case you haven't already modified your raws accordingly.

                                        -

                                        fixveins

                                        +

                                        fixveins

                                        Removes invalid references to mineral inclusions and restores missing ones. Use this if you broke your embark with tools like tiletypes, or if you accidentally placed a construction on top of a valuable mineral floor.

                                        -

                                        tweak

                                        +

                                        tweak

                                        Contains various tweaks for minor bugs.

                                        One-shot subcommands:

                                        @@ -2197,7 +2235,7 @@ With this tweak, items made from cloth and leather will gain a level of wear eve
                                        -

                                        fix-armory

                                        +

                                        fix-armory

                                        Enables a fix for storage of squad equipment in barracks.

                                        Specifically, it prevents your haulers from moving squad equipment to stockpiles, and instead queues jobs to store it on weapon racks, @@ -2251,9 +2289,9 @@ these rules is intended by Toady; the rest are invented by this plugin.

                                        -

                                        Mode switch and reclaim

                                        +

                                        Mode switch and reclaim

                                        -

                                        lair

                                        +

                                        lair

                                        This command allows you to mark the map as 'monster lair', preventing item scatter on abandon. When invoked as 'lair reset', it does the opposite.

                                        Unlike reveal, this command doesn't save the information about tiles - you @@ -2273,7 +2311,7 @@ won't be able to restore state of real monster lairs using 'lair reset'.

                                        -

                                        mode

                                        +

                                        mode

                                        This command lets you see and change the game mode directly. Not all combinations are good for every situation and most of them will produce undesirable results. There are a few good ones though.

                                        @@ -2293,9 +2331,9 @@ You just created a returnable mountain home and gained an adventurer.

                                        -

                                        Visualizer and data export

                                        +

                                        Visualizer and data export

                                        -

                                        ssense / stonesense

                                        +

                                        ssense / stonesense

                                        An isometric visualizer that runs in a second window. This requires working graphics acceleration and at least a dual core CPU (otherwise it will slow down DF).

                                        @@ -2308,19 +2346,19 @@ thread: http://df.magmawiki.com/index.php/Utility:Stonesense/Content_repository

                                        -

                                        mapexport

                                        +

                                        mapexport

                                        Export the current loaded map as a file. This will be eventually usable with visualizers.

                                        -

                                        dwarfexport

                                        +

                                        dwarfexport

                                        Export dwarves to RuneSmith-compatible XML.

                                        -

                                        Job management

                                        +

                                        Job management

                                        -

                                        job

                                        +

                                        job

                                        Command for general job query and manipulation.

                                        Options:
                                        @@ -2339,7 +2377,7 @@ in a workshop, or the unit/jobs screen.
                                        -

                                        job-material

                                        +

                                        job-material

                                        Alter the material of the selected job.

                                        Invoked as:

                                        @@ -2357,7 +2395,7 @@ over the first available choice with the matching material.
                                      • -

                                        job-duplicate

                                        +

                                        job-duplicate

                                        Duplicate the selected job in a workshop:
                                          @@ -2368,7 +2406,7 @@ instantly duplicates the job.
                                        -

                                        workflow

                                        +

                                        workflow

                                        Manage control of repeat jobs.

                                        Usage:

                                        @@ -2400,7 +2438,7 @@ this list can be copied to a file, and then reloaded using the
                                        -

                                        Function

                                        +

                                        Function

                                        When the plugin is enabled, it protects all repeat jobs from removal. If they do disappear due to any cause, they are immediately re-added to their workshop and suspended.

                                        @@ -2413,7 +2451,7 @@ the frequency of jobs being toggled.

                                        in the game UI.

                                        -

                                        Constraint format

                                        +

                                        Constraint format

                                        The contstraint spec consists of 4 parts, separated with '/' characters:

                                         ITEM[:SUBTYPE]/[GENERIC_MAT,...]/[SPECIFIC_MAT:...]/[LOCAL,<quality>]
                                        @@ -2442,7 +2480,7 @@ be used to ignore imported items or items below a certain quality.

                                      -

                                      Constraint examples

                                      +

                                      Constraint examples

                                      Keep metal bolts within 900-1000, and wood/bone within 150-200.

                                       workflow amount AMMO:ITEM_AMMO_BOLTS/METAL 1000 100
                                      @@ -2491,15 +2529,15 @@ workflow count CRAFTS///LOCAL,EXCEPTIONAL 100 90
                                       
                                      -

                                      Fortress activity management

                                      +

                                      Fortress activity management

                                      -

                                      seedwatch

                                      +

                                      seedwatch

                                      Tool for turning cooking of seeds and plants on/off depending on how much you have of them.

                                      See 'seedwatch help' for detailed description.

                                      -

                                      zone

                                      +

                                      zone

                                      Helps a bit with managing activity zones (pens, pastures and pits) and cages.

                                      Options:

                                      @@ -2598,7 +2636,7 @@ for war/hunt). Negatable.
                                      -

                                      Usage with single units

                                      +

                                      Usage with single units

                                      One convenient way to use the zone tool is to bind the command 'zone assign' to a hotkey, maybe also the command 'zone set'. Place the in-game cursor over a pen/pasture or pit, use 'zone set' to mark it. Then you can select units @@ -2607,7 +2645,7 @@ and use 'zone assign' to assign them to their new home. Allows pitting your own dwarves, by the way.

                                      -

                                      Usage with filters

                                      +

                                      Usage with filters

                                      All filters can be used together with the 'assign' command.

                                      Restrictions: It's not possible to assign units who are inside built cages or chained because in most cases that won't be desirable anyways. @@ -2625,14 +2663,14 @@ are not properly added to your own stocks; slaughtering them should work).

                                      Most filters can be negated (e.g. 'not grazer' -> race is not a grazer).

                                      -

                                      Mass-renaming

                                      +

                                      Mass-renaming

                                      Using the 'nick' command you can set the same nickname for multiple units. If used without 'assign', 'all' or 'count' it will rename all units in the current default target zone. Combined with 'assign', 'all' or 'count' (and further optional filters) it will rename units matching the filter conditions.

                                      -

                                      Cage zones

                                      +

                                      Cage zones

                                      Using the 'tocages' command you can assign units to a set of cages, for example a room next to your butcher shop(s). They will be spread evenly among available cages to optimize hauling to and butchering from them. For this to work you need @@ -2643,7 +2681,7 @@ would make no sense, but can be used together with 'nick' or 'remnick' and all the usual filters.

                                      -

                                      Examples

                                      +

                                      Examples

                                      zone assign all own ALPACA minage 3 maxage 10
                                      Assign all own alpacas who are between 3 and 10 years old to the selected @@ -2669,7 +2707,7 @@ on the current default zone.
                                      -

                                      autonestbox

                                      +

                                      autonestbox

                                      Assigns unpastured female egg-layers to nestbox zones. Requires that you create pen/pasture zones above nestboxes. If the pen is bigger than 1x1 the nestbox must be in the top left corner. Only 1 unit will be assigned per pen, regardless @@ -2698,7 +2736,7 @@ frames between runs.

                                      -

                                      autobutcher

                                      +

                                      autobutcher

                                      Assigns lifestock for slaughter once it reaches a specific count. Requires that you add the target race(s) to a watch list. Only tame units will be processed.

                                      Named units will be completely ignored (to protect specific animals from @@ -2805,8 +2843,26 @@ Load the savegame where you want to copy the settings to, run the batch file (fr autobutcher.bat

                                      +
                                      +

                                      autochop

                                      +

                                      Automatically manage tree cutting designation to keep available logs withing given +quotas.

                                      +

                                      Open the dashboard by running:

                                      +
                                      +getplants autochop
                                      +
                                      +

                                      The plugin must be activated (with 'a') before it can be used. You can then set logging quotas +and restrict designations to specific burrows (with 'Enter') if desired. The plugin's activity +cycle runs once every in game day.

                                      +

                                      If you add

                                      +
                                      +enable getplants
                                      +
                                      +

                                      to your dfhack.init there will be a hotkey to open the dashboard from the chop designation +menu.

                                      +
                                      -

                                      autolabor

                                      +

                                      autolabor

                                      Automatically manage dwarf labors.

                                      When enabled, autolabor periodically checks your dwarves and enables or disables labors. It tries to keep as many dwarves as possible busy but @@ -2821,14 +2877,14 @@ while it is enabled.

                                      -

                                      Other

                                      +

                                      Other

                                      -

                                      catsplosion

                                      +

                                      catsplosion

                                      Makes cats just multiply. It is not a good idea to run this more than once or twice.

                                      -

                                      dfusion

                                      +

                                      dfusion

                                      This is the DFusion lua plugin system by Warmist, running as a DFHack plugin. There are two parts to this plugin: an interactive script that shows a text based menu and lua modules. Some of the functionality of is intentionaly left out of the menu:
                                      @@ -2853,7 +2909,7 @@ twice.

                                      -

                                      misery

                                      +

                                      misery

                                      When enabled, every new negative dwarven thought will be multiplied by a factor (2 by default).

                                      Usage:

                                      @@ -2875,7 +2931,7 @@ twice.

                                      -

                                      strangemood

                                      +

                                      strangemood

                                      Creates a strange mood job the same way the game itself normally does it.

                                      Options:

                                      @@ -2901,7 +2957,7 @@ Valid values are "miner", "carpenter", "engraver",
                                      -

                                      Scripts

                                      +

                                      Scripts

                                      Lua or ruby scripts placed in the hack/scripts/ directory are considered for execution as if they were native DFHack commands. They are listed at the end of the 'ls' command output.

                                      @@ -2910,7 +2966,7 @@ only be listed by ls if called as 'ls -a'. This is intended as a way to hide scripts that are obscure, developer-oriented, or should be used as keybindings.

                                      Some notable scripts:

                                      -

                                      fix/*

                                      +

                                      fix/*

                                      Scripts in this subdirectory fix various bugs and issues, some of them obscure.

                                      • fix/dead-units

                                        @@ -2947,12 +3003,12 @@ hopefully avoid jamming it again, and unsuspends them.

                                      -

                                      gui/*

                                      +

                                      gui/*

                                      Scripts that implement dialogs inserted into the main game window are put in this directory.

                                      -

                                      binpatch

                                      +

                                      binpatch

                                      Checks, applies or removes binary patches directly in memory at runtime:

                                       binpatch check/apply/remove <patchname>
                                      @@ -2962,17 +3018,17 @@ script uses hack/patches/<df-v
                                       the version appropriate for the currently loaded executable.

                                      -

                                      quicksave

                                      +

                                      quicksave

                                      If called in dwarf mode, makes DF immediately auto-save the game by setting a flag normally used in seasonal auto-save.

                                      -

                                      setfps

                                      +

                                      setfps

                                      Run setfps <number> to set the FPS cap at runtime, in case you want to watch combat in slow motion or something :)

                                      -

                                      siren

                                      +

                                      siren

                                      Wakes up sleeping units, cancels breaks and stops parties either everywhere, or in the burrows given as arguments. In return, adds bad thoughts about noise, tiredness and lack of protection. Also, the units with interrupted @@ -2980,7 +3036,7 @@ breaks will go on break again a lot sooner. The script is intended for emergencies, e.g. when a siege appears, and all your military is partying.

                                      -

                                      growcrops

                                      +

                                      growcrops

                                      Instantly grow seeds inside farming plots.

                                      With no argument, this command list the various seed types currently in use in your farming plots. @@ -2992,7 +3048,7 @@ growcrops plump 40

                                      -

                                      removebadthoughts

                                      +

                                      removebadthoughts

                                      This script remove negative thoughts from your dwarves. Very useful against tantrum spirals.

                                      The script can target a single creature, when used with the him argument, @@ -3006,7 +3062,7 @@ but in the short term your dwarves will get much more joyful.

                                      quickly after you unpause.

                                      -

                                      exterminate

                                      +

                                      exterminate

                                      Kills any unit of a given race.

                                      With no argument, lists the available races and count eligible targets.

                                      With the special argument him, targets only the selected creature.

                                      @@ -3038,7 +3094,7 @@ exterminate elve magma
                                      -

                                      source

                                      +

                                      source

                                      Create an infinite magma or water source or drain on a tile.

                                      This script registers a map tile as a liquid source, and every 12 game ticks that tile receives or remove 1 new unit of flow based on the configuration.

                                      @@ -3060,14 +3116,14 @@ source add water 0 - water drain
                                      -

                                      masspit

                                      +

                                      masspit

                                      Designate all creatures in cages on top of a pit/pond activity zone for pitting. Works best with an animal stockpile on top of the zone.

                                      Works with a zone number as argument (eg Activity Zone #6 -> masspit 6) or with the game cursor on top of the area.

                                      -

                                      digfort

                                      +

                                      digfort

                                      A script to designate an area for digging according to a plan in csv format.

                                      This script, inspired from quickfort, can designate an area for digging. Your plan should be stored in a .csv file like this:

                                      @@ -3089,7 +3145,7 @@ as an offset for the pattern: instead of starting at the cursor, it will start Dwarf Fortress.exe is found).

                                      -

                                      invasion-now

                                      +

                                      invasion-now

                                      Triggers an invasion, or several in the near future.

                                      invasion-now civName trigger an invasion from the civilization with the id civName, starting in about ten ticks

                                      invasion-now civName start trigger an invasion from civName in a number of ticks between 10*start and 11*start-1 (inclusive)

                                      @@ -3097,7 +3153,7 @@ Dwarf Fortress.exe is found).

                                      Probably fails if the start time of a triggered invasion is later than the start of the next year.

                                      -

                                      digmat

                                      +

                                      digmat

                                      Designates a tile for digging. Monitors the tile, and when it is dug out, add surrounding discovered tiles of the same material for digging. Similar to 'digv', but less cheaty. Works for stone layers, soil layers, veins, etc.

                                      @@ -3106,7 +3162,7 @@ same designation for future digging (eg dig up/downstairs). When digging stairs, also designate tiles on z-1 and z+1 when they are discovered.

                                      -

                                      superdwarf

                                      +

                                      superdwarf

                                      Similar to fastdwarf, per-creature.

                                      To make any creature superfast, target it ingame using 'v' and:

                                      @@ -3116,17 +3172,17 @@ superdwarf add
                                       

                                      This plugin also shortens the 'sleeping' and 'on break' periods of targets.

                                      -

                                      drainaquifer

                                      +

                                      drainaquifer

                                      Remove all 'aquifer' tag from the map blocks. Irreversible.

                                      -

                                      deathcause

                                      +

                                      deathcause

                                      Focus a body part ingame, and this script will display the cause of death of the creature. Also works when selecting units from the 'u'nitlist viewscreen.

                                      -

                                      lua

                                      +

                                      lua

                                      There are the following ways to invoke this command:

                                      1. lua (without any parameters)

                                        @@ -3145,11 +3201,11 @@ directory. If the filename is not supplied, it loads "dfhack.lua".

                                      -

                                      embark

                                      +

                                      embark

                                      Allows to embark anywhere. Currently windows only.

                                      -

                                      lever

                                      +

                                      lever

                                      Allow manipulation of in-game levers from the dfhack console.

                                      Can list levers, including state and links, with:

                                      @@ -3163,7 +3219,7 @@ lever pull 42 --now
                                       
                                      -

                                      stripcaged

                                      +

                                      stripcaged

                                      For dumping items inside cages. Will mark selected items for dumping, then a dwarf may come and actually dump it. See also autodump.

                                      With the items argument, only dumps items laying in the cage, excluding @@ -3181,11 +3237,11 @@ stripcaged weapons 25321 34228

                                      -

                                      undump-buildings

                                      +

                                      undump-buildings

                                      Undesignates building base materials for dumping.

                                      -

                                      create-items

                                      +

                                      create-items

                                      Spawn arbitrary items under the cursor.

                                      The first argument gives the item category, the second gives the material, and the optionnal third gives the number of items to create (defaults to 20).

                                      @@ -3207,7 +3263,7 @@ create-items bar adamantine
                                      -

                                      locate-ore

                                      +

                                      locate-ore

                                      Scan the map for metal ores.

                                      Finds and designate for digging one tile of a specific metal ore. Only works for native metal ores, does not handle reaction stuff (eg STEEL).

                                      @@ -3220,7 +3276,7 @@ locate-ore iron
                                      -

                                      soundsense-season

                                      +

                                      soundsense-season

                                      It is a well known issue that Soundsense cannot detect the correct current season when a savegame is loaded and has to play random season music until a season switch occurs.

                                      @@ -3229,7 +3285,7 @@ to gamelog.txt on every map load to fix this. For best results call the script from dfhack.init.

                                      -

                                      multicmd

                                      +

                                      multicmd

                                      Run multiple dfhack commands. The argument is split around the character ; and all parts are run sequencially as independent dfhack commands. Useful for hotkeys.

                                      @@ -3240,7 +3296,7 @@ dfhack commands. Useful for hotkeys.

                                      -

                                      In-game interface tools

                                      +

                                      In-game interface tools

                                      These tools work by displaying dialogs or overlays in the game window, and are mostly implemented by lua scripts.

                                      @@ -3255,7 +3311,7 @@ guideline because it arguably just fixes small usability bugs in the game UI.

                                      -

                                      Dwarf Manipulator

                                      +

                                      Dwarf Manipulator

                                      Implemented by the 'manipulator' plugin.

                                      To activate, open the unit screen and press 'l'.

                                      images/manipulator.png @@ -3294,7 +3350,7 @@ cursor onto that cell instead of toggling it.
                                    • directly to the main dwarf mode screen.

                                      -

                                      AutoMaterial

                                      +

                                      AutoMaterial

                                      Implemented by the 'automaterial' plugin.

                                      This makes building constructions (walls, floors, fortifications, etc) a little bit easier by saving you from having to trawl through long lists of materials each time @@ -3353,7 +3409,7 @@ materials, it returns you back to this screen. If you use this along with severa enabled materials, you should be able to place complex constructions more conveniently.

                                      -

                                      gui/liquids

                                      +

                                      gui/liquids

                                      To use, bind to a key (the example config uses Alt-L) and activate in the 'k' mode.

                                      images/liquids.png

                                      This script is a gui front-end to the liquids plugin and works similar to it, @@ -3373,7 +3429,7 @@ rivers power water wheels even when full and technically not flowing.

                                      After setting up the desired operations using the described keys, use Enter to apply them.

                                      -

                                      gui/mechanisms

                                      +

                                      gui/mechanisms

                                      To use, bind to a key (the example config uses Ctrl-M) and activate in the 'q' mode.

                                      images/mechanisms.png

                                      Lists mechanisms connected to the building, and their links. Navigating the list centers @@ -3383,7 +3439,7 @@ focus on the current one. Shift-Enter has an effect equivalent to pressing Enter re-entering the mechanisms ui.

                                      -

                                      gui/rename

                                      +

                                      gui/rename

                                      Backed by the rename plugin, this script allows entering the desired name via a simple dialog in the game ui.

                                        @@ -3406,7 +3462,7 @@ their species string.

                                        unit profession change to Ctrl-Shift-T.

                                      -

                                      gui/room-list

                                      +

                                      gui/room-list

                                      To use, bind to a key (the example config uses Alt-R) and activate in the 'q' mode, either immediately or after opening the assign owner page.

                                      images/room-list.png @@ -3414,7 +3470,7 @@ either immediately or after opening the assign owner page.

                                      list, and allows unassigning them.

                                      -

                                      gui/choose-weapons

                                      +

                                      gui/choose-weapons

                                      Bind to a key (the example config uses Ctrl-W), and activate in the Equip->View/Customize page of the military screen.

                                      Depending on the cursor location, it rewrites all 'individual choice weapon' entries @@ -3425,14 +3481,14 @@ only that entry, and does it even if it is not 'individual choice'.

                                      and may lead to inappropriate weapons being selected.

                                      -

                                      gui/clone-uniform

                                      +

                                      gui/clone-uniform

                                      Bind to a key (the example config uses Ctrl-C), and activate in the Uniforms page of the military screen with the cursor in the leftmost list.

                                      When invoked, the script duplicates the currently selected uniform template, and selects the newly created copy.

                                      -

                                      gui/guide-path

                                      +

                                      gui/guide-path

                                      Bind to a key (the example config uses Alt-P), and activate in the Hauling menu with the cursor over a Guide order.

                                      images/guide-path.png @@ -3440,7 +3496,7 @@ the cursor over a Guide order.

                                      computes it when the order is executed for the first time.

                                      -

                                      gui/workshop-job

                                      +

                                      gui/workshop-job

                                      Bind to a key (the example config uses Alt-A), and activate with a job selected in a workshop in the 'q' mode.

                                      images/workshop-job.png @@ -3476,7 +3532,7 @@ and then try to change the input item type, now it won't let you select plan you have to unset the material first.

                                      -

                                      gui/workflow

                                      +

                                      gui/workflow

                                      Bind to a key (the example config uses Alt-W), and activate with a job selected in a workshop in the 'q' mode.

                                      images/workflow.png @@ -3523,7 +3579,7 @@ the current stock value. The bright green dashed line is the target limit (maximum) and the dark green line is that minus the gap (minimum).

                                      -

                                      gui/assign-rack

                                      +

                                      gui/assign-rack

                                      Bind to a key (the example config uses P), and activate when viewing a weapon rack in the 'q' mode.

                                      images/assign-rack.png @@ -3547,7 +3603,7 @@ the intended user. In order to aid in the choice, it shows the number of currently assigned racks for every valid squad.

                                      -

                                      gui/advfort

                                      +

                                      gui/advfort

                                      This script allows to perform jobs in adventure mode. For more complete help press '?' while script is running. It's most confortable to use this as a keybinding. (e.g. keybinding set Ctrl-T gui/advfort). Possible arguments:

                                      @@ -3566,7 +3622,7 @@ implies -a
                                      -

                                      gui/companion-order

                                      +

                                      gui/companion-order

                                      A script to issue orders for companions. Select companions with lower case chars, issue orders with upper case. Must be in look or talk mode to issue command on tile.

                                      images/companion-order.png @@ -3582,7 +3638,7 @@ case. Must be in look or talk mode to issue command on tile.

                                    -

                                    gui/gm-editor

                                    +

                                    gui/gm-editor

                                    There are three ways to open this editor:

                                    • using gui/gm-editor command/keybinding - opens editor on what is selected @@ -3597,14 +3653,14 @@ the same as version above.
                                    • in-game help.

                                    -

                                    gui/mod-manager

                                    +

                                    gui/mod-manager

                                    A way to simply install and remove small mods. It looks for specially formated mods in df subfolder 'mods'. Mods are not included, for example mods see: github mini mod repository

                                    images/mod-manager.png
                                    -

                                    Behavior Mods

                                    +

                                    Behavior Mods

                                    These plugins, when activated via configuration UI or by detecting certain structures in RAWs, modify the game engine behavior concerning the target objects to add features not otherwise present.

                                    @@ -3615,20 +3671,20 @@ technical challenge, and do not represent any long-term plans to produce more similar modifications of the game.

                                    -

                                    Siege Engine

                                    +

                                    Siege Engine

                                    The siege-engine plugin enables siege engines to be linked to stockpiles, and aimed at an arbitrary rectangular area across Z levels, instead of the original four directions. Also, catapults can be ordered to load arbitrary objects, not just stones.

                                    -

                                    Rationale

                                    +

                                    Rationale

                                    Siege engines are a very interesting feature, but sadly almost useless in the current state because they haven't been updated since 2D and can only aim in four directions. This is an attempt to bring them more up to date until Toady has time to work on it. Actual improvements, e.g. like making siegers bring their own, are something only Toady can do.

                                    -

                                    Configuration UI

                                    +

                                    Configuration UI

                                    The configuration front-end to the plugin is implemented by the gui/siege-engine script. Bind it to a key (the example config uses Alt-A) and activate after selecting a siege engine in 'q' mode.

                                    @@ -3651,7 +3707,7 @@ menu.

                                    -

                                    Power Meter

                                    +

                                    Power Meter

                                    The power-meter plugin implements a modified pressure plate that detects power being supplied to gear boxes built in the four adjacent N/S/W/E tiles.

                                    The configuration front-end is implemented by the gui/power-meter script. Bind it to a @@ -3662,11 +3718,11 @@ in the build menu.

                                    configuration page, but configures parameters relevant to the modded power meter building.

                                    -

                                    Steam Engine

                                    +

                                    Steam Engine

                                    The steam-engine plugin detects custom workshops with STEAM_ENGINE in their token, and turns them into real steam engines.

                                    -

                                    Rationale

                                    +

                                    Rationale

                                    The vanilla game contains only water wheels and windmills as sources of power, but windmills give relatively little power, and water wheels require flowing water, which must either be a real river and thus immovable and @@ -3677,7 +3733,7 @@ it can be done just by combining existing features of the game engine in a new way with some glue code and a bit of custom logic.

                                    -

                                    Construction

                                    +

                                    Construction

                                    The workshop needs water as its input, which it takes via a passable floor tile below it, like usual magma workshops do. The magma version also needs magma.

                                    @@ -3701,7 +3757,7 @@ short axles that can be built later than both of the engines.

                                    -

                                    Operation

                                    +

                                    Operation

                                    In order to operate the engine, queue the Stoke Boiler job (optionally on repeat). A furnace operator will come, possibly bringing a bar of fuel, and perform it. As a result, a "boiling water" item will appear @@ -3732,7 +3788,7 @@ decrease it by further 4%, and also decrease the whole steam use rate by 10%.

                                    -

                                    Explosions

                                    +

                                    Explosions

                                    The engine must be constructed using barrel, pipe and piston from fire-safe, or in the magma version magma-safe metals.

                                    During operation weak parts get gradually worn out, and @@ -3741,7 +3797,7 @@ toppled during operation by a building destroyer, or a tantruming dwarf.

                                    -

                                    Save files

                                    +

                                    Save files

                                    It should be safe to load and view engine-using fortresses from a DF version without DFHack installed, except that in such case the engines won't work. However actually making modifications @@ -3752,7 +3808,7 @@ being generated.

                                    -

                                    Add Spatter

                                    +

                                    Add Spatter

                                    This plugin makes reactions with names starting with SPATTER_ADD_ produce contaminants on the items instead of improvements. The produced contaminants are immune to being washed away by water or destroyed by diff --git a/plugins/rendermax/rendermax.cpp b/plugins/rendermax/rendermax.cpp index dcfee0092..a6b2f17c7 100644 --- a/plugins/rendermax/rendermax.cpp +++ b/plugins/rendermax/rendermax.cpp @@ -326,7 +326,7 @@ static command_result rendermax(color_ostream &out, vector & parameters return CR_WRONG_USAGE; if(!df::global::enabler->renderer->uses_opengl()) { - out.printerr("Sorry, this plugin needs open gl enabled printmode. Try STANDARD or other non-2d"); + out.printerr("Sorry, this plugin needs open gl enabled printmode. Try STANDARD or other non-2D\n"); return CR_FAILURE; } string cmd=parameters[0]; From b166f51568ea92138179b44c6949b55c12e816c9 Mon Sep 17 00:00:00 2001 From: lethosor Date: Sun, 11 May 2014 14:10:09 -0400 Subject: [PATCH 100/676] Handle missing arguments to filter/paint in tiletypes --- plugins/tiletypes.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plugins/tiletypes.cpp b/plugins/tiletypes.cpp index fd6d3735a..103a2dcfb 100644 --- a/plugins/tiletypes.cpp +++ b/plugins/tiletypes.cpp @@ -500,6 +500,12 @@ bool tryVariant(std::string value, TileType &paint) bool processTileType(color_ostream & out, TileType &paint, std::vector ¶ms, int start, int end) { + if (start == end) + { + out << "Missing argument." << std::endl; + return false; + } + int loc = start; std::string option = params[loc++]; std::string value = end <= loc ? "" : params[loc++]; From bbc417281c72d20dd31fa65c47bc472510e70522 Mon Sep 17 00:00:00 2001 From: lethosor Date: Sun, 11 May 2014 14:42:40 -0400 Subject: [PATCH 101/676] Restore old terminal settings on segfault on OS X --- package/darwin/dfhack | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/package/darwin/dfhack b/package/darwin/dfhack index 7c3be1cdf..40fc083ab 100755 --- a/package/darwin/dfhack +++ b/package/darwin/dfhack @@ -3,12 +3,15 @@ PWD=`dirname "${0}"` #thanks to Iriel for figuring this out OSREV=`uname -r | cut -d. -f1` if [ "$OSREV" -ge 11 ] ; then - export DYLD_INSERT_LIBRARIES=./hack/libdfhack.dylib export DYLD_LIBRARY_PATH=${PWD}/hack:${PWD}/libs export DYLD_FRAMEWORK_PATH=${PWD}/hack:${PWD}/libs else - export DYLD_INSERT_LIBRARIES=./hack/libdfhack.dylib export DYLD_FALLBACK_LIBRARY_PATH=${PWD}/hack:${PWD}/libs export DYLD_FALLBACK_FRAMEWORK_PATH=${PWD}/hack:${PWD}/libs fi -cd "${PWD}"; ./dwarfort.exe + +old_tty_settings=$(stty -g) +cd "${PWD}" +DYLD_INSERT_LIBRARIES=./hack/libdfhack.dylib ./dwarfort.exe +stty "$old_tty_settings" +echo "" From 795325b6ef2e9f552eea886792d6ec1ada6ee140 Mon Sep 17 00:00:00 2001 From: PeridexisErrant Date: Mon, 12 May 2014 10:22:04 +1000 Subject: [PATCH 102/676] replace all tabs with eight spaces apparently an important style thing, which I missed --- scripts/exportlegends.lua | 88 +++++++++++++++++++-------------------- scripts/log-region.lua | 40 +++++++++--------- 2 files changed, 64 insertions(+), 64 deletions(-) diff --git a/scripts/exportlegends.lua b/scripts/exportlegends.lua index df65da7b2..282560b00 100644 --- a/scripts/exportlegends.lua +++ b/scripts/exportlegends.lua @@ -12,57 +12,57 @@ if args[1] then print(' A script which exports data from legends mode. Valid local MAPS = { - [0] = "Standard biome+site map", - "Elevations including lake and ocean floors", - "Elevations respecting water level", - "Biome", - "Hydrosphere", - "Temperature", - "Rainfall", - "Drainage", - "Savagery", - "Volcanism", - "Current vegetation", - "Evil", - "Salinity", - "Structures/fields/roads/etc.", - "Trade", + [0] = "Standard biome+site map", + "Elevations including lake and ocean floors", + "Elevations respecting water level", + "Biome", + "Hydrosphere", + "Temperature", + "Rainfall", + "Drainage", + "Savagery", + "Volcanism", + "Current vegetation", + "Evil", + "Salinity", + "Structures/fields/roads/etc.", + "Trade", } function wait_for_legends_vs() - vs = dfhack.gui.getCurViewscreen() - if i < 15 then - if df.viewscreen_legendsst:is_instance(vs) then - gui.simulateInput(vs, 'LEGENDS_EXPORT_DETAILED_MAP') -- "d" on screen some number internally - dfhack.timeout(10,'frames',wait_for_export_maps_vs) - else - dfhack.timeout(10,'frames',wait_for_legends_vs) - end - end + vs = dfhack.gui.getCurViewscreen() + if i < 15 then + if df.viewscreen_legendsst:is_instance(vs) then + gui.simulateInput(vs, 'LEGENDS_EXPORT_DETAILED_MAP') -- "d" on screen some number internally + dfhack.timeout(10,'frames',wait_for_export_maps_vs) + else + dfhack.timeout(10,'frames',wait_for_legends_vs) + end + end end function wait_for_export_maps_vs() - vs = dfhack.gui.getCurViewscreen() - if df.viewscreen_export_graphical_mapst:is_instance(vs) then - vs.anon_13 = i -- anon_13 appears to be the selection cursor for this viewscreen - print('Exporting: '..MAPS[i]) - i = i + 1 - gui.simulateInput(vs, 'SELECT') -- 1 internally, enter on screen - dfhack.timeout(10,'frames',wait_for_legends_vs) - else - dfhack.timeout(10,'frames',wait_for_export_maps_vs) - end + vs = dfhack.gui.getCurViewscreen() + if df.viewscreen_export_graphical_mapst:is_instance(vs) then + vs.anon_13 = i -- anon_13 appears to be the selection cursor for this viewscreen + print('Exporting: '..MAPS[i]) + i = i + 1 + gui.simulateInput(vs, 'SELECT') -- 1 internally, enter on screen + dfhack.timeout(10,'frames',wait_for_legends_vs) + else + dfhack.timeout(10,'frames',wait_for_export_maps_vs) + end end if df.viewscreen_legendsst:is_instance( vs ) then -- dfhack.gui.getCurFocus() == "legends" - if args[1] == "all" then - gui.simulateInput(vs, df.interface_key.LEGENDS_EXPORT_MAP) - print('Exporting: world map/gen info') - gui.simulateInput(vs, df.interface_key.LEGENDS_EXPORT_XML) - print('Exporting: legends xml') - wait_for_legends_vs() - elseif args[1] == "maps" then wait_for_legends_vs() - end + if args[1] == "all" then + gui.simulateInput(vs, df.interface_key.LEGENDS_EXPORT_MAP) + print('Exporting: world map/gen info') + gui.simulateInput(vs, df.interface_key.LEGENDS_EXPORT_XML) + print('Exporting: legends xml') + wait_for_legends_vs() + elseif args[1] == "maps" then wait_for_legends_vs() + end elseif df.viewscreen_export_graphical_mapst:is_instance(vs) then - if args[1] == "maps" or "all" then wait_for_export_maps_vs() end + if args[1] == "maps" or "all" then wait_for_export_maps_vs() end else - dfhack.printerr('Not in legends view') + dfhack.printerr('Not in legends view') end diff --git a/scripts/log-region.lua b/scripts/log-region.lua index 490662ef3..ef4e9968f 100644 --- a/scripts/log-region.lua +++ b/scripts/log-region.lua @@ -9,27 +9,27 @@ end local args = {...} if args[1] == 'disable' then - dfhack.onStateChange[_ENV] = nil + dfhack.onStateChange[_ENV] = nil else - dfhack.onStateChange[_ENV] = function(op) - if op == SC_WORLD_LOADED then - if df.world_site.find(df.global.ui.site_id) ~= nil then -- added this check, now only attempts write in fort mode - local site = df.world_site.find(df.global.ui.site_id) - local fort_ent = df.global.ui.main.fortress_entity - local civ_ent = df.historical_entity.find(df.global.ui.civ_id) - local worldname = df.global.world.world_data.name - -- site positions - -- site .pos.x .pos.y - -- site .rgn_min_x .rgn_min_y .rgn_max_x .rgn_max.y - -- site .global_min_x .global_min_y .global_max_x .global_max_y - --site.name - --fort_ent.name - --civ_ent.name - - write_gamelog('Loaded '..df.global.world.cur_savegame.save_dir..', '..dfhack.TranslateName(worldname)..' ('..dfhack.TranslateName(worldname ,true)..') at coordinates ('..site.pos.x..','..site.pos.y..')'..NEWLINE.. - 'Loaded the fortress '..dfhack.TranslateName(site.name)..' ('..dfhack.TranslateName(site.name, true)..'), colonized by the group '..dfhack.TranslateName(fort_ent.name)..' ('..dfhack.TranslateName(fort_ent.name,true).. - ') of the civilization '..dfhack.TranslateName(civ_ent.name)..' ('..dfhack.TranslateName(civ_ent.name,true)..').'..NEWLINE) - end + dfhack.onStateChange[_ENV] = function(op) + if op == SC_WORLD_LOADED then + if df.world_site.find(df.global.ui.site_id) ~= nil then -- added this check, now only attempts write in fort mode + local site = df.world_site.find(df.global.ui.site_id) + local fort_ent = df.global.ui.main.fortress_entity + local civ_ent = df.historical_entity.find(df.global.ui.civ_id) + local worldname = df.global.world.world_data.name + -- site positions + -- site .pos.x .pos.y + -- site .rgn_min_x .rgn_min_y .rgn_max_x .rgn_max.y + -- site .global_min_x .global_min_y .global_max_x .global_max_y + --site.name + --fort_ent.name + --civ_ent.name + + write_gamelog('Loaded '..df.global.world.cur_savegame.save_dir..', '..dfhack.TranslateName(worldname)..' ('..dfhack.TranslateName(worldname ,true)..') at coordinates ('..site.pos.x..','..site.pos.y..')'..NEWLINE.. + 'Loaded the fortress '..dfhack.TranslateName(site.name)..' ('..dfhack.TranslateName(site.name, true)..'), colonized by the group '..dfhack.TranslateName(fort_ent.name)..' ('..dfhack.TranslateName(fort_ent.name,true).. + ') of the civilization '..dfhack.TranslateName(civ_ent.name)..' ('..dfhack.TranslateName(civ_ent.name,true)..').'..NEWLINE) + end end end end From a2f5e971e2fd8528f8906219dfe3095bc568bba9 Mon Sep 17 00:00:00 2001 From: PeridexisErrant Date: Mon, 12 May 2014 10:29:59 +1000 Subject: [PATCH 103/676] fixed whitespace in dfstatus script all indents now use a uniform eight spaces; fixed from mix of four spaces and tabs --- scripts/dfstatus.lua | 204 +++++++++++++++++++++---------------------- 1 file changed, 102 insertions(+), 102 deletions(-) diff --git a/scripts/dfstatus.lua b/scripts/dfstatus.lua index 8f494df81..3442503d9 100644 --- a/scripts/dfstatus.lua +++ b/scripts/dfstatus.lua @@ -1,121 +1,121 @@ --- dfstatus 1.0 - a quick access status screen. +-- dfstatus 1.5 - a quick access status screen. -- written by enjia2000@gmail.com, fixed by Lethosor and PeridexisErrant local gui = require 'gui' function draw() - screen2 = gui.FramedScreen{ - frame_style = gui.GREY_LINE_FRAME, - frame_title = 'dfstatus', - frame_width = 16, - frame_height = 17, - frame_inset = 1, - } + screen2 = gui.FramedScreen{ + frame_style = gui.GREY_LINE_FRAME, + frame_title = 'dfstatus', + frame_width = 16, + frame_height = 17, + frame_inset = 1, + } end if (not shown) then - draw() - screen2:show() - shown = true + draw() + screen2:show() + shown = true else - shown = nil - screen2:dismiss() + shown = nil + screen2:dismiss() end function screen2:onRenderBody(dc) - local drink = 0 - local wood = 0 - --local meat = 0 - --local raw_fish = 0 - --local plants = 0 - local prepared_meals = 0 - - local fuel = 0 - local pigiron = 0 - local iron = 0 - local steel = 0 - - local silver = 0 - local copper = 0 - local gold = 0 - - local tannedhides = 0 - local cloth = 0 - - --print("------------------------------") - for _,item in ipairs(df.global.world.items.all) do + local drink = 0 + local wood = 0 + --local meat = 0 + --local raw_fish = 0 + --local plants = 0 + local prepared_meals = 0 + + local fuel = 0 + local pigiron = 0 + local iron = 0 + local steel = 0 + + local silver = 0 + local copper = 0 + local gold = 0 + + local tannedhides = 0 + local cloth = 0 + + --print("------------------------------") + for _,item in ipairs(df.global.world.items.all) do if(not item.flags.rotten and not item.flags.dump and not item.flags.forbid) then - if(item:getType() ~= df.item_type.THREAD and item:getType() ~= df.item_type.REMAINS and item:getType() ~= df.item_type.ARMOR and item:getType() ~= df.item_type.SHOES and item:getType() ~= df.item_type.SHIELD and item:getType() ~= df.item_type.HELM and item:getType() ~= df.item_type.GOVES) then - --print(item:getType() .. ":" .. dfhack.items.getDescription(item,0)) - end - if(item:getType() == df.item_type.DRINK)then - --print(item:getType() .. ":" .. dfhack.items.getDescription(item,0)) - end - - if (item:getType() == df.item_type.WOOD) then wood = wood + item:getStackSize() - elseif (item:getType() == df.item_type.DRINK) then drink = drink + item:getStackSize() - elseif (item:getType() == df.item_type.SKIN_TANNED) then tannedhides = tannedhides + item:getStackSize() - elseif (item:getType() == df.item_type.CLOTH) then cloth = cloth + item:getStackSize() - --elseif (item:getType() == df.item_type.MEAT) then meat = meat + item:getStackSize() - --elseif (item:getType() == df.item_type.FISH_RAW) then raw_fish = raw_fish + item:getStackSize() - --elseif (item:getType() == df.item_type.PLANT) then plants = plants + item:getStackSize() - elseif (item:getType() == df.item_type.FOOD) then prepared_meals = prepared_meals + item:getStackSize() - elseif (item:getType() == df.item_type.BAR) then - for token in string.gmatch(dfhack.items.getDescription(item,0),"[^%s]+") do - if (token == "silver") then silver = silver + item:getStackSize() - elseif (token == "charcoal" or token == "coke") then fuel = fuel + item:getStackSize() - elseif (token == "iron") then iron = iron + item:getStackSize() - elseif (token == "pig") then pigiron = pigiron + item:getStackSize() - elseif (token == "copper") then copper = copper + item:getStackSize() - elseif (token == "gold") then gold = gold + item:getStackSize() - elseif (token == "steel") then steel = steel + item:getStackSize() - end - break -- only need to look at the 1st token of each item. - end - end - end - end - --print("------------------------------") - dc:string("Drinks: ".. drink, COLOR_LIGHTGREEN) - dc:newline(0) - dc:string("Meals: ".. prepared_meals, COLOR_LIGHTGREEN) - dc:newline(0) - dc:newline(0) - dc:string("Wood: ".. wood, COLOR_LIGHTGREEN) - dc:newline(0) - dc:newline(0) - dc:string("Hides: ".. tannedhides, COLOR_LIGHTGREEN) - dc:newline(0) - dc:string("Cloth: ".. cloth, COLOR_LIGHTGREEN) - dc:newline(0) --- dc:string("Raw Fish: ".. raw_fish, COLOR_LIGHTGREEN) --- dc:newline(0) --- dc:string("Plants: ".. plants, COLOR_LIGHTGREEN) --- dc:newline(0) - dc:newline(0) - dc:string("Bars:", COLOR_LIGHTGREEN) - dc:newline(1) - dc:string("Fuel: ".. fuel, COLOR_LIGHTGREEN) - dc:newline(1) - dc:string("Pig Iron: ".. pigiron, COLOR_LIGHTGREEN) - dc:newline(1) - dc:string("Steel: ".. steel, COLOR_LIGHTGREEN) - dc:newline(1) - dc:string("Iron: ".. iron, COLOR_LIGHTGREEN) - dc:newline(1) - dc:newline(1) - dc:string("Copper: ".. copper, COLOR_LIGHTGREEN) - dc:newline(1) - dc:string("Silver: ".. silver, COLOR_LIGHTGREEN) - dc:newline(1) - dc:string("Gold: ".. gold, COLOR_LIGHTGREEN) + if(item:getType() ~= df.item_type.THREAD and item:getType() ~= df.item_type.REMAINS and item:getType() ~= df.item_type.ARMOR and item:getType() ~= df.item_type.SHOES and item:getType() ~= df.item_type.SHIELD and item:getType() ~= df.item_type.HELM and item:getType() ~= df.item_type.GOVES) then + --print(item:getType() .. ":" .. dfhack.items.getDescription(item,0)) + end + if(item:getType() == df.item_type.DRINK)then + --print(item:getType() .. ":" .. dfhack.items.getDescription(item,0)) + end + + if (item:getType() == df.item_type.WOOD) then wood = wood + item:getStackSize() + elseif (item:getType() == df.item_type.DRINK) then drink = drink + item:getStackSize() + elseif (item:getType() == df.item_type.SKIN_TANNED) then tannedhides = tannedhides + item:getStackSize() + elseif (item:getType() == df.item_type.CLOTH) then cloth = cloth + item:getStackSize() + --elseif (item:getType() == df.item_type.MEAT) then meat = meat + item:getStackSize() + --elseif (item:getType() == df.item_type.FISH_RAW) then raw_fish = raw_fish + item:getStackSize() + --elseif (item:getType() == df.item_type.PLANT) then plants = plants + item:getStackSize() + elseif (item:getType() == df.item_type.FOOD) then prepared_meals = prepared_meals + item:getStackSize() + elseif (item:getType() == df.item_type.BAR) then + for token in string.gmatch(dfhack.items.getDescription(item,0),"[^%s]+") do + if (token == "silver") then silver = silver + item:getStackSize() + elseif (token == "charcoal" or token == "coke") then fuel = fuel + item:getStackSize() + elseif (token == "iron") then iron = iron + item:getStackSize() + elseif (token == "pig") then pigiron = pigiron + item:getStackSize() + elseif (token == "copper") then copper = copper + item:getStackSize() + elseif (token == "gold") then gold = gold + item:getStackSize() + elseif (token == "steel") then steel = steel + item:getStackSize() + end + break -- only need to look at the 1st token of each item. + end + end + end +end + --print("------------------------------") + dc:string("Drinks: ".. drink, COLOR_LIGHTGREEN) + dc:newline(0) + dc:string("Meals: ".. prepared_meals, COLOR_LIGHTGREEN) + dc:newline(0) + dc:newline(0) + dc:string("Wood: ".. wood, COLOR_LIGHTGREEN) + dc:newline(0) + dc:newline(0) + dc:string("Hides: ".. tannedhides, COLOR_LIGHTGREEN) + dc:newline(0) + dc:string("Cloth: ".. cloth, COLOR_LIGHTGREEN) + dc:newline(0) + -- dc:string("Raw Fish: ".. raw_fish, COLOR_LIGHTGREEN) + -- dc:newline(0) + -- dc:string("Plants: ".. plants, COLOR_LIGHTGREEN) + -- dc:newline(0) + dc:newline(0) + dc:string("Bars:", COLOR_LIGHTGREEN) + dc:newline(1) + dc:string("Fuel: ".. fuel, COLOR_LIGHTGREEN) + dc:newline(1) + dc:string("Pig Iron: ".. pigiron, COLOR_LIGHTGREEN) + dc:newline(1) + dc:string("Steel: ".. steel, COLOR_LIGHTGREEN) + dc:newline(1) + dc:string("Iron: ".. iron, COLOR_LIGHTGREEN) + dc:newline(1) + dc:newline(1) + dc:string("Copper: ".. copper, COLOR_LIGHTGREEN) + dc:newline(1) + dc:string("Silver: ".. silver, COLOR_LIGHTGREEN) + dc:newline(1) + dc:string("Gold: ".. gold, COLOR_LIGHTGREEN) end function screen2:onInput(keys) - if keys.LEAVESCREEN or keys.SELECT then - shown = nil - self:dismiss() - end + if keys.LEAVESCREEN or keys.SELECT then + shown = nil + self:dismiss() + end end From 8a488ec9147656622b5c31a7469fb380ebf96f95 Mon Sep 17 00:00:00 2001 From: PeridexisErrant Date: Mon, 12 May 2014 10:32:46 +1000 Subject: [PATCH 104/676] actually fixed whitespace to four-space indentations. I should have read the documentation earlier... --- scripts/dfstatus.lua | 196 +++++++++++++++++++++---------------------- 1 file changed, 98 insertions(+), 98 deletions(-) diff --git a/scripts/dfstatus.lua b/scripts/dfstatus.lua index 3442503d9..0ebaa141d 100644 --- a/scripts/dfstatus.lua +++ b/scripts/dfstatus.lua @@ -5,117 +5,117 @@ local gui = require 'gui' function draw() - screen2 = gui.FramedScreen{ - frame_style = gui.GREY_LINE_FRAME, - frame_title = 'dfstatus', - frame_width = 16, - frame_height = 17, - frame_inset = 1, - } + screen2 = gui.FramedScreen{ + frame_style = gui.GREY_LINE_FRAME, + frame_title = 'dfstatus', + frame_width = 16, + frame_height = 17, + frame_inset = 1, + } end if (not shown) then - draw() - screen2:show() - shown = true + draw() + screen2:show() + shown = true else - shown = nil - screen2:dismiss() + shown = nil + screen2:dismiss() end function screen2:onRenderBody(dc) - local drink = 0 - local wood = 0 - --local meat = 0 - --local raw_fish = 0 - --local plants = 0 - local prepared_meals = 0 - - local fuel = 0 - local pigiron = 0 - local iron = 0 - local steel = 0 - - local silver = 0 - local copper = 0 - local gold = 0 - - local tannedhides = 0 - local cloth = 0 + local drink = 0 + local wood = 0 + --local meat = 0 + --local raw_fish = 0 + --local plants = 0 + local prepared_meals = 0 + + local fuel = 0 + local pigiron = 0 + local iron = 0 + local steel = 0 + + local silver = 0 + local copper = 0 + local gold = 0 + + local tannedhides = 0 + local cloth = 0 + + --print("------------------------------") + for _,item in ipairs(df.global.world.items.all) do + if(not item.flags.rotten and not item.flags.dump and not item.flags.forbid) then + if(item:getType() ~= df.item_type.THREAD and item:getType() ~= df.item_type.REMAINS and item:getType() ~= df.item_type.ARMOR and item:getType() ~= df.item_type.SHOES and item:getType() ~= df.item_type.SHIELD and item:getType() ~= df.item_type.HELM and item:getType() ~= df.item_type.GOVES) then + --print(item:getType() .. ":" .. dfhack.items.getDescription(item,0)) + end + if(item:getType() == df.item_type.DRINK)then + --print(item:getType() .. ":" .. dfhack.items.getDescription(item,0)) + end - --print("------------------------------") - for _,item in ipairs(df.global.world.items.all) do - if(not item.flags.rotten and not item.flags.dump and not item.flags.forbid) then - if(item:getType() ~= df.item_type.THREAD and item:getType() ~= df.item_type.REMAINS and item:getType() ~= df.item_type.ARMOR and item:getType() ~= df.item_type.SHOES and item:getType() ~= df.item_type.SHIELD and item:getType() ~= df.item_type.HELM and item:getType() ~= df.item_type.GOVES) then - --print(item:getType() .. ":" .. dfhack.items.getDescription(item,0)) - end - if(item:getType() == df.item_type.DRINK)then - --print(item:getType() .. ":" .. dfhack.items.getDescription(item,0)) - end - - if (item:getType() == df.item_type.WOOD) then wood = wood + item:getStackSize() - elseif (item:getType() == df.item_type.DRINK) then drink = drink + item:getStackSize() - elseif (item:getType() == df.item_type.SKIN_TANNED) then tannedhides = tannedhides + item:getStackSize() - elseif (item:getType() == df.item_type.CLOTH) then cloth = cloth + item:getStackSize() - --elseif (item:getType() == df.item_type.MEAT) then meat = meat + item:getStackSize() - --elseif (item:getType() == df.item_type.FISH_RAW) then raw_fish = raw_fish + item:getStackSize() - --elseif (item:getType() == df.item_type.PLANT) then plants = plants + item:getStackSize() - elseif (item:getType() == df.item_type.FOOD) then prepared_meals = prepared_meals + item:getStackSize() - elseif (item:getType() == df.item_type.BAR) then - for token in string.gmatch(dfhack.items.getDescription(item,0),"[^%s]+") do - if (token == "silver") then silver = silver + item:getStackSize() - elseif (token == "charcoal" or token == "coke") then fuel = fuel + item:getStackSize() - elseif (token == "iron") then iron = iron + item:getStackSize() - elseif (token == "pig") then pigiron = pigiron + item:getStackSize() - elseif (token == "copper") then copper = copper + item:getStackSize() - elseif (token == "gold") then gold = gold + item:getStackSize() - elseif (token == "steel") then steel = steel + item:getStackSize() - end - break -- only need to look at the 1st token of each item. - end + if (item:getType() == df.item_type.WOOD) then wood = wood + item:getStackSize() + elseif (item:getType() == df.item_type.DRINK) then drink = drink + item:getStackSize() + elseif (item:getType() == df.item_type.SKIN_TANNED) then tannedhides = tannedhides + item:getStackSize() + elseif (item:getType() == df.item_type.CLOTH) then cloth = cloth + item:getStackSize() + --elseif (item:getType() == df.item_type.MEAT) then meat = meat + item:getStackSize() + --elseif (item:getType() == df.item_type.FISH_RAW) then raw_fish = raw_fish + item:getStackSize() + --elseif (item:getType() == df.item_type.PLANT) then plants = plants + item:getStackSize() + elseif (item:getType() == df.item_type.FOOD) then prepared_meals = prepared_meals + item:getStackSize() + elseif (item:getType() == df.item_type.BAR) then + for token in string.gmatch(dfhack.items.getDescription(item,0),"[^%s]+") do + if (token == "silver") then silver = silver + item:getStackSize() + elseif (token == "charcoal" or token == "coke") then fuel = fuel + item:getStackSize() + elseif (token == "iron") then iron = iron + item:getStackSize() + elseif (token == "pig") then pigiron = pigiron + item:getStackSize() + elseif (token == "copper") then copper = copper + item:getStackSize() + elseif (token == "gold") then gold = gold + item:getStackSize() + elseif (token == "steel") then steel = steel + item:getStackSize() end + break -- only need to look at the 1st token of each item. + end end + end end - --print("------------------------------") - dc:string("Drinks: ".. drink, COLOR_LIGHTGREEN) - dc:newline(0) - dc:string("Meals: ".. prepared_meals, COLOR_LIGHTGREEN) - dc:newline(0) - dc:newline(0) - dc:string("Wood: ".. wood, COLOR_LIGHTGREEN) - dc:newline(0) - dc:newline(0) - dc:string("Hides: ".. tannedhides, COLOR_LIGHTGREEN) - dc:newline(0) - dc:string("Cloth: ".. cloth, COLOR_LIGHTGREEN) - dc:newline(0) - -- dc:string("Raw Fish: ".. raw_fish, COLOR_LIGHTGREEN) - -- dc:newline(0) - -- dc:string("Plants: ".. plants, COLOR_LIGHTGREEN) - -- dc:newline(0) - dc:newline(0) - dc:string("Bars:", COLOR_LIGHTGREEN) - dc:newline(1) - dc:string("Fuel: ".. fuel, COLOR_LIGHTGREEN) - dc:newline(1) - dc:string("Pig Iron: ".. pigiron, COLOR_LIGHTGREEN) - dc:newline(1) - dc:string("Steel: ".. steel, COLOR_LIGHTGREEN) - dc:newline(1) - dc:string("Iron: ".. iron, COLOR_LIGHTGREEN) - dc:newline(1) - dc:newline(1) - dc:string("Copper: ".. copper, COLOR_LIGHTGREEN) - dc:newline(1) - dc:string("Silver: ".. silver, COLOR_LIGHTGREEN) - dc:newline(1) - dc:string("Gold: ".. gold, COLOR_LIGHTGREEN) + --print("------------------------------") + dc:string("Drinks: ".. drink, COLOR_LIGHTGREEN) + dc:newline(0) + dc:string("Meals: ".. prepared_meals, COLOR_LIGHTGREEN) + dc:newline(0) + dc:newline(0) + dc:string("Wood: ".. wood, COLOR_LIGHTGREEN) + dc:newline(0) + dc:newline(0) + dc:string("Hides: ".. tannedhides, COLOR_LIGHTGREEN) + dc:newline(0) + dc:string("Cloth: ".. cloth, COLOR_LIGHTGREEN) + dc:newline(0) + -- dc:string("Raw Fish: ".. raw_fish, COLOR_LIGHTGREEN) + -- dc:newline(0) + -- dc:string("Plants: ".. plants, COLOR_LIGHTGREEN) + -- dc:newline(0) + dc:newline(0) + dc:string("Bars:", COLOR_LIGHTGREEN) + dc:newline(1) + dc:string("Fuel: ".. fuel, COLOR_LIGHTGREEN) + dc:newline(1) + dc:string("Pig Iron: ".. pigiron, COLOR_LIGHTGREEN) + dc:newline(1) + dc:string("Steel: ".. steel, COLOR_LIGHTGREEN) + dc:newline(1) + dc:string("Iron: ".. iron, COLOR_LIGHTGREEN) + dc:newline(1) + dc:newline(1) + dc:string("Copper: ".. copper, COLOR_LIGHTGREEN) + dc:newline(1) + dc:string("Silver: ".. silver, COLOR_LIGHTGREEN) + dc:newline(1) + dc:string("Gold: ".. gold, COLOR_LIGHTGREEN) end function screen2:onInput(keys) - if keys.LEAVESCREEN or keys.SELECT then - shown = nil - self:dismiss() - end + if keys.LEAVESCREEN or keys.SELECT then + shown = nil + self:dismiss() + end end From 01d302efc3978cb7fa598157822ba5f1ccdf1603 Mon Sep 17 00:00:00 2001 From: PeridexisErrant Date: Mon, 12 May 2014 10:34:57 +1000 Subject: [PATCH 105/676] fixed whitespace again to four-space indentations should have read the documentation before trying a style fix... --- scripts/exportlegends.lua | 86 +++++++++++++++++++-------------------- scripts/log-region.lua | 42 +++++++++---------- 2 files changed, 64 insertions(+), 64 deletions(-) diff --git a/scripts/exportlegends.lua b/scripts/exportlegends.lua index 282560b00..a21457d71 100644 --- a/scripts/exportlegends.lua +++ b/scripts/exportlegends.lua @@ -12,57 +12,57 @@ if args[1] then print(' A script which exports data from legends mode. Valid local MAPS = { - [0] = "Standard biome+site map", - "Elevations including lake and ocean floors", - "Elevations respecting water level", - "Biome", - "Hydrosphere", - "Temperature", - "Rainfall", - "Drainage", - "Savagery", - "Volcanism", - "Current vegetation", - "Evil", - "Salinity", - "Structures/fields/roads/etc.", - "Trade", + [0] = "Standard biome+site map", + "Elevations including lake and ocean floors", + "Elevations respecting water level", + "Biome", + "Hydrosphere", + "Temperature", + "Rainfall", + "Drainage", + "Savagery", + "Volcanism", + "Current vegetation", + "Evil", + "Salinity", + "Structures/fields/roads/etc.", + "Trade", } function wait_for_legends_vs() - vs = dfhack.gui.getCurViewscreen() - if i < 15 then - if df.viewscreen_legendsst:is_instance(vs) then - gui.simulateInput(vs, 'LEGENDS_EXPORT_DETAILED_MAP') -- "d" on screen some number internally - dfhack.timeout(10,'frames',wait_for_export_maps_vs) - else - dfhack.timeout(10,'frames',wait_for_legends_vs) - end + vs = dfhack.gui.getCurViewscreen() + if i < 15 then + if df.viewscreen_legendsst:is_instance(vs) then + gui.simulateInput(vs, 'LEGENDS_EXPORT_DETAILED_MAP') -- "d" on screen some number internally + dfhack.timeout(10,'frames',wait_for_export_maps_vs) + else + dfhack.timeout(10,'frames',wait_for_legends_vs) end + end end function wait_for_export_maps_vs() - vs = dfhack.gui.getCurViewscreen() - if df.viewscreen_export_graphical_mapst:is_instance(vs) then - vs.anon_13 = i -- anon_13 appears to be the selection cursor for this viewscreen - print('Exporting: '..MAPS[i]) - i = i + 1 - gui.simulateInput(vs, 'SELECT') -- 1 internally, enter on screen - dfhack.timeout(10,'frames',wait_for_legends_vs) - else - dfhack.timeout(10,'frames',wait_for_export_maps_vs) - end + vs = dfhack.gui.getCurViewscreen() + if df.viewscreen_export_graphical_mapst:is_instance(vs) then + vs.anon_13 = i -- anon_13 appears to be the selection cursor for this viewscreen + print('Exporting: '..MAPS[i]) + i = i + 1 + gui.simulateInput(vs, 'SELECT') -- 1 internally, enter on screen + dfhack.timeout(10,'frames',wait_for_legends_vs) + else + dfhack.timeout(10,'frames',wait_for_export_maps_vs) + end end if df.viewscreen_legendsst:is_instance( vs ) then -- dfhack.gui.getCurFocus() == "legends" - if args[1] == "all" then - gui.simulateInput(vs, df.interface_key.LEGENDS_EXPORT_MAP) - print('Exporting: world map/gen info') - gui.simulateInput(vs, df.interface_key.LEGENDS_EXPORT_XML) - print('Exporting: legends xml') - wait_for_legends_vs() - elseif args[1] == "maps" then wait_for_legends_vs() - end + if args[1] == "all" then + gui.simulateInput(vs, df.interface_key.LEGENDS_EXPORT_MAP) + print('Exporting: world map/gen info') + gui.simulateInput(vs, df.interface_key.LEGENDS_EXPORT_XML) + print('Exporting: legends xml') + wait_for_legends_vs() + elseif args[1] == "maps" then wait_for_legends_vs() + end elseif df.viewscreen_export_graphical_mapst:is_instance(vs) then - if args[1] == "maps" or "all" then wait_for_export_maps_vs() end + if args[1] == "maps" or "all" then wait_for_export_maps_vs() end else - dfhack.printerr('Not in legends view') + dfhack.printerr('Not in legends view') end diff --git a/scripts/log-region.lua b/scripts/log-region.lua index ef4e9968f..9c1cc8874 100644 --- a/scripts/log-region.lua +++ b/scripts/log-region.lua @@ -9,27 +9,27 @@ end local args = {...} if args[1] == 'disable' then - dfhack.onStateChange[_ENV] = nil + dfhack.onStateChange[_ENV] = nil else - dfhack.onStateChange[_ENV] = function(op) - if op == SC_WORLD_LOADED then - if df.world_site.find(df.global.ui.site_id) ~= nil then -- added this check, now only attempts write in fort mode - local site = df.world_site.find(df.global.ui.site_id) - local fort_ent = df.global.ui.main.fortress_entity - local civ_ent = df.historical_entity.find(df.global.ui.civ_id) - local worldname = df.global.world.world_data.name - -- site positions - -- site .pos.x .pos.y - -- site .rgn_min_x .rgn_min_y .rgn_max_x .rgn_max.y - -- site .global_min_x .global_min_y .global_max_x .global_max_y - --site.name - --fort_ent.name - --civ_ent.name - - write_gamelog('Loaded '..df.global.world.cur_savegame.save_dir..', '..dfhack.TranslateName(worldname)..' ('..dfhack.TranslateName(worldname ,true)..') at coordinates ('..site.pos.x..','..site.pos.y..')'..NEWLINE.. - 'Loaded the fortress '..dfhack.TranslateName(site.name)..' ('..dfhack.TranslateName(site.name, true)..'), colonized by the group '..dfhack.TranslateName(fort_ent.name)..' ('..dfhack.TranslateName(fort_ent.name,true).. - ') of the civilization '..dfhack.TranslateName(civ_ent.name)..' ('..dfhack.TranslateName(civ_ent.name,true)..').'..NEWLINE) - end - end + dfhack.onStateChange[_ENV] = function(op) + if op == SC_WORLD_LOADED then + if df.world_site.find(df.global.ui.site_id) ~= nil then -- added this check, now only attempts write in fort mode + local site = df.world_site.find(df.global.ui.site_id) + local fort_ent = df.global.ui.main.fortress_entity + local civ_ent = df.historical_entity.find(df.global.ui.civ_id) + local worldname = df.global.world.world_data.name + -- site positions + -- site .pos.x .pos.y + -- site .rgn_min_x .rgn_min_y .rgn_max_x .rgn_max.y + -- site .global_min_x .global_min_y .global_max_x .global_max_y + --site.name + --fort_ent.name + --civ_ent.name + + write_gamelog('Loaded '..df.global.world.cur_savegame.save_dir..', '..dfhack.TranslateName(worldname)..' ('..dfhack.TranslateName(worldname ,true)..') at coordinates ('..site.pos.x..','..site.pos.y..')'..NEWLINE.. + 'Loaded the fortress '..dfhack.TranslateName(site.name)..' ('..dfhack.TranslateName(site.name, true)..'), colonized by the group '..dfhack.TranslateName(fort_ent.name)..' ('..dfhack.TranslateName(fort_ent.name,true).. + ') of the civilization '..dfhack.TranslateName(civ_ent.name)..' ('..dfhack.TranslateName(civ_ent.name,true)..').'..NEWLINE) + end + end end end From 81775d83ebbc30bab23cdc394c366a059880afb8 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Mon, 12 May 2014 21:50:32 +0400 Subject: [PATCH 106/676] Add to NEWS --- NEWS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NEWS b/NEWS index db889b4ae..ad7ed6b94 100644 --- a/NEWS +++ b/NEWS @@ -13,6 +13,8 @@ DFHack future - gui/clone-uniform: duplicates the currently selected uniform in the military screen. - fix/build-location: partial work-around for bug 5991 (trying to build wall while standing on it) - undump-buildings: removes dump designation from materials used in buildings. + - exportlegends: exports data from legends mode, allowing a set-and-forget export of large worlds. + - log-region: each time a fort is loaded identifying information will be written to the gamelog. New commands: - move the 'grow', 'extirpate' and 'immolate' commands as 'plant' subcommands From ac4ff7672728e03bcb381461115aeccd10b03485 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Mon, 12 May 2014 21:55:49 +0400 Subject: [PATCH 107/676] Add to NEWS --- NEWS | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS b/NEWS index db889b4ae..28b5bcff1 100644 --- a/NEWS +++ b/NEWS @@ -13,6 +13,7 @@ DFHack future - gui/clone-uniform: duplicates the currently selected uniform in the military screen. - fix/build-location: partial work-around for bug 5991 (trying to build wall while standing on it) - undump-buildings: removes dump designation from materials used in buildings. + - dfstatus: show an overview of critical stock quantities, including food, drinks, wood, and bars. New commands: - move the 'grow', 'extirpate' and 'immolate' commands as 'plant' subcommands From dbe903b5b79d6ede07c859311b6542116102820c Mon Sep 17 00:00:00 2001 From: Anuradha Dissanayake Date: Tue, 13 May 2014 22:15:37 +1200 Subject: [PATCH 108/676] Re-enable melting in stocks plugin. --- plugins/stocks.cpp | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/plugins/stocks.cpp b/plugins/stocks.cpp index 3b9c0317d..31d991638 100644 --- a/plugins/stocks.cpp +++ b/plugins/stocks.cpp @@ -410,9 +410,6 @@ struct item_grouped_entry bool canMelt() const { - // TODO: Fix melting - return false; - df::item *item = getFirstItem(); if (!item) return false; @@ -829,9 +826,6 @@ public: } else if (input->count(interface_key::CUSTOM_SHIFT_M)) { - //TODO: Fix melting - return; - toggleMelt(); populateItems(); } @@ -939,10 +933,7 @@ public: OutputString(COLOR_BROWN, x, y, (apply_to_all) ? "Listed" : "Selected", true, left_margin); OutputHotkeyString(x, y, "Dump", "Shift-D", true, left_margin); OutputHotkeyString(x, y, "Forbid", "Shift-F", true, left_margin); - - //TODO: Fix melting - //OutputHotkeyString(x, y, "Melt", "Shift-M", true, left_margin); - + OutputHotkeyString(x, y, "Melt", "Shift-M", true, left_margin); if (depot_info.canTrade()) OutputHotkeyString(x, y, "Mark for Trade", "Shift-T", true, left_margin); @@ -1031,9 +1022,6 @@ private: void toggleMelt() { - //TODO: Fix melting - return; - int set_to_melt = -1; auto selected = getSelectedItems(); vector items; From a516811bb1e6e7efe88ecd06b567df89162cfa0f Mon Sep 17 00:00:00 2001 From: lethosor Date: Tue, 13 May 2014 16:41:55 -0400 Subject: [PATCH 109/676] Only allow 3dveins to be run in fortress mode Prevents crash from running in arena mode --- plugins/3dveins.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/plugins/3dveins.cpp b/plugins/3dveins.cpp index 432d93cf6..c9e15aae5 100644 --- a/plugins/3dveins.cpp +++ b/plugins/3dveins.cpp @@ -42,6 +42,7 @@ using namespace MapExtras; using namespace DFHack::Random; using df::global::world; +using df::global::gametype; command_result cmd_3dveins(color_ostream &out, std::vector & parameters); @@ -1573,6 +1574,12 @@ command_result cmd_3dveins(color_ostream &con, std::vector & parame return CR_FAILURE; } + if (*gametype != game_type::DWARF_MAIN && *gametype != game_type::DWARF_RECLAIM) + { + con.printerr("Must be used in fortress mode!\n"); + return CR_FAILURE; + } + VeinGenerator generator(con); con.print("Collecting statistics...\n"); From d52a07ef76d3620a7f1eca680bba1cf69f465bd8 Mon Sep 17 00:00:00 2001 From: lethosor Date: Wed, 14 May 2014 18:56:30 -0400 Subject: [PATCH 110/676] Dismiss previous command prompt before creating a new one --- plugins/command-prompt.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/plugins/command-prompt.cpp b/plugins/command-prompt.cpp index d7a8e230c..19460f19b 100644 --- a/plugins/command-prompt.cpp +++ b/plugins/command-prompt.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -166,6 +167,10 @@ void viewscreen_commandpromptst::feed(std::set *events) DFHACK_PLUGIN("command-prompt"); command_result show_prompt(color_ostream &out, std::vector & parameters) { + if (Gui::getCurFocus() == "dfhack/commandprompt") + { + Screen::dismiss(Gui::getCurViewscreen(true)); + } std::string params; for(size_t i=0;i Date: Thu, 15 May 2014 15:51:03 -0400 Subject: [PATCH 111/676] Fix handling of newline characters in command-prompt output --- plugins/command-prompt.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/plugins/command-prompt.cpp b/plugins/command-prompt.cpp index 19460f19b..a5903f2b3 100644 --- a/plugins/command-prompt.cpp +++ b/plugins/command-prompt.cpp @@ -56,9 +56,14 @@ public: df::global::gps->display_frames=show_fps; } - void add_response(color_value v,std::string s) + void add_response(color_value v, std::string s) { - responses.push_back(std::make_pair(v,s)); + std::stringstream ss(s); + std::string part; + while (std::getline(ss, part)) + { + responses.push_back(std::make_pair(v, part)); + } } protected: std::list > responses; From 9b1d393c1ca925ac6514d664e9b03a8c9bf676f0 Mon Sep 17 00:00:00 2001 From: lethosor Date: Thu, 15 May 2014 17:30:42 -0400 Subject: [PATCH 112/676] Append newline to each section of output --- plugins/command-prompt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/command-prompt.cpp b/plugins/command-prompt.cpp index a5903f2b3..5e84db831 100644 --- a/plugins/command-prompt.cpp +++ b/plugins/command-prompt.cpp @@ -62,7 +62,7 @@ public: std::string part; while (std::getline(ss, part)) { - responses.push_back(std::make_pair(v, part)); + responses.push_back(std::make_pair(v, part + '\n')); } } protected: From 293a535aa4c47bd17a17d00fc7cdf10be92d6780 Mon Sep 17 00:00:00 2001 From: lethosor Date: Fri, 16 May 2014 20:42:18 -0400 Subject: [PATCH 113/676] Fix crash with mouse input in search plugin --- plugins/search.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/search.cpp b/plugins/search.cpp index e0213bf06..72001275c 100644 --- a/plugins/search.cpp +++ b/plugins/search.cpp @@ -180,6 +180,10 @@ public: { // Query typing mode + if (input->empty()) + { + return false; + } df::interface_key last_token = *input->rbegin(); if (last_token >= interface_key::STRING_A032 && last_token <= interface_key::STRING_A126) { From d9dc7f31c2330d614cd874acc32b261948286a36 Mon Sep 17 00:00:00 2001 From: Lethosor Date: Sat, 17 May 2014 13:48:48 -0400 Subject: [PATCH 114/676] Fix typo --- Readme.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Readme.rst b/Readme.rst index 7491c4a0c..8a3ce97c5 100644 --- a/Readme.rst +++ b/Readme.rst @@ -2947,7 +2947,7 @@ in-game help. gui/mod-manager =============== -A way to simply install and remove small mods. It looks for specially formated mods in +A way to simply install and remove small mods. It looks for specially formatted mods in df subfolder 'mods'. Mods are not included, for example mods see: `github mini mod repository `_ .. image:: images/mod-manager.png From 8cc4bea0d4c6ad830088b189730c7e6a39b20923 Mon Sep 17 00:00:00 2001 From: expwnent Date: Sat, 17 May 2014 17:59:41 -0400 Subject: [PATCH 115/676] Added petcapRemover plugin. --- NEWS | 2 + Readme.rst | 5 + plugins/CMakeLists.txt | 1 + plugins/petcapRemover.cpp | 213 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 221 insertions(+) create mode 100644 plugins/petcapRemover.cpp diff --git a/NEWS b/NEWS index 2a7508f5f..a3f959786 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,8 @@ DFHack future - Is not yet known. +New command: petcapRemover - raise the pet population cap from 50 to whatever you want + DFHack v0.34.11-r4 New commands: diff --git a/Readme.rst b/Readme.rst index a2b400206..7232a7300 100644 --- a/Readme.rst +++ b/Readme.rst @@ -1947,6 +1947,11 @@ See the bay12 thread for details: http://www.bay12forums.com/smf/index.php?topic * Some of the DFusion plugins aren't completely ported yet. This can lead to crashes. * The game will be suspended while you're using dfusion. Don't panic when it doesn't respond. +petcapRemover +------------- + +This plugin allows you to remove or raise the pet population cap. 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 allows removing the second restriction and removing or raising the first. Pets still require PET or PET_EXOTIC tags in order to reproduce. Type help petcapRemover for exact usage. In order to make population more stable and avoid sudden population booms as you go below the raised population cap, this plugin counts pregnancies toward the new population cap. It can still go over, but only in the case of multiple births. + misery ------ When enabled, every new negative dwarven thought will be multiplied by a factor (2 by default). diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 4de3c68bf..efd86b589 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -162,6 +162,7 @@ if (BUILD_SUPPORTED) DFHACK_PLUGIN(treefarm treefarm.cpp) DFHACK_PLUGIN(cleanconst cleanconst.cpp) DFHACK_PLUGIN(3dveins 3dveins.cpp) + DFHACK_PLUGIN(petcapRemover petcapRemover.cpp) endif() # this is the skeleton plugin. If you want to make your own, make a copy and then change it diff --git a/plugins/petcapRemover.cpp b/plugins/petcapRemover.cpp new file mode 100644 index 000000000..f0116131e --- /dev/null +++ b/plugins/petcapRemover.cpp @@ -0,0 +1,213 @@ + +#include "Console.h" +#include "Core.h" +#include "DataDefs.h" +#include "Export.h" +#include "PluginManager.h" +#include "modules/EventManager.h" +#include "modules/Maps.h" + +#include "df/caste_raw.h" +#include "df/caste_raw_flags.h" +#include "df/creature_raw.h" +#include "df/profession.h" +#include "df/unit.h" +#include "df/world.h" + +#include +#include + +using namespace DFHack; +using namespace std; + +static int32_t howOften = 10000; +static int32_t popcap = 100; +static int32_t pregtime = 200000; +DFHACK_PLUGIN_IS_ENABLED(is_enabled); + +command_result petcapRemover (color_ostream &out, std::vector & parameters); + +DFHACK_PLUGIN("petcapRemover"); + +DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) +{ + commands.push_back(PluginCommand( + "petcapRemover", + "Removes the pet population cap by causing pregnancies.", + petcapRemover, + false, //allow non-interactive use + "petcapRemover\n" + " does pregnancies now and schedules the next check\n" + "petcapRemover every n\n" + " set how often in ticks the plugin checks for possible pregnancies\n" + "petcapRemover cap n\n" + " sets the new cap to n. if n = 0, no cap. Caps between 1 and 50 effectively don't do anything because normal DF pregnancies will continue to happen below that cap.\n" + "petcapRemover pregtime n\n" + " sets the pregnancy duration to n ticks. Natural pregnancies are 300000 ticks for the current race and 200000 ticks for everyone else.\n" + )); + return CR_OK; +} + +DFhackCExport command_result plugin_shutdown ( color_ostream &out ) +{ + return CR_OK; +} + +bool impregnate(df::unit* female, df::unit* male); +void impregnateMany() { + map > males; + map > females; + map popcount; + auto units = df::global::world->units.all; + for ( size_t a = 0; a < units.size(); a++ ) { + df::unit* unit = units[a]; + if ( unit->flags1.bits.dead || unit->flags1.bits.active_invader || unit->flags2.bits.underworld || unit->flags2.bits.visitor_uninvited || unit->flags2.bits.visitor ) + continue; + popcount[unit->race]++; + if ( unit->relations.pregnancy_genes ) { + //already pregnant + //for player convenience and population stability, count the fetus toward the population cap + popcount[unit->race]++; + continue; + } + if ( unit->flags1.bits.caged ) + continue; + int32_t race = unit->race; + int16_t caste = unit->caste; + df::creature_raw* creatureRaw = df::global::world->raws.creatures.all[race]; + df::caste_raw* casteRaw = creatureRaw->caste[caste]; + //must have PET or PET_EXOTIC + if ( !(casteRaw->flags.is_set(df::enums::caste_raw_flags::PET) || casteRaw->flags.is_set(df::enums::caste_raw_flags::PET_EXOTIC) ) ) + continue; + //check for adulthood + if ( unit->profession == df::enums::profession::CHILD || unit->profession == df::enums::profession::BABY ) + continue; + if ( unit->sex == 1 ) + males[unit->race].push_back(a); + else + females[unit->race].push_back(a); + } + + for ( auto i = females.begin(); i != females.end(); i++ ) { + int32_t race = i->first; + vector& femalesList = i->second; + for ( size_t a = 0; a < femalesList.size(); a++ ) { + if ( popcap > 0 && popcount[race] >= popcap ) + break; + vector compatibles; + df::coord pos1 = units[femalesList[a]]->pos; + + if ( males.find(i->first) == males.end() ) + continue; + + vector& malesList = males[i->first]; + for ( size_t b = 0; b < malesList.size(); b++ ) { + df::coord pos2 = units[malesList[b]]->pos; + if ( Maps::canWalkBetween(pos1,pos2) ) + compatibles.push_back(malesList[b]); + } + if ( compatibles.empty() ) + continue; + + size_t maleIndex = (size_t)(compatibles.size()*((float)rand() / (1+(float)RAND_MAX))); + if ( impregnate(units[femalesList[a]], units[compatibles[maleIndex]]) ) + popcount[race]++; + } + } +} + +bool impregnate(df::unit* female, df::unit* male) { + if ( !female || !male ) + return false; + if ( female->relations.pregnancy_genes ) + return false; + + df::unit_genes* preg = new df::unit_genes; + *preg = male->appearance.genes; + female->relations.pregnancy_genes = preg; + female->relations.pregnancy_timer = pregtime; //300000 for dwarves + female->relations.pregnancy_caste = male->caste; + return true; +} + +void tickHandler(color_ostream& out, void* data) { + if ( !is_enabled ) + return; + CoreSuspender suspend; + impregnateMany(); + + EventManager::unregisterAll(plugin_self); + EventManager::EventHandler handle(tickHandler, howOften); + EventManager::registerTick(handle, howOften, plugin_self); +} + +command_result petcapRemover (color_ostream &out, std::vector & parameters) +{ + CoreSuspender suspend; + + for ( size_t a = 0; a < parameters.size(); a++ ) { + if ( parameters[a] == "every" ) { + if ( a+1 >= parameters.size() ) + return CR_WRONG_USAGE; + int32_t old = howOften; + howOften = atoi(parameters[a+1].c_str()); + if (howOften < -1) { + howOften = old; + return CR_WRONG_USAGE; + } + a++; + continue; + } else if ( parameters[a] == "cap" ) { + if ( a+1 >= parameters.size() ) + return CR_WRONG_USAGE; + int32_t old = popcap; + popcap = atoi(parameters[a+1].c_str()); + if ( popcap < 0 ) { + popcap = old; + return CR_WRONG_USAGE; + } + a++; + continue; + } else if ( parameters[a] == "pregtime" ) { + if ( a+1 >= parameters.size() ) + return CR_WRONG_USAGE; + int32_t old = pregtime; + pregtime = atoi(parameters[a+1].c_str()); + if ( pregtime <= 0 ) { + pregtime = old; + return CR_WRONG_USAGE; + } + a++; + continue; + } + out.print("%s, line %d: invalid argument: %s\n", __FILE__, __LINE__, parameters[a].c_str()); + return CR_WRONG_USAGE; + } + + if ( howOften < 0 ) { + is_enabled = false; + return CR_OK; + } + + is_enabled = true; + EventManager::unregisterAll(plugin_self); + EventManager::EventHandler handle(tickHandler, howOften); + EventManager::registerTick(handle, howOften, plugin_self); + out.print("petcapRemover: howOften = every %d ticks, popcap per species = %d, preg time = %d ticks.\n", howOften, popcap, pregtime); + + return CR_OK; +} + +DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) +{ + if (enable != is_enabled) + { + is_enabled = enable; + if ( !is_enabled ) { + EventManager::unregisterAll(plugin_self); + } + } + + return CR_OK; +} + From 76ec3ba58c6c942abbfbac25ba71c826344ed324 Mon Sep 17 00:00:00 2001 From: Ben Lubar Date: Sat, 17 May 2014 18:40:15 -0500 Subject: [PATCH 116/676] Fix English names containing the word "the" See DFHack/dfhack#245 --- plugins/ruby/ruby.rb | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/plugins/ruby/ruby.rb b/plugins/ruby/ruby.rb index 47924dcdf..f3504e44a 100644 --- a/plugins/ruby/ruby.rb +++ b/plugins/ruby/ruby.rb @@ -210,9 +210,17 @@ module DFHack 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 << 'the' out.last.capitalize! if out.length == 1 - (2..5).each { |i| out.last << wl.words[name.words[i]].forms[name.parts_of_speech[i]] if name.words[i] >= 0 } + 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 << '-' + else + out << '' + end + out.last << wl.words[name.words[5]].forms[name.parts_of_speech[5]] end if name.words[6] >= 0 out << 'of' From d1aee8949137ae34ea43d5771b99e267367091c8 Mon Sep 17 00:00:00 2001 From: Ben Lubar Date: Sun, 18 May 2014 13:01:07 -0500 Subject: [PATCH 117/676] remove space before hyphen --- plugins/ruby/ruby.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/ruby/ruby.rb b/plugins/ruby/ruby.rb index f3504e44a..edce8ac84 100644 --- a/plugins/ruby/ruby.rb +++ b/plugins/ruby/ruby.rb @@ -216,7 +216,7 @@ module DFHack 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 << '-' + out.last << '-' else out << '' end From 0f5802af9f0487452599523b202b34f4617500e5 Mon Sep 17 00:00:00 2001 From: lethosor Date: Sun, 18 May 2014 11:51:49 -0400 Subject: [PATCH 118/676] Add embark-tools A collection of embark-related tools (currently implementations of embark anywhere and nano embark) --- Readme.rst | 13 ++ plugins/CMakeLists.txt | 1 + plugins/embark-tools.cpp | 317 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 331 insertions(+) create mode 100644 plugins/embark-tools.cpp diff --git a/Readme.rst b/Readme.rst index 7491c4a0c..b2d98b599 100644 --- a/Readme.rst +++ b/Readme.rst @@ -2030,6 +2030,19 @@ See the bay12 thread for details: http://www.bay12forums.com/smf/index.php?topic * Some of the DFusion plugins aren't completely ported yet. This can lead to crashes. * The game will be suspended while you're using dfusion. Don't panic when it doesn't respond. +embark-tools +------------ +A collection of tools for embarking. + +Usage:: + + embark-tools enable/disable tool [tool]... + +Tools: + +* ``anywhere``: Allows embarking anywhere (including sites, mountain-only biomes, and oceans). Use with caution. +* ``nano``: An implementation of nano embark - allows resizing below 2x2 when enabled. + misery ------ When enabled, every new negative dwarven thought will be multiplied by a factor (2 by default). diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 650fe1e98..a73651608 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -165,6 +165,7 @@ if (BUILD_SUPPORTED) DFHACK_PLUGIN(strangemood strangemood.cpp) DFHACK_PLUGIN(command-prompt command-prompt.cpp) DFHACK_PLUGIN(building-hacks building-hacks.cpp LINK_LIBRARIES lua) + DFHACK_PLUGIN(embark-tools embark-tools.cpp) endif() # this is the skeleton plugin. If you want to make your own, make a copy and then change it diff --git a/plugins/embark-tools.cpp b/plugins/embark-tools.cpp new file mode 100644 index 000000000..1a02e3e65 --- /dev/null +++ b/plugins/embark-tools.cpp @@ -0,0 +1,317 @@ +#include "Console.h" +#include "Core.h" +#include "DataDefs.h" +#include "Export.h" +#include "PluginManager.h" + +#include "modules/Screen.h" +#include "modules/Gui.h" +#include +#include + +#include +#include "ColorText.h" +#include "df/viewscreen_choose_start_sitest.h" +#include "df/interface_key.h" + +using namespace DFHack; + +struct EmbarkTool +{ + std::string id; + std::string name; + std::string desc; + bool enabled; +}; + +EmbarkTool embark_tools[] = { + {"anywhere", "Embark anywhere", "Allows embarking anywhere on the world map", false}, + {"nano", "Nano embark", "Allows the embark size to be decreased below 2x2", false}, +}; +#define NUM_TOOLS sizeof(embark_tools) / sizeof(EmbarkTool) + +command_result embark_tools_cmd (color_ostream &out, std::vector & parameters); + +bool tool_exists (std::string tool_name); +bool tool_enabled (std::string tool_name); +bool tool_enable (std::string tool_name, bool enable_state); + +/* + * Logic + */ + +void resize_embark (df::viewscreen_choose_start_sitest * screen, int dx, int dy) +{ + /* Reproduces DF's embark resizing functionality + * Local area resizes up and to the right, unless it's already touching the edge + */ + int x1 = screen->embark_pos_min.x, + x2 = screen->embark_pos_max.x, + y1 = screen->embark_pos_min.y, + y2 = screen->embark_pos_max.y, + width = x2 - x1 + dx, + height = y2 - y1 + dy; + if (x1 == x2 && dx == -1) + dx = 0; + if (y1 == y2 && dy == -1) + dy = 0; + + x2 += dx; // Resize right + while (x2 > 15) + { + x2--; + x1--; + } + x1 = std::max(0, x1); + + y1 -= dy; // Resize up + while (y1 < 0) + { + y1++; + y2++; + } + y2 = std::min(15, y2); + + screen->embark_pos_min.x = x1; + screen->embark_pos_max.x = x2; + screen->embark_pos_min.y = y1; + screen->embark_pos_max.y = y2; +} + +/* + * Viewscreen hooks + */ + +void OutputString (int8_t color, int &x, int y, const std::string &text) +{ + Screen::paintString(Screen::Pen(' ', color, 0), x, y, text); + x += text.length(); +} + +struct choose_start_site_hook : df::viewscreen_choose_start_sitest +{ + typedef df::viewscreen_choose_start_sitest interpose_base; + + DEFINE_VMETHOD_INTERPOSE(void, feed, (std::set *input)) + { + bool prevent_default = false; + df::viewscreen * top = Gui::getCurViewscreen(); + VIRTUAL_CAST_VAR(screen, df::viewscreen_choose_start_sitest, top); + if (tool_enabled("anywhere")) + { + for (auto iter = input->begin(); iter != input->end(); iter++) + { + df::interface_key key = *iter; + if (key == df::interface_key::SETUP_EMBARK) + { + prevent_default = true; + screen->in_embark_normal = 1; + } + } + } + if (tool_enabled("nano")) + { + for (auto iter = input->begin(); iter != input->end(); iter++) + { + df::interface_key key = *iter; + bool is_resize = true; + int dx = 0, dy = 0; + switch (key) + { + case df::interface_key::SETUP_LOCAL_Y_UP: + dy = 1; + break; + case df::interface_key::SETUP_LOCAL_Y_DOWN: + dy = -1; + break; + case df::interface_key::SETUP_LOCAL_X_UP: + dx = 1; + break; + case df::interface_key::SETUP_LOCAL_X_DOWN: + dx = -1; + break; + default: + is_resize = false; + } + if (is_resize) { + prevent_default = true; + resize_embark(screen, dx, dy); + } + } + } + if (!prevent_default) + INTERPOSE_NEXT(feed)(input); + } + + DEFINE_VMETHOD_INTERPOSE(void, render, ()) + { + INTERPOSE_NEXT(render)(); + + df::viewscreen * top = Gui::getCurViewscreen(); + VIRTUAL_CAST_VAR(screen, df::viewscreen_choose_start_sitest, top); + + auto dim = Screen::getWindowSize(); + int x = 1, + y = dim.y - 5; + OutputString(COLOR_LIGHTMAGENTA, x, y, "Enabled: "); + std::list tools; + for (int i = 0; i < NUM_TOOLS; i++) + { + if (embark_tools[i].enabled) + { + tools.push_back(embark_tools[i].name); + tools.push_back(", "); + } + } + if (tools.size()) + { + tools.pop_back(); // Remove last , + for (auto iter = tools.begin(); iter != tools.end(); iter++) + { + OutputString(COLOR_LIGHTMAGENTA, x, y, *iter); + } + } + else + { + OutputString(COLOR_LIGHTMAGENTA, x, y, "(none)"); + } + + if (tool_enabled("anywhere")) + { + x = 20; y = dim.y - 2; + if (screen->page == 0) + { + // Only display on main page (not site finder, notes, etc.) + OutputString(COLOR_WHITE, x, y, ": Embark!"); + } + } + } +}; + +IMPLEMENT_VMETHOD_INTERPOSE(choose_start_site_hook, feed); +IMPLEMENT_VMETHOD_INTERPOSE(choose_start_site_hook, render); + +/* + * Tool management + */ + +bool tool_exists (std::string tool_name) +{ + for (int i = 0; i < NUM_TOOLS; i++) + { + if (embark_tools[i].id == tool_name) + return true; + } + return false; +} + +bool tool_enabled (std::string tool_name) +{ + for (int i = 0; i < NUM_TOOLS; i++) + { + if (embark_tools[i].id == tool_name) + return embark_tools[i].enabled; + } + return false; +} + +bool tool_enable (std::string tool_name, bool enable_state) +{ + for (int i = 0; i < NUM_TOOLS; i++) + { + if (embark_tools[i].id == tool_name) + { + embark_tools[i].enabled = enable_state; + return true; + } + } + return false; +} + +/* + * Plugin management + */ + +DFHACK_PLUGIN("embark-tools"); +DFHACK_PLUGIN_IS_ENABLED(is_enabled); + +DFhackCExport command_result plugin_init (color_ostream &out, std::vector &commands) +{ + std::string help = ""; + help += "embark-tools (enable/disable) tool [tool...]\n" + "Tools:\n"; + for (int i = 0; i < NUM_TOOLS; i++) + { + help += (" " + embark_tools[i].id + ": " + embark_tools[i].desc + "\n"); + } + commands.push_back(PluginCommand( + "embark-tools", + "A collection of embark tools", + embark_tools_cmd, + false, + help.c_str() + )); + return CR_OK; +} + +DFhackCExport command_result plugin_shutdown (color_ostream &out) +{ + INTERPOSE_HOOK(choose_start_site_hook, feed).remove(); + INTERPOSE_HOOK(choose_start_site_hook, render).remove(); + return CR_OK; +} + +DFhackCExport command_result plugin_enable (color_ostream &out, bool enable) +{ + if (is_enabled != enable) + { + if (!INTERPOSE_HOOK(choose_start_site_hook, feed).apply(enable) || + !INTERPOSE_HOOK(choose_start_site_hook, render).apply(enable)) + return CR_FAILURE; + is_enabled = enable; + } + return CR_OK; +} + +command_result embark_tools_cmd (color_ostream &out, std::vector & parameters) +{ + CoreSuspender suspend; + if (parameters.size()) + { + // Set by "enable"/"disable" - allows for multiple commands, e.g. "enable nano disable anywhere" + bool enable_state = true; + for (size_t i = 0; i < parameters.size(); i++) + { + if (parameters[i] == "enable") + { + enable_state = true; + plugin_enable(out, true); // Enable plugin + } + else if (parameters[i] == "disable") + enable_state = false; + else if (tool_exists(parameters[i])) + { + tool_enable(parameters[i], enable_state); + } + else + return CR_WRONG_USAGE; + } + } + else + { + if (is_enabled) + { + out << "Tool status:" << std::endl; + for (int i = 0; i < NUM_TOOLS; i++) + { + EmbarkTool t = embark_tools[i]; + out << t.name << " (" << t.id << "): " << (t.enabled ? "Enabled" : "Disabled") << std::endl; + } + } + else + { + out << "Plugin not enabled" << std::endl; + } + } + return CR_OK; +} From 27bc40ed219d774cbaadb25243b5333f8cecb4a0 Mon Sep 17 00:00:00 2001 From: Lethosor Date: Sun, 18 May 2014 18:53:28 -0400 Subject: [PATCH 119/676] Update Readme.rst --- Readme.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Readme.rst b/Readme.rst index b2d98b599..3451eaff0 100644 --- a/Readme.rst +++ b/Readme.rst @@ -2032,7 +2032,7 @@ See the bay12 thread for details: http://www.bay12forums.com/smf/index.php?topic embark-tools ------------ -A collection of tools for embarking. +A collection of embark-related tools. Usage:: From 6519ac1bf887b61ea613b91012f9fad331166ef3 Mon Sep 17 00:00:00 2001 From: lethosor Date: Sun, 18 May 2014 20:03:31 -0400 Subject: [PATCH 120/676] Add sand indicator and allow enabling/disabling all tools --- plugins/embark-tools.cpp | 61 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 4 deletions(-) diff --git a/plugins/embark-tools.cpp b/plugins/embark-tools.cpp index 1a02e3e65..12b00bb0b 100644 --- a/plugins/embark-tools.cpp +++ b/plugins/embark-tools.cpp @@ -27,6 +27,7 @@ struct EmbarkTool EmbarkTool embark_tools[] = { {"anywhere", "Embark anywhere", "Allows embarking anywhere on the world map", false}, {"nano", "Nano embark", "Allows the embark size to be decreased below 2x2", false}, + {"sand", "Sand indicator", "Displays an indicator when sand is present on the given embark site", false}, }; #define NUM_TOOLS sizeof(embark_tools) / sizeof(EmbarkTool) @@ -35,6 +36,7 @@ command_result embark_tools_cmd (color_ostream &out, std::vector & bool tool_exists (std::string tool_name); bool tool_enabled (std::string tool_name); bool tool_enable (std::string tool_name, bool enable_state); +void tool_update (std::string tool_name); /* * Logic @@ -78,6 +80,30 @@ void resize_embark (df::viewscreen_choose_start_sitest * screen, int dx, int dy) screen->embark_pos_max.y = y2; } +std::string sand_indicator = ""; +bool sand_dirty = true; // Flag set when update is needed +void sand_update () +{ + CoreSuspendClaimer suspend; + df::viewscreen * top = Gui::getCurViewscreen(); + VIRTUAL_CAST_VAR(screen, df::viewscreen_choose_start_sitest, top); + buffered_color_ostream out; + Core::getInstance().runCommand(out, "prospect"); + auto fragments = out.fragments(); + sand_indicator = ""; + for (auto iter = fragments.begin(); iter != fragments.end(); iter++) + { + std::string fragment = iter->second; + if (fragment.find("SAND_") != std::string::npos || + fragment.find("SAND :") != std::string::npos) + { + sand_indicator = "Sand"; + break; + } + } + sand_dirty = false; +} + /* * Viewscreen hooks */ @@ -139,6 +165,10 @@ struct choose_start_site_hook : df::viewscreen_choose_start_sitest } } } + if (tool_enabled("sand")) + { + sand_dirty = true; + } if (!prevent_default) INTERPOSE_NEXT(feed)(input); } @@ -185,6 +215,18 @@ struct choose_start_site_hook : df::viewscreen_choose_start_sitest OutputString(COLOR_WHITE, x, y, ": Embark!"); } } + if (tool_enabled("sand")) + { + if (sand_dirty) + { + sand_update(); + } + x = dim.x - 28; y = 13; + if (screen->page == 0) + { + OutputString(COLOR_YELLOW, x, y, sand_indicator); + } + } } }; @@ -217,15 +259,26 @@ bool tool_enabled (std::string tool_name) bool tool_enable (std::string tool_name, bool enable_state) { + int n = 0; for (int i = 0; i < NUM_TOOLS; i++) { - if (embark_tools[i].id == tool_name) + if (embark_tools[i].id == tool_name || tool_name == "all") { embark_tools[i].enabled = enable_state; - return true; + tool_update(tool_name); + n++; } } - return false; + return (bool)n; +} + +void tool_update (std::string tool_name) +{ + // Called whenever a tool is enabled/disabled + if (tool_name == "sand") + { + sand_dirty = true; + } } /* @@ -289,7 +342,7 @@ command_result embark_tools_cmd (color_ostream &out, std::vector & } else if (parameters[i] == "disable") enable_state = false; - else if (tool_exists(parameters[i])) + else if (tool_exists(parameters[i]) || parameters[i] == "all") { tool_enable(parameters[i], enable_state); } From 96b70a93bc0a1d92992d5c66b85dede85f857fcf Mon Sep 17 00:00:00 2001 From: lethosor Date: Sun, 18 May 2014 21:21:17 -0400 Subject: [PATCH 121/676] Update readme --- Readme.rst | 1 + plugins/embark-tools.cpp | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Readme.rst b/Readme.rst index 3451eaff0..006f06ae8 100644 --- a/Readme.rst +++ b/Readme.rst @@ -2042,6 +2042,7 @@ Tools: * ``anywhere``: Allows embarking anywhere (including sites, mountain-only biomes, and oceans). Use with caution. * ``nano``: An implementation of nano embark - allows resizing below 2x2 when enabled. +* ``sand``: Displays an indicator when sand is present in the currently-selected area, similar to the default clay/stone indicators. misery ------ diff --git a/plugins/embark-tools.cpp b/plugins/embark-tools.cpp index 12b00bb0b..23354abcc 100644 --- a/plugins/embark-tools.cpp +++ b/plugins/embark-tools.cpp @@ -94,8 +94,7 @@ void sand_update () for (auto iter = fragments.begin(); iter != fragments.end(); iter++) { std::string fragment = iter->second; - if (fragment.find("SAND_") != std::string::npos || - fragment.find("SAND :") != std::string::npos) + if (fragment.find("SAND_") != std::string::npos) { sand_indicator = "Sand"; break; From 02811c773c6623b6084d18f940f9f7fbc7bb4470 Mon Sep 17 00:00:00 2001 From: lethosor Date: Mon, 19 May 2014 15:53:29 -0400 Subject: [PATCH 122/676] Fix crash with child screens (e.g. saving a macro) --- plugins/embark-tools.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/embark-tools.cpp b/plugins/embark-tools.cpp index 23354abcc..265b1cea3 100644 --- a/plugins/embark-tools.cpp +++ b/plugins/embark-tools.cpp @@ -178,6 +178,8 @@ struct choose_start_site_hook : df::viewscreen_choose_start_sitest df::viewscreen * top = Gui::getCurViewscreen(); VIRTUAL_CAST_VAR(screen, df::viewscreen_choose_start_sitest, top); + if (!screen) + return; auto dim = Screen::getWindowSize(); int x = 1, From bb5f7b475d792b029fe47da06e13517afdddb641 Mon Sep 17 00:00:00 2001 From: lethosor Date: Mon, 19 May 2014 16:11:42 -0400 Subject: [PATCH 123/676] Add sticky (equivalent of tweak stable-cursor) --- Readme.rst | 1 + plugins/embark-tools.cpp | 76 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 73 insertions(+), 4 deletions(-) diff --git a/Readme.rst b/Readme.rst index 006f06ae8..e8a04de78 100644 --- a/Readme.rst +++ b/Readme.rst @@ -2043,6 +2043,7 @@ Tools: * ``anywhere``: Allows embarking anywhere (including sites, mountain-only biomes, and oceans). Use with caution. * ``nano``: An implementation of nano embark - allows resizing below 2x2 when enabled. * ``sand``: Displays an indicator when sand is present in the currently-selected area, similar to the default clay/stone indicators. +* ``sticky``: Maintains the selected local area while navigating the world map misery ------ diff --git a/plugins/embark-tools.cpp b/plugins/embark-tools.cpp index 265b1cea3..4d1a4afbe 100644 --- a/plugins/embark-tools.cpp +++ b/plugins/embark-tools.cpp @@ -28,6 +28,7 @@ EmbarkTool embark_tools[] = { {"anywhere", "Embark anywhere", "Allows embarking anywhere on the world map", false}, {"nano", "Nano embark", "Allows the embark size to be decreased below 2x2", false}, {"sand", "Sand indicator", "Displays an indicator when sand is present on the given embark site", false}, + {"sticky", "Stable position", "Maintains the selected local area while navigating the world map", false}, }; #define NUM_TOOLS sizeof(embark_tools) / sizeof(EmbarkTool) @@ -103,6 +104,26 @@ void sand_update () sand_dirty = false; } +int sticky_pos[] = {0, 0, 3, 3}; +bool sticky_moved = false; +void sticky_save (df::viewscreen_choose_start_sitest * screen) +{ + sticky_pos = { + screen->embark_pos_min.x, + screen->embark_pos_max.x, + screen->embark_pos_min.y, + screen->embark_pos_max.y, + }; +} + +void sticky_apply (df::viewscreen_choose_start_sitest * screen) +{ + screen->embark_pos_min.x = sticky_pos[0]; + screen->embark_pos_max.x = sticky_pos[1]; + screen->embark_pos_min.y = sticky_pos[2]; + screen->embark_pos_max.y = sticky_pos[3]; +} + /* * Viewscreen hooks */ @@ -134,6 +155,7 @@ struct choose_start_site_hook : df::viewscreen_choose_start_sitest } } } + if (tool_enabled("nano")) { for (auto iter = input->begin(); iter != input->end(); iter++) @@ -158,12 +180,52 @@ struct choose_start_site_hook : df::viewscreen_choose_start_sitest default: is_resize = false; } - if (is_resize) { + if (is_resize) + { prevent_default = true; resize_embark(screen, dx, dy); } } } + + if (tool_enabled("sticky")) + { + for (auto iter = input->begin(); iter != input->end(); iter++) + { + df::interface_key key = *iter; + bool is_motion = false; + int dx = 0, dy = 0; + switch (key) + { + case df::interface_key::CURSOR_UP: + case df::interface_key::CURSOR_DOWN: + case df::interface_key::CURSOR_LEFT: + case df::interface_key::CURSOR_RIGHT: + case df::interface_key::CURSOR_UPLEFT: + case df::interface_key::CURSOR_UPRIGHT: + case df::interface_key::CURSOR_DOWNLEFT: + case df::interface_key::CURSOR_DOWNRIGHT: + case df::interface_key::CURSOR_UP_FAST: + case df::interface_key::CURSOR_DOWN_FAST: + case df::interface_key::CURSOR_LEFT_FAST: + case df::interface_key::CURSOR_RIGHT_FAST: + case df::interface_key::CURSOR_UPLEFT_FAST: + case df::interface_key::CURSOR_UPRIGHT_FAST: + case df::interface_key::CURSOR_DOWNLEFT_FAST: + case df::interface_key::CURSOR_DOWNRIGHT_FAST: + is_motion = true; + break; + default: + is_motion = false; + } + if (is_motion) + { + sticky_save(screen); + sticky_moved = true; + } + } + } + if (tool_enabled("sand")) { sand_dirty = true; @@ -174,13 +236,19 @@ struct choose_start_site_hook : df::viewscreen_choose_start_sitest DEFINE_VMETHOD_INTERPOSE(void, render, ()) { - INTERPOSE_NEXT(render)(); - df::viewscreen * top = Gui::getCurViewscreen(); VIRTUAL_CAST_VAR(screen, df::viewscreen_choose_start_sitest, top); if (!screen) return; + if (tool_enabled("sticky") && sticky_moved) + { + sticky_apply(screen); + sticky_moved = false; + } + + INTERPOSE_NEXT(render)(); + auto dim = Screen::getWindowSize(); int x = 1, y = dim.y - 5; @@ -321,7 +389,7 @@ DFhackCExport command_result plugin_enable (color_ostream &out, bool enable) { if (!INTERPOSE_HOOK(choose_start_site_hook, feed).apply(enable) || !INTERPOSE_HOOK(choose_start_site_hook, render).apply(enable)) - return CR_FAILURE; + return CR_FAILURE; is_enabled = enable; } return CR_OK; From da31bab4199d17f46c5b4d72cb079d2d479f9f28 Mon Sep 17 00:00:00 2001 From: lethosor Date: Mon, 19 May 2014 16:33:17 -0400 Subject: [PATCH 124/676] Display embark label on all 5 map pages --- plugins/embark-tools.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/embark-tools.cpp b/plugins/embark-tools.cpp index 4d1a4afbe..0c2781d05 100644 --- a/plugins/embark-tools.cpp +++ b/plugins/embark-tools.cpp @@ -278,9 +278,9 @@ struct choose_start_site_hook : df::viewscreen_choose_start_sitest if (tool_enabled("anywhere")) { x = 20; y = dim.y - 2; - if (screen->page == 0) + if (screen->page >= 0 && screen->page <= 4) { - // Only display on main page (not site finder, notes, etc.) + // Only display on five map pages, not on site finder or notes OutputString(COLOR_WHITE, x, y, ": Embark!"); } } From fcb62ac7dd37fba5c9437d2a96a8fa3f27ae0efc Mon Sep 17 00:00:00 2001 From: lethosor Date: Mon, 19 May 2014 17:43:30 -0400 Subject: [PATCH 125/676] sticky: Fix handling of rapid input --- plugins/embark-tools.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/embark-tools.cpp b/plugins/embark-tools.cpp index 0c2781d05..1eb7f0198 100644 --- a/plugins/embark-tools.cpp +++ b/plugins/embark-tools.cpp @@ -218,7 +218,7 @@ struct choose_start_site_hook : df::viewscreen_choose_start_sitest default: is_motion = false; } - if (is_motion) + if (is_motion && !sticky_moved) { sticky_save(screen); sticky_moved = true; From 2d778be95662269cb8d9a757d240d06f330207ba Mon Sep 17 00:00:00 2001 From: lethosor Date: Tue, 20 May 2014 15:51:26 -0400 Subject: [PATCH 126/676] Fix embark sidebar updating --- plugins/embark-tools.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/plugins/embark-tools.cpp b/plugins/embark-tools.cpp index 1eb7f0198..f049841c5 100644 --- a/plugins/embark-tools.cpp +++ b/plugins/embark-tools.cpp @@ -43,6 +43,21 @@ void tool_update (std::string tool_name); * Logic */ +void update_embark_sidebar (df::viewscreen_choose_start_sitest * screen) +{ + bool is_top = false; + if (screen->embark_pos_min.y == 0) + is_top = true; + std::set keys; + keys.insert(df::interface_key::SETUP_LOCAL_Y_MUP); + screen->feed(&keys); + if (!is_top) + { + keys.insert(df::interface_key::SETUP_LOCAL_Y_MDOWN); + screen->feed(&keys); + } +} + void resize_embark (df::viewscreen_choose_start_sitest * screen, int dx, int dy) { /* Reproduces DF's embark resizing functionality @@ -79,6 +94,8 @@ void resize_embark (df::viewscreen_choose_start_sitest * screen, int dx, int dy) screen->embark_pos_max.x = x2; screen->embark_pos_min.y = y1; screen->embark_pos_max.y = y2; + + update_embark_sidebar(screen); } std::string sand_indicator = ""; @@ -122,6 +139,7 @@ void sticky_apply (df::viewscreen_choose_start_sitest * screen) screen->embark_pos_max.x = sticky_pos[1]; screen->embark_pos_min.y = sticky_pos[2]; screen->embark_pos_max.y = sticky_pos[3]; + update_embark_sidebar(screen); } /* From e483e7439f4852e65bc5d104fdbb27b77533b514 Mon Sep 17 00:00:00 2001 From: Quietust Date: Thu, 22 May 2014 08:18:01 -0500 Subject: [PATCH 127/676] Fix problems with NONE for item types and subtypes --- library/modules/Items.cpp | 2 + plugins/createitem.cpp | 139 ++++++++++++++++++++------------------ 2 files changed, 74 insertions(+), 67 deletions(-) diff --git a/library/modules/Items.cpp b/library/modules/Items.cpp index 38d63d867..f25140217 100644 --- a/library/modules/Items.cpp +++ b/library/modules/Items.cpp @@ -234,6 +234,8 @@ ITEMDEF_VECTORS #undef ITEM default: + if (items[1] == "NONE") + return true; break; } diff --git a/plugins/createitem.cpp b/plugins/createitem.cpp index 6442e818a..89c2ade36 100644 --- a/plugins/createitem.cpp +++ b/plugins/createitem.cpp @@ -26,8 +26,10 @@ #include "df/reaction_product_itemst.h" #include "df/tool_uses.h" -using namespace std; +using std::string; +using std::vector; using namespace DFHack; +using namespace df::enums; using df::global::world; using df::global::ui; @@ -71,8 +73,8 @@ bool makeItem (df::reaction_product_itemst *prod, df::unit *unit, bool second_it vector out_items; vector in_reag; vector in_items; - bool is_gloves = (prod->item_type == df::item_type::GLOVES); - bool is_shoes = (prod->item_type == df::item_type::SHOES); + bool is_gloves = (prod->item_type == item_type::GLOVES); + bool is_shoes = (prod->item_type == item_type::SHOES); df::item *container = NULL; df::building *building = NULL; @@ -81,9 +83,9 @@ bool makeItem (df::reaction_product_itemst *prod, df::unit *unit, bool second_it if (dest_building != -1) building = df::building::find(dest_building); - prod->produce(unit, &out_items, &in_reag, &in_items, 1, df::job_skill::NONE, + prod->produce(unit, &out_items, &in_reag, &in_items, 1, job_skill::NONE, df::historical_entity::find(unit->civ_id), - ((*gametype == df::game_type::DWARF_MAIN) || (*gametype == df::game_type::DWARF_RECLAIM)) ? df::world_site::find(ui->site_id) : NULL); + ((*gametype == game_type::DWARF_MAIN) || (*gametype == game_type::DWARF_RECLAIM)) ? df::world_site::find(ui->site_id) : 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 @@ -130,7 +132,7 @@ bool makeItem (df::reaction_product_itemst *prod, df::unit *unit, bool second_it command_result df_createitem (color_ostream &out, vector & parameters) { string item_str, material_str; - df::item_type item_type = df::item_type::NONE; + df::item_type item_type = item_type::NONE; int16_t item_subtype = -1; int16_t mat_type = -1; int32_t mat_index = -1; @@ -156,23 +158,23 @@ command_result df_createitem (color_ostream &out, vector & parameters) } switch (item->getType()) { - case df::item_type::FLASK: - case df::item_type::BARREL: - case df::item_type::BUCKET: - case df::item_type::ANIMALTRAP: - case df::item_type::BOX: - case df::item_type::BIN: - case df::item_type::BACKPACK: - case df::item_type::QUIVER: + case item_type::FLASK: + case item_type::BARREL: + case item_type::BUCKET: + case item_type::ANIMALTRAP: + case item_type::BOX: + case item_type::BIN: + case item_type::BACKPACK: + case item_type::QUIVER: break; - case df::item_type::TOOL: - if (item->hasToolUse(df::tool_uses::LIQUID_CONTAINER)) + case item_type::TOOL: + if (item->hasToolUse(tool_uses::LIQUID_CONTAINER)) break; - if (item->hasToolUse(df::tool_uses::FOOD_STORAGE)) + if (item->hasToolUse(tool_uses::FOOD_STORAGE)) break; - if (item->hasToolUse(df::tool_uses::SMALL_OBJECT_STORAGE)) + if (item->hasToolUse(tool_uses::SMALL_OBJECT_STORAGE)) break; - if (item->hasToolUse(df::tool_uses::TRACK_CART)) + if (item->hasToolUse(tool_uses::TRACK_CART)) break; default: out.printerr("The selected item cannot be used for item storage!\n"); @@ -195,22 +197,22 @@ command_result df_createitem (color_ostream &out, vector & parameters) } switch (building->getType()) { - case df::building_type::Coffin: - case df::building_type::Furnace: - case df::building_type::TradeDepot: - case df::building_type::Shop: - case df::building_type::Box: - case df::building_type::Weaponrack: - case df::building_type::Armorstand: - case df::building_type::Workshop: - case df::building_type::Cabinet: - case df::building_type::SiegeEngine: - case df::building_type::Trap: - case df::building_type::AnimalTrap: - case df::building_type::Cage: - case df::building_type::Wagon: - case df::building_type::NestBox: - case df::building_type::Hive: + case building_type::Coffin: + case building_type::Furnace: + case building_type::TradeDepot: + case building_type::Shop: + case building_type::Box: + case building_type::Weaponrack: + case building_type::Armorstand: + case building_type::Workshop: + case building_type::Cabinet: + case building_type::SiegeEngine: + case building_type::Trap: + case building_type::AnimalTrap: + case building_type::Cage: + case building_type::Wagon: + case building_type::NestBox: + case building_type::Hive: break; default: out.printerr("The selected building cannot be used for item storage!\n"); @@ -252,28 +254,31 @@ command_result df_createitem (color_ostream &out, vector & parameters) MaterialInfo material; vector tokens; - if (!item.find(item_str)) + if (item.find(item_str)) { - out.printerr("Unrecognized item type!\n"); + item_type = item.type; + item_subtype = item.subtype; + } + if (item_type == item_type::NONE) + { + out.printerr("You must specify a valid item type to create!\n"); return CR_FAILURE; } - item_type = item.type; - item_subtype = item.subtype; switch (item.type) { - case df::item_type::INSTRUMENT: - case df::item_type::TOY: - case df::item_type::WEAPON: - case df::item_type::ARMOR: - case df::item_type::SHOES: - case df::item_type::SHIELD: - case df::item_type::HELM: - case df::item_type::GLOVES: - case df::item_type::AMMO: - case df::item_type::PANTS: - case df::item_type::SIEGEAMMO: - case df::item_type::TRAPCOMP: - case df::item_type::TOOL: + case item_type::INSTRUMENT: + case item_type::TOY: + case item_type::WEAPON: + case item_type::ARMOR: + case item_type::SHOES: + case item_type::SHIELD: + case item_type::HELM: + case item_type::GLOVES: + case item_type::AMMO: + case item_type::PANTS: + case item_type::SIEGEAMMO: + case item_type::TRAPCOMP: + case item_type::TOOL: if (item_subtype == -1) { out.printerr("You must specify a subtype!\n"); @@ -289,12 +294,12 @@ command_result df_createitem (color_ostream &out, vector & parameters) mat_index = material.index; break; - case df::item_type::REMAINS: - case df::item_type::FISH: - case df::item_type::FISH_RAW: - case df::item_type::VERMIN: - case df::item_type::PET: - case df::item_type::EGG: + case item_type::REMAINS: + case item_type::FISH: + case item_type::FISH_RAW: + case item_type::VERMIN: + case item_type::PET: + case item_type::EGG: split_string(&tokens, material_str, ":"); if (tokens.size() != 2) { @@ -331,9 +336,9 @@ command_result df_createitem (color_ostream &out, vector & parameters) } break; - case df::item_type::CORPSE: - case df::item_type::CORPSEPIECE: - case df::item_type::FOOD: + case item_type::CORPSE: + case item_type::CORPSEPIECE: + case item_type::FOOD: out.printerr("Cannot create that type of item!\n"); return CR_FAILURE; break; @@ -376,16 +381,16 @@ command_result df_createitem (color_ostream &out, vector & parameters) prod->count = count; switch (item_type) { - case df::item_type::BAR: - case df::item_type::POWDER_MISC: - case df::item_type::LIQUID_MISC: - case df::item_type::DRINK: + case item_type::BAR: + case item_type::POWDER_MISC: + case item_type::LIQUID_MISC: + case item_type::DRINK: prod->product_dimension = 150; break; - case df::item_type::THREAD: + case item_type::THREAD: prod->product_dimension = 15000; break; - case df::item_type::CLOTH: + case item_type::CLOTH: prod->product_dimension = 10000; break; default: From 1fcaac9d2ec39ebc9f32657c867589162dffd0b8 Mon Sep 17 00:00:00 2001 From: lethosor Date: Sun, 25 May 2014 21:52:16 -0400 Subject: [PATCH 128/676] OS X Console: Implement back/forward one word escape sequences --- library/Console-darwin.cpp | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/library/Console-darwin.cpp b/library/Console-darwin.cpp index 86cd657a1..081d4833e 100644 --- a/library/Console-darwin.cpp +++ b/library/Console-darwin.cpp @@ -478,14 +478,46 @@ namespace DFHack break; case 27: // escape sequence lock->unlock(); - if(!read_char(seq[0]) || !read_char(seq[1])) + if (!read_char(seq[0])) { lock->lock(); return -2; } lock->lock(); - if(seq[0] == '[') + if (seq[0] == 'b') { + // Back one word + if (raw_cursor == 0) + break; + raw_cursor--; + while (raw_cursor > 0 && !isalnum(raw_buffer[raw_cursor])) + raw_cursor--; + while (raw_cursor > 0 && isalnum(raw_buffer[raw_cursor])) + raw_cursor--; + if (!isalnum(raw_buffer[raw_cursor])) + raw_cursor++; + prompt_refresh(); + } + else if (seq[0] == 'f') + { + // Forward one word + int len = raw_buffer.size(); + if (raw_cursor == len) + break; + raw_cursor++; + while (raw_cursor <= len && !isalnum(raw_buffer[raw_cursor])) + raw_cursor++; + while (raw_cursor <= len && isalnum(raw_buffer[raw_cursor])) + raw_cursor++; + prompt_refresh(); + } + else if(seq[0] == '[') + { + if (!read_char(seq[1])) + { + lock->lock(); + return -2; + } if (seq[1] == 'D') { left_arrow: From 58b9c02ce5dc518c0904cc405fe8128d7d1592f6 Mon Sep 17 00:00:00 2001 From: lethosor Date: Sun, 25 May 2014 22:10:40 -0400 Subject: [PATCH 129/676] Migrate esc-b/f sequences to Linux --- library/Console-linux.cpp | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/library/Console-linux.cpp b/library/Console-linux.cpp index f32fa1c2a..a49b5dbdd 100644 --- a/library/Console-linux.cpp +++ b/library/Console-linux.cpp @@ -480,14 +480,46 @@ namespace DFHack break; case 27: // escape sequence lock->unlock(); - if(!read_char(seq[0]) || !read_char(seq[1])) + if (!read_char(seq[0])) { lock->lock(); return -2; } lock->lock(); - if(seq[0] == '[') + if (seq[0] == 'b') { + // Back one word + if (raw_cursor == 0) + break; + raw_cursor--; + while (raw_cursor > 0 && !isalnum(raw_buffer[raw_cursor])) + raw_cursor--; + while (raw_cursor > 0 && isalnum(raw_buffer[raw_cursor])) + raw_cursor--; + if (!isalnum(raw_buffer[raw_cursor])) + raw_cursor++; + prompt_refresh(); + } + else if (seq[0] == 'f') + { + // Forward one word + int len = raw_buffer.size(); + if (raw_cursor == len) + break; + raw_cursor++; + while (raw_cursor <= len && !isalnum(raw_buffer[raw_cursor])) + raw_cursor++; + while (raw_cursor <= len && isalnum(raw_buffer[raw_cursor])) + raw_cursor++; + prompt_refresh(); + } + else if(seq[0] == '[') + { + if (!read_char(seq[1])) + { + lock->lock(); + return -2; + } if (seq[1] == 'D') { left_arrow: From d320fe71d11274d33c1d8ff3f59e8122478fdb4c Mon Sep 17 00:00:00 2001 From: lethosor Date: Sun, 25 May 2014 23:39:34 -0400 Subject: [PATCH 130/676] Implement extended arrow key sequences --- library/Console-darwin.cpp | 69 ++++++++++++++++++++++++++------------ 1 file changed, 48 insertions(+), 21 deletions(-) diff --git a/library/Console-darwin.cpp b/library/Console-darwin.cpp index 081d4833e..f36973d5c 100644 --- a/library/Console-darwin.cpp +++ b/library/Console-darwin.cpp @@ -305,6 +305,33 @@ namespace DFHack } /// beep. maybe? //void beep (void); + void back_word() + { + if (raw_cursor == 0) + return; + raw_cursor--; + while (raw_cursor > 0 && !isalnum(raw_buffer[raw_cursor])) + raw_cursor--; + while (raw_cursor > 0 && isalnum(raw_buffer[raw_cursor])) + raw_cursor--; + if (!isalnum(raw_buffer[raw_cursor]) && raw_cursor != 0) + raw_cursor++; + prompt_refresh(); + } + void forward_word() + { + int len = raw_buffer.size(); + if (raw_cursor == len) + return; + raw_cursor++; + while (raw_cursor <= len && !isalnum(raw_buffer[raw_cursor])) + raw_cursor++; + while (raw_cursor <= len && isalnum(raw_buffer[raw_cursor])) + raw_cursor++; + if (raw_cursor > len) + raw_cursor = len; + prompt_refresh(); + } /// A simple line edit (raw mode) int lineedit(const std::string& prompt, std::string& output, recursive_mutex * lock, CommandHistory & ch) { @@ -486,30 +513,11 @@ namespace DFHack lock->lock(); if (seq[0] == 'b') { - // Back one word - if (raw_cursor == 0) - break; - raw_cursor--; - while (raw_cursor > 0 && !isalnum(raw_buffer[raw_cursor])) - raw_cursor--; - while (raw_cursor > 0 && isalnum(raw_buffer[raw_cursor])) - raw_cursor--; - if (!isalnum(raw_buffer[raw_cursor])) - raw_cursor++; - prompt_refresh(); + back_word(); } else if (seq[0] == 'f') { - // Forward one word - int len = raw_buffer.size(); - if (raw_cursor == len) - break; - raw_cursor++; - while (raw_cursor <= len && !isalnum(raw_buffer[raw_cursor])) - raw_cursor++; - while (raw_cursor <= len && isalnum(raw_buffer[raw_cursor])) - raw_cursor++; - prompt_refresh(); + forward_word(); } else if(seq[0] == '[') { @@ -577,6 +585,7 @@ namespace DFHack else if (seq[1] > '0' && seq[1] < '7') { // extended escape + unsigned char seq3[3]; lock->unlock(); if(!read_char(seq2)) { @@ -593,6 +602,24 @@ namespace DFHack prompt_refresh(); } } + if (!read_char(seq3[0]) || !read_char(seq3[1])) + { + lock->lock(); + return -2; + } + if (seq2 == ';') + { + // Format: esc [ n ; n DIRECTION + // Ignore first character (second "n") + if (seq3[1] == 'C') + { + forward_word(); + } + else if (seq3[1] == 'D') + { + back_word(); + } + } } } break; From bdca1ee7095ddc2dd5c6e4f8d84812cf69b774a6 Mon Sep 17 00:00:00 2001 From: lethosor Date: Mon, 26 May 2014 10:03:28 -0400 Subject: [PATCH 131/676] Linux: Extended back/forward word sequences --- library/Console-linux.cpp | 69 +++++++++++++++++++++++++++------------ 1 file changed, 48 insertions(+), 21 deletions(-) diff --git a/library/Console-linux.cpp b/library/Console-linux.cpp index a49b5dbdd..d4005af3c 100644 --- a/library/Console-linux.cpp +++ b/library/Console-linux.cpp @@ -307,6 +307,33 @@ namespace DFHack } /// beep. maybe? //void beep (void); + void back_word() + { + if (raw_cursor == 0) + return; + raw_cursor--; + while (raw_cursor > 0 && !isalnum(raw_buffer[raw_cursor])) + raw_cursor--; + while (raw_cursor > 0 && isalnum(raw_buffer[raw_cursor])) + raw_cursor--; + if (!isalnum(raw_buffer[raw_cursor]) && raw_cursor != 0) + raw_cursor++; + prompt_refresh(); + } + void forward_word() + { + int len = raw_buffer.size(); + if (raw_cursor == len) + return; + raw_cursor++; + while (raw_cursor <= len && !isalnum(raw_buffer[raw_cursor])) + raw_cursor++; + while (raw_cursor <= len && isalnum(raw_buffer[raw_cursor])) + raw_cursor++; + if (raw_cursor > len) + raw_cursor = len; + prompt_refresh(); + } /// A simple line edit (raw mode) int lineedit(const std::string& prompt, std::string& output, recursive_mutex * lock, CommandHistory & ch) { @@ -488,30 +515,11 @@ namespace DFHack lock->lock(); if (seq[0] == 'b') { - // Back one word - if (raw_cursor == 0) - break; - raw_cursor--; - while (raw_cursor > 0 && !isalnum(raw_buffer[raw_cursor])) - raw_cursor--; - while (raw_cursor > 0 && isalnum(raw_buffer[raw_cursor])) - raw_cursor--; - if (!isalnum(raw_buffer[raw_cursor])) - raw_cursor++; - prompt_refresh(); + back_word(); } else if (seq[0] == 'f') { - // Forward one word - int len = raw_buffer.size(); - if (raw_cursor == len) - break; - raw_cursor++; - while (raw_cursor <= len && !isalnum(raw_buffer[raw_cursor])) - raw_cursor++; - while (raw_cursor <= len && isalnum(raw_buffer[raw_cursor])) - raw_cursor++; - prompt_refresh(); + forward_word(); } else if(seq[0] == '[') { @@ -579,6 +587,7 @@ namespace DFHack else if (seq[1] > '0' && seq[1] < '7') { // extended escape + unsigned char seq3[3]; lock->unlock(); if(!read_char(seq2)) { @@ -595,6 +604,24 @@ namespace DFHack prompt_refresh(); } } + if (!read_char(seq3[0]) || !read_char(seq3[1])) + { + lock->lock(); + return -2; + } + if (seq2 == ';') + { + // Format: esc [ n ; n DIRECTION + // Ignore first character (second "n") + if (seq3[1] == 'C') + { + forward_word(); + } + else if (seq3[1] == 'D') + { + back_word(); + } + } } } break; From 99659e7e00c13fcce107152ed8cdbd05a0fd9e15 Mon Sep 17 00:00:00 2001 From: Warmist Date: Mon, 26 May 2014 18:16:32 +0300 Subject: [PATCH 132/676] cmd-prompt needs to work on ANY screen. This fixes it. --- plugins/command-prompt.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugins/command-prompt.cpp b/plugins/command-prompt.cpp index 5e84db831..3cdd83ab3 100644 --- a/plugins/command-prompt.cpp +++ b/plugins/command-prompt.cpp @@ -182,10 +182,14 @@ command_result show_prompt(color_ostream &out, std::vector & param Screen::show(new viewscreen_commandpromptst(params)); return CR_OK; } +bool hotkey_allow_all(df::viewscreen *top) +{ + return true; +} DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { commands.push_back(PluginCommand( - "command-prompt","Shows a command prompt on window.",show_prompt,false, + "command-prompt","Shows a command prompt on window.",show_prompt,hotkey_allow_all, "command-prompt [entry] - shows a cmd prompt in df window. Entry is used for default prefix (e.g. ':lua')" )); return CR_OK; From d78f5ba384a17409ec88ba54b23836071d837007 Mon Sep 17 00:00:00 2001 From: Anuradha Dissanayake Date: Sun, 1 Jun 2014 22:13:36 +1200 Subject: [PATCH 133/676] Disable mouse query when linking levers. Update mousequery plugin version. --- plugins/mousequery.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/plugins/mousequery.cpp b/plugins/mousequery.cpp index 9cc6bb783..0fe337017 100644 --- a/plugins/mousequery.cpp +++ b/plugins/mousequery.cpp @@ -28,7 +28,7 @@ using namespace df::enums::ui_sidebar_mode; DFHACK_PLUGIN("mousequery"); -#define PLUGIN_VERSION 0.17 +#define PLUGIN_VERSION 0.18 static int32_t last_clicked_x, last_clicked_y, last_clicked_z; static int32_t last_pos_x, last_pos_y, last_pos_z; @@ -539,6 +539,13 @@ struct mousequery_hook : public df::viewscreen_dwarfmodest if (mx < 1 || mx > right_margin - 2 || my < 1 || my > gps->dimy - 2) mpos_valid = false; + // Check if in lever binding mode + if (Gui::getFocusString(Core::getTopViewscreen()) == + "dwarfmode/QueryBuilding/Some/Lever/AddJob") + { + return; + } + if (mpos_valid) { if (mpos.x != last_move_pos.x || mpos.y != last_move_pos.y || mpos.z != last_move_pos.z) From f7640811990f5d8c0638fff960fa977019d161ed Mon Sep 17 00:00:00 2001 From: Anuradha Dissanayake Date: Mon, 2 Jun 2014 15:48:34 +1200 Subject: [PATCH 134/676] Add hotkeys plugin --- Readme.rst | 13 ++ images/hotkeys.PNG | Bin 0 -> 58207 bytes plugins/CMakeLists.txt | 1 + plugins/hotkeys.cpp | 378 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 392 insertions(+) create mode 100644 images/hotkeys.PNG create mode 100644 plugins/hotkeys.cpp diff --git a/Readme.rst b/Readme.rst index 7491c4a0c..904314722 100644 --- a/Readme.rst +++ b/Readme.rst @@ -2621,6 +2621,19 @@ materials, it returns you back to this screen. If you use this along with severa enabled materials, you should be able to place complex constructions more conveniently. +Hotkeys +======= + +Opens an in-game screen showing DFHack keybindings that are valid in the current mode. + +.. image:: images/hotkeys.png + +Type ``hotkeys`` into the DFHack console to open the screen, or bind the command to a +globally active hotkey in dfhack.init, e.g.: + + ``keybinding add Ctrl-F1 hotkeys`` + + gui/liquids =========== diff --git a/images/hotkeys.PNG b/images/hotkeys.PNG new file mode 100644 index 0000000000000000000000000000000000000000..bdf9c76b8187d9254c801da9314c1bd0a1da4f3b GIT binary patch literal 58207 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 literal 0 HcmV?d00001 diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 650fe1e98..193168c71 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -159,6 +159,7 @@ if (BUILD_SUPPORTED) DFHACK_PLUGIN(mousequery mousequery.cpp) DFHACK_PLUGIN(autotrade autotrade.cpp) DFHACK_PLUGIN(stocks stocks.cpp) + DFHACK_PLUGIN(hotkeys hotkeys.cpp) DFHACK_PLUGIN(treefarm treefarm.cpp) DFHACK_PLUGIN(cleanconst cleanconst.cpp) DFHACK_PLUGIN(3dveins 3dveins.cpp) diff --git a/plugins/hotkeys.cpp b/plugins/hotkeys.cpp new file mode 100644 index 000000000..52c2e2eac --- /dev/null +++ b/plugins/hotkeys.cpp @@ -0,0 +1,378 @@ +#include "uicommon.h" + +#include "df/viewscreen_dwarfmodest.h" +#include "df/ui.h" + +#include "modules/Maps.h" +#include "modules/World.h" +#include "modules/Gui.h" + +#include "PluginManager.h" + +DFHACK_PLUGIN("hotkeys"); +#define PLUGIN_VERSION 0.1 + +static map current_bindings; +static vector sorted_keys; +static bool show_usage = false; + +static void send_key(const df::interface_key &key) +{ + set< df::interface_key > keys; + keys.insert(key); + Gui::getCurViewscreen(true)->feed(&keys); +} + +static bool can_invoke(string cmdline, df::viewscreen *screen) +{ + vector cmd_parts; + split_string(&cmd_parts, cmdline, " "); + if (toLower(cmd_parts[0]) == "hotkeys") + return false; + + return Core::getInstance().getPluginManager()->CanInvokeHotkey(cmd_parts[0], screen); +} + +static void add_binding_if_valid(string sym, string cmdline, df::viewscreen *screen) +{ + if (!can_invoke(cmdline, screen)) + return; + + current_bindings[sym] = cmdline; + sorted_keys.push_back(sym); + string keyspec = sym + "@dfhack/viewscreen_hotkeys"; + Core::getInstance().AddKeyBinding(keyspec, "hotkeys invoke " + int_to_string(sorted_keys.size() - 1)); +} + +static void find_active_keybindings(df::viewscreen *screen) +{ + current_bindings.clear(); + sorted_keys.clear(); + + vector valid_keys; + for (char c = 'A'; c <= 'Z'; c++) + { + valid_keys.push_back(string(&c, 1)); + } + + for (int i = 1; i < 10; i++) + { + valid_keys.push_back("F" + int_to_string(i)); + } + + auto current_focus = Gui::getFocusString(screen); + for (int shifted = 0; shifted < 2; shifted++) + { + for (int ctrl = 0; ctrl < 2; ctrl++) + { + for (int alt = 0; alt < 2; alt++) + { + for (auto it = valid_keys.begin(); it != valid_keys.end(); it++) + { + string sym; + if (shifted) sym += "Shift-"; + if (ctrl) sym += "Ctrl-"; + if (alt) sym += "Alt-"; + sym += *it; + + auto list = Core::getInstance().ListKeyBindings(sym); + for (auto invoke_cmd = list.begin(); invoke_cmd != list.end(); invoke_cmd++) + { + bool add_temp_binding = false; + if (invoke_cmd->find(":") == string::npos) + { + add_binding_if_valid(sym, *invoke_cmd, screen); + } + else + { + 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); + } + } + } + } + } + } + } +} + +static bool close_hotkeys_screen() +{ + auto screen = Core::getTopViewscreen(); + if (Gui::getFocusString(screen) != "dfhack/viewscreen_hotkeys") + return false; + + Screen::dismiss(Core::getTopViewscreen()); + for_each_(sorted_keys, [] (const string &sym) + { Core::getInstance().ClearKeyBindings(sym + "@dfhack/viewscreen_hotkeys"); }); + sorted_keys.clear(); + return true; +} + + +static void invoke_command(const int index) +{ + if (sorted_keys.size() <= index) + return; + + auto cmd = current_bindings[sorted_keys[index]]; + if (close_hotkeys_screen()) + { + Core::getInstance().setHotkeyCmd(cmd); + } +} + +class ViewscreenHotkeys : public dfhack_viewscreen +{ +public: + ViewscreenHotkeys(df::viewscreen *top_screen) : top_screen(top_screen) + { + hotkeys_column.multiselect = false; + hotkeys_column.auto_select = true; + hotkeys_column.setTitle("Key Binding"); + hotkeys_column.bottom_margin = 4; + hotkeys_column.allow_search = false; + + focus = Gui::getFocusString(top_screen); + populateColumns(); + } + + void populateColumns() + { + hotkeys_column.clear(); + + int max_key_length = 0; + for_each_(sorted_keys, [&] (const string &sym) + { if (sym.length() > max_key_length) { max_key_length = sym.length(); } }); + int padding = max_key_length + 2; + + for (int i = 0; i < sorted_keys.size(); i++) + { + string text = pad_string(sorted_keys[i], padding, false); + text += current_bindings[sorted_keys[i]]; + hotkeys_column.add(text, i+1); + + } + + help_start = hotkeys_column.fixWidth() + 2; + hotkeys_column.filterDisplay(); + } + + void feed(set *input) + { + if (hotkeys_column.feed(input)) + return; + + if (input->count(interface_key::LEAVESCREEN)) + { + close_hotkeys_screen(); + } + else if (input->count(interface_key::SELECT)) + { + invoke_command(hotkeys_column.highlighted_index); + } + else if (input->count(interface_key::CUSTOM_U)) + { + show_usage = !show_usage; + } + } + + void render() + { + if (Screen::isDismissed(this)) + return; + + dfhack_viewscreen::render(); + + Screen::clear(); + Screen::drawBorder(" Hotkeys "); + + hotkeys_column.display(true); + + int32_t y = gps->dimy - 3; + int32_t x = 2; + OutputHotkeyString(x, y, "Leave", "Esc"); + + x += 3; + OutputHotkeyString(x, y, "Invoke", "Enter or Hotkey"); + + x += 3; + OutputToggleString(x, y, "Show Usage", "u", show_usage); + + y = gps->dimy - 4; + x = 2; + OutputHotkeyString(x, y, focus.c_str(), "Context", false, help_start, COLOR_WHITE, COLOR_BROWN); + + if (sorted_keys.size() == 0) + return; + + y = 2; + x = help_start; + + auto width = gps->dimx - help_start - 2; + vector parts; + Core::cheap_tokenise(current_bindings[sorted_keys[hotkeys_column.highlighted_index]], parts); + if(parts.size() == 0) + return; + + string first = parts[0]; + parts.erase(parts.begin()); + + if (first[0] == '#') + return; + + Plugin *plugin = Core::getInstance().getPluginManager()->getPluginByCommand(first); + if (plugin) + { + for (auto i = 0; i < plugin->size(); i++) + { + auto pc = plugin->operator[](i); + if (pc.name == first) + { + OutputString(COLOR_BROWN, x, y, "Help", true, help_start); + vector lines; + string help_text = pc.description; + if (show_usage) + help_text += "\n\n" + pc.usage; + + split_string(&lines, help_text, "\n"); + for (auto it = lines.begin(); it != lines.end() && y < gps->dimy - 4; it++) + { + auto wrapped_lines = wrapString(*it, width); + for (auto wit = wrapped_lines.begin(); wit != wrapped_lines.end() && y < gps->dimy - 4; wit++) + { + OutputString(COLOR_WHITE, x, y, *wit, true, help_start); + } + } + break; + } + } + } + } + + virtual std::string getFocusString() + { + return "viewscreen_hotkeys"; + } + +private: + ListColumn hotkeys_column; + df::viewscreen *top_screen; + string focus; + + int32_t help_start; + + void resize(int32_t x, int32_t y) + { + dfhack_viewscreen::resize(x, y); + hotkeys_column.resize(); + } + + static vector wrapString(string str, int width) + { + vector result; + string excess; + if (str.length() > width) + { + auto cut_space = str.rfind(' ', width-1); + int excess_start; + if (cut_space == string::npos) + { + cut_space = width-1; + excess_start = cut_space; + } + else + { + excess_start = cut_space + 1; + } + + string line = str.substr(0, cut_space); + excess = str.substr(excess_start); + result.push_back(line); + auto excess_lines = wrapString(excess, width); + result.insert(result.end(), excess_lines.begin(), excess_lines.end()); + } + else + { + result.push_back(str); + } + + return result; + } +}; + + +static command_result hotkeys_cmd(color_ostream &out, vector & parameters) +{ + bool show_help = false; + if (parameters.empty()) + { + if (Maps::IsValid()) + { + auto top_screen = Core::getTopViewscreen(); + if (Gui::getFocusString(top_screen) != "dfhack/viewscreen_hotkeys") + { + find_active_keybindings(top_screen); + Screen::show(new ViewscreenHotkeys(top_screen)); + } + } + } + else + { + auto cmd = parameters[0][0]; + if (cmd == 'v') + { + out << "Hotkeys" << endl << "Version: " << PLUGIN_VERSION << endl; + } + else if (cmd == 'i') + { + int index; + stringstream index_raw(parameters[1]); + index_raw >> index; + invoke_command(index); + } + else + { + return CR_WRONG_USAGE; + } + } + + return CR_OK; +} + + +DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) +{ + if (!gps) + out.printerr("Could not insert hotkeys hooks!\n"); + + commands.push_back( + PluginCommand( + "hotkeys", "Shows ingame viewscreen with all dfhack keybindings active in current mode.", + hotkeys_cmd, false, "")); + + return CR_OK; +} + +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: + sorted_keys.clear(); + break; + default: + break; + } + + return CR_OK; +} From f07f2e10793736c55cdbaccad05236cff38e75d4 Mon Sep 17 00:00:00 2001 From: Anuradha Dissanayake Date: Mon, 2 Jun 2014 16:05:05 +1200 Subject: [PATCH 135/676] Fix autotrade plugin name in version string --- plugins/autotrade.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/autotrade.cpp b/plugins/autotrade.cpp index e31fccdbc..5eacbf2d7 100644 --- a/plugins/autotrade.cpp +++ b/plugins/autotrade.cpp @@ -502,7 +502,7 @@ static command_result autotrade_cmd(color_ostream &out, vector & parame { if (parameters.size() == 1 && toLower(parameters[0])[0] == 'v') { - out << "Building Plan" << endl << "Version: " << PLUGIN_VERSION << endl; + out << "Autotrade" << endl << "Version: " << PLUGIN_VERSION << endl; } } From 49bbd41bc38392cefaed869e89428a5cc0e74b91 Mon Sep 17 00:00:00 2001 From: Warmist Date: Mon, 2 Jun 2014 17:57:59 +0300 Subject: [PATCH 136/676] Added a way to change the mod install directory (and changed the default to hack/mods) and added simpler way to add to init.lua --- scripts/gui/mod-manager.lua | 52 +++++++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 5 deletions(-) diff --git a/scripts/gui/mod-manager.lua b/scripts/gui/mod-manager.lua index 6d56d88a6..210109807 100644 --- a/scripts/gui/mod-manager.lua +++ b/scripts/gui/mod-manager.lua @@ -1,8 +1,29 @@ +-- a graphical mod manager for df local gui=require 'gui' local widgets=require 'gui.widgets' local entity_file=dfhack.getDFPath().."/raw/objects/entity_default.txt" local init_file=dfhack.getDFPath().."/raw/init.lua" +local mod_dir=dfhack.getDFPath().."/hack/mods" +--[[ mod format: lua script that defines: + name - a name that is displayed in list + author - mod author, also displayed + description - mod description + OPTIONAL: + raws_list - a list (table) of file names that need to be copied over to df raws + patch_entity - a chunk of text to patch entity TODO: add settings to which entities to add + patch_init - a chunk of lua to add to lua init + patch_dofile - a list (table) of files to add to lua init as "dofile" + patch_files - a table of files to patch: + filename - a filename (in raws folder) to patch + patch - what to add + after - a string after which to insert + MORE OPTIONAL: + guard - a token that is used in raw files to find editions and remove them on uninstall + guard_init - a token for lua file + [pre|post]_(un)install - callback functions. Can trigger more complicated behavior +]] + function fileExists(filename) local file=io.open(filename,"rb") if file==nil then @@ -12,6 +33,10 @@ function fileExists(filename) return true end end +if not fileExists(init_file) then + local initFile=io.open(initFileName,"a") + initFile:close() +end function copyFile(from,to) --oh so primitive local filefrom=io.open(from,"rb") local fileto=io.open(to,"w+b") @@ -27,6 +52,16 @@ function patchInit(initFileName,patch_guard,code) code,patch_guard[2])) initFile:close() end +function patchDofile( initFileName,patch_guard,dofile_list ) + local initFile=io.open(initFileName,"a") + initFile:write(patch_guard[1].."\n") + for _,v in ipairs(dofile_list) do + local fixed_path=mod_dir:gsub("\\","/") + initFile:write(string.format("dofile('%s/%s')\n",fixed_path,v)) + end + initFile:write(patch_guard[2].."\n") + initFile:close() +end function patchFile(file_name,patch_guard,after_string,code) local input_lines=patch_guard[1].."\n"..code.."\n"..patch_guard[2] @@ -109,15 +144,19 @@ manager=defclass(manager,gui.FramedScreen) function manager:init(args) self.mods={} local mods=self.mods - local mlist=dfhack.internal.getDir("mods") + local mlist=dfhack.internal.getDir(mod_dir) + + if #mlist==0 then + qerror("Mod directory not found! Are you sure it is in:"..mod_dir) + end for k,v in ipairs(mlist) do if v~="." and v~=".." then - local f,modData=pcall(dofile,"mods/".. v .. "/init.lua") + local f,modData=pcall(dofile,mod_dir.."/".. v .. "/init.lua") if f then mods[modData.name]=modData modData.guard=modData.guard or {">>"..modData.name.." patch","< Date: Tue, 3 Jun 2014 12:24:45 +0400 Subject: [PATCH 137/676] Fix obvious issues in stockflow. - Duplicate definition of a function now in uicommon.h - Assertion failure due to missing core suspend claim. - Incorrect way of accessing the civ entity. - Accessing nil objects in the lua module if filter matches nothing. - Lua module breaking on reload('plugins.stockflow'). --- plugins/lua/stockflow.lua | 28 +++++++++++++--------------- plugins/stockflow.cpp | 19 +------------------ 2 files changed, 14 insertions(+), 33 deletions(-) diff --git a/plugins/lua/stockflow.lua b/plugins/lua/stockflow.lua index ec52c3deb..489a72705 100644 --- a/plugins/lua/stockflow.lua +++ b/plugins/lua/stockflow.lua @@ -1,11 +1,11 @@ local _ENV = mkmodule('plugins.stockflow') -gui = require "gui" -utils = require "utils" +local gui = require "gui" +local utils = require "utils" -reaction_list = {} -saved_orders = {} -jobs_to_create = {} +reaction_list = reaction_list or {} +saved_orders = saved_orders or {} +jobs_to_create = jobs_to_create or {} triggers = { {filled = false, divisor = 1, name = "Per empty space"}, @@ -19,11 +19,7 @@ triggers = { {name = "Never"}, } --- There must be a better way get the value of an enum. -job_types = {} -for key, value in ipairs(df.job_type) do - job_types[value] = key -end +local job_types = df.job_type entry_ints = { stockpile_id = 1, @@ -205,7 +201,7 @@ function material_reactions(reactions, itemtypes, mat_info) end function clothing_reactions(reactions, mat_info, filter) - local resources = df.global.world.entities.all[df.global.ui.civ_id].resources + local resources = df.historical_entity.find(df.global.ui.civ_id).resources local itemdefs = df.global.world.raws.itemdefs resource_reactions(reactions, job_types.MakeArmor, mat_info, resources.armor_type, itemdefs.armor, {permissible = filter}) resource_reactions(reactions, job_types.MakePants, mat_info, resources.pants_type, itemdefs.pants, {permissible = filter}) @@ -372,7 +368,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.global.world.entities.all[df.global.ui.civ_id] + local entity = df.historical_entity.find(df.global.ui.civ_id) for _, reaction_id in ipairs(entity.entity_raw.workshops.permitted_reaction_id) do local reaction = df.global.world.raws.reactions[reaction_id] local name = string.gsub(reaction.name, "^.", string.upper) @@ -740,8 +736,10 @@ function screen:onInput(keys) self:dismiss() elseif keys.SELECT then self:dismiss() - local selected = self.reactions[self.position].index - store_order(self.stockpile, selected) + local selected = self.reactions[self.position] + if selected then + store_order(self.stockpile, selected.index) + end elseif keys.STANDARDSCROLL_UP then self.position = self.position - 1 elseif keys.STANDARDSCROLL_DOWN then @@ -838,10 +836,10 @@ function screen:refilter() local displayed = {} for n = 0, PageSize*2 - 1 do local item = filtered[start + n] - local name = item.name if not item then break end + local name = item.name local x = 1 local y = FirstRow + n diff --git a/plugins/stockflow.cpp b/plugins/stockflow.cpp index b96faa47f..adff25a04 100644 --- a/plugins/stockflow.cpp +++ b/plugins/stockflow.cpp @@ -51,20 +51,6 @@ const char *usage = ( "number of identical orders already in the queue.\n" ); -/* - * Stockpile Access - */ -static building_stockpilest *get_selected_stockpile() { - if (!Gui::dwarfmode_hotkey(Core::getTopViewscreen()) || - ui->main.mode != ui_sidebar_mode::QueryBuilding) - { - return nullptr; - } - - return virtual_cast(world->selected_building); -} - - /* * Lua interface. * Currently calls out to Lua functions, but never back in. @@ -134,10 +120,6 @@ public: bool stockpile_method(const char *method, building_stockpilest *sp) { // Combines the select_order and toggle_trigger method calls, // because they share the same signature. - - // Suspension is necessary for toggle_trigger, - // but may be overkill for select_order. - // Both are used from hooks, so CoreSuspender is prohibited. CoreSuspendClaimer suspend; auto L = Lua::Core::State; @@ -168,6 +150,7 @@ public: auto L = Lua::Core::State; color_ostream_proxy out(Core::getInstance().getConsole()); + CoreSuspendClaimer suspend; Lua::StackUnwinder top(L); if (!lua_checkstack(L, 2)) From 0a0358a8c1d873c72c851cf07e43afb3947dcaf9 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Tue, 3 Jun 2014 12:17:03 +0400 Subject: [PATCH 138/676] Update structures --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index e0407d8bb..93f89c7c5 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit e0407d8bbbc965dbb62826d481f27940972ffd66 +Subproject commit 93f89c7c56f366ac8f68e883c9f853a76e12f00a From 4034df3560c72ff312e5fd44b28c6278da202342 Mon Sep 17 00:00:00 2001 From: Anuradha Dissanayake Date: Wed, 5 Feb 2014 23:35:53 +1300 Subject: [PATCH 139/676] Fix incorrect plugin name in version check --- plugins/autotrade.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/autotrade.cpp b/plugins/autotrade.cpp index e31fccdbc..5eacbf2d7 100644 --- a/plugins/autotrade.cpp +++ b/plugins/autotrade.cpp @@ -502,7 +502,7 @@ static command_result autotrade_cmd(color_ostream &out, vector & parame { if (parameters.size() == 1 && toLower(parameters[0])[0] == 'v') { - out << "Building Plan" << endl << "Version: " << PLUGIN_VERSION << endl; + out << "Autotrade" << endl << "Version: " << PLUGIN_VERSION << endl; } } From df244aa7a4310ce26f165a14bceecd26e6ece794 Mon Sep 17 00:00:00 2001 From: Anuradha Dissanayake Date: Sun, 1 Jun 2014 22:13:36 +1200 Subject: [PATCH 140/676] Disable mouse query when linking levers. Update mousequery plugin version. --- plugins/mousequery.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/plugins/mousequery.cpp b/plugins/mousequery.cpp index 9cc6bb783..0fe337017 100644 --- a/plugins/mousequery.cpp +++ b/plugins/mousequery.cpp @@ -28,7 +28,7 @@ using namespace df::enums::ui_sidebar_mode; DFHACK_PLUGIN("mousequery"); -#define PLUGIN_VERSION 0.17 +#define PLUGIN_VERSION 0.18 static int32_t last_clicked_x, last_clicked_y, last_clicked_z; static int32_t last_pos_x, last_pos_y, last_pos_z; @@ -539,6 +539,13 @@ struct mousequery_hook : public df::viewscreen_dwarfmodest if (mx < 1 || mx > right_margin - 2 || my < 1 || my > gps->dimy - 2) mpos_valid = false; + // Check if in lever binding mode + if (Gui::getFocusString(Core::getTopViewscreen()) == + "dwarfmode/QueryBuilding/Some/Lever/AddJob") + { + return; + } + if (mpos_valid) { if (mpos.x != last_move_pos.x || mpos.y != last_move_pos.y || mpos.z != last_move_pos.z) From 23acf276b4b9bd0201f90b8fab9a2dbb22212099 Mon Sep 17 00:00:00 2001 From: Quietust Date: Tue, 3 Jun 2014 08:58:31 -0500 Subject: [PATCH 141/676] Silence warning C4800 on Windows --- plugins/reveal.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/plugins/reveal.cpp b/plugins/reveal.cpp index 91ab75686..9bd63e47e 100644 --- a/plugins/reveal.cpp +++ b/plugins/reveal.cpp @@ -449,22 +449,22 @@ command_result revflood(color_ostream &out, vector & params) } if(sides) { - flood.push(foo(DFCoord(current.x + 1, current.y ,current.z),0)); - flood.push(foo(DFCoord(current.x + 1, current.y + 1 ,current.z),0)); - flood.push(foo(DFCoord(current.x, current.y + 1 ,current.z),0)); - flood.push(foo(DFCoord(current.x - 1, current.y + 1 ,current.z),0)); - flood.push(foo(DFCoord(current.x - 1, current.y ,current.z),0)); - flood.push(foo(DFCoord(current.x - 1, current.y - 1 ,current.z),0)); - flood.push(foo(DFCoord(current.x, current.y - 1 ,current.z),0)); - flood.push(foo(DFCoord(current.x + 1, current.y - 1 ,current.z),0)); + flood.push(foo(DFCoord(current.x + 1, current.y ,current.z),false)); + flood.push(foo(DFCoord(current.x + 1, current.y + 1 ,current.z),false)); + flood.push(foo(DFCoord(current.x, current.y + 1 ,current.z),false)); + flood.push(foo(DFCoord(current.x - 1, current.y + 1 ,current.z),false)); + flood.push(foo(DFCoord(current.x - 1, current.y ,current.z),false)); + flood.push(foo(DFCoord(current.x - 1, current.y - 1 ,current.z),false)); + flood.push(foo(DFCoord(current.x, current.y - 1 ,current.z),false)); + flood.push(foo(DFCoord(current.x + 1, current.y - 1 ,current.z),false)); } if(above) { - flood.push(foo(DFCoord(current.x, current.y ,current.z + 1),1)); + flood.push(foo(DFCoord(current.x, current.y ,current.z + 1),true)); } if(below) { - flood.push(foo(DFCoord(current.x, current.y ,current.z - 1),0)); + flood.push(foo(DFCoord(current.x, current.y ,current.z - 1),false)); } } MCache->WriteAll(); From a7fd6c478350abef246df4a9579d898085b4c605 Mon Sep 17 00:00:00 2001 From: lethosor Date: Mon, 2 Jun 2014 17:55:27 -0400 Subject: [PATCH 142/676] Add embark-tools settings interface --- plugins/embark-tools.cpp | 89 +++++++++++++++++++++++++++++++++++----- 1 file changed, 78 insertions(+), 11 deletions(-) diff --git a/plugins/embark-tools.cpp b/plugins/embark-tools.cpp index f049841c5..c9bbdf27e 100644 --- a/plugins/embark-tools.cpp +++ b/plugins/embark-tools.cpp @@ -22,23 +22,83 @@ struct EmbarkTool std::string name; std::string desc; bool enabled; + df::interface_key toggle_key; }; EmbarkTool embark_tools[] = { - {"anywhere", "Embark anywhere", "Allows embarking anywhere on the world map", false}, - {"nano", "Nano embark", "Allows the embark size to be decreased below 2x2", false}, - {"sand", "Sand indicator", "Displays an indicator when sand is present on the given embark site", false}, - {"sticky", "Stable position", "Maintains the selected local area while navigating the world map", false}, + {"anywhere", "Embark anywhere", "Allows embarking anywhere on the world map", + false, df::interface_key::CUSTOM_A}, + {"nano", "Nano embark", "Allows the embark size to be decreased below 2x2", + false, df::interface_key::CUSTOM_N}, + {"sand", "Sand indicator", "Displays an indicator when sand is present on the given embark site", + false, df::interface_key::CUSTOM_S}, + {"sticky", "Stable position", "Maintains the selected local area while navigating the world map", + false, df::interface_key::CUSTOM_P}, }; #define NUM_TOOLS sizeof(embark_tools) / sizeof(EmbarkTool) command_result embark_tools_cmd (color_ostream &out, std::vector & parameters); +void OutputString (int8_t color, int &x, int y, const std::string &text); + bool tool_exists (std::string tool_name); bool tool_enabled (std::string tool_name); bool tool_enable (std::string tool_name, bool enable_state); void tool_update (std::string tool_name); +class embark_tools_settings : public dfhack_viewscreen +{ +public: + embark_tools_settings () { }; + ~embark_tools_settings () { }; + void help () { }; + std::string getFocusString () { return "embark-tools/options"; }; + void render () + { + int x; + auto dim = Screen::getWindowSize(); + int width = 50, + height = 4 + 1 + NUM_TOOLS, // Padding + lower row + min_x = (dim.x - width) / 2, + max_x = (dim.x + width) / 2, + min_y = (dim.y - height) / 2, + max_y = min_y + height; + Screen::fillRect(Screen::Pen(' ', COLOR_BLACK, COLOR_DARKGREY), min_x, min_y, max_x, max_y); + Screen::fillRect(Screen::Pen(' ', COLOR_BLACK, COLOR_BLACK), min_x + 1, min_y + 1, max_x - 1, max_y - 1); + x = min_x + 2; + OutputString(COLOR_LIGHTRED, x, max_y - 2, Screen::getKeyDisplay(df::interface_key::SELECT)); + OutputString(COLOR_WHITE, x, max_y - 2, "/"); + OutputString(COLOR_LIGHTRED, x, max_y - 2, Screen::getKeyDisplay(df::interface_key::LEAVESCREEN)); + OutputString(COLOR_WHITE, x, max_y - 2, ": Done"); + for (int i = 0, y = min_y + 2; i < NUM_TOOLS; i++, y++) + { + EmbarkTool t = embark_tools[i]; + x = min_x + 2; + OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(t.toggle_key)); + OutputString(COLOR_WHITE, x, y, ": " + t.name + (t.enabled ? ": Enabled" : ": Disabled")); + } + }; + void feed (std::set * input) + { + if (input->count(df::interface_key::SELECT) || input->count(df::interface_key::LEAVESCREEN)) + { + Screen::dismiss(this); + return; + } + for (auto iter = input->begin(); iter != input->end(); iter++) + { + df::interface_key key = *iter; + for (int i = 0; i < NUM_TOOLS; i++) + { + if (embark_tools[i].toggle_key == key) + { + embark_tools[i].enabled = !embark_tools[i].enabled; + } + } + } + }; +}; + /* * Logic */ @@ -174,6 +234,12 @@ struct choose_start_site_hook : df::viewscreen_choose_start_sitest } } + if (input->count(df::interface_key::CUSTOM_S)) + { + Screen::show(new embark_tools_settings); + return; + } + if (tool_enabled("nano")) { for (auto iter = input->begin(); iter != input->end(); iter++) @@ -270,20 +336,21 @@ struct choose_start_site_hook : df::viewscreen_choose_start_sitest auto dim = Screen::getWindowSize(); int x = 1, y = dim.y - 5; - OutputString(COLOR_LIGHTMAGENTA, x, y, "Enabled: "); - std::list tools; + OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(df::interface_key::CUSTOM_S)); + OutputString(COLOR_WHITE, x, y, ": Enabled: "); + std::list parts; for (int i = 0; i < NUM_TOOLS; i++) { if (embark_tools[i].enabled) { - tools.push_back(embark_tools[i].name); - tools.push_back(", "); + parts.push_back(embark_tools[i].name); + parts.push_back(", "); } } - if (tools.size()) + if (parts.size()) { - tools.pop_back(); // Remove last , - for (auto iter = tools.begin(); iter != tools.end(); iter++) + parts.pop_back(); // Remove trailing comma + for (auto iter = parts.begin(); iter != parts.end(); iter++) { OutputString(COLOR_LIGHTMAGENTA, x, y, *iter); } From 6ec0fd6589042356e75dee3267097221bbb69054 Mon Sep 17 00:00:00 2001 From: lethosor Date: Tue, 3 Jun 2014 18:29:13 -0400 Subject: [PATCH 143/676] New tweak: confirm-embark Adds a confirmation when selecting [e]mbark on the "prepare carefully" screen. Suggested in multiple threads on forums. --- plugins/tweak.cpp | 95 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 90 insertions(+), 5 deletions(-) diff --git a/plugins/tweak.cpp b/plugins/tweak.cpp index d74ef0767..2136fff58 100644 --- a/plugins/tweak.cpp +++ b/plugins/tweak.cpp @@ -52,6 +52,7 @@ #include "df/reaction.h" #include "df/reaction_reagent_itemst.h" #include "df/reaction_reagent_flags.h" +#include "df/viewscreen_setupdwarfgamest.h" #include "df/viewscreen_layer_assigntradest.h" #include "df/viewscreen_tradegoodsst.h" #include "df/viewscreen_layer_militaryst.h" @@ -120,6 +121,8 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector \n" @@ -331,6 +334,83 @@ struct readable_build_plate_hook : df::viewscreen_dwarfmodest IMPLEMENT_VMETHOD_INTERPOSE(readable_build_plate_hook, render); +enum confirm_embark_states +{ + ECS_INACTIVE = 0, + ECS_CONFIRM, + ECS_ACCEPTED +}; +static confirm_embark_states confirm_embark_state = ECS_INACTIVE; + +struct confirm_embark_hook : df::viewscreen_setupdwarfgamest +{ + typedef df::viewscreen_setupdwarfgamest interpose_base; + + DEFINE_VMETHOD_INTERPOSE(void, feed, (set *input)) + { + bool intercept = false; + df::viewscreen * top = Gui::getCurViewscreen(); + VIRTUAL_CAST_VAR(screen, df::viewscreen_setupdwarfgamest, top); + if (screen) + { + if (screen->anon_14 == 0) // Advanced embark screen + { + if (confirm_embark_state == ECS_INACTIVE) + { + if (input->count(df::interface_key::SETUP_EMBARK)) + { + confirm_embark_state = ECS_CONFIRM; + intercept = true; + } + } + else if (confirm_embark_state == ECS_CONFIRM) + { + intercept = true; + if (input->count(df::interface_key::MENU_CONFIRM)) + confirm_embark_state = ECS_ACCEPTED; + else if (input->size()) + confirm_embark_state = ECS_INACTIVE; + } + } + } + + if (!intercept) + INTERPOSE_NEXT(feed)(input); + } + + DEFINE_VMETHOD_INTERPOSE(void, render, ()) + { + INTERPOSE_NEXT(render)(); + df::viewscreen * top = Gui::getCurViewscreen(); + VIRTUAL_CAST_VAR(screen, df::viewscreen_setupdwarfgamest, top); + auto dim = Screen::getWindowSize(); + Screen::Pen pen(' ', COLOR_WHITE, COLOR_BLACK); + if (confirm_embark_state != ECS_INACTIVE) + { + Screen::fillRect(Screen::Pen(' ', COLOR_BLACK, COLOR_BLACK), 0, 0, dim.x - 1, dim.y - 1); + } + if (confirm_embark_state == ECS_CONFIRM) + { + Screen::paintString(pen, 2, 2, "Really embark?"); + Screen::paintString(Screen::Pen(' ', COLOR_LIGHTGREEN), + 2, 4, Screen::getKeyDisplay(df::interface_key::MENU_CONFIRM)); + Screen::paintString(pen, 3, 4, ": Confirm, Other: Cancel"); + Screen::paintString(pen, dim.x - 10, dim.y - 1, "DFHack"); + } + else if (confirm_embark_state == ECS_ACCEPTED) + { + Screen::paintString(pen, 2, 2, "Embarking..."); + std::set input; + input.insert(df::interface_key::SETUP_EMBARK); + screen->feed(&input); + confirm_embark_state = ECS_INACTIVE; + } + } +}; + +IMPLEMENT_VMETHOD_INTERPOSE(confirm_embark_hook, feed); +IMPLEMENT_VMETHOD_INTERPOSE(confirm_embark_hook, render); + struct stable_temp_hook : df::item_actual { typedef df::item_actual interpose_base; @@ -1150,7 +1230,7 @@ static command_result tweak(color_ostream &out, vector ¶meters) df::unit *unit = getSelectedUnit(out, true); if (!unit) return CR_FAILURE; - + if(unit->race != df::global::ui->race_id) { out << "Selected unit does not belong to your race!" << endl; @@ -1161,15 +1241,15 @@ static command_result tweak(color_ostream &out, vector ¶meters) // see http://dffd.wimbli.com/file.php?id=6139 for a save if (unit->flags2.bits.resident) unit->flags2.bits.resident = 0; - + // case #2: migrants who have the merchant flag // happens on almost all maps after a few migrant waves if(unit->flags1.bits.merchant) unit->flags1.bits.merchant = 0; - // this one is a cheat, but bugged migrants usually have the same civ_id + // this one is a cheat, but bugged migrants usually have the same civ_id // so it should not be triggered in most cases - // if it happens that the player has 'foreign' units of the same race + // 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(unit->civ_id != df::global::ui->civ_id) unit->civ_id = df::global::ui->civ_id; @@ -1216,6 +1296,11 @@ static command_result tweak(color_ostream &out, vector ¶meters) enable_hook(out, INTERPOSE_HOOK(readable_build_plate_hook, render), parameters); } + else if (cmd == "confirm-embark") + { + enable_hook(out, INTERPOSE_HOOK(confirm_embark_hook, feed), parameters); + enable_hook(out, INTERPOSE_HOOK(confirm_embark_hook, render), parameters); + } else if (cmd == "stable-temp") { enable_hook(out, INTERPOSE_HOOK(stable_temp_hook, adjustTemperature), parameters); @@ -1280,7 +1365,7 @@ static command_result tweak(color_ostream &out, vector ¶meters) enable_hook(out, INTERPOSE_HOOK(adamantine_cloth_wear_shoes_hook, incWearTimer), parameters); enable_hook(out, INTERPOSE_HOOK(adamantine_cloth_wear_pants_hook, incWearTimer), parameters); } - else + else return CR_WRONG_USAGE; return CR_OK; From 9ddbd49e28e62fff826e89acdfcc894d38eccbfd Mon Sep 17 00:00:00 2001 From: Anuradha Dissanayake Date: Wed, 4 Jun 2014 22:01:17 +1200 Subject: [PATCH 144/676] Add stockpile autodump functionality --- Readme.rst | 8 ++ plugins/autodump.cpp | 223 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 231 insertions(+) diff --git a/Readme.rst b/Readme.rst index 7491c4a0c..d63fcbc32 100644 --- a/Readme.rst +++ b/Readme.rst @@ -2621,6 +2621,14 @@ materials, it returns you back to this screen. If you use this along with severa enabled materials, you should be able to place complex constructions more conveniently. +Stockpile Automation +==================== +Enable the autodump plugin in your dfhack.init with + ``enable autodump`` + +When querying a stockpile an option will appear to toggle autodump for this stockpile. +Any items placed in this stockpiled will be designated to be dumped. + gui/liquids =========== diff --git a/plugins/autodump.cpp b/plugins/autodump.cpp index c45b132ae..4d80309f3 100644 --- a/plugins/autodump.cpp +++ b/plugins/autodump.cpp @@ -24,16 +24,239 @@ using namespace std; #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" using namespace DFHack; using namespace df::enums; using MapExtras::Block; using MapExtras::MapCache; + using df::global::world; +using df::building_stockpilest; DFHACK_PLUGIN("autodump"); +// Stockpile interface START +static const string PERSISTENCE_KEY = "autodump/stockpiles"; + +static void mark_all_in_stockpiles(vector &stockpiles) +{ + std::vector &items = world->items.other[items_other_id::IN_PLAY]; + + // 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 + + size_t marked_count = 0; + for (size_t i = 0; i < items.size(); i++) + { + df::item *item = items[i]; + if (item->flags.whole & bad_flags.whole) + continue; + + for (auto it = stockpiles.begin(); it != stockpiles.end(); it++) + { + if (!it->inStockpile(item)) + continue; + + ++marked_count; + item->flags.bits.dump = true; + } + } + + if (marked_count) + Gui::showAnnouncement("Marked " + int_to_string(marked_count) + " items to dump", COLOR_GREEN, false); +} + +class StockpileMonitor +{ +public: + bool isMonitored(df::building_stockpilest *sp) + { + for (auto it = monitored_stockpiles.begin(); it != monitored_stockpiles.end(); it++) + { + if (it->matches(sp)) + return true; + } + + return false; + } + + void add(df::building_stockpilest *sp) + { + auto pile = PersistentStockpileInfo(sp, PERSISTENCE_KEY); + if (pile.isValid()) + { + monitored_stockpiles.push_back(PersistentStockpileInfo(pile)); + monitored_stockpiles.back().save(); + } + } + + void remove(df::building_stockpilest *sp) + { + for (auto it = monitored_stockpiles.begin(); it != monitored_stockpiles.end(); it++) + { + if (it->matches(sp)) + { + it->remove(); + monitored_stockpiles.erase(it); + break; + } + } + } + + void doCycle() + { + for (auto it = monitored_stockpiles.begin(); it != monitored_stockpiles.end();) + { + if (!it->isValid()) + it = monitored_stockpiles.erase(it); + else + ++it; + } + + mark_all_in_stockpiles(monitored_stockpiles); + } + + void reset() + { + monitored_stockpiles.clear(); + std::vector items; + DFHack::World::GetPersistentData(&items, PERSISTENCE_KEY); + + for (auto i = items.begin(); i != items.end(); i++) + { + auto pile = PersistentStockpileInfo(*i, PERSISTENCE_KEY); + if (pile.load()) + monitored_stockpiles.push_back(PersistentStockpileInfo(pile)); + else + pile.remove(); + } + } + + +private: + vector monitored_stockpiles; +}; + +static StockpileMonitor monitor; + +#define DELTA_TICKS 620 + + +DFhackCExport command_result plugin_onupdate ( color_ostream &out ) +{ + if(!Maps::IsValid()) + return CR_OK; + + static decltype(world->frame_counter) last_frame_count = 0; + + if (DFHack::World::ReadPauseState()) + return CR_OK; + + if (world->frame_counter - last_frame_count < DELTA_TICKS) + return CR_OK; + + last_frame_count = world->frame_counter; + + monitor.doCycle(); + + return CR_OK; +} + +struct dump_hook : public df::viewscreen_dwarfmodest +{ + typedef df::viewscreen_dwarfmodest interpose_base; + + bool handleInput(set *input) + { + building_stockpilest *sp = get_selected_stockpile(); + if (!sp) + return false; + + if (input->count(interface_key::CUSTOM_SHIFT_D)) + { + if (monitor.isMonitored(sp)) + monitor.remove(sp); + else + monitor.add(sp); + } + + return false; + } + + DEFINE_VMETHOD_INTERPOSE(void, feed, (set *input)) + { + if (!handleInput(input)) + INTERPOSE_NEXT(feed)(input); + } + + 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 = 26; + + OutputToggleString(x, y, "Auto dump", "Shift-D", monitor.isMonitored(sp), true, left_margin); + } +}; + +IMPLEMENT_VMETHOD_INTERPOSE(dump_hook, feed); +IMPLEMENT_VMETHOD_INTERPOSE(dump_hook, render); + +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; + } + return CR_OK; +} + +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(dump_hook, feed).apply(enable) || + !INTERPOSE_HOOK(dump_hook, render).apply(enable)) + return CR_FAILURE; + + is_enabled = enable; + } + + return CR_OK; +} + +// Stockpile interface END + command_result df_autodump(color_ostream &out, vector & parameters); command_result df_autodump_destroy_here(color_ostream &out, vector & parameters); command_result df_autodump_destroy_item(color_ostream &out, vector & parameters); From 15230f706c2980d27752e683304b88aed98a5ef6 Mon Sep 17 00:00:00 2001 From: Anuradha Dissanayake Date: Wed, 4 Jun 2014 22:13:17 +1200 Subject: [PATCH 145/676] Fix spelling error --- Readme.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Readme.rst b/Readme.rst index d63fcbc32..e50880269 100644 --- a/Readme.rst +++ b/Readme.rst @@ -2627,7 +2627,7 @@ Enable the autodump plugin in your dfhack.init with ``enable autodump`` When querying a stockpile an option will appear to toggle autodump for this stockpile. -Any items placed in this stockpiled will be designated to be dumped. +Any items placed in this stockpile will be designated to be dumped. gui/liquids =========== From 5f611ec48b65aacf8e9f55a1de579e76087330bf Mon Sep 17 00:00:00 2001 From: Anuradha Dissanayake Date: Wed, 4 Jun 2014 22:12:30 +1200 Subject: [PATCH 146/676] Add automelt plugin --- NEWS | 1 + Readme.rst | 9 ++ plugins/CMakeLists.txt | 1 + plugins/automelt.cpp | 282 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 293 insertions(+) create mode 100644 plugins/automelt.cpp diff --git a/NEWS b/NEWS index 757a9277f..3026fc665 100644 --- a/NEWS +++ b/NEWS @@ -28,6 +28,7 @@ DFHack future New plugins: - rendermax: replace the renderer with something else. Most interesting is "rendermax light"- a lighting engine for df. + - automelt: allows marking stockpiles for automelt (i.e. any items placed in stocpile will be designated for melting) Misc improvements: - digfort: improved csv parsing, add start() comment handling diff --git a/Readme.rst b/Readme.rst index 7491c4a0c..6920835a4 100644 --- a/Readme.rst +++ b/Readme.rst @@ -2621,6 +2621,15 @@ materials, it returns you back to this screen. If you use this along with severa enabled materials, you should be able to place complex constructions more conveniently. +Stockpile Automation +==================== +Enable the automelt plugin in your dfhack.init with + ``enable automelt`` + +When querying a stockpile an option will appear to toggle automelt for this stockpile. +Any items placed in this stockpile will be designated to be melted. + + gui/liquids =========== diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 650fe1e98..91a986e59 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -158,6 +158,7 @@ if (BUILD_SUPPORTED) DFHACK_PLUGIN(dwarfmonitor dwarfmonitor.cpp) DFHACK_PLUGIN(mousequery mousequery.cpp) DFHACK_PLUGIN(autotrade autotrade.cpp) + DFHACK_PLUGIN(automelt automelt.cpp) DFHACK_PLUGIN(stocks stocks.cpp) DFHACK_PLUGIN(treefarm treefarm.cpp) DFHACK_PLUGIN(cleanconst cleanconst.cpp) diff --git a/plugins/automelt.cpp b/plugins/automelt.cpp new file mode 100644 index 000000000..0c6a7b33f --- /dev/null +++ b/plugins/automelt.cpp @@ -0,0 +1,282 @@ +#include "uicommon.h" + +#include "modules/Gui.h" + +#include "df/world.h" +#include "df/world_raws.h" +#include "df/building_def.h" +#include "df/viewscreen_dwarfmodest.h" +#include "df/building_stockpilest.h" +#include "modules/Items.h" +#include "df/ui.h" +#include "modules/Maps.h" +#include "modules/World.h" +#include "df/item_quality.h" + +using df::global::world; +using df::global::cursor; +using df::global::ui; +using df::building_stockpilest; + +DFHACK_PLUGIN("automelt"); +#define PLUGIN_VERSION 0.3 + +static const string PERSISTENCE_KEY = "automelt/stockpiles"; + +static void mark_all_in_stockpiles(vector &stockpiles) +{ + std::vector &items = world->items.other[items_other_id::IN_PLAY]; + + // 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 + + size_t marked_count = 0; + for (size_t i = 0; i < items.size(); i++) + { + df::item *item = items[i]; + if (item->flags.whole & bad_flags.whole) + continue; + + if (!can_melt(item)) + continue; + + if (is_set_to_melt(item)) + continue; + + auto &melting_items = world->items.other[items_other_id::ANY_MELT_DESIGNATED]; + for (auto it = stockpiles.begin(); it != stockpiles.end(); it++) + { + if (!it->inStockpile(item)) + continue; + + ++marked_count; + insert_into_vector(melting_items, &df::item::id, item); + item->flags.bits.melt = true; + } + } + + if (marked_count) + Gui::showAnnouncement("Marked " + int_to_string(marked_count) + " items to melt", COLOR_GREEN, false); +} + +/* + * Stockpile Monitoring + */ + +class StockpileMonitor +{ +public: + bool isMonitored(df::building_stockpilest *sp) + { + for (auto it = monitored_stockpiles.begin(); it != monitored_stockpiles.end(); it++) + { + if (it->matches(sp)) + return true; + } + + return false; + } + + void add(df::building_stockpilest *sp) + { + auto pile = PersistentStockpileInfo(sp, PERSISTENCE_KEY); + if (pile.isValid()) + { + monitored_stockpiles.push_back(PersistentStockpileInfo(pile)); + monitored_stockpiles.back().save(); + } + } + + void remove(df::building_stockpilest *sp) + { + for (auto it = monitored_stockpiles.begin(); it != monitored_stockpiles.end(); it++) + { + if (it->matches(sp)) + { + it->remove(); + monitored_stockpiles.erase(it); + break; + } + } + } + + void doCycle() + { + for (auto it = monitored_stockpiles.begin(); it != monitored_stockpiles.end();) + { + if (!it->isValid()) + it = monitored_stockpiles.erase(it); + else + ++it; + } + + mark_all_in_stockpiles(monitored_stockpiles); + } + + void reset() + { + monitored_stockpiles.clear(); + std::vector items; + DFHack::World::GetPersistentData(&items, PERSISTENCE_KEY); + + for (auto i = items.begin(); i != items.end(); i++) + { + auto pile = PersistentStockpileInfo(*i, PERSISTENCE_KEY); + if (pile.load()) + monitored_stockpiles.push_back(PersistentStockpileInfo(pile)); + else + pile.remove(); + } + } + + +private: + vector monitored_stockpiles; +}; + +static StockpileMonitor monitor; + +#define DELTA_TICKS 610 + +DFhackCExport command_result plugin_onupdate ( color_ostream &out ) +{ + if(!Maps::IsValid()) + return CR_OK; + + static decltype(world->frame_counter) last_frame_count = 0; + + if (DFHack::World::ReadPauseState()) + return CR_OK; + + if (world->frame_counter - last_frame_count < DELTA_TICKS) + return CR_OK; + + last_frame_count = world->frame_counter; + + monitor.doCycle(); + + return CR_OK; +} + + +/* + * Interface + */ + +struct melt_hook : public df::viewscreen_dwarfmodest +{ + typedef df::viewscreen_dwarfmodest interpose_base; + + bool handleInput(set *input) + { + building_stockpilest *sp = get_selected_stockpile(); + if (!sp) + return false; + + if (input->count(interface_key::CUSTOM_SHIFT_M)) + { + if (monitor.isMonitored(sp)) + monitor.remove(sp); + else + monitor.add(sp); + } + + return false; + } + + DEFINE_VMETHOD_INTERPOSE(void, feed, (set *input)) + { + if (!handleInput(input)) + INTERPOSE_NEXT(feed)(input); + } + + 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 = 25; + + OutputToggleString(x, y, "Auto melt", "Shift-M", monitor.isMonitored(sp), true, left_margin); + } +}; + +IMPLEMENT_VMETHOD_INTERPOSE(melt_hook, feed); +IMPLEMENT_VMETHOD_INTERPOSE(melt_hook, render); + + +static command_result automelt_cmd(color_ostream &out, vector & parameters) +{ + if (!parameters.empty()) + { + if (parameters.size() == 1 && toLower(parameters[0])[0] == 'v') + { + out << "Automelt" << endl << "Version: " << PLUGIN_VERSION << endl; + } + } + + return CR_OK; +} + +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; + } + return CR_OK; +} + +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(melt_hook, feed).apply(enable) || + !INTERPOSE_HOOK(melt_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( + "automelt", "Automatically flag metal items in marked stockpiles for melting.", + automelt_cmd, false, "")); + + return CR_OK; +} + +DFhackCExport command_result plugin_shutdown ( color_ostream &out ) +{ + return CR_OK; +} From f29d1886ff2164932a3773d7929f745005cc64a9 Mon Sep 17 00:00:00 2001 From: lethosor Date: Wed, 4 Jun 2014 14:41:35 -0400 Subject: [PATCH 147/676] tweak confirm-embark fixes * Added a display of the total embark points remaining, for convenience * Made 'esc' work by interposing key_conflict as well * Rearranged to look more like DF's confirmations --- plugins/tweak.cpp | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/plugins/tweak.cpp b/plugins/tweak.cpp index 2136fff58..58ff6f929 100644 --- a/plugins/tweak.cpp +++ b/plugins/tweak.cpp @@ -346,6 +346,12 @@ struct confirm_embark_hook : df::viewscreen_setupdwarfgamest { typedef df::viewscreen_setupdwarfgamest interpose_base; + void OutputString(int8_t fg, int &x, int y, std::string text) + { + Screen::paintString(Screen::Pen(' ', fg, COLOR_BLACK), x, y, text); + x += text.length(); + } + DEFINE_VMETHOD_INTERPOSE(void, feed, (set *input)) { bool intercept = false; @@ -378,28 +384,39 @@ struct confirm_embark_hook : df::viewscreen_setupdwarfgamest INTERPOSE_NEXT(feed)(input); } + DEFINE_VMETHOD_INTERPOSE(bool, key_conflict, (df::interface_key key)) + { + if (key == df::interface_key::OPTIONS) + return true; + return false; + } + DEFINE_VMETHOD_INTERPOSE(void, render, ()) { INTERPOSE_NEXT(render)(); df::viewscreen * top = Gui::getCurViewscreen(); VIRTUAL_CAST_VAR(screen, df::viewscreen_setupdwarfgamest, top); auto dim = Screen::getWindowSize(); - Screen::Pen pen(' ', COLOR_WHITE, COLOR_BLACK); + int x = 0, y = 0; if (confirm_embark_state != ECS_INACTIVE) { Screen::fillRect(Screen::Pen(' ', COLOR_BLACK, COLOR_BLACK), 0, 0, dim.x - 1, dim.y - 1); } if (confirm_embark_state == ECS_CONFIRM) { - Screen::paintString(pen, 2, 2, "Really embark?"); - Screen::paintString(Screen::Pen(' ', COLOR_LIGHTGREEN), - 2, 4, Screen::getKeyDisplay(df::interface_key::MENU_CONFIRM)); - Screen::paintString(pen, 3, 4, ": Confirm, Other: Cancel"); - Screen::paintString(pen, dim.x - 10, dim.y - 1, "DFHack"); + x = 2, y = 2; + OutputString(COLOR_WHITE, x, y, "Really embark? ("); + OutputString(COLOR_LIGHTGREEN, x, y, Screen::getKeyDisplay(df::interface_key::MENU_CONFIRM)); + OutputString(COLOR_WHITE, x, y, " = yes, other = no)"); + x = 2, y = 4; + int32_t points = screen->anon_37; + OutputString(COLOR_WHITE, x, y, "Points left: "); + OutputString((points ? COLOR_YELLOW : COLOR_LIGHTGREEN), x, y, std::to_string(points)); + x = dim.x - 10, y = dim.y - 1; + OutputString(COLOR_WHITE, x, y, "DFHack"); } else if (confirm_embark_state == ECS_ACCEPTED) { - Screen::paintString(pen, 2, 2, "Embarking..."); std::set input; input.insert(df::interface_key::SETUP_EMBARK); screen->feed(&input); @@ -409,6 +426,7 @@ struct confirm_embark_hook : df::viewscreen_setupdwarfgamest }; IMPLEMENT_VMETHOD_INTERPOSE(confirm_embark_hook, feed); +IMPLEMENT_VMETHOD_INTERPOSE(confirm_embark_hook, key_conflict); IMPLEMENT_VMETHOD_INTERPOSE(confirm_embark_hook, render); struct stable_temp_hook : df::item_actual { @@ -1299,6 +1317,7 @@ static command_result tweak(color_ostream &out, vector ¶meters) else if (cmd == "confirm-embark") { enable_hook(out, INTERPOSE_HOOK(confirm_embark_hook, feed), parameters); + enable_hook(out, INTERPOSE_HOOK(confirm_embark_hook, key_conflict), parameters); enable_hook(out, INTERPOSE_HOOK(confirm_embark_hook, render), parameters); } else if (cmd == "stable-temp") From 0a1d87c09f418c8c9075083bb207d91c6185628d Mon Sep 17 00:00:00 2001 From: lethosor Date: Wed, 4 Jun 2014 23:18:19 -0400 Subject: [PATCH 148/676] confirm-embark: Make esc work again --- plugins/tweak.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/plugins/tweak.cpp b/plugins/tweak.cpp index 58ff6f929..c7cec6246 100644 --- a/plugins/tweak.cpp +++ b/plugins/tweak.cpp @@ -386,8 +386,11 @@ struct confirm_embark_hook : df::viewscreen_setupdwarfgamest DEFINE_VMETHOD_INTERPOSE(bool, key_conflict, (df::interface_key key)) { - if (key == df::interface_key::OPTIONS) - return true; + if (confirm_embark_state == ECS_CONFIRM) + { + if (key == df::interface_key::OPTIONS) + return true; + } return false; } From c29d61dca23feb42b16c2a04e54a66f2406c35d6 Mon Sep 17 00:00:00 2001 From: Lethosor Date: Fri, 6 Jun 2014 14:59:23 -0400 Subject: [PATCH 149/676] Use INTERPOSE_NEXT in key_conflict See comment on 0a1d87c09f418c8c9075083bb207d91c6185628d --- plugins/tweak.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/tweak.cpp b/plugins/tweak.cpp index c7cec6246..8e301ae5c 100644 --- a/plugins/tweak.cpp +++ b/plugins/tweak.cpp @@ -391,7 +391,7 @@ struct confirm_embark_hook : df::viewscreen_setupdwarfgamest if (key == df::interface_key::OPTIONS) return true; } - return false; + return INTERPOSE_NEXT(key_conflict)(key); } DEFINE_VMETHOD_INTERPOSE(void, render, ()) From a1f9b1d1c4fd4f8c3a085e0353d79dc6f4b3886a Mon Sep 17 00:00:00 2001 From: Quietust Date: Fri, 6 Jun 2014 14:24:57 -0500 Subject: [PATCH 150/676] Sync with structures change --- plugins/strangemood.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/strangemood.cpp b/plugins/strangemood.cpp index 35d1e15b8..81840b67b 100644 --- a/plugins/strangemood.cpp +++ b/plugins/strangemood.cpp @@ -126,7 +126,7 @@ int getCreatedMetalBars (int32_t idx) return 0; } -void selectWord (const df::world_raws::T_language::T_word_table &table, int32_t &word, df::enum_field &part, int mode) +void selectWord (const df::language_word_table &table, int32_t &word, df::enum_field &part, int mode) { if (table.parts[mode].size()) { @@ -142,7 +142,7 @@ void selectWord (const df::world_raws::T_language::T_word_table &table, int32_t } } -void generateName(df::language_name &output, int language, int mode, const df::world_raws::T_language::T_word_table &table1, const df::world_raws::T_language::T_word_table &table2) +void generateName(df::language_name &output, int language, int mode, const df::language_word_table &table1, const df::language_word_table &table2) { for (int i = 0; i < 100; i++) { From b1f73b791c240e8dbcdf6d7edb39fb8c2caf30c8 Mon Sep 17 00:00:00 2001 From: Anuradha Dissanayake Date: Sat, 7 Jun 2014 14:31:45 +1200 Subject: [PATCH 151/676] DwarfMonitor: Show current date and weather in border. --- plugins/dwarfmonitor.cpp | 62 +++++++++++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 16 deletions(-) diff --git a/plugins/dwarfmonitor.cpp b/plugins/dwarfmonitor.cpp index be271750a..804dbf053 100644 --- a/plugins/dwarfmonitor.cpp +++ b/plugins/dwarfmonitor.cpp @@ -48,7 +48,7 @@ using df::global::ui; typedef int16_t activity_type; -#define PLUGIN_VERSION 0.8 +#define PLUGIN_VERSION 0.9 #define DAY_TICKS 1200 #define DELTA_TICKS 100 @@ -66,6 +66,7 @@ struct less_second { static bool monitor_jobs = false; static bool monitor_misery = true; +static bool monitor_date = true; static map> work_history; static int misery[] = { 0, 0, 0, 0, 0, 0, 0 }; @@ -1693,24 +1694,49 @@ struct dwarf_monitor_hook : public df::viewscreen_dwarfmodest { INTERPOSE_NEXT(render)(); - if (monitor_misery && Maps::IsValid()) + if (Maps::IsValid()) { - string entries[7]; - size_t length = 9; - for (int i = 0; i < 7; i++) + if (monitor_misery) { - entries[i] = int_to_string(misery[i]); - length += entries[i].length(); + string entries[7]; + size_t length = 9; + for (int i = 0; i < 7; i++) + { + entries[i] = int_to_string(misery[i]); + length += entries[i].length(); + } + + int x = gps->dimx - length; + int y = gps->dimy - 1; + OutputString(COLOR_WHITE, x, y, "H:"); + for (int i = 0; i < 7; i++) + { + OutputString(monitor_colors[i], x, y, entries[i]); + if (i < 6) + OutputString(COLOR_WHITE, x, y, "/"); + } } - int x = gps->dimx - length; - int y = gps->dimy - 1; - OutputString(COLOR_WHITE, x, y, "H:"); - for (int i = 0; i < 7; i++) + if (monitor_date) { - OutputString(monitor_colors[i], x, y, entries[i]); - if (i < 6) - OutputString(COLOR_WHITE, x, y, "/"); + int x = gps->dimx - 30; + int y = 0; + + ostringstream date_str; + auto month = World::ReadCurrentMonth(); + auto day = World::ReadCurrentDay(); + date_str << "Date:" << World::ReadCurrentYear() << "/" << + ((month < 10) ? "0" : "") << month << "/" << + ((day < 10) ? "0" : "") << day; + + OutputString(COLOR_GREY, x, y, date_str.str()); + + x = 1; + y = gps->dimy - 1; + if (World::ReadCurrentWeather() == weather_type::Rain) + OutputString(COLOR_BLUE, x, y, "Rain"); + else if (World::ReadCurrentWeather() == weather_type::Snow) + OutputString(COLOR_WHITE, x, y, "Snow"); } } } @@ -1736,12 +1762,16 @@ static bool set_monitoring_mode(const string &mode, const bool &state) if (!monitor_jobs) reset(); } - - if (mode == "misery" || mode == "all") + else if (mode == "misery" || mode == "all") { mode_recognized = true; monitor_misery = state; } + else if (mode == "date" || mode == "all") + { + mode_recognized = true; + monitor_date = state; + } return mode_recognized; } From 2242d42c9c896d754037d88a5a5a94f0a0fb16c6 Mon Sep 17 00:00:00 2001 From: lethosor Date: Sat, 7 Jun 2014 20:15:49 -0400 Subject: [PATCH 152/676] Implement runCommand in Lua API --- library/LuaApi.cpp | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 3a374445f..91baf5fbb 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -2227,6 +2227,31 @@ static int internal_getDir(lua_State *L) } return 1; } + +static int internal_runCommand(lua_State *L) +{ + luaL_checktype(L, 1, LUA_TSTRING); + std::string command = lua_tostring(L, 1); + buffered_color_ostream out; + Core::getInstance().runCommand(out, command); + auto fragments = out.fragments(); + lua_newtable(L); + int i = 1; + for (auto iter = fragments.begin(); iter != fragments.end(); iter++, i++) + { + int color = iter->first; + std::string output = iter->second; + lua_createtable(L, 2, 0); + lua_pushinteger(L, color); + lua_rawseti(L, -2, 1); + lua_pushstring(L, output.c_str()); + lua_rawseti(L, -2, 2); + lua_rawseti(L, -2, i); + } + lua_pushvalue(L, -1); + return 1; +} + static const luaL_Reg dfhack_internal_funcs[] = { { "getAddress", internal_getAddress }, { "setAddress", internal_setAddress }, @@ -2240,6 +2265,7 @@ static const luaL_Reg dfhack_internal_funcs[] = { { "memscan", internal_memscan }, { "diffscan", internal_diffscan }, { "getDir", internal_getDir }, + { "runCommand", internal_runCommand }, { NULL, NULL } }; From 91a93a00d2222039397d910aa346beddb9b2f5e6 Mon Sep 17 00:00:00 2001 From: lethosor Date: Sat, 7 Jun 2014 20:31:14 -0400 Subject: [PATCH 153/676] Add dfhack.run_command (Lua) Simplified version of runCommand --- library/lua/dfhack.lua | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/library/lua/dfhack.lua b/library/lua/dfhack.lua index 77c62311a..10af8b43e 100644 --- a/library/lua/dfhack.lua +++ b/library/lua/dfhack.lua @@ -256,7 +256,8 @@ function dfhack.interpreter(prompt,hfile,env) print("Shortcuts:\n".. " '= foo' => '_1,_2,... = foo'\n".. " '! foo' => 'print(foo)'\n".. - "Both save the first result as '_'.") + " '~ foo' => 'printall(foo)'\n".. + "All of these save the first result as '_'.") print_banner = false end @@ -357,6 +358,16 @@ function dfhack.run_script(name,...) return f(...) end +function dfhack.run_command(command, ...) + command = command .. ' ' .. table.concat({...}, ' ') + fragments = internal.runCommand(command) + output = "" + for i, f in pairs(fragments) do + output = output .. f[2] + end + return output +end + -- Per-save init file function dfhack.getSavePath() From 055afafedca4c3294569e2e7cc6343777218b1a7 Mon Sep 17 00:00:00 2001 From: lethosor Date: Mon, 9 Jun 2014 16:50:01 -0400 Subject: [PATCH 154/676] command-prompt: Basic line editing Left/right arrows, Ctrl-A, Ctrl-E --- plugins/command-prompt.cpp | 56 ++++++++++++++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 6 deletions(-) diff --git a/plugins/command-prompt.cpp b/plugins/command-prompt.cpp index 3cdd83ab3..140cc6f4d 100644 --- a/plugins/command-prompt.cpp +++ b/plugins/command-prompt.cpp @@ -17,6 +17,7 @@ #include "df/ui.h" #include "df/graphic.h" #include "df/enabler.h" + using namespace DFHack; using namespace df::enums; @@ -50,6 +51,7 @@ public: { show_fps=df::global::gps->display_frames; df::global::gps->display_frames=0; + cursor_pos = 0; } ~viewscreen_commandpromptst() { @@ -67,8 +69,10 @@ public: } protected: std::list > responses; + int cursor_pos; bool is_response; bool show_fps; + int frame; void submit(); std::string entry; }; @@ -82,6 +86,9 @@ void prompt_ostream::flush_proxy() } void viewscreen_commandpromptst::render() { + ++frame; + if (frame >= df::global::enabler->gfps) + frame = 0; if (Screen::isDismissed(this)) return; @@ -103,12 +110,22 @@ void viewscreen_commandpromptst::render() { Screen::fillRect(Screen::Pen(' ', 7, 0),0,0,dim.x,0); Screen::paintString(Screen::Pen(' ', 7, 0), 0, 0,"[DFHack]#"); - if(entry.size()gfps / 2) ? "_" : " "; + if(cursor_pos < (dim.x - 10)) + { Screen::paintString(Screen::Pen(' ', 7, 0), 10,0 , entry); + if (entry.size() > dim.x - 10) + Screen::paintTile(Screen::Pen('\032', 7, 0), dim.x - 1, 0); + if (cursor != " ") + Screen::paintString(Screen::Pen(' ', 10, 0), 10 + cursor_pos, 0, cursor); + } else { - Screen::paintTile(Screen::Pen('>', 7, 0), 9, 0); - Screen::paintString(Screen::Pen(' ', 7, 0), 10, 0, entry.substr(entry.size()-dim.x)); + size_t start = cursor_pos - dim.x + 10 + 1; + Screen::paintTile(Screen::Pen('\033', 7, 0), 9, 0); + Screen::paintString(Screen::Pen(' ', 7, 0), 10, 0, entry.substr(start)); + if (cursor != " ") + Screen::paintString(Screen::Pen(' ', 10, 0), dim.x - 1, 0, cursor); } } } @@ -158,14 +175,41 @@ void viewscreen_commandpromptst::feed(std::set *events) auto key = *it; if (key==interface_key::STRING_A000) //delete? { - if(entry.size()) - entry.resize(entry.size()-1); + if(entry.size() && cursor_pos > 0) + { + entry.erase(cursor_pos - 1, 1); + cursor_pos--; + } + if(cursor_pos > entry.size()) + cursor_pos = entry.size(); continue; } if (key >= interface_key::STRING_A000 && key <= interface_key::STRING_A255) { - entry.push_back(char(key - interface_key::STRING_A000)); + entry.insert(cursor_pos, 1, char(key - interface_key::STRING_A000)); + cursor_pos++; + } + // Prevent number keys from moving cursor + else if(events->count(interface_key::CURSOR_RIGHT)) + { + cursor_pos++; + if (cursor_pos > entry.size()) cursor_pos = entry.size(); + break; + } + else if(events->count(interface_key::CURSOR_LEFT)) + { + cursor_pos--; + if (cursor_pos < 0) cursor_pos = 0; + break; + } + else if(events->count(interface_key::CUSTOM_CTRL_A)) + { + cursor_pos = 0; + } + else if(events->count(interface_key::CUSTOM_CTRL_E)) + { + cursor_pos = entry.size(); } } } From aafcd6c43aff79e5939960f7f0d8a0ca47b7796b Mon Sep 17 00:00:00 2001 From: lethosor Date: Mon, 9 Jun 2014 17:00:26 -0400 Subject: [PATCH 155/676] Fix 4/6 behavior --- plugins/command-prompt.cpp | 41 +++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/plugins/command-prompt.cpp b/plugins/command-prompt.cpp index 140cc6f4d..c4bbd615d 100644 --- a/plugins/command-prompt.cpp +++ b/plugins/command-prompt.cpp @@ -189,28 +189,27 @@ void viewscreen_commandpromptst::feed(std::set *events) { entry.insert(cursor_pos, 1, char(key - interface_key::STRING_A000)); cursor_pos++; + return; } - // Prevent number keys from moving cursor - else if(events->count(interface_key::CURSOR_RIGHT)) - { - cursor_pos++; - if (cursor_pos > entry.size()) cursor_pos = entry.size(); - break; - } - else if(events->count(interface_key::CURSOR_LEFT)) - { - cursor_pos--; - if (cursor_pos < 0) cursor_pos = 0; - break; - } - else if(events->count(interface_key::CUSTOM_CTRL_A)) - { - cursor_pos = 0; - } - else if(events->count(interface_key::CUSTOM_CTRL_E)) - { - cursor_pos = entry.size(); - } + } + // Prevent number keys from moving cursor + if(events->count(interface_key::CURSOR_RIGHT)) + { + cursor_pos++; + if (cursor_pos > entry.size()) cursor_pos = entry.size(); + } + else if(events->count(interface_key::CURSOR_LEFT)) + { + cursor_pos--; + if (cursor_pos < 0) cursor_pos = 0; + } + else if(events->count(interface_key::CUSTOM_CTRL_A)) + { + cursor_pos = 0; + } + else if(events->count(interface_key::CUSTOM_CTRL_E)) + { + cursor_pos = entry.size(); } } DFHACK_PLUGIN("command-prompt"); From cc07a373f3b48918060da2800e83d4f37eeb34a5 Mon Sep 17 00:00:00 2001 From: lethosor Date: Mon, 9 Jun 2014 19:38:21 -0400 Subject: [PATCH 156/676] Command-prompt history Creates duplicate entries occasionally Also disabled movies --- plugins/command-prompt.cpp | 45 +++++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/plugins/command-prompt.cpp b/plugins/command-prompt.cpp index c4bbd615d..345eee603 100644 --- a/plugins/command-prompt.cpp +++ b/plugins/command-prompt.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include "df/interface_key.h" #include "df/ui.h" @@ -25,6 +26,8 @@ using df::global::ui; using df::global::gps; using df::global::enabler; +std::vector command_history; + class viewscreen_commandpromptst; class prompt_ostream:public buffered_color_ostream { @@ -47,11 +50,15 @@ public: void help() { } std::string getFocusString() { return "commandprompt"; } + int8_t movies_okay() { return 0; } viewscreen_commandpromptst(std::string entry):is_response(false),entry(entry) { show_fps=df::global::gps->display_frames; df::global::gps->display_frames=0; cursor_pos = 0; + frame = 0; + history_idx = command_history.size(); + command_history.push_back(""); } ~viewscreen_commandpromptst() { @@ -70,6 +77,7 @@ public: protected: std::list > responses; int cursor_pos; + int history_idx; bool is_response; bool show_fps; int frame; @@ -140,6 +148,8 @@ void viewscreen_commandpromptst::submit() //color_ostream_proxy out(Core::getInstance().getConsole()); prompt_ostream out(this); Core::getInstance().runCommand(out, entry); + command_history.pop_back(); + command_history.push_back(entry); if(out.empty() && responses.empty()) Screen::dismiss(this); else @@ -149,7 +159,7 @@ void viewscreen_commandpromptst::submit() } void viewscreen_commandpromptst::feed(std::set *events) { - + int old_pos = cursor_pos; bool leave_all = events->count(interface_key::LEAVESCREEN_ALL); if (leave_all || events->count(interface_key::LEAVESCREEN)) { @@ -196,12 +206,14 @@ void viewscreen_commandpromptst::feed(std::set *events) if(events->count(interface_key::CURSOR_RIGHT)) { cursor_pos++; - if (cursor_pos > entry.size()) cursor_pos = entry.size(); + if (cursor_pos > entry.size()) + cursor_pos = entry.size(); } else if(events->count(interface_key::CURSOR_LEFT)) { cursor_pos--; - if (cursor_pos < 0) cursor_pos = 0; + if (cursor_pos < 0) + cursor_pos = 0; } else if(events->count(interface_key::CUSTOM_CTRL_A)) { @@ -211,6 +223,32 @@ void viewscreen_commandpromptst::feed(std::set *events) { cursor_pos = entry.size(); } + else if(events->count(interface_key::CURSOR_UP)) + { + if (command_history.back() == "") + { + command_history.pop_back(); + command_history.push_back(entry); + } + history_idx--; + if (history_idx < 0) + history_idx = 0; + entry = command_history[history_idx]; + cursor_pos = entry.size(); + } + else if(events->count(interface_key::CURSOR_DOWN)) + { + if (history_idx < command_history.size() - 1) + { + history_idx++; + if (history_idx >= command_history.size()) + history_idx = command_history.size() - 1; + entry = command_history[history_idx]; + cursor_pos = entry.size(); + } + } + if (old_pos != cursor_pos) + frame = 0; } DFHACK_PLUGIN("command-prompt"); command_result show_prompt(color_ostream &out, std::vector & parameters) @@ -235,6 +273,7 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector Date: Mon, 9 Jun 2014 21:51:52 -0400 Subject: [PATCH 157/676] Util method (in the Units class) to get a unit squad name (the translated name, or alias if set) --- library/include/modules/Units.h | 2 ++ library/modules/Units.cpp | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/library/include/modules/Units.h b/library/include/modules/Units.h index b51183d6b..5380d82aa 100644 --- a/library/include/modules/Units.h +++ b/library/include/modules/Units.h @@ -256,6 +256,8 @@ DFHACK_EXPORT std::string getCasteProfessionName(int race, int caste, df::profes DFHACK_EXPORT int8_t getProfessionColor(df::unit *unit, bool ignore_noble = false); DFHACK_EXPORT int8_t getCasteProfessionColor(int race, int caste, df::profession pid); + +DFHACK_EXPORT std::string getSquadName(df::unit *unit); } } #endif diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp index 20c536cd1..588292301 100644 --- a/library/modules/Units.cpp +++ b/library/modules/Units.cpp @@ -69,6 +69,7 @@ using namespace std; #include "df/unit_misc_trait.h" #include "df/unit_skill.h" #include "df/curse_attr_change.h" +#include "df/squad.h" using namespace DFHack; using namespace df::enums; @@ -1552,3 +1553,13 @@ int8_t DFHack::Units::getCasteProfessionColor(int race, int casteid, df::profess // default to dwarven peasant color return 3; } + +std::string DFHack::Units::getSquadName(df::unit *unit) +{ + if (unit->military.squad_id == -1) + return ""; + df::squad *squad = world->squads.all[unit->military.squad_id]; + if (squad->alias.size() > 0) + return squad->alias; + return Translation::TranslateName(&squad->name, true); +} From 044f958551c592047bb9f75e4dba47a44919b877 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-David=20Be=CC=81langer?= Date: Mon, 9 Jun 2014 21:58:16 -0400 Subject: [PATCH 158/676] Squad column and squad info in Dwarf Manipulator (manipulator plugin) --- plugins/manipulator.cpp | 86 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 81 insertions(+), 5 deletions(-) diff --git a/plugins/manipulator.cpp b/plugins/manipulator.cpp index 57f134447..ecba97b6d 100644 --- a/plugins/manipulator.cpp +++ b/plugins/manipulator.cpp @@ -248,11 +248,14 @@ struct UnitInfo string profession; int8_t color; int active_index; + string squad_effective_name; + string squad_info; }; enum altsort_mode { ALTSORT_NAME, ALTSORT_PROFESSION, + ALTSORT_SQUAD, ALTSORT_HAPPINESS, ALTSORT_ARRIVAL, ALTSORT_MAX @@ -278,6 +281,22 @@ bool sortByProfession (const UnitInfo *d1, const UnitInfo *d2) return (d1->profession < d2->profession); } +bool sortBySquad (const UnitInfo *d1, const UnitInfo *d2) +{ + bool gt = false; + if (d1->unit->military.squad_id == -1 && d2->unit->military.squad_id == -1) + gt = d1->name > d2->name; + else if (d1->unit->military.squad_id == -1) + gt = true; + else if (d2->unit->military.squad_id == -1) + gt = false; + else if (d1->unit->military.squad_id != d2->unit->military.squad_id) + gt = d1->squad_effective_name > d2->squad_effective_name; + else + gt = d1->unit->military.squad_position > d2->unit->military.squad_position; + return descending ? gt : !gt; +} + bool sortByHappiness (const UnitInfo *d1, const UnitInfo *d2) { if (descending) @@ -337,6 +356,7 @@ enum display_columns { DISP_COLUMN_HAPPINESS, DISP_COLUMN_NAME, DISP_COLUMN_PROFESSION, + DISP_COLUMN_SQUAD, DISP_COLUMN_LABORS, DISP_COLUMN_MAX, }; @@ -450,6 +470,13 @@ void viewscreen_unitlaborsst::refreshNames() cur->name = Translation::TranslateName(Units::getVisibleName(unit), false); cur->transname = Translation::TranslateName(Units::getVisibleName(unit), true); cur->profession = Units::getProfessionName(unit); + if (unit->military.squad_id > -1) { + cur->squad_effective_name = Units::getSquadName(unit); + cur->squad_info = stl_sprintf("%i", unit->military.squad_position + 1) + "." + cur->squad_effective_name; + } else { + cur->squad_effective_name = ""; + cur->squad_info = ""; + } } calcSize(); } @@ -458,7 +485,7 @@ void viewscreen_unitlaborsst::calcSize() { auto dim = Screen::getWindowSize(); - num_rows = dim.y - 10; + num_rows = dim.y - 11; if (num_rows > units.size()) num_rows = units.size(); @@ -469,11 +496,13 @@ void viewscreen_unitlaborsst::calcSize() int col_maxwidth[DISP_COLUMN_MAX]; col_minwidth[DISP_COLUMN_HAPPINESS] = 4; col_maxwidth[DISP_COLUMN_HAPPINESS] = 4; - col_minwidth[DISP_COLUMN_NAME] = 16; - col_maxwidth[DISP_COLUMN_NAME] = 16; // adjusted in the loop below + col_minwidth[DISP_COLUMN_NAME] = 12; + col_maxwidth[DISP_COLUMN_NAME] = 12; // adjusted in the loop below col_minwidth[DISP_COLUMN_PROFESSION] = 10; col_maxwidth[DISP_COLUMN_PROFESSION] = 10; // adjusted in the loop below - col_minwidth[DISP_COLUMN_LABORS] = num_columns*3/5; // 60% + col_minwidth[DISP_COLUMN_SQUAD] = 10; + col_maxwidth[DISP_COLUMN_SQUAD] = 10; // adjusted in the loop below + col_minwidth[DISP_COLUMN_LABORS] = num_columns / 2; // 50% col_maxwidth[DISP_COLUMN_LABORS] = NUM_COLUMNS; // get max_name/max_prof from strings length @@ -483,6 +512,8 @@ void viewscreen_unitlaborsst::calcSize() col_maxwidth[DISP_COLUMN_NAME] = units[i]->name.size(); if (col_maxwidth[DISP_COLUMN_PROFESSION] < units[i]->profession.size()) col_maxwidth[DISP_COLUMN_PROFESSION] = units[i]->profession.size(); + if (col_maxwidth[DISP_COLUMN_SQUAD] < units[i]->squad_info.size()) + col_maxwidth[DISP_COLUMN_SQUAD] = units[i]->squad_info.size(); } // check how much room we have @@ -764,6 +795,17 @@ void viewscreen_unitlaborsst::feed(set *events) } break; + case DISP_COLUMN_SQUAD: + if (enabler->mouse_lbut || enabler->mouse_rbut) + { + input_sort = ALTSORT_SQUAD; + if (enabler->mouse_lbut) + events->insert(interface_key::SECONDSCROLL_PAGEDOWN); + if (enabler->mouse_rbut) + events->insert(interface_key::SECONDSCROLL_PAGEUP); + } + break; + case DISP_COLUMN_LABORS: if (enabler->mouse_lbut || enabler->mouse_rbut) { @@ -784,6 +826,7 @@ void viewscreen_unitlaborsst::feed(set *events) case DISP_COLUMN_NAME: case DISP_COLUMN_PROFESSION: + case DISP_COLUMN_SQUAD: // left-click to view, right-click to zoom if (enabler->mouse_lbut) { @@ -882,6 +925,9 @@ void viewscreen_unitlaborsst::feed(set *events) case ALTSORT_PROFESSION: std::sort(units.begin(), units.end(), sortByProfession); break; + case ALTSORT_SQUAD: + std::sort(units.begin(), units.end(), sortBySquad); + break; case ALTSORT_HAPPINESS: std::sort(units.begin(), units.end(), sortByHappiness); break; @@ -898,6 +944,9 @@ void viewscreen_unitlaborsst::feed(set *events) altsort = ALTSORT_PROFESSION; break; case ALTSORT_PROFESSION: + altsort = ALTSORT_SQUAD; + break; + case ALTSORT_SQUAD: altsort = ALTSORT_HAPPINESS; break; case ALTSORT_HAPPINESS: @@ -950,6 +999,7 @@ void viewscreen_unitlaborsst::render() Screen::paintString(Screen::Pen(' ', 7, 0), col_offsets[DISP_COLUMN_HAPPINESS], 2, "Hap."); Screen::paintString(Screen::Pen(' ', 7, 0), col_offsets[DISP_COLUMN_NAME], 2, "Name"); Screen::paintString(Screen::Pen(' ', 7, 0), col_offsets[DISP_COLUMN_PROFESSION], 2, "Profession"); + Screen::paintString(Screen::Pen(' ', 7, 0), col_offsets[DISP_COLUMN_SQUAD], 2, "Squad"); for (int col = 0; col < col_widths[DISP_COLUMN_LABORS]; col++) { @@ -1025,6 +1075,11 @@ void viewscreen_unitlaborsst::render() bg = 0; Screen::paintString(Screen::Pen(' ', fg, bg), col_offsets[DISP_COLUMN_PROFESSION], 4 + row, profession); + + fg = 15; + string squad = cur->squad_info; + squad.resize(col_widths[DISP_COLUMN_SQUAD]); + Screen::paintString(Screen::Pen(' ', fg, bg), col_offsets[DISP_COLUMN_SQUAD], 4 + row, squad); // Print unit's skills and labor assignments for (int col = 0; col < col_widths[DISP_COLUMN_LABORS]; col++) @@ -1114,7 +1169,25 @@ void viewscreen_unitlaborsst::render() else str = stl_sprintf("Not %s (0/500)", ENUM_ATTR_STR(job_skill, caption_noun, columns[sel_column].skill)); } - Screen::paintString(Screen::Pen(' ', 9, 0), x, 3 + num_rows + 2, str); + Screen::paintString(Screen::Pen(' ', 9, 0), x, y, str); + + if (cur->unit->military.squad_id > -1) { + + x = 1; + y++; + + string squadLabel = "Squad: "; + Screen::paintString(white_pen, x, y, squadLabel); + x += squadLabel.size(); + + string squad = cur->squad_effective_name; + Screen::paintString(Screen::Pen(' ', 11, 0), x, y, squad); + x += squad.size(); + + string pos = stl_sprintf(" Pos %i", cur->unit->military.squad_position + 1); + Screen::paintString(Screen::Pen(' ', 9, 0), x, y, pos); + + } canToggle = (cur->allowEdit) && (columns[sel_column].labor != unit_labor::NONE); } @@ -1153,6 +1226,9 @@ void viewscreen_unitlaborsst::render() case ALTSORT_PROFESSION: OutputString(15, x, y, "Profession"); break; + case ALTSORT_SQUAD: + OutputString(15, x, y, "Squad"); + break; case ALTSORT_HAPPINESS: OutputString(15, x, y, "Happiness"); break; From 8ba1307944b01ae15a824617d27248980d5dae57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-David=20Be=CC=81langer?= Date: Mon, 9 Jun 2014 22:00:23 -0400 Subject: [PATCH 159/676] Update documentation related to Dwarf Manipulator's squad column --- Readme.html | 18 +++++++++--------- Readme.rst | 16 ++++++++-------- images/manipulator.png | Bin 7724 -> 30435 bytes images/manipulator2.png | Bin 7558 -> 32738 bytes 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Readme.html b/Readme.html index f03522734..d35f2d890 100644 --- a/Readme.html +++ b/Readme.html @@ -3,7 +3,7 @@ - + DFHack Readme