2012-02-21 01:44:10 -07:00
// Make the camera follow the selected unit
# include "Core.h"
# include <Console.h>
# include <Export.h>
# include <PluginManager.h>
# include "DFHack.h"
# include "DataDefs.h"
# include "modules/Gui.h"
# include "modules/World.h"
# include "modules/Maps.h"
# include <df/unit.h>
# include <df/creature_raw.h>
using namespace DFHack ;
using namespace df : : enums ;
command_result follow ( Core * c , std : : vector < std : : string > & parameters ) ;
df : : unit * followedUnit ;
int32_t prevX , prevY , prevZ ;
2012-02-22 00:30:44 -07:00
uint8_t prevMenuWidth ;
2012-02-21 01:44:10 -07:00
2012-02-21 10:19:17 -07:00
DFHACK_PLUGIN ( " follow " ) ;
2012-02-21 01:44:10 -07:00
DFhackCExport command_result plugin_init ( Core * c , std : : vector < PluginCommand > & commands )
{
commands . push_back ( PluginCommand (
" follow " , " Follow the selected unit until camera control is released " ,
2012-02-22 10:43:14 -07:00
follow , view_unit_hotkey ,
2012-02-21 01:44:10 -07:00
" Select a unit and run this plugin to make the camera follow it. Moving the camera yourself deactivates the plugin. \n "
) ) ;
followedUnit = 0 ;
prevX = prevY = prevZ = - 1 ;
2012-02-22 00:30:44 -07:00
prevMenuWidth = 0 ;
2012-02-21 01:44:10 -07:00
return CR_OK ;
}
DFhackCExport command_result plugin_shutdown ( Core * c )
{
return CR_OK ;
}
DFhackCExport command_result plugin_onstatechange ( Core * c , state_change_event event )
{
switch ( event ) {
case SC_GAME_LOADED :
2012-02-22 00:30:44 -07:00
case SC_GAME_UNLOADED : //Make sure our plugin's variables are clean
2012-02-21 01:44:10 -07:00
followedUnit = 0 ;
prevX = prevY = prevZ = - 1 ;
2012-02-22 00:30:44 -07:00
prevMenuWidth = 0 ;
2012-02-21 01:44:10 -07:00
break ;
default :
break ;
}
return CR_OK ;
}
DFhackCExport command_result plugin_onupdate ( Core * c )
{
2012-02-22 00:30:44 -07:00
if ( ! followedUnit ) return CR_OK ; //Don't do anything if we're not following a unit
2012-02-21 01:44:10 -07:00
DFHack : : World * world = c - > getWorld ( ) ;
2012-02-22 00:30:44 -07:00
if ( world - > ReadPauseState ( ) & & prevX = = - 1 ) return CR_OK ; //Wait until the game is unpaused after first running "follow" to begin following
2012-02-21 01:44:10 -07:00
df : : coord & unitPos = followedUnit - > pos ;
2012-02-22 00:30:44 -07:00
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 ;
2012-02-21 01:44:10 -07:00
gui - > getViewCoords ( x , y , z ) ;
gui - > getWindowSize ( w , h ) ;
2012-02-22 00:30:44 -07:00
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
2012-02-22 10:43:14 -07:00
2012-02-22 00:30:44 -07:00
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?
2012-02-21 01:44:10 -07:00
{
prevX = x ;
prevY = y ;
prevZ = z ;
}
2012-02-22 00:30:44 -07:00
else if ( ( prevX ! = x | | prevY ! = y | | prevZ ! = z ) & & prevMenuWidth = = menu_width ) //User has manually moved the window, stop following the unit
2012-02-21 01:44:10 -07:00
{
followedUnit = 0 ;
prevX = prevY = prevZ = - 1 ;
2012-02-22 00:30:44 -07:00
prevMenuWidth = 0 ;
2012-02-21 01:44:10 -07:00
c - > con . print ( " No longer following anything. \n " ) ;
return CR_OK ;
}
2012-02-22 10:43:14 -07:00
2012-02-21 01:44:10 -07:00
uint32_t x_max , y_max , z_max ;
2012-02-22 00:30:44 -07:00
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
2012-02-21 01:44:10 -07:00
x_max * = 16 ;
y_max * = 16 ;
2012-02-22 00:30:44 -07:00
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!
2012-02-21 01:44:10 -07:00
2012-02-22 00:30:44 -07:00
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
2012-02-22 10:43:14 -07:00
2012-02-22 00:30:44 -07:00
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 ;
2012-02-21 01:44:10 -07:00
return CR_OK ;
}
command_result follow ( Core * c , std : : vector < std : : string > & parameters )
2012-02-22 10:43:14 -07:00
{
// HOTKEY COMMAND: CORE ALREADY SUSPENDED
2012-02-21 01:44:10 -07:00
if ( ! parameters . empty ( ) )
return CR_WRONG_USAGE ;
2012-02-22 00:30:44 -07:00
if ( followedUnit )
{
c - > con . print ( " No longer following previously selected unit. \n " ) ;
followedUnit = 0 ;
}
2012-02-21 01:44:10 -07:00
followedUnit = getSelectedUnit ( c ) ;
if ( followedUnit )
{
2012-02-22 10:43:14 -07:00
std : : ostringstream ss ;
ss < < " Unpause to begin following " < < df : : global : : world - > raws . creatures . all [ followedUnit - > race ] - > name [ 0 ] ;
if ( followedUnit - > name . has_name ) ss < < " " < < followedUnit - > name . first_name ;
ss < < " . Simply manually move the view to break the following. \n " ;
c - > con . print ( ss . str ( ) . c_str ( ) ) ;
2012-02-21 01:44:10 -07:00
}
else followedUnit = 0 ;
return CR_OK ;
}