Added plugin 'zone'. Helps with assigning units to pens/pastures and pits.
							parent
							
								
									39787e9cd5
								
							
						
					
					
						commit
						772c6b1cbb
					
				| @ -0,0 +1,895 @@ | |||||||
|  | // Intention: help with activity zone management (auto-pasture animals, auto-pit goblins, ...)
 | ||||||
|  | // 
 | ||||||
|  | // the following things would be nice:
 | ||||||
|  | // - dump info about pastures, pastured animals, count non-pastured tame animals, print gender info
 | ||||||
|  | // - help finding caged dwarves? (maybe even allow to build their cages for fast release)
 | ||||||
|  | // - dump info about caged goblins, animals, ...
 | ||||||
|  | // - full automation of handling mini-pastures over nestboxes:
 | ||||||
|  | //   go through all pens, check if they are empty and placed over a nestbox
 | ||||||
|  | //   find female tame egg-layer who is not assigned to another pen (allowing to grab them from cages would be nice)
 | ||||||
|  | //   maybe check for minimum age? it's not that useful to fill nestboxes with freshly hatched birds
 | ||||||
|  | //   assign to that pasture
 | ||||||
|  | // - allow to mark old animals for slaughter automatically?
 | ||||||
|  | //   that should include a check to ensure that at least one male and one female remain for breeding
 | ||||||
|  | //   allow some fine-tuning like how many males/females should per race should be left alive
 | ||||||
|  | //   and at what age they are being marked for slaughter. don't slaughter pregnant females?
 | ||||||
|  | // - count grass tiles on pastures, move grazers to new pasture if old pasture is empty
 | ||||||
|  | //   move hungry unpastured grazers to pasture with grass
 | ||||||
|  | // 
 | ||||||
|  | // What is working so far:
 | ||||||
|  | // - print detailed info about activity zone and units under cursor (mostly for checking refs and stuff)
 | ||||||
|  | // - mark a zone which is used for future assignment commands
 | ||||||
|  | // - assign single selected creature to a zone
 | ||||||
|  | // - unassign single creature under cursor from current zone
 | ||||||
|  | // - pitting own dwarves :)
 | ||||||
|  | 
 | ||||||
|  | #include <iostream> | ||||||
|  | #include <iomanip> | ||||||
|  | #include <climits> | ||||||
|  | #include <vector> | ||||||
|  | #include <string> | ||||||
|  | #include <sstream> | ||||||
|  | #include <ctime> | ||||||
|  | #include <cstdio> | ||||||
|  | using namespace std; | ||||||
|  | 
 | ||||||
|  | #include "Core.h" | ||||||
|  | #include "Console.h" | ||||||
|  | #include "Export.h" | ||||||
|  | #include "PluginManager.h" | ||||||
|  | #include "modules/Units.h" | ||||||
|  | #include "modules/Maps.h" | ||||||
|  | #include "modules/Gui.h" | ||||||
|  | #include "modules/Materials.h" | ||||||
|  | #include "modules/MapCache.h" | ||||||
|  | #include "modules/Buildings.h" | ||||||
|  | #include "MiscUtils.h" | ||||||
|  | 
 | ||||||
|  | #include <df/ui.h> | ||||||
|  | #include "df/world.h" | ||||||
|  | #include "df/world_raws.h" | ||||||
|  | #include "df/building_def.h" | ||||||
|  | #include "df/building_civzonest.h" | ||||||
|  | #include "df/building_cagest.h" | ||||||
|  | #include "df/building_chainst.h" | ||||||
|  | #include "df/general_ref_building_civzone_assignedst.h" | ||||||
|  | #include <df/creature_raw.h> | ||||||
|  | 
 | ||||||
|  | using std::vector; | ||||||
|  | using std::string; | ||||||
|  | using namespace DFHack; | ||||||
|  | using namespace df::enums; | ||||||
|  | using df::global::world; | ||||||
|  | using df::global::cursor; | ||||||
|  | using df::global::ui; | ||||||
|  | 
 | ||||||
|  | using namespace DFHack::Gui; | ||||||
|  | 
 | ||||||
|  | struct genref : df::general_ref_building_civzone_assignedst {}; | ||||||
|  | 
 | ||||||
|  | command_result df_zone (color_ostream &out, vector <string> & parameters); | ||||||
|  | 
 | ||||||
|  | DFHACK_PLUGIN("zone"); | ||||||
|  | 
 | ||||||
|  | const string zone_help = | ||||||
|  |     "Allows easier management of pens/pastures and pits.\n" | ||||||
|  |     "Options:\n" | ||||||
|  |     "  set          - set zone under cursor as default for future assigns\n" | ||||||
|  |     "  assign       - assign selected creature to a pen or pit\n" | ||||||
|  |     "                 (can be followed by valid zone id which will then be set)\n" | ||||||
|  |     "  unassign     - unassign selected creature from it's zone\n" | ||||||
|  |     "  uinfo        - print info about selected unit\n" | ||||||
|  |     "  zinfo        - print info about zone(s) under cursor\n" | ||||||
|  | 	//"  cinfo        - print info about (built) cage under cursor\n"
 | ||||||
