From 14b471a45933c5eb8b94c834a387ea91b0644a00 Mon Sep 17 00:00:00 2001 From: Mike Stewart Date: Tue, 21 Feb 2012 23:30:44 -0800 Subject: [PATCH] Add ability menu/area map width reading and writing to the Gui module, and use the new information in follow to properly center the screen regardless of menu configuration. Also general fixing and cleanup in follow. --- library/include/modules/Gui.h | 12 ++++++ library/modules/Gui.cpp | 32 ++++++++++++++++ plugins/follow.cpp | 69 +++++++++++++++++++++++++---------- 3 files changed, 94 insertions(+), 19 deletions(-) diff --git a/library/include/modules/Gui.h b/library/include/modules/Gui.h index 2bae88ba8..3f2d41dd4 100644 --- a/library/include/modules/Gui.h +++ b/library/include/modules/Gui.h @@ -143,6 +143,18 @@ namespace DFHack * Window size in tiles */ bool getWindowSize(int32_t & width, int32_t & height); + + /* + *Menu width: + *3:3 - menu and area map closed + *2:3 - menu open single width + *1:3 - menu open double width + *1:2 - menu and area map open + *2:2 - area map open + */ + + bool getMenuWidth(uint8_t & menu_width, uint8_t & area_map_width); + bool setMenuWidth(const uint8_t menu_width, const uint8_t area_map_width); /* * Screen tiles diff --git a/library/modules/Gui.cpp b/library/modules/Gui.cpp index 30ef16d96..af41a57f1 100644 --- a/library/modules/Gui.cpp +++ b/library/modules/Gui.cpp @@ -478,6 +478,7 @@ struct Gui::Private Private() { Started = false; + StartedMenu = false; StartedScreen = false; } @@ -486,6 +487,10 @@ struct Gui::Private int32_t * window_y_offset; int32_t * window_z_offset; + bool StartedMenu; + uint8_t * menu_width_offset; + uint8_t * area_map_width_offset; + bool StartedScreen; void * screen_tiles_ptr_offset; @@ -507,6 +512,11 @@ Gui::Gui() if(d->window_z_offset && d->window_y_offset && d->window_x_offset) d->Started = true; + d->menu_width_offset = (uint8_t *) mem->getAddress("ui_menu_width"); + d->area_map_width_offset = (uint8_t *) mem->getAddress("ui_area_map_width"); + if (d->menu_width_offset && d->area_map_width_offset) + d->StartedMenu = true; + d->screen_tiles_ptr_offset = (void *) mem->getAddress ("screen_tiles_pointer"); if(d->screen_tiles_ptr_offset) d->StartedScreen = true; @@ -616,6 +626,28 @@ bool Gui::getWindowSize (int32_t &width, int32_t &height) return true; } +bool Gui::getMenuWidth(uint8_t &menu_width, uint8_t &area_map_width) +{ + if (!d->StartedMenu) return false; + + Process * p = d->owner; + + p->readByte (d->menu_width_offset, menu_width); + p->readByte (d->area_map_width_offset, area_map_width); + return true; +} + +bool Gui::setMenuWidth(const uint8_t menu_width, const uint8_t area_map_width) +{ + if (!d->StartedMenu) return false; + + Process * p = d->owner; + + p->writeByte (d->menu_width_offset, menu_width); + p->writeByte (d->area_map_width_offset, area_map_width); + return true; +} + bool Gui::getScreenTiles (int32_t width, int32_t height, t_screen screen[]) { if(!d->StartedScreen) return false; diff --git a/plugins/follow.cpp b/plugins/follow.cpp index 2c4237b3f..19536f25c 100644 --- a/plugins/follow.cpp +++ b/plugins/follow.cpp @@ -21,6 +21,7 @@ command_result follow (Core * c, std::vector & parameters); df::unit *followedUnit; int32_t prevX, prevY, prevZ; +uint8_t prevMenuWidth; DFhackCExport const char * plugin_name ( void ) { @@ -38,6 +39,7 @@ DFhackCExport command_result plugin_init ( Core * c, std::vector )); followedUnit = 0; prevX=prevY=prevZ = -1; + prevMenuWidth = 0; return CR_OK; } @@ -47,17 +49,14 @@ DFhackCExport command_result plugin_shutdown ( Core * c ) return CR_OK; } -// Called to notify the plugin about important state changes. -// Invoked with DF suspended, and always before the matching plugin_onupdate. -// More event codes may be added in the future. - DFhackCExport command_result plugin_onstatechange(Core* c, state_change_event event) { switch (event) { case SC_GAME_LOADED: - case SC_GAME_UNLOADED: + case SC_GAME_UNLOADED: //Make sure our plugin's variables are clean followedUnit = 0; prevX=prevY=prevZ = -1; + prevMenuWidth = 0; break; default: break; @@ -68,50 +67,80 @@ DFhackCExport command_result plugin_onstatechange(Core* c, state_change_event ev DFhackCExport command_result plugin_onupdate ( Core * c ) { - if (!followedUnit) return CR_OK; + if (!followedUnit) return CR_OK; //Don't do anything if we're not following a unit + DFHack::World *world =c->getWorld(); - if (world->ReadPauseState() && prevX==-1) return CR_OK; - Gui *gui = c->getGui(); + if (world->ReadPauseState() && prevX==-1) return CR_OK; //Wait until the game is unpaused after first running "follow" to begin following + df::coord &unitPos = followedUnit->pos; - int32_t x,y,z,w,h; + + Gui *gui = c->getGui(); //Get all of the relevant data for determining the size of the map on the window + int32_t x,y,z,w,h,c_x,c_y,c_z; + uint8_t menu_width, area_map_width; gui->getViewCoords(x,y,z); gui->getWindowSize(w,h); - if (prevX==-1) + gui->getMenuWidth(menu_width, area_map_width); + gui->getCursorCoords(c_x,c_y,c_z); + + if (c_z == -3000 && menu_width == 3) menu_width = 2; //Presence of the cursor means that there's actually a width-2 menu open + + h -= 2; //account for vertical borders + + 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 + else w -= 2; //No menu or area map, just account for borders + + if (prevMenuWidth == 0) prevMenuWidth = menu_width; //have we already had a menu width? + + if (prevX==-1) //have we already had previous values for the window location? { prevX = x; prevY = y; prevZ = z; } - else if(prevX != x || prevY != y || prevZ != z) + else if((prevX != x || prevY != y || prevZ != z) && prevMenuWidth == menu_width) //User has manually moved the window, stop following the unit { followedUnit = 0; prevX=prevY=prevZ = -1; + prevMenuWidth = 0; c->con.print("No longer following anything.\n"); return CR_OK; } uint32_t x_max, y_max, z_max; - Simple::Maps::getSize(x_max, y_max, z_max); - + Simple::Maps::getSize(x_max, y_max, z_max); //Get map size in tiles so we can prevent the camera from going off the edge x_max *= 16; y_max *= 16; - prevX = unitPos.x + w/2 >= x_max ? x_max-w+2 : (unitPos.x >= w/2 ? unitPos.x - w/2 : 0); - prevY = unitPos.y + h/2 >= y_max ? y_max-h+2 : (unitPos.y >= h/2 ? unitPos.y - h/2 : 0); - prevZ = unitPos.z; + x = unitPos.x + w/2 >= x_max ? x_max-w : (unitPos.x >= w/2 ? unitPos.x - w/2 : 0); //Calculate a new screen position centered on the selected unit + y = unitPos.y + h/2 >= y_max ? y_max-h : (unitPos.y >= h/2 ? unitPos.y - h/2 : 0); + z = unitPos.z; + + gui->setViewCoords(x, y, z); //Set the new screen position! - gui->setViewCoords(prevX, prevY, prevZ); + if (c_x != 3000 && !world->ReadPauseState()) gui->setCursorCoords(c_x - (prevX-x), c_y - (prevY-y), z); //If, for some reason, the cursor is active and the screen is still moving, move the cursor along with the screen + prevX = x; //Save this round's stuff for next time so we can monitor for changes made by the user + prevY = y; + prevZ = z; + prevMenuWidth = menu_width; + return CR_OK; } command_result follow (Core * c, std::vector & parameters) -{ +{ if (!parameters.empty()) return CR_WRONG_USAGE; CoreSuspender suspend(c); + if (followedUnit) + { + c->con.print("No longer following previously selected unit.\n"); + followedUnit = 0; + } followedUnit = getSelectedUnit(c); if (followedUnit) { @@ -121,7 +150,9 @@ command_result follow (Core * c, std::vector & parameters) { c->con.print(" %s", followedUnit->name.first_name.c_str()); } - c->con.print(".\n"); + c->con.print(". Simply manually move the view to break the following.\n"); + prevX=prevY=prevZ = -1; + prevMenuWidth = 0; } else followedUnit = 0; return CR_OK;