summaryrefslogtreecommitdiff
path: root/plugins/devel
diff options
context:
space:
mode:
authorAlexander Gavrilov2012-09-17 14:59:59 +0400
committerAlexander Gavrilov2012-09-17 14:59:59 +0400
commit82e870c8ddc9b64370c3828d1d4807306ac72887 (patch)
tree25477841b4e451aba3f2f52a4d6010befdf76b6e /plugins/devel
parentf2fde21b10c087fd95948a5f40ef972ac6688718 (diff)
downloaddfhack-82e870c8ddc9b64370c3828d1d4807306ac72887.tar.gz
dfhack-82e870c8ddc9b64370c3828d1d4807306ac72887.tar.bz2
dfhack-82e870c8ddc9b64370c3828d1d4807306ac72887.tar.xz
Move siege engine out of devel.
Diffstat (limited to 'plugins/devel')
-rw-r--r--plugins/devel/CMakeLists.txt1
-rw-r--r--plugins/devel/siege-engine.cpp1865
2 files changed, 0 insertions, 1866 deletions
diff --git a/plugins/devel/CMakeLists.txt b/plugins/devel/CMakeLists.txt
index 39e8f7b6..134d5cb6 100644
--- a/plugins/devel/CMakeLists.txt
+++ b/plugins/devel/CMakeLists.txt
@@ -18,7 +18,6 @@ DFHACK_PLUGIN(stripcaged stripcaged.cpp)
DFHACK_PLUGIN(rprobe rprobe.cpp)
DFHACK_PLUGIN(nestboxes nestboxes.cpp)
DFHACK_PLUGIN(vshook vshook.cpp)
-DFHACK_PLUGIN(siege-engine siege-engine.cpp LINK_LIBRARIES lua)
IF(UNIX)
DFHACK_PLUGIN(ref-index ref-index.cpp)
ENDIF()
diff --git a/plugins/devel/siege-engine.cpp b/plugins/devel/siege-engine.cpp
deleted file mode 100644
index 3b95aba3..00000000
--- a/plugins/devel/siege-engine.cpp
+++ /dev/null
@@ -1,1865 +0,0 @@
-#include "Core.h"
-#include <Console.h>
-#include <Export.h>
-#include <Error.h>
-#include <PluginManager.h>
-#include <modules/Gui.h>
-#include <modules/Screen.h>
-#include <modules/Maps.h>
-#include <modules/MapCache.h>
-#include <modules/World.h>
-#include <modules/Units.h>
-#include <modules/Job.h>
-#include <modules/Materials.h>
-#include <LuaTools.h>
-#include <TileTypes.h>
-#include <vector>
-#include <cstdio>
-#include <stack>
-#include <string>
-#include <cmath>
-#include <string.h>
-
-#include <VTableInterpose.h>
-#include "df/graphic.h"
-#include "df/building_siegeenginest.h"
-#include "df/builtin_mats.h"
-#include "df/world.h"
-#include "df/buildings_other_id.h"
-#include "df/job.h"
-#include "df/building_drawbuffer.h"
-#include "df/ui.h"
-#include "df/viewscreen_dwarfmodest.h"
-#include "df/ui_build_selector.h"
-#include "df/flow_info.h"
-#include "df/report.h"
-#include "df/proj_itemst.h"
-#include "df/unit.h"
-#include "df/unit_soul.h"
-#include "df/unit_skill.h"
-#include "df/physical_attribute_type.h"
-#include "df/creature_raw.h"
-#include "df/caste_raw.h"
-#include "df/caste_raw_flags.h"
-#include "df/assumed_identity.h"
-#include "df/game_mode.h"
-#include "df/unit_misc_trait.h"
-#include "df/job.h"
-#include "df/job_item.h"
-#include "df/item_actual.h"
-#include "df/items_other_id.h"
-#include "df/building_stockpilest.h"
-#include "df/stockpile_links.h"
-#include "df/workshop_profile.h"
-#include "df/strain_type.h"
-#include "df/material.h"
-#include "df/flow_type.h"
-
-#include "MiscUtils.h"
-
-using std::vector;
-using std::string;
-using std::stack;
-using namespace DFHack;
-using namespace df::enums;
-
-using df::global::gamemode;
-using df::global::gps;
-using df::global::world;
-using df::global::ui;
-using df::global::ui_build_selector;
-
-using Screen::Pen;
-
-DFHACK_PLUGIN("siege-engine");
-
-/*
- * Misc. utils
- */
-
-typedef std::pair<df::coord, df::coord> coord_range;
-
-static void set_range(coord_range *target, df::coord p1, df::coord p2)
-{
- if (!p1.isValid() || !p2.isValid())
- {
- *target = coord_range();
- }
- else
- {
- target->first.x = std::min(p1.x, p2.x);
- target->first.y = std::min(p1.y, p2.y);
- target->first.z = std::min(p1.z, p2.z);
- target->second.x = std::max(p1.x, p2.x);
- target->second.y = std::max(p1.y, p2.y);
- target->second.z = std::max(p1.z, p2.z);
- }
-}
-
-static bool is_range_valid(const coord_range &target)
-{
- return target.first.isValid() && target.second.isValid();
-}
-
-static bool is_in_range(const coord_range &target, df::coord pos)
-{
- return target.first.isValid() && target.second.isValid() &&
- target.first.x <= pos.x && pos.x <= target.second.x &&
- target.first.y <= pos.y && pos.y <= target.second.y &&
- target.first.z <= pos.z && pos.z <= target.second.z;
-}
-
-static std::pair<int, int> get_engine_range(df::building_siegeenginest *bld)
-{
- if (bld->type == siegeengine_type::Ballista)
- return std::make_pair(1, 200);
- else
- return std::make_pair(30, 100);
-}
-
-static void orient_engine(df::building_siegeenginest *bld, df::coord target)
-{
- int dx = target.x - bld->centerx;
- int dy = target.y - bld->centery;
-
- if (abs(dx) > abs(dy))
- bld->facing = (dx > 0) ?
- df::building_siegeenginest::Right :
- df::building_siegeenginest::Left;
- else
- bld->facing = (dy > 0) ?
- df::building_siegeenginest::Down :
- df::building_siegeenginest::Up;
-}
-
-static int random_int(int val)
-{
- return int(int64_t(rand())*val/RAND_MAX);
-}
-
-static int point_distance(df::coord speed)
-{
- return std::max(abs(speed.x), std::max(abs(speed.y), abs(speed.z)));
-}
-
-inline void normalize(float &x, float &y, float &z)
-{
- float dist = sqrtf(x*x + y*y + z*z);
- if (dist == 0.0f) return;
- x /= dist; y /= dist; z /= dist;
-}
-
-static void random_direction(float &x, float &y, float &z)
-{
- float a, b, d;
- for (;;) {
- a = (rand() + 0.5f)*2.0f/RAND_MAX - 1.0f;
- b = (rand() + 0.5f)*2.0f/RAND_MAX - 1.0f;
- d = a*a + b*b;
- if (d < 1.0f)
- break;
- }
-
- float sq = sqrtf(1-d);
- x = 2.0f*a*sq;
- y = 2.0f*b*sq;
- z = 1.0f - 2.0f*d;
-}
-
-static const int WEAR_TICKS = 806400;
-
-static bool apply_impact_damage(df::item *item, int minv, int maxv)
-{
- MaterialInfo info(item);
- if (!info.isValid())
- {
- item->setWear(3);
- return false;
- }
-
- auto &strength = info.material->strength;
-
- // Use random strain type excluding COMPRESSIVE (conveniently last)
- int type = random_int(strain_type::COMPRESSIVE);
- int power = minv + random_int(maxv-minv+1);
-
- // High elasticity materials just bend
- if (strength.strain_at_yield[type] >= 5000)
- return true;
-
- // Instant fracture?
- int fracture = strength.fracture[type];
- if (fracture <= power)
- {
- item->setWear(3);
- return false;
- }
-
- // Impact within elastic strain range?
- int yield = strength.yield[type];
- if (yield > power)
- return true;
-
- // Can wear?
- auto actual = virtual_cast<df::item_actual>(item);
- if (!actual)
- return false;
-
- // Transform plastic deformation to wear
- int max_wear = WEAR_TICKS * 4;
- int cur_wear = WEAR_TICKS * actual->wear + actual->wear_timer;
- cur_wear += int64_t(power - yield)*max_wear/(fracture - yield);
-
- if (cur_wear >= max_wear)
- {
- actual->wear = 3;
- return false;
- }
- else
- {
- actual->wear = cur_wear / WEAR_TICKS;
- actual->wear_timer = cur_wear % WEAR_TICKS;
- return true;
- }
-}
-
-/*
- * Configuration object
- */
-
-static bool enable_plugin();
-
-struct EngineInfo {
- int id;
- df::building_siegeenginest *bld;
-
- df::coord center;
- coord_range building_rect;
-
- bool is_catapult;
- int proj_speed, hit_delay;
- std::pair<int, int> fire_range;
-
- coord_range target;
-
- df::job_item_vector_id ammo_vector_id;
- df::item_type ammo_item_type;
-
- int operator_id, operator_frame;
-
- std::set<int> stockpiles;
- df::stockpile_links links;
- df::workshop_profile profile;
-
- bool hasTarget() { return is_range_valid(target); }
- bool onTarget(df::coord pos) { return is_in_range(target, pos); }
- df::coord getTargetSize() { return target.second - target.first; }
-
- bool isInRange(int dist) {
- return dist >= fire_range.first && dist <= fire_range.second;
- }
-};
-
-static std::map<df::building*, EngineInfo*> engines;
-static std::map<df::coord, df::building*> coord_engines;
-
-static EngineInfo *find_engine(df::building *bld, bool create = false)
-{
- auto ebld = strict_virtual_cast<df::building_siegeenginest>(bld);
- if (!ebld)
- return NULL;
-
- auto &obj = engines[bld];
-
- if (obj)
- {
- obj->bld = ebld;
- return obj;
- }
-
- if (!create)
- return NULL;
-
- obj = new EngineInfo();
-
- obj->id = bld->id;
- obj->bld = ebld;
- obj->center = df::coord(bld->centerx, bld->centery, bld->z);
- obj->building_rect = coord_range(
- df::coord(bld->x1, bld->y1, bld->z),
- df::coord(bld->x2, bld->y2, bld->z)
- );
- obj->is_catapult = (ebld->type == siegeengine_type::Catapult);
- obj->proj_speed = 2;
- obj->hit_delay = obj->is_catapult ? 2 : -1;
- obj->fire_range = get_engine_range(ebld);
-
- obj->ammo_vector_id = job_item_vector_id::BOULDER;
- obj->ammo_item_type = item_type::BOULDER;
-
- obj->operator_id = obj->operator_frame = -1;
-
- coord_engines[obj->center] = bld;
- return obj;
-}
-
-static EngineInfo *find_engine(lua_State *L, int idx, bool create = false, bool silent = false)
-{
- auto bld = Lua::CheckDFObject<df::building_siegeenginest>(L, idx);
-
- auto engine = find_engine(bld, create);
- if (!engine && !silent)
- luaL_error(L, "no such engine");
-
- return engine;
-}
-
-static EngineInfo *find_engine(df::coord pos)
-{
- auto engine = find_engine(coord_engines[pos]);
-
- if (engine)
- {
- auto bld0 = df::building::find(engine->id);
- auto bld = strict_virtual_cast<df::building_siegeenginest>(bld0);
- if (!bld)
- return NULL;
-
- engine->bld = bld;
- }
-
- return engine;
-}
-
-/*
- * Configuration management
- */
-
-static void clear_engines()
-{
- for (auto it = engines.begin(); it != engines.end(); ++it)
- delete it->second;
- engines.clear();
- coord_engines.clear();
-}
-
-static void load_engines()
-{
- clear_engines();
-
- auto pworld = Core::getInstance().getWorld();
- std::vector<PersistentDataItem> vec;
-
- pworld->GetPersistentData(&vec, "siege-engine/target/", true);
- for (auto it = vec.begin(); it != vec.end(); ++it)
- {
- auto engine = find_engine(df::building::find(it->ival(0)), true);
- if (!engine) continue;
- engine->target.first = df::coord(it->ival(1), it->ival(2), it->ival(3));
- engine->target.second = df::coord(it->ival(4), it->ival(5), it->ival(6));
- }
-
- pworld->GetPersistentData(&vec, "siege-engine/ammo/", true);
- for (auto it = vec.begin(); it != vec.end(); ++it)
- {
- auto engine = find_engine(df::building::find(it->ival(0)), true);
- if (!engine) continue;
- engine->ammo_vector_id = (df::job_item_vector_id)it->ival(1);
- engine->ammo_item_type = (df::item_type)it->ival(2);
- }
-
- pworld->GetPersistentData(&vec, "siege-engine/stockpiles/", true);
- for (auto it = vec.begin(); it != vec.end(); ++it)
- {
- auto engine = find_engine(df::building::find(it->ival(0)), true);
- if (!engine)
- continue;
- auto pile = df::building::find(it->ival(1));
- if (!pile || pile->getType() != building_type::Stockpile)
- {
- pworld->DeletePersistentData(*it);
- continue;;
- }
-
- engine->stockpiles.insert(it->ival(1));
- }
-
- pworld->GetPersistentData(&vec, "siege-engine/profiles/", true);
- for (auto it = vec.begin(); it != vec.end(); ++it)
- {
- auto engine = find_engine(df::building::find(it->ival(0)), true);
- if (!engine) continue;
- engine->profile.min_level = it->ival(1);
- engine->profile.max_level = it->ival(2);
- }
-
- pworld->GetPersistentData(&vec, "siege-engine/profile-workers/", true);
- for (auto it = vec.begin(); it != vec.end(); ++it)
- {
- auto engine = find_engine(df::building::find(it->ival(0)), true);
- if (!engine)
- continue;
- auto unit = df::unit::find(it->ival(1));
- if (!unit || !Units::isCitizen(unit))
- {
- pworld->DeletePersistentData(*it);
- continue;
- }
- engine->profile.permitted_workers.push_back(it->ival(1));
- }
-}
-
-static int getTargetArea(lua_State *L)
-{
- auto engine = find_engine(L, 1, false, true);
-
- if (engine && engine->hasTarget())
- {
- Lua::Push(L, engine->target.first);
- Lua::Push(L, engine->target.second);
- }
- else
- {
- lua_pushnil(L);
- lua_pushnil(L);
- }
-
- return 2;
-}
-
-static void clearTargetArea(df::building_siegeenginest *bld)
-{
- CHECK_NULL_POINTER(bld);
-
- if (auto engine = find_engine(bld))
- engine->target = coord_range();
-
- auto pworld = Core::getInstance().getWorld();
- auto key = stl_sprintf("siege-engine/target/%d", bld->id);
- pworld->DeletePersistentData(pworld->GetPersistentData(key));
-}
-
-static bool setTargetArea(df::building_siegeenginest *bld, df::coord target_min, df::coord target_max)
-{
- CHECK_NULL_POINTER(bld);
- CHECK_INVALID_ARGUMENT(target_min.isValid() && target_max.isValid());
-
- if (!enable_plugin())
- return false;
-
- auto pworld = Core::getInstance().getWorld();
- auto key = stl_sprintf("siege-engine/target/%d", bld->id);
- auto entry = pworld->GetPersistentData(key, NULL);
- if (!entry.isValid())
- return false;
-
- auto engine = find_engine(bld, true);
-
- set_range(&engine->target, target_min, target_max);
-
- entry.ival(0) = bld->id;
- entry.ival(1) = engine->target.first.x;
- entry.ival(2) = engine->target.first.y;
- entry.ival(3) = engine->target.first.z;
- entry.ival(4) = engine->target.second.x;
- entry.ival(5) = engine->target.second.y;
- entry.ival(6) = engine->target.second.z;
-
- df::coord sum = target_min + target_max;
- orient_engine(bld, df::coord(sum.x/2, sum.y/2, sum.z/2));
-
- return true;
-}
-
-static int getAmmoItem(lua_State *L)
-{
- auto engine = find_engine(L, 1, false, true);
- if (!engine)
- Lua::Push(L, item_type::BOULDER);
- else
- Lua::Push(L, engine->ammo_item_type);
- return 1;
-}
-
-static int setAmmoItem(lua_State *L)
-{
- if (!enable_plugin())
- return 0;
-
- auto engine = find_engine(L, 1, true);
- auto item_type = (df::item_type)luaL_optint(L, 2, item_type::BOULDER);
- if (!is_valid_enum_item(item_type))
- luaL_argerror(L, 2, "invalid item type");
-
- auto pworld = Core::getInstance().getWorld();
- auto key = stl_sprintf("siege-engine/ammo/%d", engine->id);
- auto entry = pworld->GetPersistentData(key, NULL);
- if (!entry.isValid())
- return 0;
-
- engine->ammo_vector_id = job_item_vector_id::ANY_FREE;
- engine->ammo_item_type = item_type;
-
- FOR_ENUM_ITEMS(job_item_vector_id, id)
- {
- auto other = ENUM_ATTR(job_item_vector_id, other, id);
- auto type = ENUM_ATTR(items_other_id, item, other);
- if (type == item_type)
- {
- engine->ammo_vector_id = id;
- break;
- }
- }
-
- entry.ival(0) = engine->id;
- entry.ival(1) = engine->ammo_vector_id;
- entry.ival(2) = engine->ammo_item_type;
-
- lua_pushboolean(L, true);
- return 1;
-}
-
-static void forgetStockpileLink(EngineInfo *engine, int pile_id)
-{
- engine->stockpiles.erase(pile_id);
-
- auto pworld = Core::getInstance().getWorld();
- auto key = stl_sprintf("siege-engine/stockpiles/%d/%d", engine->id, pile_id);
- pworld->DeletePersistentData(pworld->GetPersistentData(key));
-}
-
-static void update_stockpile_links(EngineInfo *engine)
-{
- engine->links.take_from_pile.clear();
-
- for (auto it = engine->stockpiles.begin(); it != engine->stockpiles.end(); )
- {
- int id = *it; ++it;
- auto pile = df::building::find(id);
-
- if (!pile || pile->getType() != building_type::Stockpile)
- forgetStockpileLink(engine, id);
- else
- // The vector is sorted, but we are iterating through a sorted set
- engine->links.take_from_pile.push_back(pile);
- }
-}
-
-static int getStockpileLinks(lua_State *L)
-{
- auto engine = find_engine(L, 1, false, true);
- if (!engine || engine->stockpiles.empty())
- return 0;
-
- update_stockpile_links(engine);
-
- auto &links = engine->links.take_from_pile;
- lua_createtable(L, links.size(), 0);
-
- for (size_t i = 0; i < links.size(); i++)
- {
- Lua::Push(L, links[i]);
- lua_rawseti(L, -2, i+1);
- }
-
- return 1;
-}
-
-static bool isLinkedToPile(df::building_siegeenginest *bld, df::building_stockpilest *pile)
-{
- CHECK_NULL_POINTER(bld);
- CHECK_NULL_POINTER(pile);
-
- auto engine = find_engine(bld);
-
- return engine && engine->stockpiles.count(pile->id);
-}
-
-static bool addStockpileLink(df::building_siegeenginest *bld, df::building_stockpilest *pile)
-{
- CHECK_NULL_POINTER(bld);
- CHECK_NULL_POINTER(pile);
-
- if (!enable_plugin())
- return false;
-
- auto pworld = Core::getInstance().getWorld();
- auto key = stl_sprintf("siege-engine/stockpiles/%d/%d", bld->id, pile->id);
- auto entry = pworld->GetPersistentData(key, NULL);
- if (!entry.isValid())
- return false;
-
- auto engine = find_engine(bld, true);
-
- entry.ival(0) = bld->id;
- entry.ival(1) = pile->id;
-
- engine->stockpiles.insert(pile->id);
- return true;
-}
-
-static bool removeStockpileLink(df::building_siegeenginest *bld, df::building_stockpilest *pile)
-{
- CHECK_NULL_POINTER(bld);
- CHECK_NULL_POINTER(pile);
-
- if (auto engine = find_engine(bld))
- {
- forgetStockpileLink(engine, pile->id);
- return true;
- }
-
- return false;
-}
-
-static df::workshop_profile *saveWorkshopProfile(df::building_siegeenginest *bld)
-{
- CHECK_NULL_POINTER(bld);
-
- if (!enable_plugin())
- return NULL;
-
- // Save skill limits
- auto pworld = Core::getInstance().getWorld();
- auto key = stl_sprintf("siege-engine/profiles/%d", bld->id);
- auto entry = pworld->GetPersistentData(key, NULL);
- if (!entry.isValid())
- return NULL;
-
- auto engine = find_engine(bld, true);
-
- entry.ival(0) = engine->id;
- entry.ival(1) = engine->profile.min_level;
- entry.ival(2) = engine->profile.max_level;
-
- // Save worker list
- std::vector<PersistentDataItem> vec;
- auto &workers = engine->profile.permitted_workers;
-
- key = stl_sprintf("siege-engine/profile-workers/%d", bld->id);
- pworld->GetPersistentData(&vec, key, true);
-
- for (auto it = vec.begin(); it != vec.end(); ++it)
- {
- if (linear_index(workers, it->ival(1)) < 0)
- pworld->DeletePersistentData(*it);
- }
-
- for (size_t i = 0; i < workers.size(); i++)
- {
- key = stl_sprintf("siege-engine/profile-workers/%d/%d", bld->id, workers[i]);
- entry = pworld->GetPersistentData(key, NULL);
- if (!entry.isValid())
- continue;
- entry.ival(0) = engine->id;
- entry.ival(1) = workers[i];
- }
-
- return &engine->profile;
-}
-
-static int getOperatorSkill(df::building_siegeenginest *bld, bool force = false)
-{
- CHECK_NULL_POINTER(bld);
-
- auto engine = find_engine(bld);
- if (!engine)
- return 0;
-
- if (engine->operator_id != -1 &&
- (world->frame_counter - engine->operator_frame) <= 5)
- {
- auto op_unit = df::unit::find(engine->operator_id);
- if (op_unit)
- return Units::getEffectiveSkill(op_unit, job_skill::SIEGEOPERATE);
- }
-
- if (force)
- {
- color_ostream_proxy out(Core::getInstance().getConsole());
- out.print("Forced siege operator search\n");
-
- auto &active = world->units.active;
- for (size_t i = 0; i < active.size(); i++)
- if (active[i]->pos == engine->center && Units::isCitizen(active[i]))
- return Units::getEffectiveSkill(active[i], job_skill::SIEGEOPERATE);
- }
-
- return 0;
-}
-
-/*
- * Trajectory raytracing
- */
-
-struct ProjectilePath {
- static const int DEFAULT_FUDGE = 31;
-
- df::coord origin, goal, target, fudge_delta;
- int divisor, fudge_factor;
- df::coord speed, direction;
-
- ProjectilePath(df::coord origin, df::coord goal) :
- origin(origin), goal(goal), fudge_factor(1)
- {
- fudge_delta = df::coord(0,0,0);
- calc_line();
- }
-
- ProjectilePath(df::coord origin, df::coord goal, df::coord delta, int factor) :
- origin(origin), goal(goal), fudge_delta(delta), fudge_factor(factor)
- {
- calc_line();
- }
-
- ProjectilePath(df::coord origin, df::coord goal, float zdelta, int factor = DEFAULT_FUDGE) :
- origin(origin), goal(goal), fudge_factor(factor)
- {
- fudge_delta = df::coord(0,0,int(factor * zdelta));
- calc_line();
- }
-
- void calc_line()
- {
- speed = goal - origin;
- speed.x *= fudge_factor;
- speed.y *= fudge_factor;
- speed.z *= fudge_factor;
- speed = speed + fudge_delta;
- target = origin + speed;
- divisor = point_distance(speed);
- if (divisor <= 0) divisor = 1;
- direction = df::coord(speed.x>=0?1:-1,speed.y>=0?1:-1,speed.z>=0?1:-1);
- }
-
- df::coord operator[] (int i) const
- {
- int div2 = divisor * 2;
- int bias = divisor-1;
- return origin + df::coord(
- (2*speed.x*i + direction.x*bias)/div2,
- (2*speed.y*i + direction.y*bias)/div2,
- (2*speed.z*i + direction.z*bias)/div2
- );
- }
-};
-
-static ProjectilePath decode_path(lua_State *L, int idx, df::coord origin)
-{
- idx = lua_absindex(L, idx);
-
- Lua::StackUnwinder frame(L);
- df::coord goal;
-
- lua_getfield(L, idx, "target");
- Lua::CheckDFAssign(L, &goal, frame[1]);
-
- lua_getfield(L, idx, "delta");
-
- if (!lua_isnil(L, frame[2]))
- {
- lua_getfield(L, idx, "factor");
- int factor = luaL_optnumber(L, frame[3], ProjectilePath::DEFAULT_FUDGE);
-
- if (lua_isnumber(L, frame[2]))
- return ProjectilePath(origin, goal, lua_tonumber(L, frame[2]), factor);
-
- df::coord delta;
- Lua::CheckDFAssign(L, &delta, frame[2]);
-
- return ProjectilePath(origin, goal, delta, factor);
- }
-
- return ProjectilePath(origin, goal);
-}
-
-static int projPosAtStep(lua_State *L)
-{
- auto engine = find_engine(L, 1);
- auto path = decode_path(L, 2, engine->center);
- int step = luaL_checkint(L, 3);
- Lua::Push(L, path[step]);
- return 1;
-}
-
-static bool isPassableTile(df::coord pos)
-{
- auto ptile = Maps::getTileType(pos);
-
- return !ptile || FlowPassable(*ptile);
-}
-
-static bool isTargetableTile(df::coord pos)
-{
- auto ptile = Maps::getTileType(pos);
-
- return ptile && FlowPassable(*ptile) && !isOpenTerrain(*ptile);
-}
-
-static bool isTreeTile(df::coord pos)
-{
- auto ptile = Maps::getTileType(pos);
-
- return ptile && tileShape(*ptile) == tiletype_shape::TREE;
-}
-
-static bool adjustToTarget(EngineInfo *engine, df::coord *pos)
-{
- if (isTargetableTile(*pos))
- return true;
-
- for (df::coord fudge = *pos;
- fudge.z <= engine->target.second.z; fudge.z++)
- {
- if (!isTargetableTile(fudge))
- continue;
- *pos = fudge;
- return true;
- }
-
- for (df::coord fudge = *pos;
- fudge.z >= engine->target.first.z; fudge.z--)
- {
- if (!isTargetableTile(fudge))
- continue;
- *pos = fudge;
- return true;
- }
-
- return false;
-}
-
-static int adjustToTarget(lua_State *L)
-{
- auto engine = find_engine(L, 1, true);
- df::coord pos;
- Lua::CheckDFAssign(L, &pos, 2);
- bool ok = adjustToTarget(engine, &pos);
- Lua::Push(L, pos);
- Lua::Push(L, ok);
- return 2;
-}
-
-static const char* const hit_type_names[] = {
- "wall", "floor", "ceiling", "map_edge", "tree"
-};
-
-struct PathMetrics {
- enum CollisionType {
- Impassable,
- Floor,
- Ceiling,
- MapEdge,
- Tree
- } hit_type;
-
- int collision_step, collision_z_step;
- int goal_step, goal_z_step, goal_distance;
-
- bool hits() const { return collision_step > goal_step; }
-
- PathMetrics(const ProjectilePath &path)
- {
- compute(path);
- }
-
- void compute(const ProjectilePath &path)
- {
- collision_step = goal_step = goal_z_step = 1000000;
- collision_z_step = 0;
-
- goal_distance = point_distance(path.origin - path.goal);
-
- int step = 0;
- df::coord prev_pos = path.origin;
-
- for (;;) {
- df::coord cur_pos = path[++step];
- if (cur_pos == prev_pos)
- break;
-
- if (cur_pos.z == path.goal.z)
- {
- goal_z_step = std::min(step, goal_z_step);
- if (cur_pos == path.goal)
- goal_step = step;
- }
-
- if (!Maps::isValidTilePos(cur_pos))
- {
- hit_type = PathMetrics::MapEdge;
- break;
- }
-
- if (!isPassableTile(cur_pos))
- {
- if (isTreeTile(cur_pos))
- {
- // The projectile code has a bug where it will
- // hit a tree on the same tick as a Z level change.
- if (cur_pos.z != prev_pos.z)
- {
- hit_type = Tree;
- break;
- }
- }
- else
- {
- hit_type = Impassable;
- break;
- }
- }
-
- if (cur_pos.z != prev_pos.z)
- {
- int top_z = std::max(prev_pos.z, cur_pos.z);
- auto ptile = Maps::getTileType(cur_pos.x, cur_pos.y, top_z);
-
- if (ptile && !LowPassable(*ptile))
- {
- hit_type = (cur_pos.z > prev_pos.z ? Ceiling : Floor);
- break;
- }
-
- collision_z_step = step;
- }
-
- prev_pos = cur_pos;
- }
-
- collision_step = step;
- }
-};
-
-enum TargetTileStatus {
- TARGET_OK, TARGET_RANGE, TARGET_BLOCKED, TARGET_SEMIBLOCKED
-};
-static const char* const target_tile_type_names[] = {
- "ok", "out_of_range", "blocked", "semi_blocked"
-};
-
-static TargetTileStatus calcTileStatus(EngineInfo *engine, const PathMetrics &raytrace)
-{
- if (raytrace.hits())
- {
- if (engine->isInRange(raytrace.goal_step))
- return TARGET_OK;
- else
- return TARGET_RANGE;
- }
- else
- return TARGET_BLOCKED;
-}
-
-static int projPathMetrics(lua_State *L)
-{
- auto engine = find_engine(L, 1);
- auto path = decode_path(L, 2, engine->center);
-
- PathMetrics info(path);
-
- lua_createtable(L, 0, 7);
- Lua::SetField(L, hit_type_names[info.hit_type], -1, "hit_type");
- Lua::SetField(L, info.collision_step, -1, "collision_step");
- Lua::SetField(L, info.collision_z_step, -1, "collision_z_step");
- Lua::SetField(L, info.goal_distance, -1, "goal_distance");
- if (info.goal_step < info.collision_step)
- Lua::SetField(L, info.goal_step, -1, "goal_step");
- if (info.goal_z_step < info.collision_step)
- Lua::SetField(L, info.goal_z_step, -1, "goal_z_step");
- Lua::SetField(L, target_tile_type_names[calcTileStatus(engine, info)], -1, "status");
- return 1;
-}
-
-static TargetTileStatus calcTileStatus(EngineInfo *engine, df::coord target, float zdelta)
-{
- ProjectilePath path(engine->center, target, zdelta);
- PathMetrics raytrace(path);
- return calcTileStatus(engine, raytrace);
-}
-
-static TargetTileStatus calcTileStatus(EngineInfo *engine, df::coord target)
-{
- auto status = calcTileStatus(engine, target, 0.0f);
-
- if (status == TARGET_BLOCKED)
- {
- if (calcTileStatus(engine, target, 0.5f) < TARGET_BLOCKED)
- return TARGET_SEMIBLOCKED;
-
- if (calcTileStatus(engine, target, -0.5f) < TARGET_BLOCKED)
- return TARGET_SEMIBLOCKED;
- }
-
- return status;
-}
-
-static std::string getTileStatus(df::building_siegeenginest *bld, df::coord tile_pos)
-{
- auto engine = find_engine(bld, true);
- if (!engine)
- return "invalid";
-
- return target_tile_type_names[calcTileStatus(engine, tile_pos)];
-}
-
-static void paintAimScreen(df::building_siegeenginest *bld, df::coord view, df::coord2d ltop, df::coord2d size)
-{
- auto engine = find_engine(bld, true);
- CHECK_NULL_POINTER(engine);
-
- for (int x = 0; x < size.x; x++)
- {
- for (int y = 0; y < size.y; y++)
- {
- df::coord tile_pos = view + df::coord(x,y,0);
- if (is_in_range(engine->building_rect, tile_pos))
- continue;
-
- Pen cur_tile = Screen::readTile(ltop.x+x, ltop.y+y);
- if (!cur_tile.valid())
- continue;
-
- int color;
-
- switch (calcTileStatus(engine, tile_pos))
- {
- case TARGET_OK:
- color = COLOR_GREEN;
- break;
- case TARGET_RANGE:
- color = COLOR_CYAN;
- break;
- case TARGET_BLOCKED:
- color = COLOR_RED;
- break;
- case TARGET_SEMIBLOCKED:
- color = COLOR_BROWN;
- break;
- }
-
- if (cur_tile.fg && cur_tile.ch != ' ')
- {
- cur_tile.fg = color;
- cur_tile.bg = 0;
- }
- else
- {
- cur_tile.fg = 0;
- cur_tile.bg = color;
- }
-
- cur_tile.bold = engine->onTarget(tile_pos);
-
- if (cur_tile.tile)
- cur_tile.tile_mode = Pen::CharColor;
-
- Screen::paintTile(cur_tile, ltop.x+x, ltop.y+y);
- }
- }
-}
-
-/*
- * Unit tracking
- */
-
-static const float MAX_TIME = 1000000.0f;
-
-struct UnitPath {
- df::unit *unit;
- std::map<float, df::coord> path;
-
- struct Hit {
- UnitPath *path;
- df::coord pos;
- int dist;
- float time, lmargin, rmargin;
- };
-
- static std::map<df::unit*, UnitPath*> cache;
-
- static UnitPath *get(df::unit *unit)
- {
- auto &cv = cache[unit];
- if (!cv) cv = new UnitPath(unit);
- return cv;
- };
-
- UnitPath(df::unit *unit) : unit(unit)
- {
- if (unit->flags1.bits.rider)
- {
- auto mount = df::unit::find(unit->relations.rider_mount_id);
-
- if (mount)
- {
- path = get(mount)->path;
- return;
- }
- }
-
- df::coord pos = unit->pos;
- df::coord dest = unit->path.dest;
- auto &upath = unit->path.path;
-
- if (dest.isValid() && !upath.x.empty())
- {
- float time = unit->counters.job_counter+0.5f;
- float speed = Units::computeMovementSpeed(unit)/100.0f;
-
- if (unit->counters.unconscious > 0)
- time += unit->counters.unconscious;
-
- for (size_t i = 0; i < upath.size(); i++)
- {
- df::coord new_pos = upath[i];
- if (new_pos == pos)
- continue;
-
- float delay = speed;
- if (new_pos.x != pos.x && new_pos.y != pos.y)
- delay *= 362.0/256.0;
-
- path[time] = pos;
- pos = new_pos;
- time += delay + 1;
- }
- }
-
- path[MAX_TIME] = pos;
- }
-
- void get_margin(std::map<float,df::coord>::iterator &it, float time, float *lmargin, float *rmargin)
- {
- auto it2 = it;
- *lmargin = (it == path.begin()) ? MAX_TIME : time - (--it2)->first;
- *rmargin = (it->first == MAX_TIME) ? MAX_TIME : it->first - time;
- }
-
- df::coord posAtTime(float time, float *lmargin = NULL, float *rmargin = NULL)
- {
- CHECK_INVALID_ARGUMENT(time < MAX_TIME);
-
- auto it = path.upper_bound(time);
- if (lmargin)
- get_margin(it, time, lmargin, rmargin);
- return it->second;
- }
-
- bool findHits(EngineInfo *engine, std::vector<Hit> *hit_points, float bias)
- {
- df::coord origin = engine->center;
-
- Hit info;
- info.path = this;
-
- for (auto it = path.begin(); it != path.end(); ++it)
- {
- info.pos = it->second;
- info.dist = point_distance(origin - info.pos);
- info.time = float(info.dist)*(engine->proj_speed+1) + engine->hit_delay + bias;
- get_margin(it, info.time, &info.lmargin, &info.rmargin);
-
- if (info.lmargin > 0 && info.rmargin > 0)
- {
- if (engine->onTarget(info.pos) && engine->isInRange(info.dist))
- hit_points->push_back(info);
- }
- }
-
- return !hit_points->empty();
- }
-};
-
-std::map<df::unit*, UnitPath*> UnitPath::cache;
-
-static void push_margin(lua_State *L, float margin)
-{
- if (margin == MAX_TIME)
- lua_pushnil(L);
- else
- lua_pushnumber(L, margin);
-}
-
-static int traceUnitPath(lua_State *L)
-{
- auto unit = Lua::CheckDFObject<df::unit>(L, 1);
-
- CHECK_NULL_POINTER(unit);
-
- size_t idx = 1;
- auto info = UnitPath::get(unit);
- lua_createtable(L, info->path.size(), 0);
-
- float last_time = 0.0f;
- for (auto it = info->path.begin(); it != info->path.end(); ++it)
- {
- Lua::Push(L, it->second);
- if (idx > 1)
- {
- lua_pushnumber(L, last_time);
- lua_setfield(L, -2, "from");
- }
- if (idx < info->path.size())
- {
- lua_pushnumber(L, it->first);
- lua_setfield(L, -2, "to");
- }
- lua_rawseti(L, -2, idx++);
- last_time = it->first;
- }
-
- return 1;
-}
-
-static int unitPosAtTime(lua_State *L)
-{
- auto unit = Lua::CheckDFObject<df::unit>(L, 1);
- float time = luaL_checknumber(L, 2);
-
- CHECK_NULL_POINTER(unit);
-
- float lmargin, rmargin;
- auto info = UnitPath::get(unit);
-
- Lua::Push(L, info->posAtTime(time, &lmargin, &rmargin));
- push_margin(L, lmargin);
- push_margin(L, rmargin);
- return 3;
-}
-
-static bool canTargetUnit(df::unit *unit)
-{
- CHECK_NULL_POINTER(unit);
-
- if (unit->flags1.bits.dead ||
- unit->flags3.bits.ghostly ||
- unit->flags1.bits.caged ||
- unit->flags1.bits.hidden_in_ambush)
- return false;
-
- return true;
-}
-
-static void proposeUnitHits(EngineInfo *engine, std::vector<UnitPath::Hit> *hits, float bias)
-{
- auto &active = world->units.active;
-
- for (size_t i = 0; i < active.size(); i++)
- {
- auto unit = active[i];
-
- if (!canTargetUnit(unit))
- continue;
-
- UnitPath::get(unit)->findHits(engine, hits, bias);
- }
-}
-
-static int proposeUnitHits(lua_State *L)
-{
- auto engine = find_engine(L, 1);
- float bias = luaL_optnumber(L, 2, 0);
-
- if (!engine->hasTarget())
- luaL_error(L, "target not set");
-
- std::vector<UnitPath::Hit> hits;
- proposeUnitHits(engine, &hits, bias);
-
- lua_createtable(L, hits.size(), 0);
-
- for (size_t i = 0; i < hits.size(); i++)
- {
- auto &hit = hits[i];
- lua_createtable(L, 0, 6);
- Lua::SetField(L, hit.path->unit, -1, "unit");
- Lua::SetField(L, hit.pos, -1, "pos");
- Lua::SetField(L, hit.dist, -1, "dist");
- Lua::SetField(L, hit.time, -1, "time");
- push_margin(L, hit.lmargin); lua_setfield(L, -2, "lmargin");
- push_margin(L, hit.rmargin); lua_setfield(L, -2, "rmargin");
- lua_rawseti(L, -2, i+1);
- }
-
- return 1;
-}
-
-static int computeNearbyWeight(lua_State *L)
-{
- auto engine = find_engine(L, 1);
- luaL_checktype(L, 2, LUA_TTABLE);
- luaL_checktype(L, 3, LUA_TTABLE);
- const char *fname = luaL_optstring(L, 4, "nearby_weight");
-
- std::vector<UnitPath*> units;
- std::vector<float> weights;
-
- lua_pushnil(L);
-
- while (lua_next(L, 3))
- {
- df::unit *unit;
- if (lua_isnumber(L, -2))
- unit = df::unit::find(lua_tointeger(L, -2));
- else
- unit = Lua::CheckDFObject<df::unit>(L, -2);
- if (!unit)
- continue;
- units.push_back(UnitPath::get(unit));
- weights.push_back(lua_tonumber(L, -1));
- lua_pop(L, 1);
- }
-
- lua_pushnil(L);
-
- while (lua_next(L, 2))
- {
- Lua::StackUnwinder frame(L, 1);
-
- lua_getfield(L, frame[1], "unit");
- df::unit *unit = Lua::CheckDFObject<df::unit>(L, -1);
-
- lua_getfield(L, frame[1], "time");
- float time = luaL_checknumber(L, lua_gettop(L));
-
- df::coord pos;
-
- lua_getfield(L, frame[1], "pos");
- if (lua_isnil(L, -1))
- {
- if (!unit) luaL_error(L, "either unit or pos is required");
- pos = UnitPath::get(unit)->posAtTime(time);
- }
- else
- Lua::CheckDFAssign(L, &pos, -1);
-
- float sum = 0.0f;
-
- for (size_t i = 0; i < units.size(); i++)
- {
- if (units[i]->unit == unit)
- continue;
-
- auto diff = units[i]->posAtTime(time) - pos;
- float dist = 1 + sqrtf(diff.x*diff.x + diff.y*diff.y + diff.z*diff.z);
- sum += weights[i]/(dist*dist);
- }
-
- lua_pushnumber(L, sum);
- lua_setfield(L, frame[1], fname);
- }
-
- return 0;
-}
-
-/*
- * Projectile hook
- */
-
-static const int offsets[8][2] = {
- { -1, -1 }, { 0, -1 }, { 1, -1 },
- { -1, 0 }, { 1, 0 },
- { -1, 1 }, { 0, 1 }, { 1, 1 }
-};
-
-struct projectile_hook : df::proj_itemst {
- typedef df::proj_itemst interpose_base;
-
- void aimAtPoint(EngineInfo *engine, const ProjectilePath &path)
- {
- target_pos = path.target;
-
- // Debug
- Maps::getTileOccupancy(path.goal)->bits.arrow_color = COLOR_LIGHTMAGENTA;
-
- PathMetrics raytrace(path);
-
- // Materialize map blocks, or the projectile will crash into them
- for (int i = 0; i < raytrace.collision_step; i++)
- Maps::ensureTileBlock(path[i]);
-
- // Find valid hit point for catapult stones
- if (flags.bits.high_flying)
- {
- if (raytrace.hits())
- fall_threshold = raytrace.goal_step;
- else
- fall_threshold = (raytrace.collision_z_step+raytrace.collision_step-1)/2;
-
- while (fall_threshold < raytrace.collision_step-1)
- {
- if (isTargetableTile(path[fall_threshold]))
- break;
-
- fall_threshold++;
- }
- }
-
- fall_threshold = std::max(fall_threshold, engine->fire_range.first);
- fall_threshold = std::min(fall_threshold, engine->fire_range.second);
- }
-
- void aimAtPoint(EngineInfo *engine, int skill, const ProjectilePath &path)
- {
- df::coord fail_target = path.goal;
-
- orient_engine(engine->bld, path.goal);
-
- // Debug
- Maps::getTileOccupancy(path.goal)->bits.arrow_color = COLOR_LIGHTRED;
-
- // Dabbling always hit in 7x7 area
- if (skill < skill_rating::Novice)
- {
- fail_target.x += random_int(7)-3;
- fail_target.y += random_int(7)-3;
- aimAtPoint(engine, ProjectilePath(path.origin, fail_target));
- return;
- }
-
- // Exact hit chance
- float hit_chance = 1.04f - powf(0.8f, skill);
-
- if (float(rand())/RAND_MAX < hit_chance)
- {
- aimAtPoint(engine, path);
- return;
- }
-
- // Otherwise perturb
- if (skill <= skill_rating::Proficient)
- {
- // 5x5
- fail_target.x += random_int(5)-2;
- fail_target.y += random_int(5)-2;
- }
- else
- {
- // 3x3
- int idx = random_int(8);
- fail_target.x += offsets[idx][0];
- fail_target.y += offsets[idx][1];
- }
-
- ProjectilePath fail(path.origin, fail_target, path.fudge_delta, path.fudge_factor);
- aimAtPoint(engine, fail);
- }
-
- void aimAtArea(EngineInfo *engine, int skill)
- {
- df::coord target, last_passable;
- df::coord tbase = engine->target.first;
- df::coord tsize = engine->getTargetSize();
- bool success = false;
-
- for (int i = 0; i < 50; i++)
- {
- target = tbase + df::coord(
- random_int(tsize.x), random_int(tsize.y), random_int(tsize.z)
- );
-
- if (adjustToTarget(engine, &target))
- last_passable = target;
- else
- continue;
-
- ProjectilePath path(engine->center, target, engine->is_catapult ? 0.5f : 0.0f);
- PathMetrics raytrace(path);
-
- if (raytrace.hits() && engine->isInRange(raytrace.goal_step))
- {
- aimAtPoint(engine, skill, path);
- return;
- }
- }
-
- if (!last_passable.isValid())
- last_passable = target;
-
- aimAtPoint(engine, skill, ProjectilePath(engine->center, last_passable));
- }
-
- static int safeAimProjectile(lua_State *L)
- {
- color_ostream &out = *Lua::GetOutput(L);
- auto proj = (projectile_hook*)lua_touserdata(L, 1);
- auto engine = (EngineInfo*)lua_touserdata(L, 2);
- int skill = lua_tointeger(L, 3);
-
- if (!Lua::PushModulePublic(out, L, "plugins.siege-engine", "doAimProjectile"))
- luaL_error(L, "Projectile aiming AI not available");
-
- Lua::PushDFObject(L, engine->bld);
- Lua::Push(L, proj->item);
- Lua::Push(L, engine->target.first);
- Lua::Push(L, engine->target.second);
- Lua::Push(L, skill);
-
- lua_call(L, 5, 1);
-
- if (lua_isnil(L, -1))
- proj->aimAtArea(engine, skill);
- else
- proj->aimAtPoint(engine, skill, decode_path(L, -1, engine->center));
-
- return 0;
- }
-
- void doCheckMovement()
- {
- if (flags.bits.parabolic || distance_flown != 0 ||
- fall_counter != fall_delay || item == NULL)
- return;
-
- auto engine = find_engine(origin_pos);
- if (!engine || !engine->hasTarget())
- return;
-
- auto L = Lua::Core::State;
- CoreSuspendClaimer suspend;
- color_ostream_proxy out(Core::getInstance().getConsole());
-
- int skill = getOperatorSkill(engine->bld, true);
-
- // Dabbling can't aim
- if (skill < skill_rating::Novice)
- aimAtArea(engine, skill);
- else
- {
- lua_pushcfunction(L, safeAimProjectile);
- lua_pushlightuserdata(L, this);
- lua_pushlightuserdata(L, engine);
- lua_pushinteger(L, skill);
-
- if (!Lua::Core::SafeCall(out, 3, 0))
- aimAtArea(engine, skill);
- }
-
- switch (item->getType())
- {
- case item_type::CAGE:
- flags.bits.bouncing = false;
- break;
- case item_type::BIN:
- case item_type::BARREL:
- flags.bits.bouncing = false;
- break;
- default:
- break;
- }
- }
-
- void doLaunchContents()
- {
- // Translate cartoon flight speed to parabolic
- float speed = 100000.0f / (fall_delay + 1);
- int min_zspeed = (fall_delay+1)*4900;
-
- float bonus = 1.0f + 0.1f*(origin_pos.z -cur_pos.z);
- bonus *= 1.0f + (distance_flown - 60) / 200.0f;
- speed *= bonus;
-
- // Flight direction vector
- df::coord dist = target_pos - origin_pos;
- float vx = dist.x, vy = dist.y, vz = fabs((float)dist.z);
- normalize(vx, vy, vz);
-
- int start_z = 0;
-
- // Start at tile top, if hit a wall
- ProjectilePath path(origin_pos, target_pos);
- auto next_pos = path[distance_flown+1];
- if (next_pos.z == cur_pos.z && !isPassableTile(next_pos))
- start_z = 49000;
-
- MapExtras::MapCache mc;
- std::vector<df::item*> contents;
- Items::getContainedItems(item, &contents);
-
- for (size_t i = 0; i < contents.size(); i++)
- {
- auto child = contents[i];
-
- // Liquids are vaporized so that they cover nearby units
- if (child->isLiquid())
- {
- auto flow = Maps::spawnFlow(
- cur_pos,
- flow_type::MaterialVapor,
- child->getMaterial(), child->getMaterialIndex(),
- 100
- );
-
- // should it leave a puddle too?..
- if (flow && Items::remove(mc, child))
- continue;
- }
-
- auto proj = Items::makeProjectile(mc, child);
- if (!proj) continue;
-
- bool keep = apply_impact_damage(child, 50000, int(250000*bonus));
-
- proj->flags.bits.no_impact_destroy = keep;
- //proj->flags.bits.bouncing = true;
- proj->flags.bits.piercing = true;
- proj->flags.bits.parabolic = true;
- proj->flags.bits.unk9 = true;
- proj->flags.bits.no_collide = true;
-
- proj->pos_z = start_z;
-
- float sx, sy, sz;
- random_direction(sx, sy, sz);
- sx += vx*0.7; sy += vy*0.7; sz += vz*0.7;
- if (sz < 0) sz = -sz;
- normalize(sx, sy, sz);
-
- proj->speed_x = int(speed * sx);
- proj->speed_y = int(speed * sy);
- proj->speed_z = std::max(min_zspeed, int(speed * sz));
- }
- }
-
- DEFINE_VMETHOD_INTERPOSE(bool, checkMovement, ())
- {
- if (flags.bits.high_flying || flags.bits.piercing)
- doCheckMovement();
-
- return INTERPOSE_NEXT(checkMovement)();
- }
-
- DEFINE_VMETHOD_INTERPOSE(bool, checkImpact, (bool no_damage_floor))
- {
- if (!flags.bits.has_hit_ground && !flags.bits.parabolic &&
- flags.bits.high_flying && !flags.bits.bouncing &&
- !flags.bits.no_impact_destroy && target_pos != origin_pos &&
- item && item->flags.bits.container)
- {
- doLaunchContents();
- }
-
- return INTERPOSE_NEXT(checkImpact)(no_damage_floor);
- }
-};
-
-IMPLEMENT_VMETHOD_INTERPOSE(projectile_hook, checkMovement);
-IMPLEMENT_VMETHOD_INTERPOSE(projectile_hook, checkImpact);
-
-/*
- * Building hook
- */
-
-struct building_hook : df::building_siegeenginest {
- typedef df::building_siegeenginest interpose_base;
-
- DEFINE_VMETHOD_INTERPOSE(df::workshop_profile*, getWorkshopProfile, ())
- {
- if (auto engine = find_engine(this))
- return &engine->profile;
-
- return INTERPOSE_NEXT(getWorkshopProfile)();
- }
-
- DEFINE_VMETHOD_INTERPOSE(df::stockpile_links*, getStockpileLinks, ())
- {
- if (auto engine = find_engine(this))
- {
- update_stockpile_links(engine);
- return &engine->links;
- }
-
- return INTERPOSE_NEXT(getStockpileLinks)();
- }
-
- DEFINE_VMETHOD_INTERPOSE(void, updateAction, ())
- {
- INTERPOSE_NEXT(updateAction)();
-
- if (jobs.empty())
- return;
-
- if (auto engine = find_engine(this))
- {
- auto job = jobs[0];
- bool save_op = false;
-
- switch (job->job_type)
- {
- case job_type::LoadCatapult:
- if (!job->job_items.empty())
- {
- auto item = job->job_items[0];
- item->item_type = engine->ammo_item_type;
- item->vector_id = engine->ammo_vector_id;
-
- switch (item->item_type)
- {
- case item_type::NONE:
- case item_type::BOULDER:
- case item_type::BLOCKS:
- item->mat_type = 0;
- break;
-
- case item_type::BIN:
- case item_type::BARREL:
- item->mat_type = -1;
- // A hack to make it take objects assigned to stockpiles.
- // Since reaction_id is not set, the actual value is not used.
- item->contains.resize(1);
- break;
-
- default:
- item->mat_type = -1;
- break;
- }
- }
- // fallthrough
-
- case job_type::LoadBallista:
- case job_type::FireCatapult:
- case job_type::FireBallista:
- if (auto worker = Job::getWorker(job))
- {
- engine->operator_id = worker->id;
- engine->operator_frame = world->frame_counter;
- }
- break;
-
- default:
- break;
- }
- }
- }
-};
-
-IMPLEMENT_VMETHOD_INTERPOSE(building_hook, getWorkshopProfile);
-IMPLEMENT_VMETHOD_INTERPOSE(building_hook, getStockpileLinks);
-IMPLEMENT_VMETHOD_INTERPOSE(building_hook, updateAction);
-
-/*
- * Initialization
- */
-
-DFHACK_PLUGIN_LUA_FUNCTIONS {
- DFHACK_LUA_FUNCTION(clearTargetArea),
- DFHACK_LUA_FUNCTION(setTargetArea),
- DFHACK_LUA_FUNCTION(isLinkedToPile),
- DFHACK_LUA_FUNCTION(addStockpileLink),
- DFHACK_LUA_FUNCTION(removeStockpileLink),
- DFHACK_LUA_FUNCTION(saveWorkshopProfile),
- DFHACK_LUA_FUNCTION(getTileStatus),
- DFHACK_LUA_FUNCTION(paintAimScreen),
- DFHACK_LUA_FUNCTION(canTargetUnit),
- DFHACK_LUA_FUNCTION(isPassableTile),
- DFHACK_LUA_FUNCTION(isTreeTile),
- DFHACK_LUA_FUNCTION(isTargetableTile),
- DFHACK_LUA_END
-};
-
-DFHACK_PLUGIN_LUA_COMMANDS {
- DFHACK_LUA_COMMAND(getTargetArea),
- DFHACK_LUA_COMMAND(getAmmoItem),
- DFHACK_LUA_COMMAND(setAmmoItem),
- DFHACK_LUA_COMMAND(getStockpileLinks),
- DFHACK_LUA_COMMAND(projPosAtStep),
- DFHACK_LUA_COMMAND(projPathMetrics),
- DFHACK_LUA_COMMAND(adjustToTarget),
- DFHACK_LUA_COMMAND(traceUnitPath),
- DFHACK_LUA_COMMAND(unitPosAtTime),
- DFHACK_LUA_COMMAND(proposeUnitHits),
- DFHACK_LUA_COMMAND(computeNearbyWeight),
- DFHACK_LUA_END
-};
-
-static bool is_enabled = false;
-
-static void enable_hooks(bool enable)
-{
- is_enabled = enable;
-
- INTERPOSE_HOOK(projectile_hook, checkMovement).apply(enable);
- INTERPOSE_HOOK(projectile_hook, checkImpact).apply(enable);
-
- INTERPOSE_HOOK(building_hook, getWorkshopProfile).apply(enable);
- INTERPOSE_HOOK(building_hook, getStockpileLinks).apply(enable);
- INTERPOSE_HOOK(building_hook, updateAction).apply(enable);
-
- if (enable)
- load_engines();
- else
- clear_engines();
-}
-
-static bool enable_plugin()
-{
- if (is_enabled)
- return true;
-
- auto pworld = Core::getInstance().getWorld();
- auto entry = pworld->GetPersistentData("siege-engine/enabled", NULL);
- if (!entry.isValid())
- return false;
-
- enable_hooks(true);
- return true;
-}
-
-static void clear_caches(color_ostream &out)
-{
- if (!UnitPath::cache.empty())
- {
- for (auto it = UnitPath::cache.begin(); it != UnitPath::cache.end(); ++it)
- delete it->second;
-
- UnitPath::cache.clear();
- }
-}
-
-DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event)
-{
- switch (event) {
- case SC_MAP_LOADED:
- {
- auto pworld = Core::getInstance().getWorld();
- bool enable = pworld->GetPersistentData("siege-engine/enabled").isValid();
-
- if (enable)
- {
- out.print("Enabling the siege engine plugin.\n");
- enable_hooks(true);
- }
- else
- enable_hooks(false);
- }
- break;
- case SC_MAP_UNLOADED:
- enable_hooks(false);
- break;
- default:
- break;
- }
-
- return CR_OK;
-}
-
-DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
-{
- if (Core::getInstance().isMapLoaded())
- plugin_onstatechange(out, SC_MAP_LOADED);
-
- return CR_OK;
-}
-
-DFhackCExport command_result plugin_shutdown ( color_ostream &out )
-{
- enable_hooks(false);
- return CR_OK;
-}
-
-DFhackCExport command_result plugin_onupdate ( color_ostream &out )
-{
- clear_caches(out);
- return CR_OK;
-}