|
| 1 | +#include "modules/Job.h" |
| 2 | +#include "modules/Items.h" |
| 3 | + |
| 4 | +#include "df/building_animaltrapst.h" |
| 5 | +#include "df/buildingitemst.h" |
| 6 | +#include "df/general_ref_building_holderst.h" |
| 7 | +#include "df/general_ref_contained_in_itemst.h" |
| 8 | +#include "df/general_ref_contains_itemst.h" |
| 9 | +#include "df/job.h" |
| 10 | + |
| 11 | +using namespace df::enums; |
| 12 | + |
| 13 | +struct animaltrap_reuse_hook : df::building_animaltrapst { |
| 14 | + typedef df::building_animaltrapst interpose_base; |
| 15 | + DEFINE_VMETHOD_INTERPOSE(void, updateAction, ()) |
| 16 | + { |
| 17 | + // Skip processing if the trap isn't fully built |
| 18 | + if (getBuildStage() != getMaxBuildStage()) return; |
| 19 | + |
| 20 | + // The first item is always the trap itself, and the second item (if any) is always either the Bait or the caught Vermin |
| 21 | + if ((contained_items.size() > 1) && (contained_items[1]->item->getType() == df::item_type::VERMIN)) |
| 22 | + { |
| 23 | + auto trap = contained_items[0]->item; |
| 24 | + auto vermin = contained_items[1]->item; |
| 25 | + |
| 26 | + // Make sure we don't already have a "Release Small Creature" job |
| 27 | + for (size_t j = 0; j < jobs.size(); j++) |
| 28 | + { |
| 29 | + if (jobs[j]->job_type == df::job_type::ReleaseSmallCreature) |
| 30 | + return; |
| 31 | + // Also bail out if the player marked the building for destruction |
| 32 | + if (jobs[j]->job_type == df::job_type::DestroyBuilding) |
| 33 | + return; |
| 34 | + } |
| 35 | + |
| 36 | + // Create the job |
| 37 | + auto job = new df::job(); |
| 38 | + Job::linkIntoWorld(job, true); |
| 39 | + |
| 40 | + job->job_type = df::job_type::ReleaseSmallCreature; |
| 41 | + job->pos.x = centerx; |
| 42 | + job->pos.y = centery; |
| 43 | + job->pos.z = z; |
| 44 | + |
| 45 | + // Attach the vermin to the job |
| 46 | + Job::attachJobItem(job, vermin, df::job_role_type::Hauled, -1, -1); |
| 47 | + |
| 48 | + // Link the job to the building |
| 49 | + df::general_ref *ref = df::allocate<df::general_ref_building_holderst>(); |
| 50 | + ref->setID(id); |
| 51 | + job->general_refs.push_back(ref); |
| 52 | + |
| 53 | + jobs.push_back(job); |
| 54 | + |
| 55 | + // Hack: put the vermin inside the trap ITEM right away, otherwise the job will cancel. |
| 56 | + // Normally, this doesn't happen until after the trap is deconstructed, but the game still |
| 57 | + // seems to handle everything correctly and doesn't leave any bad references afterwards. |
| 58 | + if (!Items::getContainer(vermin)) |
| 59 | + { |
| 60 | + // We can't use Items::moveToContainer, because that would remove it from the Building |
| 61 | + // (and cause the game to no longer recognize the trap as being "full"). |
| 62 | + // Instead, manually add the references and set the necessary bits. |
| 63 | + |
| 64 | + ref = df::allocate<df::general_ref_contained_in_itemst>(); |
| 65 | + ref->setID(trap->id); |
| 66 | + vermin->general_refs.push_back(ref); |
| 67 | + |
| 68 | + ref = df::allocate<df::general_ref_contains_itemst>(); |
| 69 | + ref->setID(vermin->id); |
| 70 | + trap->general_refs.push_back(ref); |
| 71 | + |
| 72 | + vermin->flags.bits.in_inventory = true; |
| 73 | + trap->flags.bits.weight_computed = false; |
| 74 | + // Don't set this flag here (even though it would normally get set), |
| 75 | + // since the game doesn't clear it after the vermin gets taken out |
| 76 | + // trap->flags.bits.container = true; |
| 77 | + } |
| 78 | + return; |
| 79 | + } |
| 80 | + INTERPOSE_NEXT(updateAction)(); |
| 81 | + } |
| 82 | +}; |
| 83 | + |
| 84 | +IMPLEMENT_VMETHOD_INTERPOSE(animaltrap_reuse_hook, updateAction); |
0 commit comments