Merge branch 'diggingInvaders' into 0.34.11-r4
Conflicts: scripts/devel/invasion-now.luadevelop
commit
9d3ee11349
@ -0,0 +1,35 @@
|
|||||||
|
PROJECT (diggingInvaders)
|
||||||
|
# A list of source files
|
||||||
|
SET(PROJECT_SRCS
|
||||||
|
diggingInvaders.cpp
|
||||||
|
edgeCost.cpp
|
||||||
|
assignJob.cpp
|
||||||
|
)
|
||||||
|
# A list of headers
|
||||||
|
SET(PROJECT_HDRS
|
||||||
|
edgeCost.h
|
||||||
|
assignJob.h
|
||||||
|
)
|
||||||
|
SET_SOURCE_FILES_PROPERTIES( ${PROJECT_HDRS} PROPERTIES HEADER_FILE_ONLY TRUE)
|
||||||
|
|
||||||
|
# mash them together (headers are marked as headers and nothing will try to compile them)
|
||||||
|
LIST(APPEND PROJECT_SRCS ${PROJECT_HDRS})
|
||||||
|
|
||||||
|
#linux
|
||||||
|
IF(UNIX)
|
||||||
|
add_definitions(-DLINUX_BUILD)
|
||||||
|
SET(PROJECT_LIBS
|
||||||
|
# add any extra linux libs here
|
||||||
|
${PROJECT_LIBS}
|
||||||
|
)
|
||||||
|
# windows
|
||||||
|
ELSE(UNIX)
|
||||||
|
SET(PROJECT_LIBS
|
||||||
|
# add any extra linux libs here
|
||||||
|
${PROJECT_LIBS}
|
||||||
|
$(NOINHERIT)
|
||||||
|
)
|
||||||
|
ENDIF(UNIX)
|
||||||
|
# this makes sure all the stuff is put in proper places and linked to dfhack
|
||||||
|
|
||||||
|
DFHACK_PLUGIN(diggingInvaders ${PROJECT_SRCS} LINK_LIBRARIES ${PROJECT_LIBS})
|
@ -0,0 +1,300 @@
|
|||||||
|
#include "assignJob.h"
|
||||||
|
|
||||||
|
#include "modules/Buildings.h"
|
||||||
|
#include "modules/Items.h"
|
||||||
|
#include "modules/Job.h"
|
||||||
|
#include "modules/Materials.h"
|
||||||
|
|
||||||
|
#include "df/building.h"
|
||||||
|
#include "df/construction.h"
|
||||||
|
#include "df/coord.h"
|
||||||
|
#include "df/general_ref.h"
|
||||||
|
#include "df/general_ref_building_holderst.h"
|
||||||
|
#include "df/general_ref_unit.h"
|
||||||
|
//#include "df/general_ref_unit_holderst.h"
|
||||||
|
#include "df/general_ref_unit_workerst.h"
|
||||||
|
#include "df/historical_entity.h"
|
||||||
|
#include "df/item.h"
|
||||||
|
#include "df/itemdef_weaponst.h"
|
||||||
|
#include "df/item_quality.h"
|
||||||
|
#include "df/item_type.h"
|
||||||
|
#include "df/item_weaponst.h"
|
||||||
|
#include "df/job.h"
|
||||||
|
#include "df/job_skill.h"
|
||||||
|
#include "df/job_type.h"
|
||||||
|
#include "df/reaction_product_itemst.h"
|
||||||
|
#include "df/reaction_reagent.h"
|
||||||
|
#include "df/ui.h"
|
||||||
|
#include "df/unit.h"
|
||||||
|
#include "df/unit_inventory_item.h"
|
||||||
|
#include "df/world_site.h"
|
||||||
|
|
||||||
|
void getRidOfOldJob(df::unit* unit) {
|
||||||
|
if ( unit->job.current_job == NULL ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
df::job* job = unit->job.current_job;
|
||||||
|
unit->job.current_job = NULL;
|
||||||
|
if ( job->list_link->prev != NULL ) {
|
||||||
|
job->list_link->prev->next = job->list_link->next;
|
||||||
|
}
|
||||||
|
if ( job->list_link->next != NULL ) {
|
||||||
|
job->list_link->next->prev = job->list_link->prev;
|
||||||
|
}
|
||||||
|
//TODO: consider building pointers?
|
||||||
|
//for now, just let the memory leak TODO: fix
|
||||||
|
//delete job->list_link;
|
||||||
|
//delete job;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t assignJob(color_ostream& out, Edge firstImportantEdge, unordered_map<df::coord,df::coord,PointHash> parentMap, unordered_map<df::coord,cost_t,PointHash>& costMap, vector<int32_t>& invaders, unordered_set<df::coord,PointHash>& requiresZNeg, unordered_set<df::coord,PointHash>& requiresZPos, MapExtras::MapCache& cache, DigAbilities& abilities ) {
|
||||||
|
df::unit* firstInvader = df::unit::find(invaders[0]);
|
||||||
|
if ( !firstInvader ) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//do whatever you need to do at the first important edge
|
||||||
|
df::coord pt1 = firstImportantEdge.p1;
|
||||||
|
df::coord pt2 = firstImportantEdge.p2;
|
||||||
|
if ( costMap[pt1] > costMap[pt2] ) {
|
||||||
|
df::coord temp = pt1;
|
||||||
|
pt1 = pt2;
|
||||||
|
pt2 = temp;
|
||||||
|
}
|
||||||
|
//out.print("first important edge: (%d,%d,%d) -> (%d,%d,%d)\n", pt1.x,pt1.y,pt1.z, pt2.x,pt2.y,pt2.z);
|
||||||
|
|
||||||
|
int32_t jobId = -1;
|
||||||
|
|
||||||
|
df::map_block* block1 = Maps::getTileBlock(pt1);
|
||||||
|
df::map_block* block2 = Maps::getTileBlock(pt2);
|
||||||
|
bool passable1 = block1->walkable[pt1.x&0xF][pt1.y&0xF];
|
||||||
|
bool passable2 = block2->walkable[pt2.x&0xF][pt2.y&0xF];
|
||||||
|
|
||||||
|
df::coord location;
|
||||||
|
df::building* building = Buildings::findAtTile(pt2);
|
||||||
|
df::coord buildingPos = pt2;
|
||||||
|
if ( pt1.z > pt2.z ) {
|
||||||
|
building = Buildings::findAtTile(df::coord(pt2.x,pt2.y,pt2.z+1));
|
||||||
|
buildingPos = df::coord(pt2.x,pt2.y,pt2.z+1);
|
||||||
|
}
|
||||||
|
if ( building != NULL ) {
|
||||||
|
df::coord destroyFrom = parentMap[buildingPos];
|
||||||
|
if ( destroyFrom.z != buildingPos.z ) {
|
||||||
|
//TODO: deal with this
|
||||||
|
}
|
||||||
|
//out.print("%s, line %d: Destroying building %d at (%d,%d,%d) from (%d,%d,%d).\n", __FILE__, __LINE__, building->id, buildingPos.x,buildingPos.y,buildingPos.z, destroyFrom.x,destroyFrom.y,destroyFrom.z);
|
||||||
|
|
||||||
|
df::job* job = new df::job;
|
||||||
|
job->job_type = df::enums::job_type::DestroyBuilding;
|
||||||
|
//job->flags.bits.special = 1;
|
||||||
|
df::general_ref_building_holderst* buildingRef = new df::general_ref_building_holderst;
|
||||||
|
buildingRef->building_id = building->id;
|
||||||
|
job->general_refs.push_back(buildingRef);
|
||||||
|
df::general_ref_unit_workerst* workerRef = new df::general_ref_unit_workerst;
|
||||||
|
workerRef->unit_id = firstInvader->id;
|
||||||
|
job->general_refs.push_back(workerRef);
|
||||||
|
getRidOfOldJob(firstInvader);
|
||||||
|
firstInvader->job.current_job = job;
|
||||||
|
firstInvader->path.path.x.clear();
|
||||||
|
firstInvader->path.path.y.clear();
|
||||||
|
firstInvader->path.path.z.clear();
|
||||||
|
firstInvader->path.dest = destroyFrom;
|
||||||
|
location = destroyFrom;
|
||||||
|
firstInvader->job.hunt_target = NULL;
|
||||||
|
firstInvader->job.destroy_target = NULL;
|
||||||
|
|
||||||
|
building->jobs.clear();
|
||||||
|
building->jobs.push_back(job);
|
||||||
|
Job::linkIntoWorld(job);
|
||||||
|
jobId = job->id;
|
||||||
|
job->completion_timer = abilities.jobDelay[CostDimension::DestroyBuilding];
|
||||||
|
} else {
|
||||||
|
df::tiletype* type1 = Maps::getTileType(pt1);
|
||||||
|
df::tiletype* type2 = Maps::getTileType(pt2);
|
||||||
|
df::tiletype_shape shape1 = ENUM_ATTR(tiletype, shape, *type1);
|
||||||
|
df::tiletype_shape shape2 = ENUM_ATTR(tiletype, shape, *type2);
|
||||||
|
bool construction2 = ENUM_ATTR(tiletype, material, *type2) == df::enums::tiletype_material::CONSTRUCTION;
|
||||||
|
if ( construction2 ) {
|
||||||
|
df::job* job = new df::job;
|
||||||
|
job->job_type = df::enums::job_type::RemoveConstruction;
|
||||||
|
df::general_ref_unit_workerst* workerRef = new df::general_ref_unit_workerst;
|
||||||
|
workerRef->unit_id = firstInvader->id;
|
||||||
|
job->general_refs.push_back(workerRef);
|
||||||
|
job->pos = pt2;
|
||||||
|
getRidOfOldJob(firstInvader);
|
||||||
|
firstInvader->job.current_job = job;
|
||||||
|
firstInvader->path.path.x.clear();
|
||||||
|
firstInvader->path.path.y.clear();
|
||||||
|
firstInvader->path.path.z.clear();
|
||||||
|
firstInvader->path.dest = pt1;
|
||||||
|
location = pt1;
|
||||||
|
firstInvader->job.hunt_target = NULL;
|
||||||
|
firstInvader->job.destroy_target = NULL;
|
||||||
|
Job::linkIntoWorld(job);
|
||||||
|
jobId = job->id;
|
||||||
|
df::construction* constr = df::construction::find(pt2);
|
||||||
|
bool smooth = constr != NULL && constr->item_type != df::enums::item_type::BOULDER;
|
||||||
|
if ( smooth )
|
||||||
|
job->completion_timer = abilities.jobDelay[CostDimension::DestroySmoothConstruction];
|
||||||
|
else
|
||||||
|
job->completion_timer = abilities.jobDelay[CostDimension::DestroyRoughConstruction];
|
||||||
|
} else {
|
||||||
|
bool walkable_low1 = shape1 == df::tiletype_shape::STAIR_DOWN || shape1 == df::tiletype_shape::STAIR_UPDOWN;
|
||||||
|
bool walkable_low2 = shape2 == df::tiletype_shape::STAIR_DOWN || shape2 == df::tiletype_shape::STAIR_UPDOWN;
|
||||||
|
bool walkable_high1 = shape1 == df::tiletype_shape::STAIR_UP || shape1 == df::tiletype_shape::STAIR_UPDOWN;
|
||||||
|
bool walkable_high2 = shape2 == df::tiletype_shape::STAIR_UP || shape2 == df::tiletype_shape::STAIR_UPDOWN;
|
||||||
|
//must be a dig job
|
||||||
|
bool up1 = !walkable_high1 && requiresZPos.find(pt1) != requiresZPos.end();
|
||||||
|
bool up2 = !walkable_high2 && requiresZPos.find(pt2) != requiresZPos.end();
|
||||||
|
bool down1 = !walkable_low1 && requiresZNeg.find(pt1) != requiresZNeg.end();
|
||||||
|
bool down2 = !walkable_low2 && requiresZNeg.find(pt2) != requiresZNeg.end();
|
||||||
|
bool up;
|
||||||
|
bool down;
|
||||||
|
df::coord goHere;
|
||||||
|
df::coord workHere;
|
||||||
|
if ( pt1.z == pt2.z ) {
|
||||||
|
up = up2;
|
||||||
|
down = down2;
|
||||||
|
goHere = pt1;
|
||||||
|
workHere = pt2;
|
||||||
|
} else {
|
||||||
|
if ( up1 || down1 ) {
|
||||||
|
up = up1;
|
||||||
|
down = down1;
|
||||||
|
goHere = pt1;
|
||||||
|
workHere = pt1;
|
||||||
|
} else {
|
||||||
|
up = up2;
|
||||||
|
down = down2;
|
||||||
|
goHere = pt1;
|
||||||
|
workHere = pt2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
df::job* job = new df::job;
|
||||||
|
if ( up && down ) {
|
||||||
|
job->job_type = df::enums::job_type::CarveUpDownStaircase;
|
||||||
|
//out.print("%s, line %d: type = up/down\n", __FILE__, __LINE__);
|
||||||
|
} else if ( up && !down ) {
|
||||||
|
job->job_type = df::enums::job_type::CarveUpwardStaircase;
|
||||||
|
//out.print("%s, line %d: type = up\n", __FILE__, __LINE__);
|
||||||
|
} else if ( !up && down ) {
|
||||||
|
job->job_type = df::enums::job_type::CarveDownwardStaircase;
|
||||||
|
//out.print("%s, line %d: type = down\n", __FILE__, __LINE__);
|
||||||
|
} else {
|
||||||
|
job->job_type = df::enums::job_type::Dig;
|
||||||
|
//out.print("%s, line %d: type = dig\n", __FILE__, __LINE__);
|
||||||
|
}
|
||||||
|
//out.print("%s, line %d: up=%d,up1=%d,up2=%d, down=%d,down1=%d,down2=%d\n", __FILE__, __LINE__, up,up1,up2, down,down1,down2);
|
||||||
|
job->pos = workHere;
|
||||||
|
firstInvader->path.dest = goHere;
|
||||||
|
location = goHere;
|
||||||
|
df::general_ref_unit_workerst* ref = new df::general_ref_unit_workerst;
|
||||||
|
ref->unit_id = firstInvader->id;
|
||||||
|
job->general_refs.push_back(ref);
|
||||||
|
firstInvader->job.hunt_target = NULL;
|
||||||
|
firstInvader->job.destroy_target = NULL;
|
||||||
|
getRidOfOldJob(firstInvader);
|
||||||
|
firstInvader->job.current_job = job;
|
||||||
|
firstInvader->path.path.x.clear();
|
||||||
|
firstInvader->path.path.y.clear();
|
||||||
|
firstInvader->path.path.z.clear();
|
||||||
|
Job::linkIntoWorld(job);
|
||||||
|
jobId = job->id;
|
||||||
|
job->completion_timer = abilities.jobDelay[CostDimension::Dig];
|
||||||
|
|
||||||
|
//TODO: test if he already has a pick
|
||||||
|
bool hasPick = false;
|
||||||
|
for ( size_t a = 0; a < firstInvader->inventory.size(); a++ ) {
|
||||||
|
df::unit_inventory_item* inv_item = firstInvader->inventory[a];
|
||||||
|
if ( inv_item->mode != df::unit_inventory_item::Weapon || inv_item->body_part_id != firstInvader->body.weapon_bp )
|
||||||
|
continue;
|
||||||
|
df::item* oldItem = inv_item->item;
|
||||||
|
if ( oldItem->getType() != df::enums::item_type::WEAPON )
|
||||||
|
continue;
|
||||||
|
df::item_weaponst* oldWeapon = (df::item_weaponst*)oldItem;
|
||||||
|
df::itemdef_weaponst* oldType = oldWeapon->subtype;
|
||||||
|
if ( oldType->skill_melee != df::enums::job_skill::MINING )
|
||||||
|
continue;
|
||||||
|
hasPick = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( hasPick )
|
||||||
|
return firstInvader->id;
|
||||||
|
|
||||||
|
//create and give a pick
|
||||||
|
//based on createitem.cpp
|
||||||
|
df::reaction_product_itemst *prod = NULL;
|
||||||
|
//TODO: consider filtering based on entity/civ stuff
|
||||||
|
for ( size_t a = 0; a < df::global::world->raws.itemdefs.weapons.size(); a++ ) {
|
||||||
|
df::itemdef_weaponst* oldType = df::global::world->raws.itemdefs.weapons[a];
|
||||||
|
if ( oldType->skill_melee != df::enums::job_skill::MINING )
|
||||||
|
continue;
|
||||||
|
prod = df::allocate<df::reaction_product_itemst>();
|
||||||
|
prod->item_type = df::item_type::WEAPON;
|
||||||
|
prod->item_subtype = a;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ( prod == NULL ) {
|
||||||
|
out.print("%s, %d: no valid item.\n", __FILE__, __LINE__);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
DFHack::MaterialInfo material;
|
||||||
|
if ( !material.find("OBSIDIAN") ) {
|
||||||
|
out.print("%s, %d: no water.\n", __FILE__, __LINE__);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
prod->mat_type = material.type;
|
||||||
|
prod->mat_index = material.index;
|
||||||
|
prod->probability = 100;
|
||||||
|
prod->count = 1;
|
||||||
|
prod->product_dimension = 1;
|
||||||
|
|
||||||
|
vector<df::item*> out_items;
|
||||||
|
vector<df::reaction_reagent*> in_reag;
|
||||||
|
vector<df::item*> in_items;
|
||||||
|
prod->produce(firstInvader, &out_items, &in_reag, &in_items, 1, df::job_skill::NONE,
|
||||||
|
df::historical_entity::find(firstInvader->civ_id),
|
||||||
|
df::world_site::find(df::global::ui->site_id));
|
||||||
|
|
||||||
|
if ( out_items.size() != 1 ) {
|
||||||
|
out.print("%s, %d: wrong size: %d.\n", __FILE__, __LINE__, out_items.size());
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
out_items[0]->moveToGround(firstInvader->pos.x, firstInvader->pos.y, firstInvader->pos.z);
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
//check for existing item there
|
||||||
|
for ( size_t a = 0; a < firstInvader->inventory.size(); a++ ) {
|
||||||
|
df::unit_inventory_item* inv_item = firstInvader->inventory[a];
|
||||||
|
if ( false || inv_item->body_part_id == part ) {
|
||||||
|
//throw it on the ground
|
||||||
|
Items::moveToGround(cache, inv_item->item, firstInvader->pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
Items::moveToInventory(cache, out_items[0], firstInvader, df::unit_inventory_item::T_mode::Weapon, firstInvader->body.weapon_bp);
|
||||||
|
|
||||||
|
delete prod;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
//tell EVERYONE to move there
|
||||||
|
for ( size_t a = 0; a < invaders.size(); a++ ) {
|
||||||
|
df::unit* invader = invaders[a];
|
||||||
|
invader->path.path.x.clear();
|
||||||
|
invader->path.path.y.clear();
|
||||||
|
invader->path.path.z.clear();
|
||||||
|
invader->path.dest = location;
|
||||||
|
//invader->flags1.bits.invades = true;
|
||||||
|
//invader->flags1.bits.marauder = true;
|
||||||
|
//invader->flags2.bits.visitor_uninvited = true;
|
||||||
|
invader->relations.group_leader_id = invader->id;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return firstInvader->id;
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "edgeCost.h"
|
||||||
|
|
||||||
|
#include "modules/MapCache.h"
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
int32_t assignJob(color_ostream& out, Edge firstImportantEdge, unordered_map<df::coord,df::coord,PointHash> parentMap, unordered_map<df::coord,cost_t,PointHash>& costMap, vector<int32_t>& invaders, unordered_set<df::coord,PointHash>& requiresZNeg, unordered_set<df::coord,PointHash>& requiresZPos, MapExtras::MapCache& cache, DigAbilities& abilities);
|
||||||
|
|
@ -0,0 +1,624 @@
|
|||||||
|
#include "assignJob.h"
|
||||||
|
#include "edgeCost.h"
|
||||||
|
|
||||||
|
#include "Core.h"
|
||||||
|
#include "Console.h"
|
||||||
|
#include "DataDefs.h"
|
||||||
|
#include "Export.h"
|
||||||
|
#include "PluginManager.h"
|
||||||
|
#include "Types.h"
|
||||||
|
|
||||||
|
#include "modules/Buildings.h"
|
||||||
|
#include "modules/EventManager.h"
|
||||||
|
#include "modules/Gui.h"
|
||||||
|
#include "modules/Job.h"
|
||||||
|
#include "modules/Maps.h"
|
||||||
|
#include "modules/MapCache.h"
|
||||||
|
#include "modules/Units.h"
|
||||||
|
#include "modules/World.h"
|
||||||
|
|
||||||
|
#include "df/body_part_raw_flags.h"
|
||||||
|
#include "df/building.h"
|
||||||
|
#include "df/building_type.h"
|
||||||
|
#include "df/caste_body_info.h"
|
||||||
|
#include "df/coord.h"
|
||||||
|
#include "df/creature_raw.h"
|
||||||
|
#include "df/general_ref.h"
|
||||||
|
#include "df/general_ref_building_holderst.h"
|
||||||
|
#include "df/general_ref_unit.h"
|
||||||
|
#include "df/general_ref_unit_holderst.h"
|
||||||
|
#include "df/general_ref_unit_workerst.h"
|
||||||
|
#include "df/global_objects.h"
|
||||||
|
#include "df/invasion_info.h"
|
||||||
|
#include "df/item.h"
|
||||||
|
#include "df/itemdef_weaponst.h"
|
||||||
|
#include "df/item_quality.h"
|
||||||
|
#include "df/item_weaponst.h"
|
||||||
|
#include "df/inorganic_raw.h"
|
||||||
|
#include "df/job.h"
|
||||||
|
#include "df/job_list_link.h"
|
||||||
|
#include "df/job_skill.h"
|
||||||
|
#include "df/job_type.h"
|
||||||
|
#include "df/map_block.h"
|
||||||
|
#include "df/strain_type.h"
|
||||||
|
#include "df/tile_building_occ.h"
|
||||||
|
#include "df/tile_occupancy.h"
|
||||||
|
#include "df/tiletype.h"
|
||||||
|
#include "df/tiletype_material.h"
|
||||||
|
#include "df/tiletype_shape.h"
|
||||||
|
#include "df/tiletype_shape_basic.h"
|
||||||
|
#include "df/ui.h"
|
||||||
|
#include "df/unit.h"
|
||||||
|
#include "df/unit_inventory_item.h"
|
||||||
|
#include "df/world.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <ctime>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
|
#include <set>
|
||||||
|
#include <vector>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
using namespace DFHack;
|
||||||
|
using namespace df::enums;
|
||||||
|
|
||||||
|
command_result diggingInvadersCommand(color_ostream &out, std::vector <std::string> & parameters);
|
||||||
|
void watchForJobComplete(color_ostream& out, void* ptr);
|
||||||
|
void newInvasionHandler(color_ostream& out, void* ptr);
|
||||||
|
void clearDijkstra();
|
||||||
|
void findAndAssignInvasionJob(color_ostream& out, void*);
|
||||||
|
//int32_t manageInvasion(color_ostream& out);
|
||||||
|
|
||||||
|
DFHACK_PLUGIN("diggingInvaders");
|
||||||
|
|
||||||
|
//TODO: when world unloads
|
||||||
|
static int32_t lastInvasionJob=-1;
|
||||||
|
static int32_t lastInvasionDigger = -1;
|
||||||
|
static int32_t edgesPerTick = 100;
|
||||||
|
//static EventManager::EventHandler jobCompleteHandler(watchForJobComplete, 5);
|
||||||
|
static bool enabled=false;
|
||||||
|
static bool activeDigging=false;
|
||||||
|
static unordered_set<string> diggingRaces;
|
||||||
|
static unordered_set<int32_t> invaderJobs;
|
||||||
|
static df::coord lastDebugEdgeCostPoint;
|
||||||
|
unordered_map<string, DigAbilities> digAbilities;
|
||||||
|
|
||||||
|
static cost_t costWeightDefault[] = {
|
||||||
|
//Distance
|
||||||
|
1,
|
||||||
|
//Destroy Building
|
||||||
|
2,
|
||||||
|
//Dig
|
||||||
|
10000,
|
||||||
|
//DestroyRoughConstruction
|
||||||
|
1000,
|
||||||
|
//DestroySmoothConstruction
|
||||||
|
100,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int32_t jobDelayDefault[] = {
|
||||||
|
//Distance
|
||||||
|
-1,
|
||||||
|
//Destroy Building
|
||||||
|
1000,
|
||||||
|
//Dig
|
||||||
|
1000,
|
||||||
|
//DestroyRoughConstruction
|
||||||
|
1000,
|
||||||
|
//DestroySmoothConstruction
|
||||||
|
100,
|
||||||
|
};
|
||||||
|
|
||||||
|
DFhackCExport command_result plugin_init (color_ostream &out, std::vector <PluginCommand> &commands)
|
||||||
|
{
|
||||||
|
commands.push_back(PluginCommand(
|
||||||
|
"diggingInvaders", "Makes invaders dig to your dwarves.",
|
||||||
|
diggingInvadersCommand, false, /* true means that the command can't be used from non-interactive user interface */
|
||||||
|
" diggingInvaders 0\n disables the plugin\n"
|
||||||
|
" diggingInvaders 1\n enables the plugin\n"
|
||||||
|
" diggingInvaders enable\n enables the plugin\n"
|
||||||
|
" diggingInvaders disable\n disables the plugin\n"
|
||||||
|
" diggingInvaders add GOBLIN\n registers the race GOBLIN as a digging invader. Case-sensitive.\n"
|
||||||
|
" diggingInvaders remove GOBLIN\n unregisters the race GOBLIN as a digging invader. Case-sensitive.\n"
|
||||||
|
" diggingInvaders setCost GOBLIN walk n\n sets the walk cost in the path algorithm for the race GOBLIN\n"
|
||||||
|
" diggingInvaders setCost GOBLIN destroyBuilding n\n"
|
||||||
|
" diggingInvaders setCost GOBLIN dig n\n"
|
||||||
|
" diggingInvaders setCost GOBLIN destroyRoughConstruction n\n rough constructions are made from boulders\n"
|
||||||
|
" diggingInvaders setCost GOBLIN destroySmoothConstruction n\n smooth constructions are made from blocks or bars instead of boulders\n"
|
||||||
|
" diggingInvaders setDelay GOBLIN destroyBuilding n\n adds to the job_completion_timer of destroy building jobs that are assigned to invaders\n"
|
||||||
|
" diggingInvaders setDelay GOBLIN dig n\n"
|
||||||
|
" diggingInvaders setDelay GOBLIN destroyRoughConstruction n\n"
|
||||||
|
" diggingInvaders setDelay GOBLIN destroySmoothConstruction n\n"
|
||||||
|
" diggingInvaders now\n makes invaders try to dig now, if plugin is enabled\n"
|
||||||
|
" diggingInvaders clear\n clears all digging invader races\n"
|
||||||
|
" diggingInvaders edgesPerTick n\n makes the pathfinding algorithm work on at most n edges per tick. Set to 0 or lower to make it unlimited."
|
||||||
|
// " diggingInvaders\n Makes invaders try to dig now.\n"
|
||||||
|
));
|
||||||
|
|
||||||
|
//*df::global::debug_showambush = true;
|
||||||
|
return CR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
|
||||||
|
{
|
||||||
|
return CR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event)
|
||||||
|
{
|
||||||
|
switch (event) {
|
||||||
|
case DFHack::SC_WORLD_LOADED:
|
||||||
|
//TODO: check game mode
|
||||||
|
//in case there are invaders when the game is loaded, we check if there's work to be done
|
||||||
|
activeDigging = enabled;
|
||||||
|
clearDijkstra();
|
||||||
|
findAndAssignInvasionJob(out, (void*)0);
|
||||||
|
break;
|
||||||
|
case DFHack::SC_WORLD_UNLOADED:
|
||||||
|
// cleanup
|
||||||
|
lastInvasionJob = lastInvasionDigger = -1;
|
||||||
|
enabled = false;
|
||||||
|
activeDigging = false;
|
||||||
|
clearDijkstra();
|
||||||
|
invaderJobs.clear();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return CR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
df::coord getRoot(df::coord point, unordered_map<df::coord, df::coord>& rootMap);
|
||||||
|
|
||||||
|
class PointComp {
|
||||||
|
public:
|
||||||
|
unordered_map<df::coord, cost_t, PointHash> *pointCost;
|
||||||
|
PointComp(unordered_map<df::coord, cost_t, PointHash> *p): pointCost(p) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t operator()(df::coord p1, df::coord p2) {
|
||||||
|
if ( p1 == p2 ) return 0;
|
||||||
|
auto i1 = pointCost->find(p1);
|
||||||
|
auto i2 = pointCost->find(p2);
|
||||||
|
if ( i1 == pointCost->end() && i2 == pointCost->end() )
|
||||||
|
return p1 < p2;
|
||||||
|
if ( i1 == pointCost->end() )
|
||||||
|
return true;
|
||||||
|
if ( i2 == pointCost->end() )
|
||||||
|
return false;
|
||||||
|
cost_t c1 = (*i1).second;
|
||||||
|
cost_t c2 = (*i2).second;
|
||||||
|
if ( c1 != c2 )
|
||||||
|
return c1 < c2;
|
||||||
|
return p1 < p2;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//bool important(df::coord pos, map<df::coord, set<Edge> >& edges, df::coord prev, set<df::coord>& importantPoints, set<Edge>& importantEdges);
|
||||||
|
|
||||||
|
void newInvasionHandler(color_ostream& out, void* ptr) {
|
||||||
|
if ( activeDigging )
|
||||||
|
return;
|
||||||
|
activeDigging = true;
|
||||||
|
findAndAssignInvasionJob(out, (void*)0);
|
||||||
|
}
|
||||||
|
|
||||||
|
command_result diggingInvadersCommand(color_ostream& out, std::vector<std::string>& parameters) {
|
||||||
|
for ( size_t a = 0; a < parameters.size(); a++ ) {
|
||||||
|
if ( parameters[a] == "1" || parameters[a] == "enable" ) {
|
||||||
|
enabled = true;
|
||||||
|
} else if ( parameters[a] == "0" || parameters[a] == "disable" ) {
|
||||||
|
enabled = false;
|
||||||
|
} else if ( parameters[a] == "add" || parameters[a] == "remove" ) {
|
||||||
|
if ( a+1 >= parameters.size() )
|
||||||
|
return CR_WRONG_USAGE;
|
||||||
|
string race = parameters[a+1];
|
||||||
|
if ( parameters[a] == "add" ) {
|
||||||
|
diggingRaces.insert(race);
|
||||||
|
DigAbilities& abilities = digAbilities[race];
|
||||||
|
memcpy(abilities.costWeight, costWeightDefault, costDim*sizeof(cost_t));
|
||||||
|
memcpy(abilities.jobDelay, jobDelayDefault, costDim*sizeof(int32_t));
|
||||||
|
} else {
|
||||||
|
diggingRaces.erase(race);
|
||||||
|
digAbilities.erase(race);
|
||||||
|
}
|
||||||
|
a++;
|
||||||
|
|
||||||
|
} else if ( parameters[a] == "setCost" || parameters[a] == "setDelay" ) {
|
||||||
|
if ( a+3 >= parameters.size() )
|
||||||
|
return CR_WRONG_USAGE;
|
||||||
|
|
||||||
|
string raceString = parameters[a+1];
|
||||||
|
if ( digAbilities.find(raceString) == digAbilities.end() ) {
|
||||||
|
DigAbilities bob;
|
||||||
|
memset(&bob, 0xFF, sizeof(bob));
|
||||||
|
digAbilities[raceString] = bob;
|
||||||
|
}
|
||||||
|
DigAbilities& abilities = digAbilities[raceString];
|
||||||
|
|
||||||
|
string costStr = parameters[a+2];
|
||||||
|
int32_t costDim = -1;
|
||||||
|
if ( costStr == "walk" ) {
|
||||||
|
costDim = CostDimension::Walk;
|
||||||
|
if ( parameters[a] == "setDelay" )
|
||||||
|
return CR_WRONG_USAGE;
|
||||||
|
} else if ( costStr == "destroyBuilding" ) {
|
||||||
|
costDim = CostDimension::DestroyBuilding;
|
||||||
|
} else if ( costStr == "dig" ) {
|
||||||
|
costDim = CostDimension::Dig;
|
||||||
|
} else if ( costStr == "destroyRoughConstruction" ) {
|
||||||
|
costDim = CostDimension::DestroyRoughConstruction;
|
||||||
|
} else if ( costStr == "destroySmoothConstruction" ) {
|
||||||
|
costDim = CostDimension::DestroySmoothConstruction;
|
||||||
|
} else {
|
||||||
|
return CR_WRONG_USAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
cost_t value;
|
||||||
|
stringstream asdf(parameters[a+3]);
|
||||||
|
asdf >> value;
|
||||||
|
//if ( parameters[a] == "setCost" && value <= 0 )
|
||||||
|
// return CR_WRONG_USAGE;
|
||||||
|
if ( parameters[a] == "setCost" ) {
|
||||||
|
abilities.costWeight[costDim] = value;
|
||||||
|
} else {
|
||||||
|
abilities.jobDelay[costDim] = value;
|
||||||
|
}
|
||||||
|
a += 3;
|
||||||
|
} else if ( parameters[a] == "edgeCost" ) {
|
||||||
|
if ( a+1 >= parameters.size() )
|
||||||
|
return CR_WRONG_USAGE;
|
||||||
|
|
||||||
|
string raceString = parameters[a+1];
|
||||||
|
|
||||||
|
if ( digAbilities.find(raceString) == digAbilities.end() ) {
|
||||||
|
out.print("Race %s does not have dig abilities assigned.\n", raceString.c_str());
|
||||||
|
return CR_WRONG_USAGE;
|
||||||
|
}
|
||||||
|
DigAbilities& abilities = digAbilities[raceString];
|
||||||
|
|
||||||
|
df::coord bob = Gui::getCursorPos();
|
||||||
|
out.print("(%d,%d,%d), (%d,%d,%d): cost = %lld\n", lastDebugEdgeCostPoint.x, lastDebugEdgeCostPoint.y, lastDebugEdgeCostPoint.z, bob.x, bob.y, bob.z, getEdgeCost(out, lastDebugEdgeCostPoint, bob, abilities));
|
||||||
|
lastDebugEdgeCostPoint = bob;
|
||||||
|
a++;
|
||||||
|
} else if ( parameters[a] == "now" ) {
|
||||||
|
activeDigging = true;
|
||||||
|
findAndAssignInvasionJob(out, (void*)0);
|
||||||
|
} else if ( parameters[a] == "clear" ) {
|
||||||
|
diggingRaces.clear();
|
||||||
|
digAbilities.clear();
|
||||||
|
} else if ( parameters[a] == "edgesPerTick" ) {
|
||||||
|
if ( a+1 >= parameters.size() )
|
||||||
|
return CR_WRONG_USAGE;
|
||||||
|
stringstream asdf(parameters[a+1]);
|
||||||
|
int32_t edgeCount = 100;
|
||||||
|
asdf >> edgeCount;
|
||||||
|
edgesPerTick = edgeCount;
|
||||||
|
a++;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return CR_WRONG_USAGE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
activeDigging = enabled;
|
||||||
|
out.print("diggingInvaders: enabled = %d, activeDigging = %d, edgesPerTick = %d\n", enabled, activeDigging, edgesPerTick);
|
||||||
|
|
||||||
|
EventManager::unregisterAll(plugin_self);
|
||||||
|
if ( enabled ) {
|
||||||
|
EventManager::EventHandler handler(newInvasionHandler, 1000);
|
||||||
|
EventManager::registerListener(EventManager::EventType::INVASION, handler, plugin_self);
|
||||||
|
clearDijkstra();
|
||||||
|
findAndAssignInvasionJob(out, (void*)0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return CR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//dijkstra globals
|
||||||
|
vector<int32_t> invaders;
|
||||||
|
unordered_set<df::coord, PointHash> invaderPts;
|
||||||
|
unordered_set<df::coord, PointHash> localPts;
|
||||||
|
unordered_map<df::coord,df::coord,PointHash> parentMap;
|
||||||
|
unordered_map<df::coord,cost_t,PointHash> costMap;
|
||||||
|
|
||||||
|
PointComp comp(&costMap);
|
||||||
|
set<df::coord, PointComp> fringe(comp);
|
||||||
|
EventManager::EventHandler findJobTickHandler(findAndAssignInvasionJob, 1);
|
||||||
|
|
||||||
|
int32_t localPtsFound = 0;
|
||||||
|
unordered_set<df::coord,PointHash> closedSet;
|
||||||
|
unordered_map<df::coord,int32_t,PointHash> workNeeded; //non-walking work needed to get there
|
||||||
|
bool foundTarget = false;
|
||||||
|
int32_t edgeCount = 0;
|
||||||
|
|
||||||
|
void clearDijkstra() {
|
||||||
|
invaders.clear();
|
||||||
|
invaderPts.clear();
|
||||||
|
localPts.clear();
|
||||||
|
parentMap.clear();
|
||||||
|
costMap.clear();
|
||||||
|
comp = PointComp(&costMap);
|
||||||
|
fringe = set<df::coord,PointComp>(comp);
|
||||||
|
localPtsFound = edgeCount = 0;
|
||||||
|
foundTarget = false;
|
||||||
|
closedSet.clear();
|
||||||
|
workNeeded.clear();
|
||||||
|
}
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void findAndAssignInvasionJob(color_ostream& out, void* tickTime) {
|
||||||
|
CoreSuspender suspend;
|
||||||
|
//returns the worker id of the job created //used to
|
||||||
|
//out.print("%s, %d: %d\n", __FILE__, __LINE__, (int32_t)tickTime);
|
||||||
|
|
||||||
|
if ( !enabled || !activeDigging ) {
|
||||||
|
clearDijkstra();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
EventManager::unregister(EventManager::EventType::TICK, findJobTickHandler, plugin_self);
|
||||||
|
EventManager::registerTick(findJobTickHandler, 1, plugin_self);
|
||||||
|
|
||||||
|
if ( fringe.empty() ) {
|
||||||
|
df::unit* lastDigger = df::unit::find(lastInvasionDigger);
|
||||||
|
if ( lastDigger && lastDigger->job.current_job && lastDigger->job.current_job->id == lastInvasionJob ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//out.print("%s,%d: lastDigger = %d, last job = %d, last digger's job = %d\n", __FILE__, __LINE__, lastInvasionDigger, lastInvasionJob, !lastDigger ? -1 : (!lastDigger->job.current_job ? -1 : lastDigger->job.current_job->id));
|
||||||
|
lastInvasionDigger = lastInvasionJob = -1;
|
||||||
|
|
||||||
|
clearDijkstra();
|
||||||
|
unordered_set<uint16_t> invaderConnectivity;
|
||||||
|
unordered_set<uint16_t> localConnectivity;
|
||||||
|
|
||||||
|
//find all locals and invaders
|
||||||
|
for ( size_t a = 0; a < df::global::world->units.all.size(); a++ ) {
|
||||||
|
df::unit* unit = df::global::world->units.all[a];
|
||||||
|
if ( unit->flags1.bits.dead )
|
||||||
|
continue;
|
||||||
|
if ( Units::isCitizen(unit) ) {
|
||||||
|
if ( localPts.find(unit->pos) != localPts.end() )
|
||||||
|
continue;
|
||||||
|
localPts.insert(unit->pos);
|
||||||
|
df::map_block* block = Maps::getTileBlock(unit->pos);
|
||||||
|
localConnectivity.insert(block->walkable[unit->pos.x&0xF][unit->pos.y&0xF]);
|
||||||
|
} else if ( unit->flags1.bits.active_invader ) {
|
||||||
|
df::creature_raw* raw = df::creature_raw::find(unit->race);
|
||||||
|
if ( raw == NULL ) {
|
||||||
|
out.print("%s,%d: WTF? Couldn't find creature raw.\n", __FILE__, __LINE__);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
if ( diggingRaces.find(raw->creature_id) == diggingRaces.end() )
|
||||||
|
continue;
|
||||||
|
*/
|
||||||
|
if ( digAbilities.find(raw->creature_id) == digAbilities.end() )
|
||||||
|
continue;
|
||||||
|
if ( invaderPts.find(unit->pos) != invaderPts.end() )
|
||||||
|
continue;
|
||||||
|
//must be able to wield a pick: this is overly pessimistic
|
||||||
|
if ( unit->status2.limbs_grasp_max <= 0 || unit->status2.limbs_grasp_count < unit->status2.limbs_grasp_max )
|
||||||
|
continue;
|
||||||
|
df::map_block* block = Maps::getTileBlock(unit->pos);
|
||||||
|
invaderConnectivity.insert(block->walkable[unit->pos.x&0xF][unit->pos.y&0xF]);
|
||||||
|
if ( invaderPts.size() > 0 )
|
||||||
|
continue;
|
||||||
|
invaderPts.insert(unit->pos);
|
||||||
|
costMap[unit->pos] = 0;
|
||||||
|
fringe.insert(unit->pos);
|
||||||
|
invaders.push_back(unit->id);
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( invaders.empty() || localPts.empty() ) {
|
||||||
|
activeDigging = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//if local connectivity is not disjoint from invader connectivity, no digging required
|
||||||
|
bool overlap = false;
|
||||||
|
for ( auto a = localConnectivity.begin(); a != localConnectivity.end(); a++ ) {
|
||||||
|
uint16_t conn = *a;
|
||||||
|
if ( invaderConnectivity.find(conn) == invaderConnectivity.end() )
|
||||||
|
continue;
|
||||||
|
overlap = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ( overlap ) {
|
||||||
|
//still keep checking next frame: might kill a few outsiders then dig down
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
df::unit* firstInvader = df::unit::find(invaders[0]);
|
||||||
|
if ( firstInvader == NULL ) {
|
||||||
|
fringe.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
df::creature_raw* creature_raw = df::creature_raw::find(firstInvader->race);
|
||||||
|
if ( creature_raw == NULL || digAbilities.find(creature_raw->creature_id) == digAbilities.end() ) {
|
||||||
|
//inappropriate digger: no dig abilities
|
||||||
|
fringe.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DigAbilities& abilities = digAbilities[creature_raw->creature_id];
|
||||||
|
//TODO: check that firstInvader is an appropriate digger
|
||||||
|
//out << firstInvader->id << endl;
|
||||||
|
//out << firstInvader->pos.x << ", " << firstInvader->pos.y << ", " << firstInvader->pos.z << endl;
|
||||||
|
//out << __LINE__ << endl;
|
||||||
|
|
||||||
|
uint32_t xMax, yMax, zMax;
|
||||||
|
Maps::getSize(xMax,yMax,zMax);
|
||||||
|
xMax *= 16;
|
||||||
|
yMax *= 16;
|
||||||
|
MapExtras::MapCache cache;
|
||||||
|
|
||||||
|
clock_t t0 = clock();
|
||||||
|
clock_t totalEdgeTime = 0;
|
||||||
|
int32_t edgesExpanded = 0;
|
||||||
|
while(!fringe.empty()) {
|
||||||
|
if ( edgesPerTick > 0 && edgesExpanded++ >= edgesPerTick ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
df::coord pt = *(fringe.begin());
|
||||||
|
fringe.erase(fringe.begin());
|
||||||
|
//out.print("line %d: fringe size = %d, localPtsFound = %d / %d, closedSetSize = %d, pt = %d,%d,%d\n", __LINE__, fringe.size(), localPtsFound, localPts.size(), closedSet.size(), pt.x,pt.y,pt.z);
|
||||||
|
if ( closedSet.find(pt) != closedSet.end() ) {
|
||||||
|
out.print("%s, line %d: Double closure! Bad!\n", __FILE__, __LINE__);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
closedSet.insert(pt);
|
||||||
|
|
||||||
|
if ( localPts.find(pt) != localPts.end() ) {
|
||||||
|
localPtsFound++;
|
||||||
|
if ( true || localPtsFound >= localPts.size() ) {
|
||||||
|
foundTarget = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ( workNeeded.find(pt) == workNeeded.end() || workNeeded[pt] == 0 ) {
|
||||||
|
//there are still dwarves to kill that don't require digging to get to
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cost_t myCost = costMap[pt];
|
||||||
|
clock_t edgeTime = clock();
|
||||||
|
vector<Edge>* myEdges = getEdgeSet(out, pt, cache, xMax, yMax, zMax, abilities);
|
||||||
|
totalEdgeTime += (clock() - edgeTime);
|
||||||
|
for ( auto a = myEdges->begin(); a != myEdges->end(); a++ ) {
|
||||||
|
Edge &e = *a;
|
||||||
|
if ( e.p1 == df::coord() )
|
||||||
|
break;
|
||||||
|
edgeCount++;
|
||||||
|
df::coord& other = e.p1;
|
||||||
|
if ( other == pt )
|
||||||
|
other = e.p2;
|
||||||
|
//if ( closedSet.find(other) != closedSet.end() )
|
||||||
|
// continue;
|
||||||
|
auto i = costMap.find(other);
|
||||||
|
if ( i != costMap.end() ) {
|
||||||
|
cost_t cost = (*i).second;
|
||||||
|
if ( cost <= myCost + e.cost ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
fringe.erase((*i).first);
|
||||||
|
}
|
||||||
|
costMap[other] = myCost + e.cost;
|
||||||
|
fringe.insert(other);
|
||||||
|
parentMap[other] = pt;
|
||||||
|
workNeeded[other] = (e.cost > 1 ? 1 : 0) + workNeeded[pt];
|
||||||
|
}
|
||||||
|
delete myEdges;
|
||||||
|
}
|
||||||
|
clock_t time = clock() - t0;
|
||||||
|
//out.print("tickTime = %d, time = %d, totalEdgeTime = %d, total points = %d, total edges = %d, time per point = %.3f, time per edge = %.3f, clocks/sec = %d\n", (int32_t)tickTime, time, totalEdgeTime, closedSet.size(), edgeCount, (float)time / closedSet.size(), (float)time / edgeCount, CLOCKS_PER_SEC);
|
||||||
|
fringe.clear();
|
||||||
|
|
||||||
|
if ( !foundTarget )
|
||||||
|
return;
|
||||||
|
|
||||||
|
unordered_set<df::coord, PointHash> requiresZNeg;
|
||||||
|
unordered_set<df::coord, PointHash> requiresZPos;
|
||||||
|
|
||||||
|
//find important edges
|
||||||
|
Edge firstImportantEdge(df::coord(), df::coord(), -1);
|
||||||
|
//df::coord closest;
|
||||||
|
//cost_t closestCostEstimate=0;
|
||||||
|
//cost_t closestCostActual=0;
|
||||||
|
for ( auto i = localPts.begin(); i != localPts.end(); i++ ) {
|
||||||
|
df::coord pt = *i;
|
||||||
|
if ( costMap.find(pt) == costMap.end() )
|
||||||
|
continue;
|
||||||
|
if ( parentMap.find(pt) == parentMap.end() )
|
||||||
|
continue;
|
||||||
|
//closest = pt;
|
||||||
|
//closestCostEstimate = costMap[closest];
|
||||||
|
//if ( workNeeded[pt] == 0 )
|
||||||
|
// continue;
|
||||||
|
while ( parentMap.find(pt) != parentMap.end() ) {
|
||||||
|
//out.print("(%d,%d,%d)\n", pt.x, pt.y, pt.z);
|
||||||
|
df::coord parent = parentMap[pt];
|
||||||
|
cost_t cost = getEdgeCost(out, parent, pt, abilities);
|
||||||
|
if ( cost < 0 ) {
|
||||||
|
//path invalidated
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//closestCostActual += cost;
|
||||||
|
if ( Maps::canStepBetween(parent, pt) ) {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if ( pt.x == parent.x && pt.y == parent.y ) {
|
||||||
|
if ( pt.z < parent.z ) {
|
||||||
|
requiresZNeg.insert(parent);
|
||||||
|
requiresZPos.insert(pt);
|
||||||
|
} else if ( pt.z > parent.z ) {
|
||||||
|
requiresZNeg.insert(pt);
|
||||||
|
requiresZPos.insert(parent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//if ( workNeeded[pt] > workNeeded[parent] ) {
|
||||||
|
//importantEdges.push_front(Edge(pt,parent,0));
|
||||||
|
//}
|
||||||
|
firstImportantEdge = Edge(pt,parent,0);
|
||||||
|
//out.print("(%d,%d,%d) -> (%d,%d,%d)\n", parent.x,parent.y,parent.z, pt.x,pt.y,pt.z);
|
||||||
|
}
|
||||||
|
pt = parent;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ( firstImportantEdge.p1 == df::coord() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
if ( closestCostActual != closestCostEstimate ) {
|
||||||
|
out.print("%s,%d: closest = (%d,%d,%d), estimate = %lld != actual = %lld\n", __FILE__, __LINE__, closest.x,closest.y,closest.z, closestCostEstimate, closestCostActual);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
assignJob(out, firstImportantEdge, parentMap, costMap, invaders, requiresZNeg, requiresZPos, cache, abilities);
|
||||||
|
lastInvasionDigger = firstInvader->id;
|
||||||
|
lastInvasionJob = firstInvader->job.current_job ? firstInvader->job.current_job->id : -1;
|
||||||
|
invaderJobs.erase(lastInvasionJob);
|
||||||
|
for ( df::job_list_link* link = &df::global::world->job_list; link != NULL; link = link->next ) {
|
||||||
|
if ( link->item == NULL )
|
||||||
|
continue;
|
||||||
|
df::job* job = link->item;
|
||||||
|
if ( invaderJobs.find(job->id) == invaderJobs.end() ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//cancel it
|
||||||
|
job->flags.bits.item_lost = 1;
|
||||||
|
out.print("%s,%d: cancelling job %d.\n", __FILE__,__LINE__, job->id);
|
||||||
|
//invaderJobs.remove(job->id);
|
||||||
|
}
|
||||||
|
invaderJobs.erase(lastInvasionJob);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
df::coord getRoot(df::coord point, map<df::coord, df::coord>& rootMap) {
|
||||||
|
map<df::coord, df::coord>::iterator i = rootMap.find(point);
|
||||||
|
if ( i == rootMap.end() ) {
|
||||||
|
rootMap[point] = point;
|
||||||
|
return point;
|
||||||
|
}
|
||||||
|
df::coord parent = (*i).second;
|
||||||
|
if ( parent == point )
|
||||||
|
return parent;
|
||||||
|
df::coord root = getRoot(parent, rootMap);
|
||||||
|
rootMap[point] = root;
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,481 @@
|
|||||||
|
#include "edgeCost.h"
|
||||||
|
|
||||||
|
#include "modules/Buildings.h"
|
||||||
|
#include "modules/Maps.h"
|
||||||
|
#include "modules/MapCache.h"
|
||||||
|
|
||||||
|
#include "df/building.h"
|
||||||
|
#include "df/building_bridgest.h"
|
||||||
|
#include "df/building_hatchst.h"
|
||||||
|
#include "df/building_type.h"
|
||||||
|
#include "df/construction.h"
|
||||||
|
#include "df/coord.h"
|
||||||
|
#include "df/item_type.h"
|
||||||
|
#include "df/map_block.h"
|
||||||
|
#include "df/tile_building_occ.h"
|
||||||
|
#include "df/tiletype.h"
|
||||||
|
#include "df/tiletype_material.h"
|
||||||
|
#include "df/tiletype_shape.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
/*
|
||||||
|
cost_t costWeight[] = {
|
||||||
|
//Distance
|
||||||
|
1,
|
||||||
|
//Destroy Building
|
||||||
|
2,
|
||||||
|
//Dig
|
||||||
|
10000,
|
||||||
|
//DestroyConstruction
|
||||||
|
100,
|
||||||
|
};
|
||||||
|
|
||||||
|
int32_t jobDelay[] = {
|
||||||
|
//Distance
|
||||||
|
-1,
|
||||||
|
//Destroy Building
|
||||||
|
1000,
|
||||||
|
//Dig
|
||||||
|
1000,
|
||||||
|
//DestroyConstruction
|
||||||
|
1000
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
/*
|
||||||
|
limitations
|
||||||
|
ramps
|
||||||
|
cave-ins
|
||||||
|
*/
|
||||||
|
cost_t getEdgeCost(color_ostream& out, df::coord pt1, df::coord pt2, DigAbilities& abilities) {
|
||||||
|
int32_t dx = pt2.x - pt1.x;
|
||||||
|
int32_t dy = pt2.y - pt1.y;
|
||||||
|
int32_t dz = pt2.z - pt1.z;
|
||||||
|
cost_t cost = abilities.costWeight[CostDimension::Walk];
|
||||||
|
if ( cost < 0 )
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if ( Maps::getTileBlock(pt1) == NULL || Maps::getTileBlock(pt2) == NULL )
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
df::tiletype* type2 = Maps::getTileType(pt2);
|
||||||
|
df::tiletype_shape shape2 = ENUM_ATTR(tiletype, shape, *type2);
|
||||||
|
|
||||||
|
if ( Maps::getTileBlock(pt1)->designation[pt1.x&0xF][pt1.y&0xF].bits.flow_size >= 4 )
|
||||||
|
return -1;
|
||||||
|
if ( Maps::getTileBlock(pt2)->designation[pt2.x&0xF][pt2.y&0xF].bits.flow_size >= 4 )
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if ( shape2 == df::enums::tiletype_shape::EMPTY ) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( shape2 == df::enums::tiletype_shape::TREE )
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
if () {
|
||||||
|
df::map_block* temp = Maps::getTileBlock(df::coord(pt1.x,pt1.y,pt1.z-1));
|
||||||
|
if ( temp && temp->designation[pt1.x&0xF][pt1.y&0xF]
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
if ( Maps::canStepBetween(pt1, pt2) ) {
|
||||||
|
return cost;
|
||||||
|
}
|
||||||
|
|
||||||
|
df::building* building2 = Buildings::findAtTile(pt2);
|
||||||
|
if ( building2 ) {
|
||||||
|
if ( abilities.costWeight[CostDimension::DestroyBuilding] < 0 )
|
||||||
|
return -1;
|
||||||
|
cost += abilities.costWeight[CostDimension::DestroyBuilding];
|
||||||
|
if ( dx*dx + dy*dy > 1 )
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool construction2 = ENUM_ATTR(tiletype, material, *type2) == df::enums::tiletype_material::CONSTRUCTION;
|
||||||
|
if ( construction2 ) {
|
||||||
|
//smooth or not?
|
||||||
|
df::construction* constr = df::construction::find(pt2);
|
||||||
|
bool smooth = constr != NULL && constr->item_type != df::enums::item_type::BOULDER;
|
||||||
|
if ( smooth ) {
|
||||||
|
if ( abilities.costWeight[CostDimension::DestroySmoothConstruction] < 0 )
|
||||||
|
return -1;
|
||||||
|
cost += abilities.costWeight[CostDimension::DestroySmoothConstruction];
|
||||||
|
} else {
|
||||||
|
if ( abilities.costWeight[CostDimension::DestroyRoughConstruction] < 0 )
|
||||||
|
return -1;
|
||||||
|
cost += abilities.costWeight[CostDimension::DestroyRoughConstruction];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( dz == 0 ) {
|
||||||
|
if ( !building2 && !construction2 ) {
|
||||||
|
//it has to be a wall
|
||||||
|
if ( shape2 == df::enums::tiletype_shape::RAMP_TOP ) {
|
||||||
|
return -1;
|
||||||
|
} else if ( shape2 != df::enums::tiletype_shape::WALL ) {
|
||||||
|
//out << "shape = " << (int32_t)shape2 << endl;
|
||||||
|
//out << __FILE__ << ", line " << __LINE__ << ": WTF?" << endl;
|
||||||
|
return cost;
|
||||||
|
}
|
||||||
|
if ( abilities.costWeight[CostDimension::Dig] < 0 ) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
cost += abilities.costWeight[CostDimension::Dig];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ( dx == 0 && dy == 0 ) {
|
||||||
|
df::tiletype* type1 = Maps::getTileType(pt1);
|
||||||
|
df::tiletype_shape shape1 = ENUM_ATTR(tiletype, shape, *type1);
|
||||||
|
if ( dz > 0 ) {
|
||||||
|
bool walkable_low2 = shape2 == df::tiletype_shape::STAIR_DOWN || shape2 == df::tiletype_shape::STAIR_UPDOWN;
|
||||||
|
if ( !walkable_low2 ) {
|
||||||
|
if ( building2 || construction2 )
|
||||||
|
return -1;
|
||||||
|
if ( abilities.costWeight[CostDimension::Dig] < 0 ) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
cost += abilities.costWeight[CostDimension::Dig];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool walkable_high1 = shape1 == df::tiletype_shape::STAIR_UP || shape1 == df::tiletype_shape::STAIR_UPDOWN;
|
||||||
|
if ( !walkable_high1 ) {
|
||||||
|
if ( shape1 != df::enums::tiletype_shape::WALL ) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if ( abilities.costWeight[CostDimension::Dig] < 0 ) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
cost += abilities.costWeight[CostDimension::Dig];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( building2 ) {
|
||||||
|
//moving up through an open bridge or a usable hatch is fine. other buildings are not
|
||||||
|
bool unforbiddenHatch = false;
|
||||||
|
if ( building2->getType() == df::building_type::Hatch ) {
|
||||||
|
df::building_hatchst* hatch = (df::building_hatchst*)building2;
|
||||||
|
if ( !hatch->door_flags.bits.forbidden && !(hatch->door_flags.bits.operated_by_mechanisms&&hatch->door_flags.bits.closed) )
|
||||||
|
unforbiddenHatch = true;
|
||||||
|
}
|
||||||
|
bool inactiveBridge = false;
|
||||||
|
if ( building2->getType() == df::building_type::Bridge ) {
|
||||||
|
df::building_bridgest* bridge = (df::building_bridgest*)building2;
|
||||||
|
bool xMin = pt2.x == bridge->x1;
|
||||||
|
bool xMax = pt2.x == bridge->x2;
|
||||||
|
bool yMin = pt2.y == bridge->y1;
|
||||||
|
bool yMax = pt2.y == bridge->y2;
|
||||||
|
if ( !bridge->gate_flags.bits.closed ) {
|
||||||
|
//if it's open, we could still be in the busy part of it
|
||||||
|
if ( bridge->direction == df::building_bridgest::T_direction::Left && !xMin ) {
|
||||||
|
inactiveBridge = true;
|
||||||
|
} else if ( bridge->direction == df::building_bridgest::T_direction::Right && !xMax ) {
|
||||||
|
inactiveBridge = true;
|
||||||
|
} else if ( bridge->direction == df::building_bridgest::T_direction::Up && !yMax ) {
|
||||||
|
inactiveBridge = true;
|
||||||
|
} else if ( bridge->direction == df::building_bridgest::T_direction::Down && !yMin ) {
|
||||||
|
inactiveBridge = true;
|
||||||
|
} else if ( bridge->direction == df::building_bridgest::T_direction::Retracting ) {
|
||||||
|
inactiveBridge = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( !unforbiddenHatch && !inactiveBridge )
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*bool forbidden = false;
|
||||||
|
if ( building2 && building2->getType() == df::building_type::Hatch ) {
|
||||||
|
df::building_hatchst* hatch = (df::building_hatchst*)building2;
|
||||||
|
if ( hatch->door_flags.bits.forbidden )
|
||||||
|
forbidden = true;
|
||||||
|
}
|
||||||
|
if ( forbidden )
|
||||||
|
return -1;*/
|
||||||
|
} else {
|
||||||
|
bool walkable_high2 = shape2 == df::tiletype_shape::STAIR_UP || shape2 == df::tiletype_shape::STAIR_UPDOWN;
|
||||||
|
if ( !walkable_high2 ) {
|
||||||
|
if ( building2 || construction2 )
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if ( shape2 != df::enums::tiletype_shape::WALL )
|
||||||
|
return -1;
|
||||||
|
if ( abilities.costWeight[CostDimension::Dig] < 0 ) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
cost += abilities.costWeight[CostDimension::Dig];
|
||||||
|
}
|
||||||
|
bool walkable_low1 = shape1 == df::tiletype_shape::STAIR_DOWN || shape1 == df::tiletype_shape::STAIR_UPDOWN;
|
||||||
|
if ( !walkable_low1 ) {
|
||||||
|
//if ( building1 || construction1 )
|
||||||
|
//return -1;
|
||||||
|
//TODO: consider ramps
|
||||||
|
if ( shape1 == df::tiletype_shape::RAMP )
|
||||||
|
return -1;
|
||||||
|
if ( abilities.costWeight[CostDimension::Dig] < 0 ) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
cost += abilities.costWeight[CostDimension::Dig];
|
||||||
|
}
|
||||||
|
|
||||||
|
df::building* building1 = Buildings::findAtTile(pt1);
|
||||||
|
//if you're moving down, and you're on a bridge, and that bridge is lowered, then you can't do it
|
||||||
|
if ( building1 && building1->getType() == df::building_type::Bridge ) {
|
||||||
|
df::building_bridgest* bridge = (df::building_bridgest*)building2;
|
||||||
|
if ( bridge->gate_flags.bits.closed ) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
//open bridges moving down, standing on bad spot
|
||||||
|
if ( bridge->direction == df::building_bridgest::T_direction::Left && pt1.x == bridge->x1 )
|
||||||
|
return -1;
|
||||||
|
if ( bridge->direction == df::building_bridgest::T_direction::Right && pt1.x == bridge->x2 )
|
||||||
|
return -1;
|
||||||
|
if ( bridge->direction == df::building_bridgest::T_direction::Up && pt1.y == bridge->y1 )
|
||||||
|
return -1;
|
||||||
|
if ( bridge->direction == df::building_bridgest::T_direction::Down && pt1.y == bridge->y2 )
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool forbidden = false;
|
||||||
|
if ( building1 && building1->getType() == df::building_type::Hatch ) {
|
||||||
|
df::building_hatchst* hatch = (df::building_hatchst*)building1;
|
||||||
|
if ( hatch->door_flags.bits.forbidden || hatch->door_flags.bits.closed && hatch->door_flags.bits.operated_by_mechanisms )
|
||||||
|
forbidden = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//you can deconstruct a hatch from the side
|
||||||
|
if ( building1 && forbidden /*&& building1->getType() == df::building_type::Hatch*/ ) {
|
||||||
|
/*
|
||||||
|
df::coord support[] = {df::coord(pt1.x-1, pt1.y, pt1.z), df::coord(pt1.x+1,pt1.y,pt1.z), df::coord(pt1.x,pt1.y-1,pt1.z), df::coord(pt1.x,pt1.y+1,pt1.z)};
|
||||||
|
if ( abilities.costWeight[CostDimension::DestroyBuilding] < 0 ) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
cost_t minCost = -1;
|
||||||
|
for ( size_t a = 0; a < 4; a++ ) {
|
||||||
|
df::tiletype* supportType = Maps::getTileType(support[a]);
|
||||||
|
df::tiletype_shape shape = ENUM_ATTR(tiletype, shape, *supportType);
|
||||||
|
df::tiletype_shape_basic basic = ENUM_ATTR(tiletype_shape, basic_shape, shape);
|
||||||
|
cost_t cost2 = 2*abilities.costWeight[CostDimension::Walk] + abilities.costWeight[CostDimension::DestroyBuilding];
|
||||||
|
if ( !Maps::canStepBetween(pt1, support[a]) ) {
|
||||||
|
switch(basic) {
|
||||||
|
case tiletype_shape_basic::Open:
|
||||||
|
//TODO: check for a hatch or a bridge: that makes it ok
|
||||||
|
continue;
|
||||||
|
case tiletype_shape_basic::Wall:
|
||||||
|
if ( ENUM_ATTR(tiletype, material, *supportType) == df::enums::tiletype_material::CONSTRUCTION ) {
|
||||||
|
if ( abilities.costWeight[CostDimension::DestroyConstruction] < 0 ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
cost2 += abilities.costWeight[CostDimension::DestroyConstruction];
|
||||||
|
} else {
|
||||||
|
if ( abilities.costWeight[CostDimension::Dig] < 0 ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
cost2 += abilities.costWeight[CostDimension::Dig];
|
||||||
|
}
|
||||||
|
case tiletype_shape_basic::Ramp:
|
||||||
|
//TODO: check for a hatch or a bridge: that makes it ok
|
||||||
|
if ( shape == df::enums::tiletype_shape::RAMP_TOP ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
case tiletype_shape_basic::Stair:
|
||||||
|
case tiletype_shape_basic::Floor:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ( Buildings::findAtTile(support[a]) ) {
|
||||||
|
if ( abilities.costWeight[CostDimension::DestroyBuilding] < 0 ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
cost2 += abilities.costWeight[CostDimension::DestroyBuilding];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( minCost == -1 || cost2 < minCost )
|
||||||
|
minCost = cost2;
|
||||||
|
}
|
||||||
|
if ( minCost == -1 )
|
||||||
|
return -1;
|
||||||
|
cost += minCost;
|
||||||
|
|
||||||
|
*/
|
||||||
|
//note: assignJob is not ready for this level of sophistication, so don't allow it
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//nonvertical
|
||||||
|
//out.print("%s, line %d: (%d,%d,%d)->(%d,%d,%d)\n", __FILE__, __LINE__, pt1.x,pt1.y,pt1.z, pt2.x,pt2.y,pt2.z);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cost;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
cost_t getEdgeCostOld(color_ostream& out, df::coord pt1, df::coord pt2) {
|
||||||
|
//first, list all the facts
|
||||||
|
int32_t dx = pt2.x - pt1.x;
|
||||||
|
int32_t dy = pt2.y - pt1.y;
|
||||||
|
int32_t dz = pt2.z - pt1.z;
|
||||||
|
cost_t cost = costWeight[CostDimension::Walk];
|
||||||
|
|
||||||
|
if ( false ) {
|
||||||
|
if ( Maps::canStepBetween(pt1,pt2) )
|
||||||
|
return cost;
|
||||||
|
return 100 + cost;
|
||||||
|
}
|
||||||
|
|
||||||
|
Maps::ensureTileBlock(pt1);
|
||||||
|
Maps::ensureTileBlock(pt2);
|
||||||
|
df::tiletype* type1 = Maps::getTileType(pt1);
|
||||||
|
df::tiletype* type2 = Maps::getTileType(pt2);
|
||||||
|
df::map_block* block1 = Maps::getTileBlock(pt1);
|
||||||
|
df::map_block* block2 = Maps::getTileBlock(pt2);
|
||||||
|
df::tiletype_shape shape1 = ENUM_ATTR(tiletype, shape, *type1);
|
||||||
|
df::tiletype_shape shape2 = ENUM_ATTR(tiletype, shape, *type2);
|
||||||
|
|
||||||
|
bool construction1 = ENUM_ATTR(tiletype, material, *type1) == df::enums::tiletype_material::CONSTRUCTION;
|
||||||
|
bool construction2 = ENUM_ATTR(tiletype, material, *type2) == df::enums::tiletype_material::CONSTRUCTION;
|
||||||
|
bool passable1 = block1->walkable[pt1.x&0xF][pt1.y&0xF] != 0;
|
||||||
|
bool passable2 = block2->walkable[pt2.x&0xF][pt2.y&0xF] != 0;
|
||||||
|
bool passable_high1 = ENUM_ATTR(tiletype_shape, passable_high, shape1);
|
||||||
|
bool passable_high2 = ENUM_ATTR(tiletype_shape, passable_high, shape2);
|
||||||
|
bool passable_low1 = ENUM_ATTR(tiletype_shape, passable_low, shape1);
|
||||||
|
bool passable_low2 = ENUM_ATTR(tiletype_shape, passable_low, shape2);
|
||||||
|
|
||||||
|
bool building1, building2;
|
||||||
|
bool sameBuilding = false;
|
||||||
|
{
|
||||||
|
df::enums::tile_building_occ::tile_building_occ awk = block1->occupancy[pt1.x&0x0F][pt1.y&0x0F].bits.building;
|
||||||
|
building1 = awk == df::enums::tile_building_occ::Obstacle || awk == df::enums::tile_building_occ::Impassable;
|
||||||
|
awk = block2->occupancy[pt2.x&0x0F][pt2.y&0x0F].bits.building;
|
||||||
|
building2 = awk == df::enums::tile_building_occ::Obstacle || awk == df::enums::tile_building_occ::Impassable;
|
||||||
|
if ( building1 && building2 ) {
|
||||||
|
df::building* b1 = Buildings::findAtTile(pt1);
|
||||||
|
df::building* b2 = Buildings::findAtTile(pt2);
|
||||||
|
sameBuilding = b1 == b2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( Maps::canStepBetween(pt1, pt2) ) {
|
||||||
|
if ( building2 && !sameBuilding ) {
|
||||||
|
cost += costWeight[CostDimension::DestroyBuilding];
|
||||||
|
}
|
||||||
|
return cost;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( shape2 == df::enums::tiletype_shape::EMPTY ) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//cannot step between: find out why
|
||||||
|
if ( dz == 0 ) {
|
||||||
|
if ( passable2 && !passable1 ) {
|
||||||
|
return cost;
|
||||||
|
}
|
||||||
|
if ( passable1 && passable2 ) {
|
||||||
|
out << __FILE__ << ", line " << __LINE__ << ": WTF?" << endl;
|
||||||
|
return cost;
|
||||||
|
}
|
||||||
|
//pt2 is not passable. it must be a construction, a building, or a wall.
|
||||||
|
if ( building2 ) {
|
||||||
|
if ( sameBuilding ) {
|
||||||
|
//don't charge twice for destroying the same building
|
||||||
|
return cost;
|
||||||
|
}
|
||||||
|
cost += costWeight[CostDimension::DestroyBuilding];
|
||||||
|
return cost;
|
||||||
|
}
|
||||||
|
if ( construction2 ) {
|
||||||
|
//impassible constructions must be deconstructed
|
||||||
|
cost += costWeight[CostDimension::DestroyConstruction];
|
||||||
|
return cost;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( shape2 == df::enums::tiletype_shape::TREE ) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( shape2 == df::enums::tiletype_shape::RAMP_TOP ) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//it has to be a wall
|
||||||
|
if ( shape2 != df::enums::tiletype_shape::WALL ) {
|
||||||
|
out << "shape = " << (int32_t)shape2 << endl;
|
||||||
|
out << __FILE__ << ", line " << __LINE__ << ": WTF?" << endl;
|
||||||
|
return cost;
|
||||||
|
}
|
||||||
|
|
||||||
|
cost += costWeight[CostDimension::Dig];
|
||||||
|
return cost;
|
||||||
|
}
|
||||||
|
|
||||||
|
//dz != 0
|
||||||
|
if ( dx == 0 && dy == 0 ) {
|
||||||
|
if ( dz > 0 ) {
|
||||||
|
if ( passable_low2 )
|
||||||
|
return cost;
|
||||||
|
if ( building2 || construction2 ) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
cost += costWeight[CostDimension::Dig];
|
||||||
|
return cost;
|
||||||
|
}
|
||||||
|
|
||||||
|
//descending
|
||||||
|
if ( passable_high2 )
|
||||||
|
return cost;
|
||||||
|
|
||||||
|
if ( building2 || construction2 ) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//must be a wall?
|
||||||
|
if ( shape2 != df::enums::tiletype_shape::WALL ) {
|
||||||
|
out.print("%s, line %n: WTF?\n", __FILE__, __LINE__);
|
||||||
|
return cost;
|
||||||
|
}
|
||||||
|
|
||||||
|
cost += costWeight[CostDimension::Dig];
|
||||||
|
return cost;
|
||||||
|
}
|
||||||
|
|
||||||
|
//moving diagonally
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
vector<Edge>* getEdgeSet(color_ostream &out, df::coord point, MapExtras::MapCache& cache, int32_t xMax, int32_t yMax, int32_t zMax, DigAbilities& abilities) {
|
||||||
|
//vector<Edge>* result = new vector<Edge>(26);
|
||||||
|
vector<Edge>* result = new vector<Edge>();
|
||||||
|
result->reserve(26);
|
||||||
|
|
||||||
|
//size_t count = 0;
|
||||||
|
for ( int32_t dx = -1; dx <= 1; dx++ ) {
|
||||||
|
for ( int32_t dy = -1; dy <= 1; dy++ ) {
|
||||||
|
for ( int32_t dz = -1; dz <= 1; dz++ ) {
|
||||||
|
df::coord neighbor(point.x+dx, point.y+dy, point.z+dz);
|
||||||
|
if ( !Maps::isValidTilePos(neighbor) )
|
||||||
|
continue;
|
||||||
|
if ( dz != 0 && /*(point.x == 0 || point.y == 0 || point.z == 0 || point.x == xMax-1 || point.y == yMax-1 || point.z == zMax-1) ||*/ (neighbor.x == 0 || neighbor.y == 0 || neighbor.z == 0 || neighbor.x == xMax-1 || neighbor.y == yMax-1 || neighbor.z == zMax-1) )
|
||||||
|
continue;
|
||||||
|
if ( dx == 0 && dy == 0 && dz == 0 )
|
||||||
|
continue;
|
||||||
|
cost_t cost = getEdgeCost(out, point, neighbor, abilities);
|
||||||
|
if ( cost == -1 )
|
||||||
|
continue;
|
||||||
|
Edge edge(point, neighbor, cost);
|
||||||
|
//(*result)[count] = edge;
|
||||||
|
result->push_back(edge);
|
||||||
|
//count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,98 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Core.h"
|
||||||
|
#include "Console.h"
|
||||||
|
#include "DataDefs.h"
|
||||||
|
|
||||||
|
#include "modules/Maps.h"
|
||||||
|
#include "modules/MapCache.h"
|
||||||
|
|
||||||
|
#include "df/coord.h"
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
//cost is [path cost, building destruction cost, dig cost, construct cost]. Minimize constructions, then minimize dig cost, then minimize path cost.
|
||||||
|
enum CostDimension {
|
||||||
|
Walk,
|
||||||
|
DestroyBuilding,
|
||||||
|
Dig,
|
||||||
|
DestroyRoughConstruction,
|
||||||
|
DestroySmoothConstruction,
|
||||||
|
//Construct,
|
||||||
|
costDim
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef int64_t cost_t;
|
||||||
|
|
||||||
|
struct DigAbilities {
|
||||||
|
cost_t costWeight[costDim];
|
||||||
|
int32_t jobDelay[costDim];
|
||||||
|
};
|
||||||
|
|
||||||
|
//extern cost_t costWeight[costDim];
|
||||||
|
//extern int32_t jobDelay[costDim];
|
||||||
|
extern std::unordered_map<std::string, DigAbilities> digAbilities;
|
||||||
|
/*
|
||||||
|
const cost_t costWeight[] = {
|
||||||
|
//Distance
|
||||||
|
1,
|
||||||
|
//Destroy Building
|
||||||
|
2,
|
||||||
|
//Dig
|
||||||
|
10000,
|
||||||
|
//DestroyConstruction
|
||||||
|
100,
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
|
class Edge {
|
||||||
|
public:
|
||||||
|
//static map<df::coord, int32_t> pointCost;
|
||||||
|
df::coord p1;
|
||||||
|
df::coord p2;
|
||||||
|
cost_t cost;
|
||||||
|
Edge() {
|
||||||
|
cost = -1;
|
||||||
|
}
|
||||||
|
Edge(const Edge& e): p1(e.p1), p2(e.p2), cost(e.cost) {
|
||||||
|
|
||||||
|
}
|
||||||
|
Edge(df::coord p1In, df::coord p2In, cost_t costIn): cost(costIn) {
|
||||||
|
if ( p2In < p1In ) {
|
||||||
|
p1 = p2In;
|
||||||
|
p2 = p1In;
|
||||||
|
} else {
|
||||||
|
p1 = p1In;
|
||||||
|
p2 = p2In;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const Edge& e) const {
|
||||||
|
return (cost == e.cost && p1 == e.p1 && p2 == e.p2);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<(const Edge& e) const {
|
||||||
|
if ( cost != e.cost )
|
||||||
|
return cost < e.cost;
|
||||||
|
if ( p1.z != e.p1.z )
|
||||||
|
return p1.z < e.p1.z;
|
||||||
|
if ( p1 != e.p1 )
|
||||||
|
return p1 < e.p1;
|
||||||
|
if ( p2.z != e.p2.z )
|
||||||
|
return p2.z < e.p2.z;
|
||||||
|
if ( p2 != e.p2 )
|
||||||
|
return p2 < e.p2;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PointHash {
|
||||||
|
size_t operator()(const df::coord c) const {
|
||||||
|
return c.x * 65537 + c.y * 17 + c.z;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
cost_t getEdgeCost(color_ostream& out, df::coord pt1, df::coord pt2, DigAbilities& abilities);
|
||||||
|
std::vector<Edge>* getEdgeSet(color_ostream &out, df::coord point, MapExtras::MapCache& cache, int32_t xMax, int32_t yMax, int32_t zMax, DigAbilities& abilities);
|
||||||
|
|
Loading…
Reference in New Issue