|  |     //"  rinfo        - print info about restraint under cursor\n"
 | ||||||
|  | 	"  all          - in combination with 'zinfo' or 'cinfo': prints all zones/units\n" | ||||||
|  |     "  verbose      - print some more info, mostly useless debug stuff\n" | ||||||
|  |     "Example:\n" | ||||||
|  |     "  (ingame) move cursor to a pen/pasture or pit zone\n" | ||||||
|  |     "  (dfhack) 'zone set' to use this zone for future assignments\n" | ||||||
|  |     "  (dfhack) map 'zone assign' to a hotkey of your choice\n" | ||||||
|  |     "  (ingame) select a unit with 'v', 'k' or in the unit list or inside a cage\n" | ||||||
|  | 	"  (ingame) press hotkey to assign unit to it's new home (or pit)\n" | ||||||
|  |     ; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands) | ||||||
|  | { | ||||||
|  |     commands.push_back(PluginCommand( | ||||||
|  |         "zone", "manage activity zones.", | ||||||
|  |         df_zone, false, | ||||||
|  |         zone_help.c_str() | ||||||
|  |         )); | ||||||
|  |     return CR_OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | DFhackCExport command_result plugin_shutdown ( color_ostream &out ) | ||||||
|  | { | ||||||
|  |     return CR_OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ///////////////
 | ||||||
|  | // Various small tool functions
 | ||||||
|  | // 
 | ||||||
|  | int32_t getCreatureAge(df::unit* unit); | ||||||
|  | bool isTame(df::unit* unit); | ||||||
|  | bool isTrained(df::unit* unit); | ||||||
|  | bool isTrained(df::unit* creature); | ||||||
|  | bool isWar(df::unit* creature); | ||||||
|  | bool isHunter(df::unit* creature); | ||||||
|  | bool isOwnCiv(df::unit* creature); | ||||||
|  | bool isActivityZone(df::building * building); | ||||||
|  | bool isPenPasture(df::building * building); | ||||||
|  | bool isPit(df::building * building); | ||||||
|  | bool isActive(df::building * building); | ||||||
|  | int32_t findBuildingIndexById(int32_t id); | ||||||
|  | int32_t findPenPitAtCursor(); | ||||||
|  | int32_t findCageAtCursor(); | ||||||
|  | int32_t findChainAtCursor(); | ||||||
|  | 
 | ||||||
|  | df::general_ref_building_civzone_assignedst * createCivzoneRef(); | ||||||
|  | bool unassignUnitFromZone(df::unit* unit); | ||||||
|  | command_result assignUnitToZone(color_ostream& out, df::unit* unit, df::building* building, bool verbose); | ||||||
|  | void unitInfo(color_ostream & out, df::unit* creature, bool list_refs); | ||||||
|  | void zoneInfo(color_ostream & out, df::building* building, bool list_refs); | ||||||
|  | void cageInfo(color_ostream & out, df::building* building, bool list_refs); | ||||||
|  | void chainInfo(color_ostream & out, df::building* building, bool list_refs); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | int32_t getUnitAge(df::unit* unit) | ||||||
|  | { | ||||||
|  | 	// If the birthday this year has not yet passed, subtract one year.
 | ||||||
|  | 	// ASSUMPTION: birth_time is on the same scale as cur_year_tick
 | ||||||
|  |     int32_t yearDifference = *df::global::cur_year - unit->relations.birth_year; | ||||||
|  |     if (unit->relations.birth_time >= *df::global::cur_year_tick) | ||||||
|  |         yearDifference--; | ||||||
|  |     return yearDifference; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool isTame(df::unit* creature) | ||||||
|  | { | ||||||
|  |     bool tame = false; | ||||||
|  |     if(creature->flags1.bits.tame) | ||||||
|  |     { | ||||||
|  |         switch (creature->training_level) | ||||||
|  |         { | ||||||
|  |         case df::animal_training_level::Trained: | ||||||
|  |         case df::animal_training_level::WellTrained: | ||||||
|  |         case df::animal_training_level::SkilfullyTrained: | ||||||
|  |         case df::animal_training_level::ExpertlyTrained: | ||||||
|  |         case df::animal_training_level::ExceptionallyTrained: | ||||||
|  |         case df::animal_training_level::MasterfullyTrained: | ||||||
|  |         case df::animal_training_level::Domesticated: | ||||||
|  |             tame=true; | ||||||
|  |             break; | ||||||
|  |         case df::animal_training_level::SemiWild: //??
 | ||||||
|  |         case df::animal_training_level::Unk8:     //??
 | ||||||
|  |         case df::animal_training_level::WildUntamed: | ||||||
|  |         default: | ||||||
|  |             tame=false; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return tame; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // check if trained (might be useful if pasturing war dogs etc)
 | ||||||
|  | bool isTrained(df::unit* creature) | ||||||
|  | { | ||||||
|  |     bool trained = false; | ||||||
|  |     if(creature->flags1.bits.tame) | ||||||
|  |     { | ||||||
|  |         switch (creature->training_level) | ||||||
|  |         { | ||||||
|  |         case df::animal_training_level::Trained: | ||||||
|  |         case df::animal_training_level::WellTrained: | ||||||
|  |         case df::animal_training_level::SkilfullyTrained: | ||||||
|  |         case df::animal_training_level::ExpertlyTrained: | ||||||
|  |         case df::animal_training_level::ExceptionallyTrained: | ||||||
|  |         case df::animal_training_level::MasterfullyTrained: | ||||||
|  |         //case df::animal_training_level::Domesticated:
 | ||||||
|  |             trained = true; | ||||||
|  |             break; | ||||||
|  |         default: | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return trained; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // check for profession "war creature"
 | ||||||
|  | bool isWar(df::unit* creature) | ||||||
|  | { | ||||||
|  |     if(   creature->profession  == df::profession::TRAINED_WAR | ||||||
|  |        || creature->profession2 == df::profession::TRAINED_WAR) | ||||||
|  |         return true; | ||||||
|  |     else | ||||||
|  |         return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // check for profession "hunting creature"
 | ||||||
|  | bool isHunter(df::unit* creature) | ||||||
|  | { | ||||||
|  |     if(   creature->profession  == df::profession::TRAINED_HUNTER | ||||||
|  |        || creature->profession2 == df::profession::TRAINED_HUNTER) | ||||||
|  |         return true; | ||||||
|  |     else | ||||||
|  |         return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // check if creature belongs to the player's civilization
 | ||||||
|  | // (don't try to pasture/slaughter random untame animals)
 | ||||||
|  | bool isOwnCiv(df::unit* creature) | ||||||
|  | { | ||||||
|  |     if(creature->civ_id == ui->civ_id) | ||||||
|  |         return true; | ||||||
|  |     else | ||||||
|  |         return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // dump some unit info
 | ||||||
|  | void unitInfo(color_ostream & out, df::unit* unit, bool list_refs = false) | ||||||
|  | { | ||||||
|  |     out.print("Unit %d", unit->id); //race %d, civ %d,", creature->race, creature->civ_id
 | ||||||
|  |     if(unit->name.has_name) | ||||||
|  |         out << ", name: " << unit->name.first_name << " " << unit->name.nickname; | ||||||
|  |     df::creature_raw *raw = df::global::world->raws.creatures.all[unit->race]; | ||||||
|  |     out << " " << raw->creature_id << " ("; | ||||||
|  |     switch(unit->sex) | ||||||
|  |     { | ||||||
|  |     case -1: | ||||||
|  |         out << "n/a"; | ||||||
|  |         break; | ||||||
|  |     case 0: | ||||||
|  |         out << "female"; | ||||||
|  |         break; | ||||||
|  |     case 1: | ||||||
|  |         out << "male"; | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         out << "INVALID!"; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     out << ")"; | ||||||
|  |     out << ", age: " << getUnitAge(unit); | ||||||
|  |      | ||||||
|  |     if(isTame(unit)) | ||||||
|  |         out << ", tame";     | ||||||
|  |     if(isOwnCiv(unit)) | ||||||
|  |         out << ", owned"; | ||||||
|  |     if(isWar(unit)) | ||||||
|  |         out << ", war"; | ||||||
|  |     if(isHunter(unit)) | ||||||
|  |         out << ", hunter"; | ||||||
|  |      | ||||||
|  |     out << endl; | ||||||
|  | 
 | ||||||
|  |     if(!list_refs) | ||||||
|  |         return; | ||||||
|  | 
 | ||||||
|  |     //out << "number of refs: " << creature->refs.size() << endl;
 | ||||||
|  |     for(size_t r = 0; r<unit->refs.size(); r++) | ||||||
|  |     { | ||||||
|  |         df::general_ref* ref = unit->refs.at(r); | ||||||
|  |         df::general_ref_type refType = ref->getType(); | ||||||
|  |         out << "  ref#" << r <<" refType#" << refType << " "; //endl;
 | ||||||
|  |         switch(refType) | ||||||
|  |         { | ||||||
|  |         case df::general_ref_type::BUILDING_CIVZONE_ASSIGNED: | ||||||
|  |             { | ||||||
|  |             out << "assigned to zone"; | ||||||
|  |             df::building_civzonest * civAss = (df::building_civzonest *) ref->getBuilding(); | ||||||
|  |             out << " #" << civAss->id; | ||||||
|  |             } | ||||||
|  |             break; | ||||||
|  |         case df::general_ref_type::CONTAINED_IN_ITEM: | ||||||
|  |             out << "contained in item"; | ||||||
|  |             break; | ||||||
|  |         case df::general_ref_type::BUILDING_CAGED: | ||||||
|  |             out << "caged"; | ||||||
|  |             break; | ||||||
|  |         case df::general_ref_type::BUILDING_CHAIN: | ||||||
|  |             out << "chained"; | ||||||
|  |             break; | ||||||
|  |         default: | ||||||
|  |             //out << "unhandled reftype";
 | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         out << endl; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool isActivityZone(df::building * building) | ||||||
|  | { | ||||||
|  |     if(    building->getType() == building_type::Civzone | ||||||
|  |         && building->getSubtype() == civzone_type::ActivityZone) | ||||||
|  |         return true; | ||||||
|  |     else | ||||||
|  |         return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool isPenPasture(df::building * building) | ||||||
|  | { | ||||||
|  |     if(!isActivityZone(building)) | ||||||
|  |         return false; | ||||||
|  | 
 | ||||||
|  |     df::building_civzonest * civ = (df::building_civzonest *) building; | ||||||
|  | 
 | ||||||
|  |     if(civ->zone_flags.bits.pen_pasture) | ||||||
|  |         return true; | ||||||
|  |     else | ||||||
|  |         return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool isPit(df::building * building) | ||||||
|  | { | ||||||
|  |     if(!isActivityZone(building)) | ||||||
|  |         return false; | ||||||
|  | 
 | ||||||
|  |     df::building_civzonest * civ = (df::building_civzonest *) building; | ||||||
|  | 
 | ||||||
|  |     if(civ->zone_flags.bits.pit_pond && civ->pit_flags==0) | ||||||
|  |         return true; | ||||||
|  |     else  | ||||||
|  |         return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool isCage(df::building * building) | ||||||
|  | { | ||||||
|  |     if(building->getType() == building_type::Cage) | ||||||
|  |         return true; | ||||||
|  | 	else | ||||||
|  | 		return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool isChain(df::building * building) | ||||||
|  | { | ||||||
|  |     if(building->getType() == building_type::Chain) | ||||||
|  |         return true; | ||||||
|  | 	else | ||||||
|  | 		return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool isActive(df::building * building) | ||||||
|  | { | ||||||
|  |     if(!isActivityZone(building)) | ||||||
|  |         return false; | ||||||
|  | 
 | ||||||
|  |     df::building_civzonest * civ = (df::building_civzonest *) building; | ||||||
|  |     if(civ->zone_flags.bits.active) | ||||||
|  |         return true; | ||||||
|  |     else | ||||||
|  |         return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int32_t findBuildingIndexById(int32_t id) | ||||||
|  | { | ||||||
|  |     for (size_t b = 0; b < world->buildings.all.size(); b++) | ||||||
|  |     { | ||||||
|  |         if(world->buildings.all.at(b)->id == id) | ||||||
|  |             return b; | ||||||
|  |     } | ||||||
|  |     return -1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // returns id of pen/pit at cursor position (-1 if nothing found)
 | ||||||
|  | int32_t findPenPitAtCursor() | ||||||
|  | { | ||||||
|  |     int32_t foundID = -1; | ||||||
|  |      | ||||||
|  |     if(cursor->x == -30000) | ||||||
|  |         return -1; | ||||||
|  | 
 | ||||||
|  |     for (size_t b = 0; b < world->buildings.all.size(); b++) | ||||||
|  |     { | ||||||
|  |         df::building* building = world->buildings.all[b]; | ||||||
|  | 
 | ||||||
|  |         // find zone under cursor
 | ||||||
|  |         if (!(building->x1 <= cursor->x && cursor->x <= building->x2 && | ||||||
|  |             building->y1 <= cursor->y && cursor->y <= building->y2 && | ||||||
|  |             building->z == cursor->z)) | ||||||
|  |             continue; | ||||||
|  | 
 | ||||||
|  |         if(isPenPasture(building) || isPit(building)) | ||||||
|  |         { | ||||||
|  |             foundID = building->id; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return foundID; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // returns id of cage at cursor position (-1 if nothing found)
 | ||||||
|  | int32_t findCageAtCursor() | ||||||
|  | { | ||||||
|  |     int32_t foundID = -1; | ||||||
|  |      | ||||||
|  |     if(cursor->x == -30000) | ||||||
|  |         return -1; | ||||||
|  | 
 | ||||||
|  |     for (size_t b = 0; b < world->buildings.all.size(); b++) | ||||||
|  |     { | ||||||
|  |         df::building* building = world->buildings.all[b]; | ||||||
|  | 
 | ||||||
|  |         if (!(building->x1 <= cursor->x && cursor->x <= building->x2 && | ||||||
|  |             building->y1 <= cursor->y && cursor->y <= building->y2 && | ||||||
|  |             building->z == cursor->z)) | ||||||
|  |             continue; | ||||||
|  | 
 | ||||||
|  |         if(isCage(building)) | ||||||
|  |         { | ||||||
|  |             foundID = building->id; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return foundID; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int32_t findChainAtCursor() | ||||||
|  | { | ||||||
|  |     int32_t foundID = -1; | ||||||
|  |      | ||||||
|  |     if(cursor->x == -30000) | ||||||
|  |         return -1; | ||||||
|  | 
 | ||||||
|  |     for (size_t b = 0; b < world->buildings.all.size(); b++) | ||||||
|  |     { | ||||||
|  |         df::building* building = world->buildings.all[b]; | ||||||
|  | 
 | ||||||
|  |         // find zone under cursor
 | ||||||
|  |         if (!(building->x1 <= cursor->x && cursor->x <= building->x2 && | ||||||
|  |             building->y1 <= cursor->y && cursor->y <= building->y2 && | ||||||
|  |             building->z == cursor->z)) | ||||||
|  |             continue; | ||||||
|  | 
 | ||||||
|  |         if(isChain(building)) | ||||||
|  |         { | ||||||
|  |             foundID = building->id; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return foundID; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | df::general_ref_building_civzone_assignedst * createCivzoneRef() | ||||||
|  | { | ||||||
|  |     static bool vt_initialized = false; | ||||||
|  |     df::general_ref_building_civzone_assignedst* newref = NULL; | ||||||
|  | 
 | ||||||
|  |     // after having run successfully for the first time it's safe to simply create the object
 | ||||||
|  |     if(vt_initialized) | ||||||
|  |     { | ||||||
|  |         newref = (df::general_ref_building_civzone_assignedst*) | ||||||
|  |             df::general_ref_building_civzone_assignedst::_identity.instantiate(); | ||||||
|  |         return newref; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // being called for the first time, need to initialize the vtable
 | ||||||
|  |     for(size_t i = 0; i < world->units.all.size(); i++) | ||||||
|  |     { | ||||||
|  |         df::unit * creature = world->units.all[i];                     | ||||||
|  |         for(size_t r = 0; r<creature->refs.size(); r++) | ||||||
|  |         { | ||||||
|  |             df::general_ref* ref; | ||||||
|  |             ref = creature->refs.at(r); | ||||||
|  |             if(ref->getType() == df::general_ref_type::BUILDING_CIVZONE_ASSIGNED) | ||||||
|  |             { | ||||||
|  |                 if (strict_virtual_cast<df::general_ref_building_civzone_assignedst>(ref)) | ||||||
|  |                 { | ||||||
|  |                     // !! calling new() doesn't work, need _identity.instantiate() instead !!
 | ||||||
|  |                     newref = (df::general_ref_building_civzone_assignedst*)  | ||||||
|  |                         df::general_ref_building_civzone_assignedst::_identity.instantiate(); | ||||||
|  |                     vt_initialized = true; | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if(vt_initialized) | ||||||
|  |             break; | ||||||
|  |     } | ||||||
|  |     return newref; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // check if unit is already assigned to a zone, remove that ref from unit and old zone
 | ||||||
|  | // returns false if no pasture information was found
 | ||||||
|  | // helps as workaround for http://www.bay12games.com/dwarves/mantisbt/view.php?id=4475 by the way
 | ||||||
|  | // (pastured animals assigned to chains will get hauled back and forth because the pasture ref is not deleted)
 | ||||||
|  | bool unassignUnitFromZone(df::unit* unit) | ||||||
|  | { | ||||||
|  |     bool success = false; | ||||||
|  |     for (size_t or = 0; or < unit->refs.size(); or++) | ||||||
|  |     { | ||||||
|  |         df::general_ref * oldref = unit->refs[or]; | ||||||
|  |         if(oldref->getType() == df::general_ref_type::BUILDING_CIVZONE_ASSIGNED) | ||||||
|  |         { | ||||||
|  |             unit->refs.erase(unit->refs.begin() + or); | ||||||
|  |             df::building_civzonest * oldciv = (df::building_civzonest *) oldref->getBuilding(); | ||||||
|  |             for(size_t oc=0; oc<oldciv->assigned_creature.size(); oc++) | ||||||
|  |             { | ||||||
|  |                 if(oldciv->assigned_creature[oc] == unit->id) | ||||||
|  |                 { | ||||||
|  |                     oldciv->assigned_creature.erase(oldciv->assigned_creature.begin() + oc); | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             delete oldref; | ||||||
|  |             success = true; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return success; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // assign to pen or pit
 | ||||||
|  | command_result assignUnitToZone(color_ostream& out, df::unit* unit, df::building* building, bool verbose = false) | ||||||
|  | { | ||||||
|  |     //if(!isOwnCiv(unit) || !isTame(unit))
 | ||||||
|  |     //{
 | ||||||
|  |     //    out << "Creature must be tame and your own." << endl;
 | ||||||
|  |     //    return CR_WRONG_USAGE;
 | ||||||
|  |     //}
 | ||||||
|  | 
 | ||||||
|  |     // building must be a pen/pasture or pit
 | ||||||
|  |     //df::building * building = world->buildings.all.at(index);
 | ||||||
|  |     if(!isPenPasture(building) && !isPit(building)) | ||||||
|  |     { | ||||||
|  |         out << "Invalid building type. This is not a pen/pasture or pit." << endl; | ||||||
|  |         //target_zone = -1;
 | ||||||
|  |         return CR_WRONG_USAGE; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // try to get a fresh civzone ref
 | ||||||
|  |     df::general_ref_building_civzone_assignedst * ref = createCivzoneRef(); | ||||||
|  |     if(!ref) | ||||||
|  |     { | ||||||
|  |         out << "Could not find a clonable activity zone reference" << endl | ||||||
|  |             << "You need to pen/pasture/pit at least one creature" << endl | ||||||
|  |             << "before using 'assign' for the first time." << endl; | ||||||
|  |         return CR_WRONG_USAGE; | ||||||
|  |     } | ||||||
|  |          | ||||||
|  |     // check if unit is already pastured, remove that ref from unit and old pasture
 | ||||||
|  |     if(verbose) | ||||||
|  |     { | ||||||
|  |         if(unassignUnitFromZone(unit)) | ||||||
|  |             out << "old zone info cleared."; | ||||||
|  |         else | ||||||
|  |             out << "no old zone info found."; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 	ref->building_id = building->id; | ||||||
|  |     unit->refs.push_back(ref); | ||||||
|  | 
 | ||||||
|  |     df::building_civzonest * civz = (df::building_civzonest *) building; | ||||||
|  |     civz->assigned_creature.push_back(unit->id); | ||||||
|  | 
 | ||||||
|  |     df::creature_raw *raw = df::global::world->raws.creatures.all[unit->race]; | ||||||
|  |     out << "Unit " << unit->id  | ||||||
|  |         << "(" << raw->creature_id << ")"  | ||||||
|  |         << " assigned to zone " << building->id; | ||||||
|  |     if(isPit(building)) | ||||||
|  |         out << " (pit)."; | ||||||
|  |     if(isPenPasture(building)) | ||||||
|  |         out << " (pen/pasture)."; | ||||||
|  |     out << endl; | ||||||
|  | 
 | ||||||
|  |     return CR_OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | // dump some zone info
 | ||||||
|  | void zoneInfo(color_ostream & out, df::building* building, bool list_refs = false) | ||||||
|  | { | ||||||
|  |     if(building->getType()!= building_type::Civzone) | ||||||
|  |         return; | ||||||
|  | 
 | ||||||
|  |     if(building->getSubtype() != civzone_type::ActivityZone) | ||||||
|  |         return; | ||||||
|  | 
 | ||||||
|  |     string name; | ||||||
|  |     building->getName(&name); | ||||||
|  |     out.print("Building %i - \"%s\" - type %s (%i)", | ||||||
|  |                 building->id, | ||||||
|  |                 name.c_str(), | ||||||
|  |                 ENUM_KEY_STR(building_type, building->getType()).c_str(), | ||||||
|  |                 building->getType()); | ||||||
|  |     out.print(", subtype %s (%i)", | ||||||
|  |                 ENUM_KEY_STR(civzone_type, (df::civzone_type)building->getSubtype()).c_str(), | ||||||
|  |                 building->getSubtype()); | ||||||
|  |     out.print("\n"); | ||||||
|  | 
 | ||||||
|  |     df::building_civzonest * civ = (df::building_civzonest *) building; | ||||||
|  |     if(civ->zone_flags.bits.active) | ||||||
|  |         out << "active"; | ||||||
|  |     else | ||||||
|  |         out << "not active"; | ||||||
|  | 
 | ||||||
|  |     // look at zone flags, ignore fishing, water, hospital, ...
 | ||||||
|  |     // in fact, only deal with pits and pens
 | ||||||
|  |     if(civ->zone_flags.bits.pen_pasture) | ||||||
|  |         out << ", pen/pasture"; | ||||||
|  |     else if (civ->zone_flags.bits.pit_pond && civ->pit_flags==0) | ||||||
|  |         out << ", pit"; | ||||||
|  |     else | ||||||
|  |         return; | ||||||
|  |     out << endl; | ||||||
|  | 
 | ||||||
|  |     int32_t creaturecount = civ->assigned_creature.size(); | ||||||
|  |     out << "Creatures in this zone: " << creaturecount << endl; | ||||||
|  |     for(size_t c = 0; c < creaturecount; c++) | ||||||
|  |     { | ||||||
|  |         int32_t cindex = civ->assigned_creature.at(c); | ||||||
|  | 
 | ||||||
|  |         // print list of all units assigned to that zone
 | ||||||
|  |         for(size_t i = 0; i < world->units.all.size(); i++) | ||||||
|  |         { | ||||||
|  |             df::unit * creature = world->units.all[i]; | ||||||
|  |             if(creature->id != cindex) | ||||||
|  |                 continue; | ||||||
|  |                      | ||||||
|  |             unitInfo(out, creature, false); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // dump some cage info
 | ||||||
|  | void cageInfo(color_ostream & out, df::building* building, bool list_refs = false) | ||||||
|  | { | ||||||
|  |     if(!isCage(building)) | ||||||
|  |         return; | ||||||
|  | 
 | ||||||
|  |     string name; | ||||||
|  |     building->getName(&name); | ||||||
|  |     out.print("Building %i - \"%s\" - type %s (%i)", | ||||||
|  |                 building->id, | ||||||
|  |                 name.c_str(), | ||||||
|  |                 ENUM_KEY_STR(building_type, building->getType()).c_str(), | ||||||
|  |                 building->getType()); | ||||||
|  |     out.print("\n"); | ||||||
|  | 
 | ||||||
|  |     df::building_cagest * cage = (df::building_cagest*) building; | ||||||
|  |     int32_t creaturecount = cage->assigned_creature.size(); | ||||||
|  |     out << "Creatures in this cage: " << creaturecount << endl; | ||||||
|  |     for(size_t c = 0; c < creaturecount; c++) | ||||||
|  |     { | ||||||
|  |         int32_t cindex = cage->assigned_creature.at(c); | ||||||
|  | 
 | ||||||
|  |         // print list of all units assigned to that cage
 | ||||||
|  |         for(size_t i = 0; i < world->units.all.size(); i++) | ||||||
|  |         { | ||||||
|  |             df::unit * creature = world->units.all[i]; | ||||||
|  |             if(creature->id != cindex) | ||||||
|  |                 continue; | ||||||
|  |                      | ||||||
|  |             unitInfo(out, creature, false); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | // dump some chain/restraint info
 | ||||||
|  | void chainInfo(color_ostream & out, df::building* building, bool list_refs = false) | ||||||
|  | { | ||||||
|  |     if(!isChain(building)) | ||||||
|  |         return; | ||||||
|  | 
 | ||||||
|  |     string name; | ||||||
|  |     building->getName(&name); | ||||||
|  |     out.print("Building %i - \"%s\" - type %s (%i)", | ||||||
|  |                 building->id, | ||||||
|  |                 name.c_str(), | ||||||
|  |                 ENUM_KEY_STR(building_type, building->getType()).c_str(), | ||||||
|  |                 building->getType()); | ||||||
|  |     out.print("\n"); | ||||||
|  | 
 | ||||||
|  |     df::building_chainst* chain = (df::building_chainst*) building; | ||||||
|  | 	if(chain->assigned) | ||||||
|  | 	{ | ||||||
|  | 		out << "assigned: "; | ||||||
|  | 		unitInfo(out, chain->assigned, true); | ||||||
|  | 	} | ||||||
|  | 	if(chain->chained) | ||||||
|  | 	{ | ||||||
|  | 		out << "chained: "; | ||||||
|  | 		unitInfo(out, chain->chained, true); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | command_result df_zone (color_ostream &out, vector <string> & parameters) | ||||||
|  | { | ||||||
|  |     CoreSuspender suspend; | ||||||
|  | 
 | ||||||
|  |     bool need_cursor = false; // for zone_info, zone_assign, ...
 | ||||||
|  |     bool unit_info = false; | ||||||
|  |     bool zone_info = false; | ||||||
|  |     //bool cage_info = false;
 | ||||||
|  |     //bool chain_info = false;
 | ||||||
|  | 
 | ||||||
|  |     bool zone_assign = false; | ||||||
|  |     bool zone_unassign = false; | ||||||
|  |     bool zone_set = false; | ||||||
|  |     bool verbose = false; | ||||||
|  |     bool all = false; | ||||||
|  |     static int target_zone = -1; | ||||||
|  | 
 | ||||||
|  |     for (size_t i = 0; i < parameters.size(); i++) | ||||||
|  |     { | ||||||
|  |         string & p = parameters[i]; | ||||||
|  |          | ||||||
|  |         if (p == "help" || p == "?") | ||||||
|  |         { | ||||||
|  |             out << zone_help << endl; | ||||||
|  |             return CR_OK; | ||||||
|  |         } | ||||||
|  |         else if(p == "zinfo") | ||||||
|  |         { | ||||||
|  |             zone_info = true; | ||||||
|  |         } | ||||||
|  |         else if(p == "uinfo") | ||||||
|  |         { | ||||||
|  |             unit_info = true; | ||||||
|  |         } | ||||||
|  |         //else if(p == "cinfo")
 | ||||||
|  |         //{
 | ||||||
|  |         //    cage_info = true;
 | ||||||
|  |         //}
 | ||||||
|  |         //else if(p == "rinfo")
 | ||||||
|  |         //{
 | ||||||
|  |         //    chain_info = true;
 | ||||||
|  |         //}
 | ||||||
|  |         else if(p == "verbose") | ||||||
|  |         { | ||||||
|  |             verbose = true; | ||||||
|  |         } | ||||||
|  |         else if(p == "unassign") | ||||||
|  |         { | ||||||
|  |             zone_unassign = true; | ||||||
|  |         } | ||||||
|  |         else if(p == "assign") | ||||||
|  |         { | ||||||
|  |             if(i == parameters.size()-1) | ||||||
|  |             { | ||||||
|  |                 if(target_zone == -1) | ||||||
|  |                 { | ||||||
|  |                     out.printerr("No zone id specified and current one is invalid!"); | ||||||
|  |                     return CR_WRONG_USAGE; | ||||||
|  |                 } | ||||||
|  |                 else | ||||||
|  |                 { | ||||||
|  |                     out << "No zone id specified. Will try to use #" << target_zone << endl; | ||||||
|  |                     zone_assign = true; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 stringstream ss(parameters[i+1]); | ||||||
|  |                 i++; | ||||||
|  |                 ss >> target_zone; | ||||||
|  |                 out << "Assign selected unit to zone #" << target_zone <<std::endl; | ||||||
|  |                 zone_assign = true; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         else if(p == "set") | ||||||
|  |         { | ||||||
|  |             zone_set = true; | ||||||
|  |         } | ||||||
|  |         else if(p == "all") | ||||||
|  |         { | ||||||
|  |             all = true; | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |             return CR_WRONG_USAGE; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if((zone_info && !all) || zone_set) | ||||||
|  |         need_cursor = true; | ||||||
|  | 
 | ||||||
|  |     if(need_cursor && cursor->x == -30000) | ||||||
|  |     { | ||||||
|  |         out.printerr("No cursor; place cursor over activity zone.\n"); | ||||||
|  |         return CR_FAILURE; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // give info on unit(s)
 | ||||||
|  |     if(unit_info) | ||||||
|  |     { | ||||||
|  |         if(all) | ||||||
|  |         { | ||||||
|  |             // todo: this should really be filtered somehow (prints even dead units etc)
 | ||||||
|  |             for(size_t c=0; c<world->units.all.size(); c++) | ||||||
|  |             { | ||||||
|  |                 df::unit *unit = world->units.all[c]; | ||||||
|  |                 unitInfo(out, unit, verbose); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             df::unit *unit = getSelectedUnit(out); | ||||||
|  |             if (!unit) | ||||||
|  |                 return CR_FAILURE; | ||||||
|  |             unitInfo(out, unit, verbose); | ||||||
|  |         } | ||||||
|  |         return CR_OK; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // give info on zone(s), chain or cage under cursor
 | ||||||
|  |     // (doesn't use the findXyzAtCursor() methods because zones might overlap and contain a cage or chain)
 | ||||||
|  |     if(zone_info) // || chain_info || cage_info)
 | ||||||
|  |     { | ||||||
|  |         for (size_t b = 0; b < world->buildings.all.size(); b++) | ||||||
|  |         { | ||||||
|  |             df::building * building = world->buildings.all[b]; | ||||||
|  | 
 | ||||||
|  |             // find building under cursor
 | ||||||
|  |             if (!all && | ||||||
|  |                 !(building->x1 <= cursor->x && cursor->x <= building->x2 && | ||||||
|  |                 building->y1 <= cursor->y && cursor->y <= building->y2 && | ||||||
|  |                 building->z == cursor->z)) | ||||||
|  |                 continue; | ||||||
|  | 
 | ||||||
|  |             zoneInfo(out, building, verbose); | ||||||
|  |             chainInfo(out, building, verbose); | ||||||
|  |             cageInfo(out, building, verbose); | ||||||
|  |         } | ||||||
|  |         return CR_OK; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // set building at cursor position to be new target zone
 | ||||||
|  |     if(zone_set) | ||||||
|  |     { | ||||||
|  |         target_zone = findPenPitAtCursor(); | ||||||
|  |         if(target_zone==-1) | ||||||
|  |         { | ||||||
|  |             out << "No pen/pasture or pit under cursor!" << endl; | ||||||
|  |             return CR_WRONG_USAGE; | ||||||
|  |         } | ||||||
|  |         out << "Current zone set to #" << target_zone << endl; | ||||||
|  |         return CR_OK; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // de-assign from pen or pit
 | ||||||
|  |     if(zone_unassign) | ||||||
|  |     { | ||||||
|  |         // must have unit selected
 | ||||||
|  |         df::unit *unit = getSelectedUnit(out); | ||||||
|  |         if (!unit) | ||||||
|  |         { | ||||||
|  |             out << "No unit selected." << endl; | ||||||
|  |             return CR_WRONG_USAGE; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // remove assignment reference from unit and old zone
 | ||||||
|  |         if(unassignUnitFromZone(unit)) | ||||||
|  |             out << "Unit unassigned." << endl; | ||||||
|  |         else | ||||||
|  |             out << "Unit is not assigned to a zone!" << endl; | ||||||
|  |         return CR_OK; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // assign to pen or pit
 | ||||||
|  |     if(zone_assign) | ||||||
|  |     { | ||||||
|  |         // must have unit selected
 | ||||||
|  |         df::unit *unit = getSelectedUnit(out); | ||||||
|  |         if (!unit) | ||||||
|  |         { | ||||||
|  |             out << "No unit selected." << endl; | ||||||
|  |             return CR_WRONG_USAGE; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  | 		// try to get building index from the id
 | ||||||
|  |         int32_t index = findBuildingIndexById(target_zone); | ||||||
|  |         if(index == -1) | ||||||
|  |         { | ||||||
|  |             out << "Invalid building id." << endl; | ||||||
|  |             target_zone = -1; | ||||||
|  |             return CR_WRONG_USAGE; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         df::building * building = world->buildings.all.at(index); | ||||||
|  | 		return assignUnitToZone(out, unit, building, verbose); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return CR_OK; | ||||||
|  | } | ||||||
		Loading…
	
		Reference in New Issue