// Creature dump #include #include #include #include #include using namespace std; #include #include #include #include template void print_bits ( T val, std::ostream& out ) { T n_bits = sizeof ( val ) * CHAR_BIT; for ( unsigned i = 0; i < n_bits; ++i ) { out<< !!( val & 1 ) << " "; val >>= 1; } } vector< vector > englishWords; vector< vector > foreignWords; uint32_t numCreatures; vector creaturestypes; void printDwarves(DFHack::API & DF) { int dwarfCounter = 0; for (uint32_t i = 0; i < numCreatures; i++) { DFHack::t_creature temp; DF.ReadCreature(i, temp); string type = creaturestypes[temp.type].id; if (type == "DWARF" && !temp.flags1.bits.dead && !temp.flags2.bits.killed) { cout << i << ":"; if (temp.name.nickname[0]) { cout << temp.name.nickname; } else { cout << temp.name.first_name; } string transName = DF.TranslateName(temp.name,englishWords,foreignWords, false); cout << " " << temp.custom_profession; //transName; if (dwarfCounter%3 != 2) { cout << '\t'; } else { cout << endl; } dwarfCounter++; } } } bool getDwarfSelection(DFHack::API & DF, DFHack::t_creature & toChange,string & changeString, string & commandString,int & eraseAmount,int &dwarfNum,bool &isName) { static string lastText; bool dwarfSuccess = false; while (!dwarfSuccess) { string input; cout << "\nSelect Dwarf to Change or q to Quit" << endl; DF.Resume(); getline (cin, input); DF.Suspend(); if (input == "q") { return false; } else if (input == "r") { printDwarves(DF); } else if (!input.empty()) { int num; stringstream(input) >> num;//= atol(input.c_str()); dwarfSuccess = DF.ReadCreature(num,toChange); string type = creaturestypes[toChange.type].id; if (type != "DWARF") { dwarfSuccess = false; } else { dwarfNum = num; } } } bool changeType = false; while (!changeType) { string input; cout << "\n(n)ickname or (p)rofession?" << endl; getline(cin, input); if (input == "q") { return false; } if (input == "n") { commandString = "pzyn"; eraseAmount = string(toChange.name.nickname).length(); changeType = true; isName = true; } else if (input == "p") { commandString = "pzyp"; eraseAmount = string(toChange.custom_profession).length(); changeType = true; isName = false; } } bool changeValue = false; while (!changeValue) { string input; cout << "value to change to?" << endl; getline(cin, input); if (input == "q") { return false; } if (!lastText.empty() && input.empty()) { changeValue = true; } else if ( !input.empty()) { lastText = input; changeValue = true; } } changeString = lastText; return true; } bool waitTillChanged(DFHack::API &DF, int creatureToCheck, string changeValue, bool isName) { DFHack::DFWindow * w = DF.getWindow(); DF.Suspend(); DFHack::t_creature testCre; DF.ReadCreature(creatureToCheck,testCre); int tryCount = 0; if (isName) { while (testCre.name.nickname != changeValue && tryCount <50) { DF.Resume(); w->TypeSpecial(DFHack::WAIT,1,100); DF.Suspend(); DF.ReadCreature(creatureToCheck,testCre); tryCount++; } } else { while (testCre.custom_profession != changeValue && tryCount < 50) { DF.Resume(); w->TypeSpecial(DFHack::WAIT,1,100); DF.Suspend(); DF.ReadCreature(creatureToCheck,testCre); tryCount++; } } if (tryCount >= 50) { cerr << "Something went wrong, make sure that DF is at the correct screen"; return false; } DF.Resume(); return true; } bool waitTillScreenState(DFHack::API &DF, string screenState,bool EqualTo=true) { DFHack::DFWindow * w = DF.getWindow(); DFHack::t_viewscreen current; DF.Suspend(); DF.ReadViewScreen(current); string nowScreenState; DF.getMemoryInfo()->resolveClassIDToClassname(current.type,nowScreenState); int tryCount = 0; while (((EqualTo && nowScreenState != screenState) || (!EqualTo && nowScreenState == screenState)) && tryCount < 50) { DF.Resume(); w->TypeSpecial(DFHack::WAIT,1,100); DF.Suspend(); DF.ReadViewScreen(current); tryCount++; } if (tryCount >= 50) { cerr << "Something went wrong, DF at " << nowScreenState << endl; return false; } DF.Resume(); return true; } bool waitTillCursorState(DFHack::API &DF, bool On) { DFHack::DFWindow * w = DF.getWindow(); int32_t x,y,z; int tryCount = 0; DF.Suspend(); bool cursorResult = DF.getCursorCoords(x,y,z); while (tryCount < 50 && On && !cursorResult || !On && cursorResult) { DF.Resume(); w->TypeSpecial(DFHack::WAIT,1,100); tryCount++; DF.Suspend(); cursorResult = DF.getCursorCoords(x,y,z); } if (tryCount >= 50) { cerr << "Something went wrong, cursor at x: " << x << " y: " << y << " z: " << z << endl; return false; } DF.Resume(); return true; } bool waitTillMenuState(DFHack::API &DF, uint32_t menuState,bool EqualTo=true) { int tryCount = 0; DFHack::DFWindow * w = DF.getWindow(); DF.Suspend(); uint32_t testState = DF.ReadMenuState(); while (tryCount < 50 && ((EqualTo && menuState != testState) || (!EqualTo && menuState == testState))) { DF.Resume(); w->TypeSpecial(DFHack::WAIT,1,100); tryCount++; DF.Suspend(); testState = DF.ReadMenuState(); } if (tryCount >= 50) { cerr << "Something went wrong, menuState: "<resolveClassIDToClassname(current.type,classname); while (classname != "viewscreen_dwarfmode") { w->TypeSpecial(DFHack::F9); // cancel out of text input in names // DF.TypeSpecial(DFHack::ENTER); // cancel out of text input in hotkeys w->TypeSpecial(DFHack::SPACE); // should move up a level if (!waitTillScreenState(DF,classname,false)) return false; // wait until screen changes from current DF.ReadViewScreen(current); DF.getMemoryInfo()->resolveClassIDToClassname(current.type,classname); } if (DF.ReadMenuState() != 0) {// if menu state != 0 then there is a menu, so escape it w->TypeSpecial(DFHack::F9); w->TypeSpecial(DFHack::ENTER); // exit out of any text prompts w->TypeSpecial(DFHack::SPACE); // go back to base state if (!waitTillMenuState(DF,0))return false; } DF.Resume(); return true; } bool setCursorToCreature(DFHack::API &DF) { DFHack::DFWindow * w = DF.getWindow(); int32_t x,y,z; DF.Suspend(); DF.getCursorCoords(x,y,z); DF.Resume(); if (x == -30000) { w->TypeStr("v"); if (!waitTillCursorState(DF,true)) return false; } else { // reset the cursor to be the creature cursor w->TypeSpecial(DFHack::SPACE); if (!waitTillCursorState(DF,false)) return false; w->TypeStr("v"); if (!waitTillCursorState(DF,true)) return false; } return true; } int main (void) { DFHack::API DF("Memory.xml"); if (!DF.Attach()) { cerr << "DF not found" << endl; return 1; } DF.Suspend(); DFHack::memory_info * mem = DF.getMemoryInfo(); if (!DF.ReadCreatureMatgloss(creaturestypes)) { cerr << "Can't get the creature types." << endl; return 1; } DF.InitReadNameTables(englishWords,foreignWords); DF.InitReadCreatures(numCreatures); DF.InitViewAndCursor(); DFHack::Process * p = DF.getProcess(); DFHack::DFWindow * w = DF.getWindow(); DFHack::t_creature toChange; string changeString,commandString; int eraseAmount; int toChangeNum; bool isName; bool useKeys = true; string input2; // use key event emulation or direct writing? cout << "\nUse \n1:Key simulation\n2:Direct Writing" << endl; getline(cin,input2); if (input2 == "1") { useKeys = true; } else { useKeys = false; } printDwarves(DF); while (getDwarfSelection(DF,toChange,changeString,commandString,eraseAmount,toChangeNum,isName)) { // limit length, DF doesn't accept input after this point if (changeString.size() > 39) { changeString.resize(39); } start: bool completed = false; if (useKeys) { if (moveToBaseWindow(DF) && setCursorToCreature(DF)) { DF.Suspend(); DF.setCursorCoords(toChange.x, toChange.y,toChange.z); vector underCursor; while (!DF.getCurrentCursorCreatures(underCursor)) { DF.Resume(); w->TypeSpecial(DFHack::WAIT,1,100); DF.Suspend(); DF.setCursorCoords(toChange.x, toChange.y,toChange.z); DF.ReadCreature(toChangeNum,toChange); } //CurrentCursorCreatures gives the creatures in the order that you see them with the 'k' cursor. //The 'v' cursor displays them in the order of last, then first,second,third and so on //Pretty weird, but it works //The only place that seems to display which creature is currently selected is on the stack, whose location is likely not static, so not usable if (underCursor[underCursor.size()-1] != toChange.origin) { for (int i = 0;iTypeStr("v",100); if (underCursor[i] == toChange.origin) { break; } } } DF.Resume(); w->TypeStr(commandString.c_str()); if (waitTillScreenState(DF,"viewscreen_customize_unit")) { DF.Resume(); w->TypeSpecial(DFHack::BACK_SPACE,eraseAmount); if (waitTillChanged(DF,toChangeNum,"",isName)) { DF.Resume(); w->TypeStr(changeString.c_str()); if (waitTillChanged(DF,toChangeNum,changeString,isName)) { DF.Resume(); w->TypeSpecial(DFHack::ENTER); w->TypeSpecial(DFHack::SPACE); // should take you to unit screen if everything worked if (waitTillScreenState(DF,"viewscreen_unit")) { DF.Resume(); w->TypeSpecial(DFHack::SPACE); if (waitTillScreenState(DF,"viewscreen_dwarfmode")) { DF.Resume(); w->TypeSpecial(DFHack::SPACE); if (waitTillCursorState(DF,false)) { completed = true; } } } } } } } if (!completed) { cerr << "Something went wrong, please reset DF to its original state, then press any key to continue" << endl; goto start; } } else { // will only work with the shm probably should check for it, but I don't know how, // I have the writeString function do nothing for normal mode if (commandString == "pzyn") // change nickname { p->writeSTLString(toChange.origin+mem->getOffset("creature_nick_name"),changeString); } else { p->writeSTLString(toChange.origin+mem->getOffset("creature_custom_profession"),changeString); } } DF.Suspend(); printDwarves(DF); } return 0; }