/********************************************* * Purpose: * * - Display creatures * - Modify skills and labors of creatures * - Kill creatures * - Etc. * * Version: 0.1.1 * Date: 2011-04-07 * Author: raoulxq (based on creaturedump.cpp from peterix) * Todo: * - Option to add/remove single skills * - Ghosts/Merchants/etc. should be tagged as not own creatures * - Filter by nickname with -n * - Filter by first name with -fn * - Filter by last name with -ln * - Add pattern matching (or at least matching) to -n/-fn/-ln * - Set nickname with --setnick (only if -i is given) * - Show skills/labors only when -ss/-sl/-v is given or a skill/labor is changed * - Make -1 the default for everything but -i * - Imply -i if first argument is a number * - Search for nick/profession if first argument is a string without - (i.e. no switch) * - Switch --showhappy (show dwarf's experiences which make her un-/happy) * - Switch --makefriendly * - Switch --listskills, showing first 3 important skills * Done: * - More space for "Current Job" * - Attempted to revive creature(s) with --revive, but it doesn't work (flag is there but invisible) * - Switch -rcs, remove civil skills * - Switch -rms, remove military skills (who would want that?) * - Allow comma separated list of IDs for -i * - '-c all' shows all creatures * - Rename from skillmodify.cpp to creature.cpp * - Kill creature(s) with --kill * - Hide skills with level 0 and 0 experience points * - Add --showallflags flag to display all flags (default: display a few important ones) * - Add --showdead flag to also display dead creatures * - Display more creature flags * - Show creature type (again) * - Add switch -1/--summary to only display one line for every creature. Good for an overview. * - Display current job (has been there all the time, but not shown in Windows due to missing memory offsets) * - Remove magic numbers * - Show social skills only when -ss is given * - Hide hauler labors when +sh is given * - Add -v for verbose * - Override forbidden mass-designation with -f * - Option to add/remove single labors * - Switches -ras and rl should only be possible with -nn or -i * - Switch -rh removes hauler jobs * - Dead creatures should not be displayed * - Childs should not get labors assigned to * - Babies should not get labors assigned to * - Switch -al adds labor number n * - Switch -rl removes labor number n * - Switch -ral removes all labors * - Switch -ll lists all available labors ********************************************* */ #include #include #include #include #include #include using namespace std; #define DFHACK_WANT_MISCUTILS #include #include /* Note about magic numbers: * If you have an idea how to better solve this, tell me. Currently I'd be * either dependent on Toady One's implementation (#defining numbers) or * Memory.xml (#defining text). I voted for Toady One's numbers to be more * stable, but could be wrong. * * Ideally there would be a flag "is_military" or "is_social" in Memory.xml. */ /* Social skills */ #define SKILL_PERSUASION 72 #define SKILL_NEGOTIATION 73 #define SKILL_JUDGING_INTENT 74 #define SKILL_INTIMIDATION 79 #define SKILL_CONVERSATION 80 #define SKILL_COMEDY 81 #define SKILL_FLATTERY 82 #define SKILL_CONSOLING 83 #define SKILL_PACIFICATION 84 /* Misc skills */ #define SKILL_WEAPONSMITHING 27 #define SKILL_ARMORSMITHING 28 #define SKILL_RECORD_KEEPING 77 #define SKILL_WAX_WORKING 115 /* Some military skills */ #define SKILL_COORDINATION 95 #define SKILL_BALANCE 96 #define SKILL_LEADERSHIP 97 #define SKILL_TEACHING 98 #define SKILL_FIGHTING 99 #define SKILL_ARCHERY 100 #define SKILL_WRESTLING 101 #define SKILL_BITING 102 #define SKILL_STRIKING 103 #define SKILL_KICKING 104 #define SKILL_DODGING 105 #define LABOR_STONE_HAULING 1 #define LABOR_WOOD_HAULING 2 #define LABOR_BURIAL 3 #define LABOR_FOOD_HAULING 4 #define LABOR_REFUSE_HAULING 5 #define LABOR_ITEM_HAULING 6 #define LABOR_FURNITURE_HAULING 7 #define LABOR_ANIMAL_HAULING 8 #define LABOR_CLEANING 9 #define LABOR_FEED_PATIENTS_PRISONERS 22 #define LABOR_RECOVERING_WOUNDED 23 #define PROFESSION_CHILD 96 #define PROFESSION_BABY 97 #define NOT_SET INT_MIN #define MAX_MOOD 4 #define NO_MOOD -1 bool quiet=true; bool verbose = false; bool showhauler = true; bool showsocial = false; bool showfirstlineonly = false; bool showdead = false; bool showallflags = false; int hauler_labors[] = { LABOR_STONE_HAULING ,LABOR_WOOD_HAULING ,LABOR_BURIAL ,LABOR_FOOD_HAULING ,LABOR_REFUSE_HAULING ,LABOR_ITEM_HAULING ,LABOR_FURNITURE_HAULING ,LABOR_ANIMAL_HAULING ,LABOR_CLEANING ,LABOR_FEED_PATIENTS_PRISONERS ,LABOR_RECOVERING_WOUNDED }; int social_skills[] = { SKILL_PERSUASION ,SKILL_NEGOTIATION ,SKILL_JUDGING_INTENT ,SKILL_INTIMIDATION ,SKILL_CONVERSATION ,SKILL_COMEDY ,SKILL_FLATTERY ,SKILL_CONSOLING ,SKILL_PACIFICATION }; int military_skills[] = { SKILL_COORDINATION ,SKILL_BALANCE ,SKILL_LEADERSHIP ,SKILL_TEACHING ,SKILL_FIGHTING ,SKILL_ARCHERY ,SKILL_WRESTLING ,SKILL_BITING ,SKILL_STRIKING ,SKILL_KICKING ,SKILL_DODGING }; void usage(int argc, const char * argv[]) { cout << "Usage:" << endl << argv[0] << " [option 1] [option 2] [...]" << endl << endl << "Display options:" << endl << "-q : Suppress \"Press any key to continue\" at program termination" << endl << "-v : Increase verbosity" << endl << endl << "Choosing which creatures to display and/or modify " << "(note that all criteria" << endl << "must match, so adding " << " more narrows things down):" << endl << "-i id1[,id2,...]: Only show/modify creature with this id" << endl << "-c creature : Show/modify this creature type instead of dwarves" << endl << " ('all' to show all creatures)" << endl << "-nn/--nonicks : Only show/modify creatures with no custom nickname (migrants)" << endl << "--nicks : Only show/modify creatures with custom nickname" << endl << "--showdead : Also show/modify dead creatures" << endl << "--type : Show/modify all creatures of given type" << endl << " : Can be used multiple times" << endl << " types:" << endl << " * dead: all dead creatures" << endl << " * demon: all demons" << endl << " * diplomat: all diplomats" << endl << " * FB: all forgotten beasts" << endl << " * female: all female creatures" << endl << " * ghost: all ghosts" << endl << " * male: all male creatures" << endl << " * merchants: all merchants (including pack animals)" << endl << " * neuter: all neuter creatuers" << endl << " * pregnant: all pregnant creatures" << endl << " * tame: all tame creatues" << endl << " * wild: all wild creatures" << endl << endl << "What information to display:" << endl << "-saf : Show all flags of a creature" << endl << "--showallflags : Show all flags of a creature" << endl << "-ll/--listlabors: List available labors" << endl << "-ss : Show social skills" << endl << "+sh : Hide hauler labors" << endl << "-1/--summary : Only display one line per creature" << endl << endl << "Options to modify selected creatures:" << endl << "-al : Add labor to creature" << endl << "-rl : Remove labor from creature" << endl << "-ras : Remove all skills from creature (i.e. set them to zero)" << endl << "-rcs : Remove civil skills from creature (i.e. set them to zero)" << endl << "-rms : Remove military skills from creature (i.e. set them to zero)" << endl << "-ral : Remove all labors from creature" << endl << "-ah : Add hauler labors (stone hauling, etc.) to creature" << endl << "-rh : Remove hauler labors (stone hauling, etc.) from creature" << endl // Disabling mood doesn't work as intented << "--setmood : Set mood to n (-1 = no mood, max=4, buggy!)" << endl << "--kill : Kill creature(s) (leaves behind corpses)" << endl << "--erase : Remove creature(s) from game without killing" << endl << "--tame : Tames animals, recruits intelligent creatures." << endl << "--slaugher : Mark a creature for slaughter, even sentients" << endl << "--butcher : Same as --slaugher" << endl // Doesn't seem to work //<< "--revive : Attempt to revive creature(s) (remove dead and killed flag)" << endl // Setting happiness doesn't work really, because hapiness is recalculated //<< "--sethappiness : Set happiness to n" << endl << "-f : Force an action" << endl << endl << "Examples:" << endl << endl << "Show all dwarfs:" << endl << argv[0] << " -c Dwarf" << endl << endl << "Show summary of all creatures (spoiler: includes unknown creatures):" << endl << argv[0] << " -1 -c all" << endl << endl << "Kill that nasty ogre" << endl << argv[0] << " -i 52 --kill" << endl << endl << "Check that the ogre is really dead" << endl << argv[0] << " -c ogre --showdead" << endl << endl << "Remove all skills from dwarfs 15 and 32:" << endl << argv[0] << " -i 15,32 -ras" << endl << endl << "Remove all skills and labors from dwarfs with no custom nickname:" << endl << argv[0] << " -c DWARF -nn -ras -ral" << endl << endl << "Add hauling labors to all dwarfs without nickname (e.g. migrants):" << endl << argv[0] << " -c DWARF -nn -ah" << endl << endl << "Show list of labor ids:" << endl << argv[0] << " -c DWARF -ll" << endl << endl << "Add engraving labor to all dwarfs without nickname (get the labor id from the list above):" << endl << argv[0] << " -c DWARF -nn -al 13" << endl << endl << "Make Urist, Stodir and Ingish miners:" << endl << argv[0] << " -i 31,42,77 -al 0" << endl << endl << "Make all demons friendly:" << endl << argv[0] << " --type demon --tame" << endl ; if (quiet == false) { cout << "Press any key to continue" << endl; cin.ignore(); } } DFHack::Materials * Materials; DFHack::VersionInfo *mem; DFHack::Creatures * Creatures = NULL; // Note that toCaps() changes the string itself and I'm using it a few times in // an unsafe way below. Didn't crash yet however. std::string toCaps(std::string s) { const int length = s.length(); std::locale loc(""); bool caps=true; if (length == 0) { return s; } for(int i=0; i!=length ; ++i) { if (caps) { s[i] = std::toupper(s[i],loc); caps = false; } else if (s[i] == '_' || s[i] == ' ') { s[i] = ' '; caps = true; } else { s[i] = std::tolower(s[i],loc); } } return s; } int strtoint(const string &str) { stringstream ss(str); int result; return ss >> result ? result : -1; } // A C++ standard library function should be used instead bool is_in(int m, int set[], int set_size) { for (int i=0; i v, int comp) { for (size_t i=0; igetTranslation(); DFHack::VersionInfo *mem = DF->getMemoryInfo(); string type="(no type)"; if (Materials->raceEx[creature.race].rawname[0]) { type = toCaps(Materials->raceEx[creature.race].rawname); } string name="(no name)"; if(creature.name.nickname[0]) { name = creature.name.nickname; } else { if(creature.name.first_name[0]) { name = toCaps(creature.name.first_name); string transName = Tran->TranslateName(creature.name,false); if(!transName.empty()) { name += " " + toCaps(transName); } } } string profession=""; try { profession = mem->getProfession(creature.profession); } catch (exception& e) { cout << "Error retrieving creature profession: " << e.what() << endl; } if(creature.custom_profession[0]) { profession = creature.custom_profession; } string jobid; stringstream ss; ss << "(" << creature.current_job.jobId << ")"; jobid = ss.str(); string job="No Job/On Break" + (creature.current_job.jobId == 0 ? "" : jobid); if(creature.current_job.active) { job=mem->getJob(creature.current_job.jobId); int p=job.size(); while (p>0 && (job[p]==' ' || job[p]=='\t')) p--; if (p <= 1) // Display numeric jobID if unknown job { job = jobid; } } if (showfirstlineonly) { printf("%3d", index); printf(" %-17s", type.c_str()); printf(" %-24s", name.c_str()); printf(" %-16s", toCaps(profession).c_str()); printf(" %-38s", job.c_str()); printf(" %5d", creature.happiness); if (showdead) { printf(" %-5s", creature.flags1.bits.dead ? "Dead" : "Alive"); } printf("\n"); return; } else { printf("ID: %d", index); printf(", %s", type.c_str()); printf(", %s", name.c_str()); printf(", %s", toCaps(profession).c_str()); printf(", Job: %s", job.c_str()); printf(", Happiness: %d", creature.happiness); printf("\n"); printf("Origin: %p\n", creature.origin); printf("Civ #: %d\n", creature.civ); } if((creature.mood != NO_MOOD) && (creature.mood<=MAX_MOOD)) { cout << "Creature is in a strange mood (mood=" << creature.mood << "), skill: " << mem->getSkill(creature.mood_skill) << endl; vector mymat; if(Creatures->ReadJob(&creature, mymat)) { for(unsigned int i = 0; i < mymat.size(); i++) { printf("\t%s(%d)\t%d %d %d - %.8x\n", Materials->getDescription(mymat[i]).c_str(), mymat[i].itemType, mymat[i].subType, mymat[i].subIndex, mymat[i].index, mymat[i].flags); } } } if(creature.has_default_soul) { // Print out skills int skillid; int skillrating; int skillexperience; string skillname; cout << setiosflags(ios::left); for(unsigned int i = 0; i < creature.defaultSoul.numSkills;i++) { skillid = creature.defaultSoul.skills[i].id; bool is_social = is_in(skillid, social_skills, sizeof(social_skills)/sizeof(social_skills[0])); if (!is_social || (is_social && showsocial)) { skillrating = creature.defaultSoul.skills[i].rating; skillexperience = creature.defaultSoul.skills[i].experience; try { skillname = mem->getSkill(skillid); } catch(DFHack::Error::AllMemdef &e) { skillname = "Unknown skill"; cout << e.what() << endl; } if (skillrating > 0 || skillexperience > 0) { cout << "(Skill " << int(skillid) << ") " << setw(16) << skillname << ": " << skillrating << "/" << skillexperience << endl; } } } for(unsigned int i = 0; i < NUM_CREATURE_LABORS;i++) { if(!creature.labors[i]) continue; string laborname; try { laborname = mem->getLabor(i); } catch(exception &) { laborname = "(Undefined)"; } bool is_labor = is_in(i, hauler_labors, sizeof(hauler_labors)/sizeof(hauler_labors[0])); if (!is_labor || (is_labor && showhauler)) cout << "(Labor " << i << ") " << setw(16) << laborname << endl; } } if (creature.pregnancy_timer > 0) cout << "Pregnant: " << creature.pregnancy_timer << " ticks to " << "birth." << endl; if (showallflags) { DFHack::t_creaturflags1 f1 = creature.flags1; DFHack::t_creaturflags2 f2 = creature.flags2; DFHack::t_creaturflags3 f3 = creature.flags3; if(f1.bits.dead){cout << "Flag: dead" << endl; } if(f1.bits.had_mood){cout<TranslateName(creature.artifact_name,false); cout << "Artifact: " << artifact_name << endl; } } cout << endl; } class creature_filter { public: enum sex_filter { SEX_FEMALE = 0, SEX_MALE = 1, SEX_ANY = 254, // Our magin number for ignoring sex. SEX_NEUTER = 255 }; bool dead; bool demon; bool diplomat; bool find_nonicks; bool find_nicks; bool forgotten_beast; bool ghost; bool merchant; bool pregnant; bool tame; bool wild; sex_filter sex; string creature_type; std::vector creature_id; #define DEFAULT_CREATURE_STR "Default" creature_filter() { // By default we only select dwarves, except that if we use the // --type option we want to default to everyone. So we start out // with a special string, and if remains unchanged after all // the options have been processed we turn it to DWARF. creature_type = DEFAULT_CREATURE_STR; dead = false; demon = false; diplomat = false; find_nonicks = false; find_nicks = false; forgotten_beast = false; ghost = false; merchant = false; pregnant = false; sex = SEX_ANY; tame = false; wild = false; } // If the creature type is still the default, then change it to allow // for all creatures. If the creature type has been explicitly set, // then don't alter it. void defaultTypeToAll() { if (creature_type == DEFAULT_CREATURE_STR) creature_type = ""; } // If the creature type is still the default, change it to DWARF void defaultTypeToDwarf() { if (creature_type == DEFAULT_CREATURE_STR) creature_type = "Dwarf"; } void process_type(string type) { type = toCaps(type); // If we're going by type, then by default all species are // permitted. defaultTypeToAll(); if (type == "Dead") { dead = true; showdead = true; } else if (type == "Demon") demon = true; else if (type == "Diplomat") diplomat = true; else if (type == "Fb" || type == "Beast") forgotten_beast = true; else if (type == "Ghost") ghost = true; else if (type == "Merchant") merchant = true; else if (type == "Pregnant") pregnant = true; else if (type == "Tame") tame = true; else if (type == "Wild") wild = true; else if (type == "Male") sex = SEX_MALE; else if (type == "Female") sex = SEX_FEMALE; else if (type == "Neuter") sex = SEX_NEUTER; else { cerr << "ERROR: Unknown type '" << type << "'" << endl; } } void doneProcessingOptions() { string temp = toCaps(creature_type); creature_type = temp; defaultTypeToDwarf(); } bool creatureMatches(const DFHack::t_creature & creature, uint32_t creature_idx) { // A list of ids overrides everything else. if (creature_id.size() > 0) return (find_int(creature_id, creature_idx)); // If it's not a list of ids, it has not match all given criteria. const DFHack::t_creaturflags1 &f1 = creature.flags1; const DFHack::t_creaturflags2 &f2 = creature.flags2; const DFHack::t_creaturflags3 &f3 = creature.flags3; if(f1.bits.dead && !showdead) return false; bool hasnick = (creature.name.nickname[0] != '\0'); if(hasnick && find_nonicks) return false; if(!hasnick && find_nicks) return false; string race_name = string(Materials->raceEx[creature.race].rawname); if(!creature_type.empty() && creature_type != toCaps(race_name)) return false; if(dead && !f1.bits.dead) return false; if(demon && !f2.bits.underworld) return false; if(diplomat && !f1.bits.diplomat) return false; if(forgotten_beast && !f2.bits.visitor_uninvited) return false; if(ghost && !f3.bits.ghostly) return false; if(merchant && !f1.bits.merchant) return false; if(pregnant && creature.pregnancy_timer == 0) return false; if (sex != SEX_ANY && creature.sex != (uint8_t) sex) return false; if(tame && !f1.bits.tame) return false; if(wild && !f2.bits.roaming_wilderness_population_source && !f2.bits.roaming_wilderness_population_source_not_a_map_feature) { return false; } return true; } }; int main (int argc, const char* argv[]) { // let's be more useful when double-clicked on windows #ifndef LINUX_BUILD quiet = false; #endif creature_filter filter; bool remove_skills = false; bool remove_civil_skills = false; bool remove_military_skills = false; bool remove_labors = false; bool kill_creature = false; bool erase_creature = false; bool revive_creature = false; bool make_hauler = false; bool remove_hauler = false; bool add_labor = false; int add_labor_n = NOT_SET; bool remove_labor = false; int remove_labor_n = NOT_SET; bool set_happiness = false; int set_happiness_n = NOT_SET; bool set_mood = false; int set_mood_n = NOT_SET; bool list_labors = false; bool force_massdesignation = false; bool tame_creature = false; bool slaughter_creature = false; if (argc == 1) { usage(argc, argv); return 1; } for(int i = 1; i < argc; i++) { string arg_cur = argv[i]; string arg_next = ""; int arg_next_int = NOT_SET; /* Check if argv[i+1] is a number >= 0 */ if (i < argc-1) { arg_next = argv[i+1]; arg_next_int = strtoint(arg_next); if (arg_next != "0" && arg_next_int == 0) { arg_next_int = NOT_SET; } } if(arg_cur == "-q") { quiet = true; } else if(arg_cur == "+q") { quiet = false; } else if(arg_cur == "-v") { verbose = true; } else if(arg_cur == "-1" || arg_cur == "--summary") { showfirstlineonly = true; } else if(arg_cur == "-ss" || arg_cur == "--showsocial") { showsocial = true; } else if(arg_cur == "+sh" || arg_cur == "-nosh" || arg_cur == "--noshowhauler") { showhauler = false; } else if(arg_cur == "--showdead") { showdead = true; } else if(arg_cur == "--showallflags" || arg_cur == "-saf") { showallflags = true; } else if(arg_cur == "-ras") { remove_skills = true; } else if(arg_cur == "-rcs") { remove_civil_skills = true; } else if(arg_cur == "-rms") { remove_military_skills = true; } else if(arg_cur == "-f") { force_massdesignation = true; } // list labors else if(arg_cur == "-ll" || arg_cur == "--listlabors") { list_labors = true; } // add single labor else if(arg_cur == "-al" && i < argc-1) { if (arg_next_int == NOT_SET || arg_next_int >= NUM_CREATURE_LABORS) { usage(argc, argv); return 1; } add_labor = true; add_labor_n = arg_next_int; i++; } // remove single labor else if(arg_cur == "-rl" && i < argc-1) { if (arg_next_int == NOT_SET || arg_next_int >= NUM_CREATURE_LABORS) { usage(argc, argv); return 1; } remove_labor = true; remove_labor_n = arg_next_int; i++; } else if(arg_cur == "--setmood" && i < argc-1) { if (arg_next_int < NO_MOOD || arg_next_int > MAX_MOOD) { usage(argc, argv); return 1; } set_mood = true; set_mood_n = arg_next_int; i++; } else if(arg_cur == "--sethappiness" && i < argc-1) { if (arg_next_int < 1 || arg_next_int >= 2000) { usage(argc, argv); return 1; } set_happiness = true; set_happiness_n = arg_next_int; i++; } else if(arg_cur == "--kill") { kill_creature = true; showallflags = true; showdead = true; } else if(arg_cur == "--erase") { erase_creature = true; showallflags = true; showdead = true; } else if(arg_cur == "--revive") { revive_creature = true; showdead = true; showallflags = true; } else if(arg_cur == "-ral") { remove_labors = true; } else if(arg_cur == "-ah") { make_hauler = true; } else if(arg_cur == "-rh") { remove_hauler = true; } else if(arg_cur == "-nn" || arg_cur == "--nonicks") { filter.find_nonicks = true; } else if(arg_cur == "--nicks") { filter.find_nicks = true; } else if(arg_cur == "-c" && i < argc-1) { filter.creature_type = argv[i+1]; i++; } else if(arg_cur == "-i" && i < argc-1) { std::stringstream ss(argv[i+1]); int num; while (ss >> num) { filter.creature_id.push_back(num); ss.ignore(1); } filter.creature_type = ""; // if -i is given, match all creatures showdead = true; i++; } else if(arg_cur == "--type" && i < argc-1) { filter.process_type(arg_next); i++; } else if (arg_cur == "--tame") tame_creature = true; else if (arg_cur == "--slaugher" || arg_cur == "--butcher") slaughter_creature = true; else { if (arg_cur != "-h") { cout << "Unknown option '" << arg_cur << "'" << endl; cout << endl; } usage(argc, argv); return 1; } } filter.doneProcessingOptions(); DFHack::ContextManager DFMgr("Memory.xml"); DFHack::Context* DF; try { DF = DFMgr.getSingleContext(); DF->Attach(); } catch (exception& e) { cerr << e.what() << endl; if (quiet == false) { cin.ignore(); } return 1; } Creatures = DF->getCreatures(); Materials = DF->getMaterials(); DFHack::Translation * Tran = DF->getTranslation(); uint32_t numCreatures; if(!Creatures->Start(numCreatures)) { cerr << "Can't get creatures" << endl; if (quiet == false) { cin.ignore(); } return 1; } if(!numCreatures) { cerr << "No creatures to print" << endl; if (quiet == false) { cin.ignore(); } return 1; } mem = DF->getMemoryInfo(); Materials->ReadInorganicMaterials(); Materials->ReadOrganicMaterials(); Materials->ReadWoodMaterials(); Materials->ReadPlantMaterials(); Materials->ReadCreatureTypes(); Materials->ReadCreatureTypesEx(); Materials->ReadDescriptorColors(); if(!Tran->Start()) { cerr << "Can't get name tables" << endl; return 1; } // List all available labors (reproduces contents of Memory.xml) if (list_labors == true) { string laborname; for (int i=0; i < NUM_CREATURE_LABORS; i++) { try { laborname = mem->getLabor(i); cout << "Labor " << int(i) << ": " << laborname << endl; } catch (exception&) { if (verbose) { laborname = "Unknown"; cout << "Labor " << int(i) << ": " << laborname << endl; } } } } else { if (showfirstlineonly) { printf("ID Type Name/nickname Job title Current job Happy%s\n", showdead?" Dead ":""); printf("--- ----------------- ------------------------ ---------------- -------------------------------------- -----%s\n", showdead?" -----":""); } vector addrs; for(uint32_t creature_idx = 0; creature_idx < numCreatures; creature_idx++) { DFHack::t_creature creature; Creatures->ReadCreature(creature_idx,creature); /* Check if we want to display/change this creature or skip it */ if(filter.creatureMatches(creature, creature_idx)) { printCreature(DF,creature,creature_idx); addrs.push_back(creature.origin); bool dochange = ( remove_skills || remove_civil_skills || remove_military_skills || remove_labors || add_labor || remove_labor || make_hauler || remove_hauler || kill_creature || erase_creature || revive_creature || set_happiness || set_mood || tame_creature || slaughter_creature ); if (toCaps(filter.creature_type) == "Dwarf" && (creature.profession == PROFESSION_CHILD || creature.profession == PROFESSION_BABY)) { dochange = false; } bool allow_massdesignation = filter.creature_id.size()==0 || toCaps(filter.creature_type) != "Dwarf" || filter.find_nonicks == true || force_massdesignation; if (dochange == true && allow_massdesignation == false) { cout << "Not changing creature because none of -c (other than dwarf), -i or -nn was" << endl << "selected. Add -f (force) to override this safety measure." << endl; dochange = false; } if (dochange) { if(creature.has_default_soul) { if (kill_creature && !creature.flags1.bits.dead) { DFHack::t_creaturflags1 f1 = creature.flags1; DFHack::t_creaturflags2 f2 = creature.flags2; DFHack::t_creaturflags3 f3 = creature.flags3; f3.bits.scuttle = true; cout << "Writing flags..." << endl; if (!Creatures->WriteFlags(creature_idx, f1.whole, f2.whole, f3.whole)) { cout << "Error writing creature flags!" << endl; } // We want the flags to be shown after our // modification, but they are not read back creature.flags1 = f1; creature.flags2 = f2; creature.flags3 = f3; } if (erase_creature && !creature.flags1.bits.dead) { /* [quote author=Eldrick Tobin link=topic=58809.msg2178545#msg2178545 date=1302638055] After extensive testing that just ate itself -.-; Runesmith does not unset the following: - Active Invader (sets if they are just about the invade, as Currently Invading removes this one) - Hidden Ambusher (Just in Case, however it is still set when an Active Invader) - Hidden in Ambush (Just in Case, however it is still set when an Active Invader, until discovery) - Incoming (Sets if something is here yet... wave X of a siege here) - Invader -Fleeing/Leaving - Currently Invading When it nukes something it basically just sets them to 'dead'. It does not also set them to 'killed'. Show dead will show everything (short of 'vanished'/'deleted' I'd suspect) so one CAN go through the intensive process to revive a broken siege. These particular flags are not visible at the same exact time so multiple passes -even through a narrow segment- are advised. Problem I ran into (last thing before I mention something more DFHack related): I set the Killed Flag (but not dead), and I got mortally wounded siegers that refused to just pift in Magma. [color=purple]Likely missing upper torsoes on examination[/color]. */ /* This is from an invading creature's flags: ID: 560, Crocodile Cave, Nako, Standard, Job: No Job, Happiness: 100 Flag: Marauder Flag: Can Swap Flag: Active Invader Flag: Invader Origin Flag: Coward Flag: Hidden Ambusher Flag: Invades Flag: Ridden Flag: Calculated Nerves Flag: Calculated Bodyparts Flag: Calculated Insulation Flag: Vision Good Flag: Breathing Good */ DFHack::t_creaturflags1 f1 = creature.flags1; DFHack::t_creaturflags2 f2 = creature.flags2; f1.bits.dead = 1; f2.bits.killed = 1; f1.bits.active_invader = 0; /*!< 17: Active invader (for organized ones) */ f1.bits.hidden_ambusher = 0; /*!< 21: Active marauder/invader moving inward? */ f1.bits.hidden_in_ambush = 0; f1.bits.invades = 0; /*!< 22: Marauder resident/invader moving in all the way */ cout << "Writing flags..." << endl; if (!Creatures->WriteFlags(creature_idx, f1.whole, f2.whole)) { cout << "Error writing creature flags!" << endl; } // We want the flags to be shown after our modification, but they are not read back creature.flags1 = f1; creature.flags2 = f2; } if (revive_creature && creature.flags1.bits.dead) { DFHack::t_creaturflags1 f1 = creature.flags1; DFHack::t_creaturflags2 f2 = creature.flags2; f1.bits.dead = 0; f2.bits.killed = 0; f1.bits.active_invader = 1; /*!< 17: Active invader (for organized ones) */ f1.bits.hidden_ambusher = 1; /*!< 21: Active marauder/invader moving inward? */ f1.bits.hidden_in_ambush = 1; f1.bits.invades = 1; /*!< 22: Marauder resident/invader moving in all the way */ cout << "Writing flags..." << endl; if (!Creatures->WriteFlags(creature_idx, f1.whole, f2.whole)) { cout << "Error writing creature flags!" << endl; } // We want the flags to be shown after our modification, but they are not read back creature.flags1 = f1; creature.flags2 = f2; } if (set_mood) { /* Doesn't really work to disable a mood */ cout << "Setting mood to " << set_mood_n << "..." << endl; Creatures->WriteMood(creature_idx, set_mood_n); DFHack::t_creaturflags1 f1 = creature.flags1; DFHack::t_creaturflags2 f2 = creature.flags2; f1.bits.has_mood = (set_mood_n == NO_MOOD ? 0 : 1); if (!Creatures->WriteFlags(creature_idx, f1.whole, f2.whole)) { cout << "Error writing creature flags!" << endl; } creature.flags1 = f1; creature.flags2 = f2; } if (set_happiness) { cout << "Setting happiness to " << set_happiness_n << "..." << endl; Creatures->WriteHappiness(creature_idx, set_happiness_n); } if (remove_skills || remove_civil_skills || remove_military_skills) { DFHack::t_soul & soul = creature.defaultSoul; cout << "Removing skills..." << endl; for(unsigned int sk = 0; sk < soul.numSkills;sk++) { bool is_military = is_in(soul.skills[sk].id, military_skills, sizeof(military_skills)/sizeof(military_skills[0])); if (remove_skills || (remove_civil_skills && !is_military) || (remove_military_skills && is_military)) { soul.skills[sk].rating=0; soul.skills[sk].experience=0; } } // Doesn't work anyways, so better leave it alone //soul.numSkills=0; if (Creatures->WriteSkills(creature_idx, soul) == true) { cout << "Success writing skills." << endl; } else { cout << "Error writing skills." << endl; } } if (add_labor || remove_labor || remove_labors || make_hauler || remove_hauler) { if (add_labor) { cout << "Adding labor " << add_labor_n << "..." << endl; creature.labors[add_labor_n] = 1; } if (remove_labor) { cout << "Removing labor " << remove_labor_n << "..." << endl; creature.labors[remove_labor_n] = 0; } if (remove_labors) { cout << "Removing labors..." << endl; for(unsigned int lab = 0; lab < NUM_CREATURE_LABORS; lab++) { creature.labors[lab] = 0; } } if (remove_hauler) { for (int labs=0; labs < sizeof(hauler_labors)/sizeof(hauler_labors[0]); labs++) { creature.labors[hauler_labors[labs]] = 0; } } if (make_hauler) { cout << "Setting hauler labors..." << endl; for (int labs=0; labs < sizeof(hauler_labors)/sizeof(hauler_labors[0]); labs++) { creature.labors[hauler_labors[labs]] = 1; } } if (Creatures->WriteLabors(creature_idx, creature.labors) == true) { cout << "Success writing labors." << endl; } else { cout << "Error writing labors." << endl; } } if (tame_creature) { bool tame = true; DFHack::t_creaturflags1 f1 = creature.flags1; DFHack::t_creaturflags2 f2 = creature.flags2; // Site residents are intelligent, so don't // tame them. if (f2.bits.resident) tame = false; f1.bits.diplomat = false; f1.bits.merchant = false; f2.bits.resident = false; f2.bits.underworld = false; f2.bits.visitor_uninvited = false; f2.bits.roaming_wilderness_population_source = false; f2.bits.roaming_wilderness_population_source_not_a_map_feature = false; // Creatures which already belong to a civ might // be intelligent, so don't tame them. if (creature.civ == -1) f1.bits.tame = tame; if (!Creatures->WriteFlags(creature_idx, f1.whole, f2.whole)) { cout << "Error writing creature flags!" << endl; } int32_t civ = Creatures->GetDwarfCivId(); if (!Creatures->WriteCiv(creature_idx, civ)) { cout << "Error writing creature civ!" << endl; } creature.flags1 = f1; creature.flags2 = f2; } if (slaughter_creature) { DFHack::t_creaturflags1 f1 = creature.flags1; DFHack::t_creaturflags2 f2 = creature.flags2; f2.bits.slaughter = true; if (!Creatures->WriteFlags(creature_idx, f1.whole, f2.whole)) { cout << "Error writing creature flags!" << endl; } creature.flags1 = f1; creature.flags2 = f2; } } else { cout << "Error removing skills: Creature has no default soul." << endl; } printCreature(DF,creature,creature_idx); } /* End remove skills/labors */ } /* if (print creature) */ } /* End for(all creatures) */ } /* End if (we need to walk creatures) */ Creatures->Finish(); DF->Detach(); if (quiet == false) { cout << "Done. Press any key to continue" << endl; cin.ignore(); } return 0; }