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 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYVASDYW?*38`K->tz`!skz$e6&gM&kqgF}FW zgO`Itii1OrgCkr`EkaGLP)#jQP0dVA%~DOxO-;>9P0dG5El>>vV%5~r)YLN6)HK!9 z6gW6EG&HKz)ST4R64lgf)zoy=)Kob*4As>7)YMWL85`BqW~ixcR#W@`|9>DOBO{{} za{~w*IB+10k+EEgk@^4sh6d*U|C!$!GnO+lGB+^)XJ&rO$jB_kSbxa)AOnNOc~2L| zkczmsbJ^=3Tb+N(8nkI{-157>zWkeZ_wT>^xttCRM*@y+dvl_*K*>wJWRms%*FX6^ zKDMm-x9-<{;nQ`W?u%W&@4u$L#^LYZ!!PXLYbhW25_*;Y#D32=Z@qfgN=)0~=9*K)L{BDb7!KRm&1iU>I!$3F-pGpz3Y9s zg~IP?-HX_7n<$)d=j)i-o#|ktQ{|qZs_^mz<4d9}Rzx@HZ= zXJ@9Jy6){|ZgeWDN_L4u>zfxow|^7(CVmMu&N{z`FX_|Nl6cSW zQ%l%WqWC(PnN~%8eY)t$s?ZCI7j&(9#j&%et8Z;+@2k`A*RUL5wJ7gh73MgBKlqQf zHshsTtAffKWG1ZSYLbaLaWb}^m9tjM*ipU6|s`16o|I(vt8b^4pT zO-tnMQ#!tWXh=_L){ta+@=@^o;&oHPC#=@CDQ9pLWa_pmXI1!=w{hM)2>}lN?+c0r zm{?RJ&M+Tg+^NK9a#r5M_c2S%w9Vyi_jC_6vj{x+_weDve~n(89|fK4<@=WurmtCZ z&X<3qfZ!YsBgXv?o-)k=Tgw&Wcq{VPU*^k#%%234PafCQ>fqlfrS~L;dB+5u_jevz z2lH%fI_t>rTtVS0v!&(_3ps)LOPD?Lzp6cUeXwcHUgK|hCu?P-zffG}&3IyC zkpl}aL)+rK0=`e(eNEHL-@Tc;v7CE~z%ge1w>-A7=UF?}H=EcT$=cU34J6m-v@M9k z#p8kEwUbY^U&h$#8Du@$RDO(QVK+yxkf5Mk(p>L-lkVwob*z3VIJK`xb?@SeCY@=o zUNfrU)tOXB6%j=rf*}Wz7@P+?YVuTo*u&oo{xLy?v68KxaG~% z;=gr=K-0X8E;-Yzq8leeZ*G$oeyz~i+~&;p{_P!8-o4xvf$hJ(^(tg;T;aH(VKU>R zkCV&N8)O7n9=$qssEe0xlUOucLA&dm`>e%09Q~VHCS5pIewnj1PpRAVt(@$H5)FlE zM{m8|q!YIH&a92rJ*tZYSc>m^n4EgV#B`v>p}l`W&5`6EA8Mw(@ecO6B*=d<>FTVb zznWYh#Ll(uoSJa)giHaepN?$ZBP^wZg73DcdqrEq7P>d z%P{b6{BXe0Pn~JI73~g$xf~EVWc?=V zk@m|wGx%pU>!^ia77%^8sPAWk-wj(?sQ{jIh9iuIvqLAOy{z46)7Yxo#BF66;>&BR z^&wDe9*+ROwvxhsbrMr|FDV;!l1&e6DE&jj0EZ%u*F?(OD$Kpdg^d z$P~Uz_J%DlcPF zVYy#_(#CkbiBC9n)+uZ%$qQMa+#jM6$+WuEaM|LyZAXGXJQQv5+a@}bcca)T#Y!ga z{%y`{Ll}Y+^&C#KZ~3aM7JszD$S#bpQiA<$8UEXX{&&B8M);Ym)+n{SzK~P73$x?wHAWK)o|bQ09!g zMfjy7=6%Mx93AQ#CRynyFf{mPwVtTC;_t|)7`N*3Y4g79Nz?9$pB9ez8Xvj!>AI#J z4304d=^rFauXfl>E&cHB-JKl1O$lZvzaQ|*nX*$mjk#%#PF9jDD@WhUy_L38O9d1v z#j;O=Qo+*it99mj%rD!ZksJ}_RFZtJlk3sSHI?TzVoKt5{1XB#)4ev87Be(1ymL0? z^M?%w`)}{Qy?b}`mB%299td=Ajwsz*BX(Li;v~Zd1MlMS9(RHC$6BTQ{EDaC=gepp zP?&d0>uv<=&9@)UbeNoe+x?Vt%@UpI5*Jtvw7PyBY{EZIQl1IQlRW`yggD)v^ zjqCTLpID|nU6=A)`yum*>9hYbgiSc$tRph7y*>Pf&D7F6pB-l{1*HU`M%Rkh3s#h< zD?B;C#c|$|>4AZ^U&sVy#*d`}dT)6D?|U2cJE@YXJ9@5adFoY}yB9dP-QE?h za*z?|KCZh*f13J+8ycEwopBQ#-PJEYi<4u~m12EjQOSC7i}j=pEjEp*k;2cK_#Y~F z+?chZX^Gf*u8!~tX%BU#sc&%DdE!xr$hD)&62?rtw|-aWO?@Td5K|v4I{8Zt-=+|= zlfn@J{m<8JQk)j|ocYnpHGvL0Ht6pM*_ah4;BtMwE~nHd)$>&|csrJFDso`E?kIL9 zsgmimUqeJg;zB-!7t^j5Dai1gykYdwL}A`3BZsQZ5fQdxw?UC!7Ct@3mt)G#NpI6O z-&`iVbp4AR`}fy7czk175N&=zH&x95}Q0CkgeT11s!Nw!Pb(!{wJ2lt1v_1Bjzi$GC z;p%NHVzcMPD1{2hByANew|OW3?&A!$lsvoasSPRq?3t6Mol2@?;*Q$tw^2adS;wQ7 zJ$%WEeO6BFpnTH(4ixK8R2O+fh#YijVOE@Yrt{^L1`CDhGmm~^5!O2yeG&5xGzF+_m?AMD;^g6%j2}a;eNnl4?3nk`DK?F)7v>*Gvf<-U zbNhMX_o-7$W2=^s8bALwnH4)% zUq4 zaDkv$NSbn*cfiQOY+{tpyz^J5D9O9V{|owZ;p9E-m%NWR-T?&+*9)ij*9z|VT%FhQ zq-$n(n#_ckQ@^loj+l2Ks8uQPXt6+EHM7v~UtT6Wg8XxR_%?!T5Jmrl!tANWpA@Rq zSM^CHW@;A}I<#qXa6XvxJ85S{-SJ`pO_@lh?ohQ26_p?IR2oG$Yv2CkAboM!aSNYW4EkTIvH!o?6}#TDO)RHlCj`L4v9eix0`1P?)0`6;A>Hlyt+Ge zV@~tHCh=?kz6Tfy}Q`0<^^`lPM&z?iibRu=ie_Ea3 z``>>(m5IBHMQrN=>EBNzwTmWRI~>h)!S&sruh+L#sf2ILjm`R@b?{(qF6+Ux6BT0G z@t*e2_eH!*IX`3BwAz$cdxauyRCu%HFkf`;mUb(NQ5NE1Vc2y-KS-YO$hlt?d_l45 ze{a==6gKKiOW$bI-6~pLK2>1lX_l}CrN*U_I#Kq|?_AYRue@j^w|;$u<%NYD3?fQO zdfVPf{hVl5xN)t5(J!M0o9@u!S=-m0->E;%`gTYs+g59n%bYszB)t+0<#^gGZH}w3 zHDobctyMk3u>aPghBmE^yDtvKoln|3vsuOD+*XjTiPPI7{&MOF*p+;3J|6MI(Co;j zjNj+?Z&H?!nAK66A;4|I#US-=F;`J>ZE^8kzV!lH=aWSD2Ue`pnYR0z*{(Is1y}a^ zB#81qzjMd=NuyYnNkr1aN$-N%YmV9RC*KU8=zQ{xs^g=;$u*C66u;|!>UaHuZdQ=W zh6N`+eEQ7p8>-pdC?%Q9lBh@zm} z6-{sE9@xZmaiObXfdXW}^yKXMLs^uiwGMhOlY{F)yN4pardZubOu8}Zu^+^a^y{J}0^pVmp zzgCCk^EX`9dpRNR^>ktX>-noHCeLD?#Iox9P4Uy^hnWK3f96Pjoh0L(;A%CoC}hLs z35F(ap^`SaITL56Z~UYv_PFp+?Z!J(D>#&<9X-XYCU|qfWYrCu7FcW5C(m~Ne$?puN`{R()7E^Q zR~fH!pO^d9ucxg(4@66hf*g~4)=4BCwJAT!p`hi`p}N|l|8|n%QRkSci<@M7xVR54 z*!NnxOIL2zQKR&h8X5CRN1gU2gWOV8S65kC*Ji~a;yK#;`!_oK1sHw-vNbQ zQ%X+9(vXg1L-~ts5!-zELnr7wIVbmV!i8ODTsJw2Xun^*#6OJxWwrJn$vb_gl4e%_ z@yIL6kL%q%zZm4BZk2|Ju!}!B0)M>lyCf*k*_5C<@qnVR!o{*JHYS@E2D`4faFt=A zUFjdA=arcqQxhZ~C(AZW;lGf1ZpP71cQ-WnGrXC|ouI?z;B@A>f?5>TUiFH38nqTj@^k>D%$Lz(I_fNiOR5Z&U?3RapIb+Us@1zKe0S%|Q=d2j|od0IIJJ^j@C*re_59FKM8rOfNqG-WG7T|+s_%N6e|yEd6ua(d9R zuNBAJ=(BV>q(;P3V+?{xo6F191s%UtaI(H?sl2Jy@DrCCI~q7H9wr# zd@`5UUwaPQ5tot&U!N37$}!A3z~r6K**)v1*RH+2AKz_Tk{spe?HZ zb4`t=%Q2Uq0;dHwm=?U$zEke1x>KPqQ1N)}_D`ja8x79nbePUGjbv7knByGySK(>` z=OW8(h6?ml(v{-+w=7s>5-Mnta)B zFIV5cc=EfFj@gk}AGaLhf5EpxVUohE_jjH$3eW5^kl!?guf?OtkmaGX)`{3KM->aB zSVxw%5=rG9t_(*SHY^Afe^Z^?u~lAv{+=d}A5s?t6au1~GUgRe=iQjKO8%64xxVl^ zdBfP&{)3htM>VzHYTtPwz`eTd%f>@*PY7hs>KA25iqx5~RmW4{Y{@$pg{8_jFMPLg zI;0fAc(N_x>80;eoD71!el2^K^p^cZR^I1%$)ZPC6?z3XN?JK=l+Wjha%8*asI}W~ z^=2La4-+@fYuxH2on)!Nto?GD!mQ_a_Qra$hb|CHVQT8NceyFkQYs+h=)W!C#&@R! zW*Y)5)`o@(v>pb<-0nOpN2fif+8;5VaPaQzY`5ILa%Iz-Z3eyU=GGf8OgYiRvr%RO z!Xe0wL+{P2Z&pVCX*>`r!-^ZcsZaI&a_sXrnowaEBIlN+&b7R|qGw zet67&AUJE8X}Ey$kw*em^Pb;{JmPZN&7bMCOyhyGkKQ#i*?vB%aPsGcgLA%K*cvY& z&?oQDF}r4sO}8k+sb7w1^NQ8C9{Hdl@2bH#Ibn0Lm54yMKd%x~Pus3Z$`6d?gq-uzVW9)Ih}8+nZXjso)(Vyw!Y}+=@*8avaK%fE**dS_U~TCroBJolaD#AYFfDIrqkH+P*s7x?Epn)#W8A0kt(5 zOx?x`ZO%O|XJmPId$h1;DfbC9zAf8)P|B(HJCDG(-0cbR$McH!Czkuir`zu|cJ!aO zF|JU|`(LVe_|3j$+xBjHW?8=VpM9!J(>4AQH!qdr;hjqsx3xzwzWAvqxIQ72!A#oB zaM`|&3tL2043u&;4@L=HJa$D~)a{?uyalIap1jmNFl%!J564Qs29Fz0vZlw_p5M7y zQH(z|chuf$vZX)mV-ba5t-O;Obxal|h%+h32hF=GMF<$s<<#B=G;faP7%?2jy zhPEGhN}opRaNLa5+4|Mz`MEF`pB~nfh9{gltCW7eV%eVX$ZLV!B=&dbS&9T(c3c*@ zIpfJYQDxuUyOC=StYypGdaLrBti!&NbMjB?7EheRyKzHn&_$gWR|76yJy9fkr0Qm- zzvi+TxB1ys94sDt?Xq@teLw5lIyaZF3mr*6i#mBuEYw+$F5~ml>(`bJp0EX`UzcB9 zzl7cH>VZ}MTj#v#lQb-3oS4=nJGH?;O4(WG!L`#2!ZYhbIWq%`R~(#t#_<}rN7($Q z|CfhNi98p}=qz?m^l4)8FWHatgEr^h+ZE8D^}tZ!c0NZM$9Ihf+mak~_*vF6moh%H zEMHjqms969v$stC#sg=+dGly>hiWP*TXpqWHS}o;$=uN?|5|z1K*6z@pW%WMgSyqu z*KO+;_#_;Cb42=kg6PEb+tWl<7`AB^vRXDxRCs=2t-^_e%k6p@nT{5JnldT!+}uP) z+yC>~%MKrUDEc-}l|N+lr>6(ixh9{mRpSs!PqdxTyoNN@Q+Op=@`=2=?^V=f6B*^TMQkbx%c9NsOvOMNwMUEBD z4r|^kh-W0_6;Ftro4V0%we-GjRqk-9qimIBa%T$PO?neQzp*9B=iHA3hYejV4d-9% z_-CpZR9Y02IQz>MUp@ohA774?KKwZ|`!ILp8U;=txmjR+-PXDpK-Zl`i{*h)$cj9Cw1qS9X@r<@k7Ydoq~^< zPs}ZnRrx#aE9^PCG3%& z_f1#Fdu}iHS9mD=D&V+%K54ZevSx)Zw5X?ep3nYnrbF8R+N z#5g`VPK&5Qa^&oM+gUrE`~)FScOTsY0L-1+A=33j0f#+xfHJQZECYoDXf zoAMK-lb1D^CU1XyDUtu>!{?Js)+#IQ;8$HYai5xJ%ts$#_LkP-zE5Jd8=4o-vA%L3 z_U?`O2b}mm_-^8I+O_jo-RpBiAT8MA@ZSYDI_IAKICGdHZM4-5y#$6VgRlHb<IW7ts#=e>rv5$#=9f9Gc77-_;%=y-s1) zZ!Z<*`f+h}?-gEYzrb{+rHi^d~*1adb?$L zs?^1perm}Fi&VfJgS*h+q#o!za)0wU)fH#B{Sa^=l`uAnRLTs$EyfsZNR zjAPh=;{poXR#pUhJt%mt*Sw5FZBpWN{;jLL<}d{Xn#Z+R_0CYa{%L}3-U$Lf0mvKyLf=URVKSm7~i`x?~_kC_L!M0a%+Y~Wgy{c_=M6@I1alu%!$ zrHf&p4LI7FI1ZSgoKP=egY=pzV0#IoSf?XeXh~%LUq+_*cnQvR>SJC@0%O zGX3h^R~H+XXmItGZnzx6*|qzuW*h?_N7e(u3BMxt?47e)gXP2XD~;0&C7DI7zL|5J z@D-PP%pW4l5)r>*d$43LiTkEqK-*RSxZ(WZ>kP&XKt#vV93a!=T;Q$cqN5Hz?)H0 z!8I!5*3Rz8_!qv>nmp6|*8E_7={=XTkEPS?{^Tb!n-p)n3%z?pPIs`7kZ#f13h=jI(gx$VK>hz)j<;U}wG^ov;^ z9=zsnZf?#oJ9Xo{+p=qQ8e1zC2u!eFB+$f}-ZEwVt@E1|uSxxoSfdbn(A`{j%lzVY zx8Ek^S5po549Ilc)9tTx1Fg?dl2<=$kdeYPac%SLHjpQzw5BWnN?u@+t}8q_1KVyyjfx z-4!x5ZDR#rQLv%cmqbH`0MEEr5)L^XrhFZ~m#*`4@U#_s&d_`F`p!v3qwGl*p8q0S z!~DV=TP8-NZ2ZJBEz6BjGL}8^yVouk*(%Xxb9Ud+P)QW&^|CMdKIzJZlwtw9)Xcbv zA_5Jcc{k=v+Lz?8ihJ_wk5gycN!z}fJllGbh*Hr+I}Pivr#)TldlC+_zX`6^w^UfS z(q_Wnr7C7Kgc^!ET5B%6?0)6a&>=FR)Bbbk(*_F#Wp(Byv9tKENc0CZdrW%yp6$ee zOwoo2!P;8e-s0~!Uh^>?*`~+JvEeR5$#utBW!>_eGdL9Le+kb^-=WMPxW<0RY_=zV z&fU$pcaXtzm-5~Ff=Y?IPljL0bzgm%`P1>R{(hbl59U|OHtexe+grrz(RNq)RmO9@ zo8iUlTiEy7oNG^1DW0&~`bqNUyE)sU8yL>3Fx)>k-Dnhab44@4vYEtMFf|6Z4azrwu!DI#?A?crNu1$qwV} zV19Hcch%0D#uu}Dwf=2h_dnA7WlZ#k3k*N<|NhwjyS9eMK}O(w(#*&1PX!drJh$hH zOcm&U_Rpw)g3yHhn{2wb?y%_&wc?qY@R*b1s^HlqK}BBYMGwDD4xjLb|4HPU&(`I? znUkD#4k_nsuQ>x>qT7TBhZ>{FR1Fd;F)EtX$f3LdI4)Y3{xzu=E{}bohkyEJn_o zd6kpS6;F`*^)6{{6`vAIh1l!4Pj1Nz%x*4`Phd5bTVZ_vsF8zBR?*BNhaA2|=hb+A zT{ySvyWTY(Cp#SlzK-=voCR(!vws@9=Ifdz)(Y!tCi$`JFJU%a@%vrU+&xV(t7HVG z`(ARMF5r5kPX5TIi2t8Y>z9NMX)vu75u(3SxZNr>LPx3;3yZbJ%t*Vz_@{(s`n!u;^E*x@_3eEWkx)?0gbf4tq zdA%&vnmToPWn+7^&5o+dlXdEQx9Nx#9cX8D|Mko3cte6U&xGU0uj+}MyYGMRZcoUx zrIqy`-Y~B}>B#LdcM3x$TU$h1!&h0p69;y0&~r#z(t78thvr_pcQcQAO?dFr-D<)F z@t}SBVU|ZWrG&=EzieZi>6ft9sQW3)nR4Uc$e${<3*4`MJ?$NPecR@w;(dzOB;>W_ zlPnoO-?dO+U%Q04Qt8Mug*}Ba#m5w{8OXn8S~5l1;Qm>jxe>Fjzf0Jvt#I_+yXU*Q zw>C9&zDj0%)DSW2JNpc#&5dWpxT@PDv|isXmwoby@#Bil)6W#n@9OAVd&gqk%#8s* zG{Y{*3h-+;eOPEHJg2#Ucc$TyUk4g26!fZVc#95*KGuIbQHsH;M|YLZ)X-g;l3Lsh zk0N!-i|2(!td>$$dC2%=L167n$Bu^^9mGy=j(GRXX6=S^9ElzhA|C6QPw+VL^mCs` zsuVjN9C@7alep~$#s$tMb-#VvB5J-^R`+>4>|;K%IbwOyyJxm*KSXOQv&e2a#~`V% z5Zv&`b87*}#CfNz-ek7U$yRsh5pZZvKf-KkdgM~Nzhx`)Z>Sdz~C+^s=32 z;+m7uF_rmKU!>0Yq@A-rZgCRgU%yE)jQIqc>~iU(MB%;RAT!^k+4uB5{PC?lrz6x@ zZuze6rNPBh-F@wZ6!<(QM}5~5JK^QwJWDRAHpsyy>(QZCj7CSYUcA^~l)SJ&YQpL1 z%#RMent1nUl2gJlr-#cKk1!VAlsTfb$MpEUmy3R>zgN4yBJ33FZODnbN zg*beeqt|nK$1jGRU(|)GK-yIm?$4UXwdwX&QI;QD>t7}IZD&7J{O}pmqsTQ+=cI2u zb5rKY>@F^Lrl8Fmj&q!hY2H2GWK9bz|IQl=fBG(Mi1{~})3*C*q)sst$9XB0sjM5E zV-r~gGp2MOi&^|T{pzDF{;%BPT=o8)Y?*iTlg>2DX!bxug_p_OS7%EmIX>45vpbTN zlW)l#B8I(EL&%!eejwMyb8=s(!zDCM;Xr|LuY4`6NpPEzeX_Q{hD^2QI|w z9C7@xDb?xaUzUc7D@Q_C>r7?UdMCf9J5)zx)8l(_4t5)41lXTMFP>a7g~3T&Tl$&X zmy-;I4lz5v9X$I;ZH?ORSV4t{M^pB0<*E;heSChkU9(O1)0Jyj*v)1)e|g85QmGpp zmhJE$>A|aC!YUg;ayq|G`>cHztejYp%=GKXizSD(E~$GYfKzS2ZGqKS;x|bZm8vGM zeV)FkX*FMb=x*8V=O-Naq%&>#riybDzO0?_*G#myu(UOVL*dHi#r@j9=dIRee8(gl zG4JT7kZanruK&Hc=~d$AcX=^^TpW)Ym25nIl>V{Y)z|P`QQofcX{3&S>PZz@7Vdpd zcBdJtw0Q8|RTTU7;f=G}%QNEsm+T#xPH&EwcWM#O`%t?hTIFYxiX_@)CP46^~f?TLoe)bjrJ0}LK2@}t@DK!49 z4`NSwIC=S|XK!bvCfO`ltI()m`G&W^K~D)3A=h=Ln&w~E3Ff#UXR+sz*<=4-E9VA& zR0J6`>z?WAE0eP}=W3r&+^Nm}#A#E+yi-UmbtnGNp5>uzO`i^OKQODMsmjoXe*vU-b7c%{L;pCTf zkxznO?>x)xSSq3L`oeS8BaD*4i<`A~%Y`_~OyJiGx7FHx@-+LZq@6MX>?#b}4GV*0 zwb+k5E&A(Rni#25KK+N@8sQ1+tC@-n)%{a-pM|MEX=dEHL1)_Tr}tR>6J~l>a41Zl zdbFrK@%AWf(Da?d+pl->{%A$Iz%>pt!{M?HH`AUvJJT=X+gn18O$vukSX zDW@zI_)dgh;!x0=zJ5u)l|zgk`;n)gbf)bNtbBKBbHqEhCp)#zC+(ExbgqE#89v^b zn!fQ)?~+!V@~4qH<;|i;io@p}Eiz~7c)rf2`)OW;oc2eZY1ZsVHb-=ye)?%e=jo>n zJ7VS?E!w{S*?$rCC$F#bPdi$)efhJ0BI2Jve!ZNs(PsYFssq2>s?P3PTq0-c`O7C# z=Y0Ci*}sGDD9TsVJkgo-gPB zdA;tZc~TQ*+8>>J_vo*JK;iPsvwjE1@O3a7>9Q}8Ns?8ZetNN*-gM@mQ%{TDa&~~- j1oCPT#K+jFzx7S=StiGylo~NGFfe$!`njxgN@xNA1&KN_ 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