diff --git a/NEWS.rst b/NEWS.rst index 54954029b..751daceb3 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -112,6 +112,8 @@ Misc Improvements - `createitem`: Can now create items anywhere without specifying a unit, as long as a unit exists on the map - `devel/export-dt-ini`: Updated for 0.42.06 - `devel/find-offsets`: Automated several more scans +- `fix/diplomats`: replaces ``fixdiplomats`` +- `fix/merchants`: replaces ``fixmerchants`` - `gui/gm-editor`: Now supports finding some items with a numeric ID (with ``i``) - `lua`: Now supports some built-in variables like `gui/gm-editor`, e.g. ``unit``, ``screen`` - `remotefortressreader`: Can now trigger keyboard events diff --git a/docs/Plugins.rst b/docs/Plugins.rst index 4e1886e8d..0c4a3237b 100644 --- a/docs/Plugins.rst +++ b/docs/Plugins.rst @@ -180,18 +180,6 @@ Bugfixes .. contents:: :local: -fixdiplomats -============ -Adds a Diplomat position to all Elven civilizations, allowing them to negotiate -tree cutting quotas - and you to violate them and start wars. -This was vanilla behaviour until ``0.31.12``, in which the "bug" was "fixed". - -fixmerchants -============ -Adds the Guild Representative position to all Human civilizations, -allowing them to make trade agreements. This was the default behaviour in -``0.28.181.40d`` and earlier. - .. _fix-unit-occupancy: fix-unit-occupancy diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index dd1c6344f..cc0de019f 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -99,7 +99,6 @@ if (BUILD_SUPPORTED) DFHACK_PLUGIN(fastdwarf fastdwarf.cpp) DFHACK_PLUGIN(filltraffic filltraffic.cpp) DFHACK_PLUGIN(fix-armory fix-armory.cpp) - DFHACK_PLUGIN(fixpositions fixpositions.cpp) DFHACK_PLUGIN(fix-unit-occupancy fix-unit-occupancy.cpp) DFHACK_PLUGIN(fixveins fixveins.cpp) DFHACK_PLUGIN(flows flows.cpp) diff --git a/plugins/fixpositions.cpp b/plugins/fixpositions.cpp deleted file mode 100644 index e136ee0a9..000000000 --- a/plugins/fixpositions.cpp +++ /dev/null @@ -1,244 +0,0 @@ -// Fix Entity Positions - make sure Elves have diplomats and Humans have guild representatives - -#include "Core.h" -#include -#include -#include - -#include -#include "df/world.h" -#include "df/historical_entity.h" -#include "df/entity_raw.h" -#include "df/entity_position.h" -#include "df/entity_position_responsibility.h" -#include "df/entity_position_assignment.h" - -using std::string; -using std::vector; -using namespace DFHack; -using namespace df::enums; - -DFHACK_PLUGIN("fixpositions"); -REQUIRE_GLOBAL(world); - -command_result df_fixdiplomats (color_ostream &out, vector ¶meters) -{ - if (!parameters.empty()) - return CR_WRONG_USAGE; - - CoreSuspender suspend; - - int checked = 0, fixed = 0; - for (int i = 0; i < world->entities.all.size(); i++) - { - df::historical_entity *ent = world->entities.all[i]; - // only work with civilizations - ignore groups and religions - if (ent->type != 0) - continue; - // only add diplomats for tree cap diplomacy - if (!ent->entity_raw->flags.is_set(entity_raw_flags::TREE_CAP_DIPLOMACY)) - continue; - checked++; - - bool update = true; - df::entity_position *pos = NULL; - // see if we need to add a new position or modify an existing one - for (int j = 0; j < ent->positions.own.size(); j++) - { - pos = ent->positions.own[j]; - if (pos->responsibilities[entity_position_responsibility::MAKE_INTRODUCTIONS] && - pos->responsibilities[entity_position_responsibility::MAKE_PEACE_AGREEMENTS] && - pos->responsibilities[entity_position_responsibility::MAKE_TOPIC_AGREEMENTS]) - { - // a diplomat position exists with the proper responsibilities - skip to the end - update = false; - break; - } - // Diplomat position already exists, but has the wrong options - modify it instead of creating a new one - if (pos->code == "DIPLOMAT") - break; - pos = NULL; - } - if (update) - { - // either there's no diplomat, or there is one and it's got the wrong responsibilities - if (!pos) - { - // there was no diplomat - create it - pos = new df::entity_position; - ent->positions.own.push_back(pos); - - pos->code = "DIPLOMAT"; - pos->id = ent->positions.next_position_id++; - pos->flags.set(entity_position_flags::DO_NOT_CULL); - pos->flags.set(entity_position_flags::MENIAL_WORK_EXEMPTION); - pos->flags.set(entity_position_flags::SLEEP_PRETENSION); - pos->flags.set(entity_position_flags::PUNISHMENT_EXEMPTION); - pos->flags.set(entity_position_flags::ACCOUNT_EXEMPT); - pos->flags.set(entity_position_flags::DUTY_BOUND); - pos->flags.set(entity_position_flags::COLOR); - pos->flags.set(entity_position_flags::HAS_RESPONSIBILITIES); - pos->flags.set(entity_position_flags::IS_DIPLOMAT); - pos->flags.set(entity_position_flags::IS_LEADER); - // not sure what these flags do, but the game sets them for a valid diplomat - pos->flags.set(entity_position_flags::unk_12); - pos->flags.set(entity_position_flags::unk_1a); - pos->flags.set(entity_position_flags::unk_1b); - pos->name[0] = "Diplomat"; - pos->name[1] = "Diplomats"; - pos->precedence = 70; - pos->color[0] = 7; - pos->color[1] = 0; - pos->color[2] = 1; - } - // assign responsibilities - pos->responsibilities[entity_position_responsibility::MAKE_INTRODUCTIONS] = true; - pos->responsibilities[entity_position_responsibility::MAKE_PEACE_AGREEMENTS] = true; - pos->responsibilities[entity_position_responsibility::MAKE_TOPIC_AGREEMENTS] = true; - } - - // make sure the diplomat position, whether we created it or not, is set up for proper assignment - bool assign = true; - for (int j = 0; j < ent->positions.assignments.size(); j++) - { - if (ent->positions.assignments[j]->position_id == pos->id) - { - // it is - nothing more to do here - assign = false; - break; - } - } - if (assign) - { - // it isn't - set it up - df::entity_position_assignment *asn = new df::entity_position_assignment; - ent->positions.assignments.push_back(asn); - - asn->id = ent->positions.next_assignment_id++; - asn->position_id = pos->id; - asn->flags.extend(0x1F); // make room for 32 flags - asn->flags.set(0); // and set the first one - } - if (update || assign) - fixed++; - } - out.print("Fixed %d of %d civilizations to enable tree cap diplomacy.\n", fixed, checked); - return CR_OK; -} - -command_result df_fixmerchants (color_ostream &out, vector ¶meters) -{ - if (!parameters.empty()) - return CR_WRONG_USAGE; - - CoreSuspender suspend; - - int checked = 0, fixed = 0; - for (int i = 0; i < world->entities.all.size(); i++) - { - df::historical_entity *ent = world->entities.all[i]; - // only work with civilizations - ignore groups and religions - if (ent->type != 0) - continue; - // only add guild reps for merchant nobility - if (!ent->entity_raw->flags.is_set(entity_raw_flags::MERCHANT_NOBILITY)) - continue; - checked++; - - bool update = true; - df::entity_position *pos = NULL; - // see if we need to add a new position or modify an existing one - for (int j = 0; j < ent->positions.own.size(); j++) - { - pos = ent->positions.own[j]; - if (pos->responsibilities[entity_position_responsibility::TRADE]) - { - // a guild rep exists with the proper responsibilities - skip to the end - update = false; - break; - } - // Guild Representative position already exists, but has the wrong options - modify it instead of creating a new one - if (pos->code == "GUILD_REPRESENTATIVE") - break; - pos = NULL; - } - if (update) - { - // either there's no guild rep, or there is one and it's got the wrong responsibilities - if (!pos) - { - // there was no guild rep - create it - pos = new df::entity_position; - ent->positions.own.push_back(pos); - - pos->code = "GUILD_REPRESENTATIVE"; - pos->id = ent->positions.next_position_id++; - pos->flags.set(entity_position_flags::DO_NOT_CULL); - pos->flags.set(entity_position_flags::MENIAL_WORK_EXEMPTION); - pos->flags.set(entity_position_flags::SLEEP_PRETENSION); - pos->flags.set(entity_position_flags::PUNISHMENT_EXEMPTION); - pos->flags.set(entity_position_flags::ACCOUNT_EXEMPT); - pos->flags.set(entity_position_flags::DUTY_BOUND); - pos->flags.set(entity_position_flags::COLOR); - pos->flags.set(entity_position_flags::HAS_RESPONSIBILITIES); - pos->flags.set(entity_position_flags::IS_DIPLOMAT); - pos->flags.set(entity_position_flags::IS_LEADER); - // not sure what these flags do, but the game sets them for a valid guild rep - pos->flags.set(entity_position_flags::unk_12); - pos->flags.set(entity_position_flags::unk_1a); - pos->flags.set(entity_position_flags::unk_1b); - pos->name[0] = "Guild Representative"; - pos->name[1] = "Guild Representatives"; - pos->precedence = 40; - pos->color[0] = 7; - pos->color[1] = 0; - pos->color[2] = 1; - } - // assign responsibilities - pos->responsibilities[entity_position_responsibility::TRADE] = true; - } - - // make sure the guild rep position, whether we created it or not, is set up for proper assignment - bool assign = true; - for (int j = 0; j < ent->positions.assignments.size(); j++) - { - if (ent->positions.assignments[j]->position_id == pos->id) - { - // it is - nothing more to do here - assign = false; - break; - } - } - if (assign) - { - // it isn't - set it up - df::entity_position_assignment *asn = new df::entity_position_assignment; - ent->positions.assignments.push_back(asn); - - asn->id = ent->positions.next_assignment_id++; - asn->position_id = pos->id; - asn->flags.extend(0x1F); // make room for 32 flags - asn->flags.set(0); // and set the first one - } - if (update || assign) - fixed++; - } - out.print("Fixed %d of %d civilizations to enable merchant nobility.\n", fixed, checked); - return CR_OK; -} - -DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) -{ - commands.push_back(PluginCommand( - "fixdiplomats", "Add Diplomat position to Elven civilizations for tree cap diplomacy.", - df_fixdiplomats, false)); - commands.push_back(PluginCommand( - "fixmerchants", "Add Guild Representative position to Human civilizations for merchant nobility.", - df_fixmerchants, false)); - return CR_OK; -} - -DFhackCExport command_result plugin_shutdown ( color_ostream &out ) -{ - return CR_OK; -} diff --git a/scripts/fix/diplomats.lua b/scripts/fix/diplomats.lua new file mode 100644 index 000000000..b1bcfe70b --- /dev/null +++ b/scripts/fix/diplomats.lua @@ -0,0 +1,100 @@ +-- Add Elven diplomats to negotiate tree caps +--[[=begin + +fix/diplomats +============= +Adds a Diplomat position to all Elven civilizations, allowing them to negotiate +tree cutting quotas - and you to violate them and start wars. +This was vanilla behaviour until ``0.31.12``, in which the "bug" was "fixed". + +=end]] + + +function update_pos(pos, ent) + pos = df.entity_position:new() + ent.positions.own:insert('#', pos) + + pos.code = "DIPLOMAT" + pos.id = ent.positions.next_position_id + 1 + pos.flags.DO_NOT_CULL = true + pos.flags.MENIAL_WORK_EXEMPTION = true + pos.flags.SLEEP_PRETENSION = true + pos.flags.PUNISHMENT_EXEMPTION = true + pos.flags.ACCOUNT_EXEMPT = true + pos.flags.DUTY_BOUND = true + pos.flags.COLOR = true + pos.flags.HAS_RESPONSIBILITIES = true + pos.flags.IS_DIPLOMAT = true + pos.flags.IS_LEADER = true + -- not sure what these flags do, but the game sets them for a valid diplomat + pos.flags.unk_12 = true + pos.flags.unk_1a = true + pos.flags.unk_1b = true + pos.name[0] = "Diplomat" + pos.name[1] = "Diplomats" + pos.precedence = 70 + pos.color[0] = 7 + pos.color[1] = 0 + pos.color[2] = 1 + + return pos +end + + + +checked = 0 +fixed = 0 + +for _,ent in pairs(df.global.world.entities.all) do + if ent.type == 0 and ent.entity_raw.flags.TREE_CAP_DIPLOMACY then + checked = checked + 1 + + update = true + -- see if we need to add a new position or modify an existing one + for _,pos in pairs(ent.positions.own) do + if pos.responsibilities.MAKE_INTRODUCTIONS and + pos.responsibilities.MAKE_PEACE_AGREEMENTS and + pos.responsibilities.MAKE_TOPIC_AGREEMENTS then + -- a diplomat position exists with the proper responsibilities - skip to the end + update = false + break + end + -- Diplomat position already exists, but has the wrong options - modify it instead of creating a new one + if pos.code == "DIPLOMAT" then break end + pos = nil + end + if update then + -- either there's no diplomat, or there is one and it's got the wrong responsibilities + if not pos then + pos = add_guild_rep(pos, ent) + end + -- assign responsibilities + pos.responsibilities.MAKE_INTRODUCTIONS = true + pos.responsibilities.MAKE_PEACE_AGREEMENTS = true + pos.responsibilities.MAKE_TOPIC_AGREEMENTS = true + end + -- make sure the diplomat position, whether we created it or not, is set up for proper assignment + assign = true + for _,p in pairs(ent.positions.assignments) do + if p.position_id == pos.id then -- it is - nothing more to do here + assign = false + break + end + end + if assign then -- it isn't - set it up + asn = df.entity_position_assignment:new() + ent.positions.assignments:insert('#', asn); + + asn.id = ent.positions.next_assignment_id + ent.positions.next_assignment_id = asn.id + 1 + asn.position_id = pos.id + asn.flags:resize(math.max(32, #asn.flags)) -- make room for 32 flags + asn.flags[0] = true -- and set the first one + end + if update or assign then + fixed = fixed + 1 + end + end +end + +print("Enabled tree cap diplomacy for "..fixed.." of "..checked.." civilizations.") diff --git a/scripts/fix/merchants.lua b/scripts/fix/merchants.lua new file mode 100644 index 000000000..ae25ec9b2 --- /dev/null +++ b/scripts/fix/merchants.lua @@ -0,0 +1,95 @@ +-- Allow humans to make trade agreements +--[[=begin + +fix/merchants +============= +Adds the Guild Representative position to all Human civilizations, +allowing them to make trade agreements. This was the default behaviour in +``0.28.181.40d`` and earlier. + +=end]] +checked = 0 +fixed = 0 + +function add_guild_rep(pos, ent) + -- there was no guild rep - create it + pos = df.entity_position:new() + ent.positions.own:insert('#', pos) + + pos.code = "GUILD_REPRESENTATIVE" + pos.id = ent.positions.next_position_id + 1 + pos.flags.DO_NOT_CULL = true + pos.flags.MENIAL_WORK_EXEMPTION = true + pos.flags.SLEEP_PRETENSION = true + pos.flags.PUNISHMENT_EXEMPTION = true + pos.flags.ACCOUNT_EXEMPT = true + pos.flags.DUTY_BOUND = true + pos.flags.COLOR = true + pos.flags.HAS_RESPONSIBILITIES = true + pos.flags.IS_DIPLOMAT = true + pos.flags.IS_LEADER = true + -- not sure what these flags do, but the game sets them for a valid guild rep + pos.flags.unk_12 = true + pos.flags.unk_1a = true + pos.flags.unk_1b = true + pos.name[0] = "Guild Representative" + pos.name[1] = "Guild Representatives" + pos.precedence = 40 + pos.color[0] = 7 + pos.color[1] = 0 + pos.color[2] = 1 + return pos +end + + +for _,ent in pairs(df.global.world.entities.all) do + if ent.type == 0 and ent.entity_raw.flags.MERCHANT_NOBILITY then + checked = checked + 1 + + update = true + -- see if we need to add a new position or modify an existing one + for _,pos in pairs(ent.positions.own) do + if pos.responsibilities.TRADE then + -- a guild rep exists with the proper responsibilities - skip to the end + update = false + break + end + -- Guild Representative position already exists, but has the wrong options - modify it instead of creating a new one + if pos.code == "GUILD_REPRESENTATIVE" then break end + pos = nil + end + if update then + -- either there's no guild rep, or there is one and it's got the wrong responsibilities + if not pos then + pos = add_guild_rep(pos, ent) + end + -- assign responsibilities + pos.responsibilities.ESTABLISH_COLONY_TRADE_AGREEMENTS = true + end + + -- make sure the guild rep position, whether we created it or not, is set up for proper assignment + assign = true + for _,p in pairs(ent.positions.assignments) do + if p.position_id == pos.id then -- it is - nothing more to do here + assign = false + break + end + end + if assign then + -- it isn't - set it up + asn = df.entity_position_assignment:new() + ent.positions.assignments:insert('#', asn) + + asn.id = ent.positions.next_assignment_id + ent.positions.next_assignment_id = asn.id + 1 + asn.position_id = pos.id + asn.flags:resize(math.max(32, #asn.flags)) -- make room for 32 flags + asn.flags[0] = true -- and set the first one + end + if update or assign then + fixed = fixed + 1 + end + end +end + +print("Added merchant nobility for "..fixed.." of "..checked.." civilizations.")