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.

develop
Mike Stewart 2012-02-21 23:30:44 -08:00
parent f8d0b83b0a
commit 14b471a459
3 changed files with 94 additions and 19 deletions

@ -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

@ -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;

@ -21,6 +21,7 @@ command_result follow (Core * c, std::vector <std::string> & 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 <PluginCommand>
));
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 <std::string> & 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 <std::string> & 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;