diff options
| author | Alexander Gavrilov | 2012-09-12 20:57:25 +0400 |
|---|---|---|
| committer | Alexander Gavrilov | 2012-09-12 20:57:25 +0400 |
| commit | f06f9af6b80068fbafe340a59ff7d755399509d4 (patch) | |
| tree | 9cf964ff50ff76aca83a0ec492ba44b9f1578db0 | |
| parent | 7c71aeab5f44c7fce106a0efc07c1ea2860e4638 (diff) | |
| download | dfhack-f06f9af6b80068fbafe340a59ff7d755399509d4.tar.gz dfhack-f06f9af6b80068fbafe340a59ff7d755399509d4.tar.bz2 dfhack-f06f9af6b80068fbafe340a59ff7d755399509d4.tar.xz | |
Throw items from bins around in siege engine, like minecarts do.
| -rw-r--r-- | library/modules/World.cpp | 6 | ||||
| m--------- | library/xml | 0 | ||||
| -rw-r--r-- | plugins/devel/siege-engine.cpp | 170 | ||||
| -rw-r--r-- | plugins/lua/siege-engine.lua | 3 |
4 files changed, 154 insertions, 25 deletions
diff --git a/library/modules/World.cpp b/library/modules/World.cpp index e14aa02a..67b8c123 100644 --- a/library/modules/World.cpp +++ b/library/modules/World.cpp @@ -300,6 +300,8 @@ PersistentDataItem World::GetPersistentData(const std::string &key, bool *added) void World::GetPersistentData(std::vector<PersistentDataItem> *vec, const std::string &key, bool prefix) { + vec->clear(); + if (!BuildPersistentCache()) return; @@ -343,8 +345,10 @@ bool World::DeletePersistentData(const PersistentDataItem &item) auto eqrange = d->persistent_index.equal_range(item.key_value); - for (auto it = eqrange.first; it != eqrange.second; ++it) + for (auto it2 = eqrange.first; it2 != eqrange.second; ) { + auto it = it2; ++it2; + if (it->second != -item.id) continue; diff --git a/library/xml b/library/xml -Subproject d55f1cf43dd71d3abee724bfa88a0a401b4ccaa +Subproject 2bc8fbdf71143398817d31e06e169a01cce37c5 diff --git a/plugins/devel/siege-engine.cpp b/plugins/devel/siege-engine.cpp index 6385111b..b41ac931 100644 --- a/plugins/devel/siege-engine.cpp +++ b/plugins/devel/siege-engine.cpp @@ -6,6 +6,7 @@ #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> @@ -136,6 +137,30 @@ 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; +} + /* * Configuration management */ @@ -172,7 +197,7 @@ struct EngineInfo { } }; -static std::map<df::building*, EngineInfo> engines; +static std::map<df::building*, EngineInfo*> engines; static std::map<df::coord, df::building*> coord_engines; static std::set<df::building*> recheck_piles; @@ -183,17 +208,18 @@ static EngineInfo *find_engine(df::building *bld, bool create = false) if (!ebld) return NULL; - auto it = engines.find(bld); - if (it != engines.end()) + auto &obj = engines[bld]; + + if (obj) { - it->second.bld = ebld; - return &it->second; + obj->bld = ebld; + return obj; } if (!create) return NULL; - auto *obj = &engines[bld]; + obj = new EngineInfo(); obj->id = bld->id; obj->bld = ebld; @@ -246,6 +272,8 @@ static EngineInfo *find_engine(df::coord pos) static void clear_engines() { + for (auto it = engines.begin(); it != engines.end(); ++it) + delete it->second; engines.clear(); coord_engines.clear(); recheck_piles.clear(); @@ -373,14 +401,14 @@ static int getAmmoItem(lua_State *L) 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"); - if (!enable_plugin()) - return 0; - auto pworld = Core::getInstance().getWorld(); auto key = stl_sprintf("siege-engine/ammo/%d", engine->id); auto entry = pworld->GetPersistentData(key, NULL); @@ -526,7 +554,7 @@ static void recheck_pile_links(color_ostream &out, EngineInfo *engine) engine->links.give_to_pile.clear(); } -static int getShotSkill(df::building_siegeenginest *bld) +static int getOperatorSkill(df::building_siegeenginest *bld, bool force = false) { CHECK_NULL_POINTER(bld); @@ -534,10 +562,24 @@ static int getShotSkill(df::building_siegeenginest *bld) if (!engine) return 0; - 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); + 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; } @@ -1213,15 +1255,18 @@ struct projectile_hook : df::proj_itemst { 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, 3, 1); + lua_call(L, 5, 1); if (lua_isnil(L, -1)) proj->aimAtArea(engine); @@ -1233,7 +1278,8 @@ struct projectile_hook : df::proj_itemst { void doCheckMovement() { - if (distance_flown != 0 || fall_counter != fall_delay) + if (flags.bits.parabolic || distance_flown != 0 || + fall_counter != fall_delay || item == NULL) return; auto engine = find_engine(origin_pos); @@ -1244,12 +1290,78 @@ struct projectile_hook : df::proj_itemst { CoreSuspendClaimer suspend; color_ostream_proxy out(Core::getInstance().getConsole()); + int skill = getOperatorSkill(engine->bld, true); + lua_pushcfunction(L, safeAimProjectile); lua_pushlightuserdata(L, this); lua_pushlightuserdata(L, engine); + lua_pushinteger(L, skill); - if (!Lua::Core::SafeCall(out, 2, 0)) + if (!Lua::Core::SafeCall(out, 3, 0)) aimAtArea(engine); + + 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; + + // Flight direction vector + df::coord dist = target_pos - origin_pos; + float vx = dist.x, vy = dist.y, vz = fabs(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]; + auto proj = Items::makeProjectile(mc, child); + if (!proj) continue; + + proj->flags.bits.no_impact_destroy = true; + //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 = int(speed * sz); + } } DEFINE_VMETHOD_INTERPOSE(bool, checkMovement, ()) @@ -1259,9 +1371,23 @@ struct projectile_hook : df::proj_itemst { 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 @@ -1299,6 +1425,7 @@ struct building_hook : df::building_siegeenginest { if (auto engine = find_engine(this)) { auto job = jobs[0]; + bool save_op = false; switch (job->job_type) { @@ -1330,20 +1457,16 @@ struct building_hook : df::building_siegeenginest { break; } } - break; + // fallthrough + case job_type::LoadBallista: case job_type::FireCatapult: case job_type::FireBallista: if (auto worker = Job::getWorker(job)) { - color_ostream_proxy out(Core::getInstance().getConsole()); - out.print("operator %d\n", worker->id); - engine->operator_id = worker->id; engine->operator_frame = world->frame_counter; } - else - engine->operator_id = -1; break; default: @@ -1397,6 +1520,7 @@ 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, canLinkToStockpile).apply(enable); INTERPOSE_HOOK(building_hook, getStockpileLinks).apply(enable); diff --git a/plugins/lua/siege-engine.lua b/plugins/lua/siege-engine.lua index d078ce1d..89c47659 100644 --- a/plugins/lua/siege-engine.lua +++ b/plugins/lua/siege-engine.lua @@ -33,7 +33,8 @@ function findShotHeight(engine, target) end end -function doAimProjectile(engine, target_min, target_max) +function doAimProjectile(engine, item, target_min, target_max, skill) + print(item, df.skill_rating[skill]) local targets = proposeUnitHits(engine) if #targets > 0 then local rnd = math.random(#targets) |
