Implement a program to hack away bug 3708 (unengraveable ghosts).
TODO: Test long-term consequences.develop
parent
ebc4d21e66
commit
1d805ca328
@ -0,0 +1,217 @@
|
||||
/* Fixes bug 3708 (Ghosts that can't be engraved on a slab).
|
||||
|
||||
Cause of the bug:
|
||||
|
||||
In order to be engraved on a slab, the creature must be
|
||||
a historical figure, i.e. be in the historical figure list
|
||||
of the Legends mode. It seems that caravan guards are not
|
||||
added to that list until they do something notable, e.g.
|
||||
kill a goblin. Unfortunately, their own death doesn't
|
||||
trigger this sometimes.
|
||||
|
||||
Solution:
|
||||
|
||||
Steal a historical figure entry from a dead goblin, by
|
||||
replacing the IDs in the structures; also overwrite his
|
||||
name, race and profession to make the menus make slightly
|
||||
more sense.
|
||||
|
||||
Downsides:
|
||||
|
||||
- Obviously, this is an ugly hack.
|
||||
- The Legends mode still lists the guard as belonging to
|
||||
the goblin civilization, and killed by whoever killed the
|
||||
original goblin. There might be other inconsistencies.
|
||||
|
||||
Positive sides:
|
||||
|
||||
- Avoids messing with tricky creature control code,
|
||||
by allowing the ghost to be removed naturally.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <climits>
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <stdio.h>
|
||||
using namespace std;
|
||||
|
||||
#define DFHACK_WANT_MISCUTILS
|
||||
#include <DFHack.h>
|
||||
|
||||
enum likeType
|
||||
{
|
||||
FAIL = 0,
|
||||
MATERIAL = 1,
|
||||
ITEM = 2,
|
||||
FOOD = 3
|
||||
};
|
||||
|
||||
DFHack::Materials * Materials;
|
||||
DFHack::VersionInfo *mem;
|
||||
DFHack::Creatures * Creatures = NULL;
|
||||
|
||||
void printCreature(DFHack::Context * DF, const DFHack::t_creature & creature)
|
||||
{
|
||||
cout << "Address: " << hex << creature.origin << dec << ", creature race: " << Materials->raceEx[creature.race].rawname
|
||||
<< ", position: " << creature.x << "x " << creature.y << "y "<< creature.z << "z" << endl
|
||||
<< "Name: " << creature.name.first_name;
|
||||
|
||||
if (creature.name.nickname[0])
|
||||
cout << " `" << creature.name.nickname << "'";
|
||||
|
||||
DFHack::Translation * Tran = DF->getTranslation();
|
||||
|
||||
cout << " " << Tran->TranslateName(creature.name,false)
|
||||
<< " (" << Tran->TranslateName(creature.name,true) << ")" << endl;
|
||||
|
||||
cout << "Profession: " << mem->getProfession(creature.profession);
|
||||
|
||||
if(creature.custom_profession[0])
|
||||
cout << ", custom: " << creature.custom_profession;
|
||||
|
||||
uint32_t dayoflife = creature.birth_year*12*28 + creature.birth_time/1200;
|
||||
cout << endl
|
||||
<< "Born on the year " << creature.birth_year
|
||||
<< ", month " << (creature.birth_time/1200/28)
|
||||
<< ", day " << ((creature.birth_time/1200) % 28 + 1)
|
||||
<< ", " << dayoflife << " days lived." << endl << endl;
|
||||
}
|
||||
|
||||
|
||||
int main (int numargs, char ** args)
|
||||
{
|
||||
DFHack::World * World;
|
||||
DFHack::ContextManager DFMgr("Memory.xml");
|
||||
DFHack::Context* DF;
|
||||
try
|
||||
{
|
||||
DF = DFMgr.getSingleContext();
|
||||
DF->Attach();
|
||||
}
|
||||
catch (exception& e)
|
||||
{
|
||||
cerr << e.what() << endl;
|
||||
#ifndef LINUX_BUILD
|
||||
cin.ignore();
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
Creatures = DF->getCreatures();
|
||||
Materials = DF->getMaterials();
|
||||
World = DF->getWorld();
|
||||
DFHack::Translation * Tran = DF->getTranslation();
|
||||
|
||||
uint32_t numCreatures;
|
||||
if(!Creatures->Start(numCreatures))
|
||||
{
|
||||
cerr << "Can't get creatures" << endl;
|
||||
#ifndef LINUX_BUILD
|
||||
cin.ignore();
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
Materials->ReadCreatureTypes();
|
||||
Materials->ReadCreatureTypesEx();
|
||||
|
||||
mem = DF->getMemoryInfo();
|
||||
DFHack::Process *p = DF->getProcess();
|
||||
|
||||
if(!Tran->Start())
|
||||
{
|
||||
cerr << "Can't get name tables" << endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
DFHack::OffsetGroup *ogc = mem->getGroup("Creatures")->getGroup("creature");
|
||||
uint32_t o_flags3 = ogc->getOffset("flags3");
|
||||
uint32_t o_c_hfid = ogc->getGroup("advanced")->getOffset("hist_figure_id");
|
||||
|
||||
std::list<uint32_t> goblins;
|
||||
std::list<uint32_t> ghosts;
|
||||
|
||||
for(uint32_t i = 0; i < numCreatures; i++)
|
||||
{
|
||||
DFHack::t_creature temp;
|
||||
Creatures->ReadCreature(i,temp);
|
||||
|
||||
int32_t hfid = p->readDWord(temp.origin + o_c_hfid);
|
||||
|
||||
if (hfid > 0) {
|
||||
if (temp.flags1.bits.dead) {
|
||||
std::string name = Materials->raceEx[temp.race].rawname;
|
||||
if (name == "GOBLIN")
|
||||
goblins.push_back(i);
|
||||
}
|
||||
} else {
|
||||
uint32_t flags3 = p->readDWord(temp.origin + o_flags3);
|
||||
if (!(flags3 & 0x1000))
|
||||
continue;
|
||||
|
||||
ghosts.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (goblins.size() >= ghosts.size() && ghosts.size() > 0)
|
||||
{
|
||||
DFHack::OffsetGroup *ogf = mem->getGroup("Legends")->getGroup("figures");
|
||||
uint32_t f_vector = p->readDWord(ogf->getAddress("vector"));
|
||||
uint32_t f_id = ogf->getOffset("figure_id");
|
||||
uint32_t f_unit = ogf->getOffset("unit_id");
|
||||
uint32_t f_name = ogf->getOffset("name");
|
||||
uint32_t f_race = ogf->getOffset("race");
|
||||
uint32_t f_profession = ogf->getOffset("profession");
|
||||
|
||||
for (std::list<uint32_t>::iterator it = ghosts.begin(); it != ghosts.end(); ++it)
|
||||
{
|
||||
int i = *it;
|
||||
DFHack::t_creature ghost;
|
||||
Creatures->ReadCreature(i,ghost);
|
||||
|
||||
printCreature(DF,ghost);
|
||||
|
||||
int igoblin = goblins.front();
|
||||
goblins.pop_front();
|
||||
DFHack::t_creature goblin;
|
||||
Creatures->ReadCreature(igoblin,goblin);
|
||||
|
||||
printCreature(DF,goblin);
|
||||
|
||||
int32_t hfid = p->readDWord(goblin.origin + o_c_hfid);
|
||||
uint32_t fptr = p->readDWord(f_vector + 4*hfid);
|
||||
|
||||
if (p->readDWord(fptr + f_id) != hfid ||
|
||||
p->readDWord(fptr + f_unit) != goblin.id ||
|
||||
p->readWord(fptr + f_race) != goblin.race)
|
||||
{
|
||||
cout << "Data structure inconsistency detected, aborting.";
|
||||
break;
|
||||
}
|
||||
|
||||
if (1) {
|
||||
p->writeDWord(goblin.origin + o_c_hfid, -1);
|
||||
p->writeDWord(ghost.origin + o_c_hfid, hfid);
|
||||
p->writeDWord(fptr + f_unit, ghost.id);
|
||||
p->writeWord(fptr + f_race, ghost.race);
|
||||
p->writeWord(fptr + f_profession, ghost.profession);
|
||||
Creatures->CopyNameTo(ghost, fptr + f_name);
|
||||
cout << "Pair succesfully patched." << endl << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "No suitable ghosts, or not enough goblins." << endl;
|
||||
}
|
||||
|
||||
Creatures->Finish();
|
||||
DF->Detach();
|
||||
#ifndef LINUX_BUILD
|
||||
cout << "Done. Press any key to continue" << endl;
|
||||
cin.ignore();
|
||||
#endif
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue