command_result readFlag (Core * c, vector <string> & parameters) { c->Suspend(); // init the map if(!Maps::IsValid()) { c->con.printerr("Can't init map. Make sure you have a map loaded in DF.\n"); c->Resume(); return CR_FAILURE; } int32_t cx, cy, cz; if(!Gui::getCursorCoords(cx,cy,cz)) { c->con.printerr("Cursor is not active.\n"); c->Resume(); return CR_FAILURE; } DFCoord cursor = DFCoord(cx,cy,cz); MapExtras::MapCache * MCache = new MapExtras::MapCache(); t_occupancy oc = MCache->occupancyAt(cursor); c->con.print("Current Value: %d\n", oc.bits.building); c->Resume(); return CR_OK; }
DFhackCExport command_result filltraffic(DFHack::Core * c, std::vector<std::string> & params) { //Maximum map size. uint32_t x_max,y_max,z_max; //Source and target traffic types. e_traffic source = traffic_normal; e_traffic target = traffic_normal; //Option flags bool updown = false; bool checkpit = true; bool checkbuilding = true; //Loop through parameters for(int i = 0; i < params.size();i++) { if(params[i] == "help" || params[i] == "?") { c->con.print("Flood-fill selected traffic type from the cursor.\n" "Traffic Type Codes:\n" "\tH: High Traffic\n" "\tN: Normal Traffic\n" "\tL: Low Traffic\n" "\tR: Restricted Traffic\n" "Other Options:\n" "\tX: Fill accross z-levels.\n" "\tB: Include buildings and stockpiles.\n" "\tP: Include empty space.\n" "Example:\n" "'filltraffic H' - When used in a room with doors,\n" " it will set traffic to HIGH in just that room." ); return CR_OK; } switch (toupper(params[i][0])) { case 'H': target = traffic_high; break; case 'N': target = traffic_normal; break; case 'L': target = traffic_low; break; case 'R': target = traffic_restricted; break; case 'X': updown = true; break; case 'B': checkbuilding = false; break; case 'P': checkpit = false; break; } } //Initialization. c->Suspend(); DFHack::Maps * Maps = c->getMaps(); DFHack::Gui * Gui = c->getGui(); // init the map if(!Maps->Start()) { c->con.printerr("Can't init map. Make sure you have a map loaded in DF.\n"); c->Resume(); return CR_FAILURE; } int32_t cx, cy, cz; Maps->getSize(x_max,y_max,z_max); uint32_t tx_max = x_max * 16; uint32_t ty_max = y_max * 16; Gui->getCursorCoords(cx,cy,cz); while(cx == -30000) { c->con.printerr("Cursor is not active.\n"); c->Resume(); return CR_FAILURE; } DFHack::DFCoord xy ((uint32_t)cx,(uint32_t)cy,cz); MapExtras::MapCache * MCache = new MapExtras::MapCache(Maps); DFHack::t_designation des = MCache->designationAt(xy); int16_t tt = MCache->tiletypeAt(xy); DFHack::t_occupancy oc; if (checkbuilding) oc = MCache->occupancyAt(xy); source = des.bits.traffic; if(source == target) { c->con.printerr("This tile is already set to the target traffic type.\n"); delete MCache; c->Resume(); return CR_FAILURE; } if(DFHack::isWallTerrain(tt)) { c->con.printerr("This tile is a wall. Please select a passable tile.\n"); delete MCache; c->Resume(); return CR_FAILURE; } if(checkpit && DFHack::isOpenTerrain(tt)) { c->con.printerr("This tile is a hole. Please select a passable tile.\n"); delete MCache; c->Resume(); return CR_FAILURE; } if(checkbuilding && oc.bits.building) { c->con.printerr("This tile contains a building. Please select an empty tile.\n"); delete MCache; c->Resume(); return CR_FAILURE; } c->con.print("%d/%d/%d ... FILLING!\n", cx,cy,cz); //Naive four-way or six-way flood fill with possible tiles on a stack. stack <DFHack::DFCoord> flood; flood.push(xy); while(!flood.empty()) { xy = flood.top(); flood.pop(); des = MCache->designationAt(xy); if (des.bits.traffic != source) continue; tt = MCache->tiletypeAt(xy); if(DFHack::isWallTerrain(tt)) continue; if(checkpit && DFHack::isOpenTerrain(tt)) continue; if (checkbuilding) { oc = MCache->occupancyAt(xy); if(oc.bits.building) continue; } //This tile is ready. Set its traffic level and add surrounding tiles. if (MCache->testCoord(xy)) { des.bits.traffic = target; MCache->setDesignationAt(xy,des); if (xy.x > 0) { flood.push(DFHack::DFCoord(xy.x - 1, xy.y, xy.z)); } if (xy.x < tx_max - 1) { flood.push(DFHack::DFCoord(xy.x + 1, xy.y, xy.z)); } if (xy.y > 0) { flood.push(DFHack::DFCoord(xy.x, xy.y - 1, xy.z)); } if (xy.y < ty_max - 1) { flood.push(DFHack::DFCoord(xy.x, xy.y + 1, xy.z)); } if (updown) { if (xy.z > 0 && DFHack::LowPassable(tt)) { flood.push(DFHack::DFCoord(xy.x, xy.y, xy.z - 1)); } if (xy.z < z_max && DFHack::HighPassable(tt)) { flood.push(DFHack::DFCoord(xy.x, xy.y, xy.z + 1)); } } } } MCache->WriteAll(); c->Resume(); return CR_OK; }
void Offscreen::drawBuffer( rect2d window,int z,std::vector<screenTile>& buffer ) { if(!df::global::world) return; //TODO static array of images for each tiletype MapExtras::MapCache cache; int w=window.second.x-window.first.x; int h=window.second.y-window.first.y; rect2d localWindow=mkrect_wh(0,0,w+1,h+1); if(buffer.size()!=w*h) buffer.resize(w*h); //basic tiletype stuff here for(int x=window.first.x;x<window.second.x;x++) //todo, make it by block, minimal improvement over cache prob though for(int y=window.first.y;y<window.second.y;y++) { DFCoord coord(x,y,z); df::tiletype tt=cache.tiletypeAt(coord); df::tiletype_shape shape = ENUM_ATTR(tiletype,shape,tt); df::tiletype_shape_basic basic_shape = ENUM_ATTR(tiletype_shape, basic_shape, shape); t_matpair mat=cache.staticMaterialAt(coord); df::tiletype_material tileMat= ENUM_ATTR(tiletype,material,tt); df::tile_designation d=cache.designationAt(coord); df::tile_occupancy o=cache.occupancyAt(coord); df::tiletype_special sp=ENUM_ATTR(tiletype,special,tt); int wx=x-window.first.x; int wy=y-window.first.y; screenTile& curTile=buffer[wx*h+wy]; if(d.bits.hidden) { curTile.tile=0; continue; } if(shape==df::tiletype_shape::EMPTY || shape==df::tiletype_shape::RAMP_TOP) { //empty,liquids and '.' for other stuff... DFCoord coord2(x,y,z-1); df::tiletype tt2=cache.tiletypeAt(coord2); df::tiletype_shape shape2 = ENUM_ATTR(tiletype,shape,tt); df::tiletype_material tileMat2= ENUM_ATTR(tiletype,material,tt2); df::tile_designation d2=cache.designationAt(coord2); df::tiletype_special sp2=ENUM_ATTR(tiletype,special,tt2); bool unDug2= (sp2!=df::tiletype_special::SMOOTH && shape2==df::tiletype_shape::WALL); if (d2.bits.flow_size>0) { if(shape!=df::tiletype_shape::RAMP_TOP) //don't show liquid amount on ramp tops curTile.tile='0'+d2.bits.flow_size; //TODO lookup setting for this else curTile.tile=tilePics[tt]; curTile.fg=(d2.bits.liquid_type)?(COLOR_RED):(COLOR_BLUE); continue; } else if(shape2==df::tiletype_shape::EMPTY) { curTile.tile=178; //look up settings curTile.fg=COLOR_CYAN; continue; } else { if(shape==df::tiletype_shape::RAMP_TOP) curTile.tile=tilePics[tt]; else curTile.tile='.'; colorTile(tileMat2,cache,coord2,curTile,unDug2); continue; } } bool inliquid=false; bool unDug= (sp!=df::tiletype_special::SMOOTH && shape==df::tiletype_shape::WALL); if (d.bits.flow_size>0) { curTile.tile='0'+d.bits.flow_size; curTile.fg=(d.bits.liquid_type)?(COLOR_RED):(COLOR_BLUE); curTile.bold=true; inliquid=true; } if(!inliquid && shape!=df::tiletype_shape::RAMP_TOP) { curTile.tile=tilePics[tt]; colorTile(tileMat,cache,coord,curTile,unDug); if(!unDug) { curTile.bg=0; } } else { if(shape==df::tiletype_shape::RAMP || shape==df::tiletype_shape::BROOK_BED || shape==df::tiletype_shape::RAMP_TOP) curTile.tile=tilePics[tt]; if(!inliquid) colorTile(tileMat,cache,coord,curTile,true); } } //plants for(int bx=window.first.x/16;bx<=window.second.x/16;bx++) //blocks have items by id. So yeah each item a search would be slow for(int by=window.first.y/16;by<=window.second.y/16;by++) { MapExtras::Block* b=cache.BlockAt(DFCoord(bx,by,z)); if(!b || !b->getRaw()) continue; std::vector<df::plant*>& plants=b->getRaw()->plants; for(int i=0;i<plants.size();i++) { df::plant* p=plants[i]; if(p->pos.z==z && isInRect(df::coord2d(p->pos.x,p->pos.y),window)) { int wx=p->pos.x-window.first.x; int wy=p->pos.y-window.first.y; screenTile& curTile=buffer[wx*h+wy]; drawPlant(p,curTile); } } std::vector<df::block_square_event*>& events=b->getRaw()->block_events; for(size_t i=0;i<events.size();i++)//maybe aggregate all the events to one array and move to a function. { df::block_square_event* e=events[i]; switch(e->getType()) { case df::block_square_event_type::grass: { df::block_square_event_grassst* grass=static_cast<df::block_square_event_grassst*>(e); MaterialInfo mat(419, grass->plant_index); if(mat.isPlant()) { df::plant_raw* p=mat.plant; for(int x=0;x<16;x++) for(int y=0;y<16;y++) { int wx=x+bx*16-window.first.x; int wy=y+by*16-window.first.y; if(isInRect(df::coord2d(wx,wy),localWindow) && grass->amount[x][y]>0) { screenTile& curTile=buffer[wx*h+wy]; /* df::tiletype tt=b->tiletypeAt(df::coord2d(x,y)); df::tiletype_special sp=ENUM_ATTR(tiletype,special,tt); df::tiletype_special::DEAD; df::tiletype_special::WET; df::tiletype_special::NORMAL; +variants */ curTile.tile=p->tiles.grass_tiles[0]; curTile.fg=p->colors.grass_colors_0[0]; curTile.bg=p->colors.grass_colors_1[0]; curTile.bold=p->colors.grass_colors_2[0]; } } //TODO alt-tiles } break; } case df::block_square_event_type::material_spatter: { //liquid: //0 nothing //1->49 color //50->99 wave //100->255 two waves //color only, if small //draw waves, if pool df::block_square_event_material_spatterst* spatter=static_cast<df::block_square_event_material_spatterst*>(e); MaterialInfo mat(spatter); if(mat.material) { for(int x=0;x<16;x++) for(int y=0;y<16;y++) { int wx=x+bx*16-window.first.x; int wy=y+by*16-window.first.y; uint8_t amount=spatter->amount[x][y]; if(isInRect(df::coord2d(wx,wy),localWindow) && amount>0) { screenTile& curTile=buffer[wx*h+wy]; curTile.fg=mat.material->tile_color[0]; curTile.bold=mat.material->tile_color[2]; if(spatter->mat_state==df::matter_state::Liquid && amount>49) { if(amount>99) curTile.tile=247; else curTile.tile=126; } } } } break; } default:; } } std::vector<df::flow_info*>& flows=b->getRaw()->flows; for(size_t i=0;i<flows.size();i++) { df::flow_info* f=flows[i]; int wx=f->pos.x-window.first.x; int wy=f->pos.y-window.first.y; if(f->density>0 && isInRect(df::coord2d(wx,wy),localWindow)) { screenTile& curTile=buffer[wx*h+wy]; drawFlow(f,curTile); } } } //in df items blink between stuff, but i don't have time for that //also move up, before flows std::vector<df::item*>& items=df::global::world->items.other[df::items_other_id::IN_PLAY]; for(int i=0;i<items.size();i++) { df::item* it=items[i]; if(it->flags.bits.on_ground && it->pos.z==z && isInRect(df::coord2d(it->pos.x,it->pos.y),window)) { int wx=it->pos.x-window.first.x; int wy=it->pos.y-window.first.y; screenTile& curTile=buffer[wx*h+wy]; drawItem(it,curTile); } } //buildings std::vector<df::building*>& buildings=df::global::world->buildings.all; for(int i=0;i<buildings.size();i++) { df::building* build=buildings[i]; if(z!=build->z) continue; if(!build->isVisibleInUI()) continue; if(isInRect(df::coord2d(build->x1,build->y1),window)||isInRect(df::coord2d(build->x2,build->y2),window)) { df::building_drawbuffer drawBuffer; build->getDrawExtents(&drawBuffer); int bw=drawBuffer.x2-drawBuffer.x1; int bh=drawBuffer.y2-drawBuffer.y1; build->drawBuilding(&drawBuffer,0); //might be viewscreen dependant int wx=build->x1-window.first.x; int wy=build->y1-window.first.y; for(int x=0;x<=bw;x++) for(int y=0;y<=bh;y++) { df::coord2d p(x+wx,y+wy); if(isInRect(p,localWindow)) { screenTile& curTile=buffer[p.x*h+p.y]; if(drawBuffer.tile[x][y]!=32) { curTile.tile=drawBuffer.tile[x][y]; curTile.fg=drawBuffer.fore[x][y]; curTile.bg=drawBuffer.back[x][y]; curTile.bold=drawBuffer.bright[x][y]; } } } } } //units. TODO No multi tile units yet. std::vector<df::unit*>& units=df::global::world->units.active; for(int i=0;i<units.size();i++) { df::unit* u=units[i]; if(!u->flags1.bits.dead && u->pos.z==z && isInRect(df::coord2d(u->pos.x,u->pos.y),window)) { int wx=u->pos.x-window.first.x; int wy=u->pos.y-window.first.y; screenTile& curTile=buffer[wx*h+wy]; drawUnit(u,curTile); } } }
command_result writeFlag (Core * c, vector <string> & parameters) { if (parameters.size() == 0) { c->con.print("No value specified\n"); return CR_FAILURE; } if (parameters[0] == "help" || parameters[0] == "?") { c->con.print("Set the building occupancy flag.\n" "Value must be between 0 and 7, inclusive.\n"); return CR_OK; } char value; switch (parameters[0][0]) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': value = parameters[0][0] - '0'; break; default: c->con.print("Invalid value specified\n"); return CR_FAILURE; break; //Redundant. } c->Suspend(); // init the map if(!Maps::IsValid()) { c->con.printerr("Can't init map. Make sure you have a map loaded in DF.\n"); c->Resume(); return CR_FAILURE; } int32_t cx, cy, cz; if(!Gui::getCursorCoords(cx,cy,cz)) { c->con.printerr("Cursor is not active.\n"); c->Resume(); return CR_FAILURE; } DFCoord cursor = DFCoord(cx,cy,cz); MapExtras::MapCache * MCache = new MapExtras::MapCache(); t_occupancy oc = MCache->occupancyAt(cursor); oc.bits.building = value; MCache->setOccupancyAt(cursor, oc); MCache->WriteAll(); c->Resume(); return CR_OK; }
command_result filltraffic(color_ostream &out, std::vector<std::string> & params) { // HOTKEY COMMAND; CORE ALREADY SUSPENDED //Maximum map size. uint32_t x_max,y_max,z_max; //Source and target traffic types. df::tile_traffic source = tile_traffic::Normal; df::tile_traffic target = tile_traffic::Normal; //Option flags bool updown = false; bool checkpit = true; bool checkbuilding = true; //Loop through parameters for(size_t i = 0; i < params.size();i++) { if (params[i] == "help" || params[i] == "?" || params[i].size() != 1) return CR_WRONG_USAGE; switch (toupper(params[i][0])) { case 'H': target = tile_traffic::High; break; case 'N': target = tile_traffic::Normal; break; case 'L': target = tile_traffic::Low; break; case 'R': target = tile_traffic::Restricted; break; case 'X': updown = true; break; case 'B': checkbuilding = false; break; case 'P': checkpit = false; break; default: return CR_WRONG_USAGE; } } if (!Maps::IsValid()) { out.printerr("Map is not available!\n"); return CR_FAILURE; } int32_t cx, cy, cz; Maps::getSize(x_max,y_max,z_max); uint32_t tx_max = x_max * 16; uint32_t ty_max = y_max * 16; Gui::getCursorCoords(cx,cy,cz); while(cx == -30000) { out.printerr("Cursor is not active.\n"); return CR_FAILURE; } DFCoord xy ((uint32_t)cx,(uint32_t)cy,cz); MapExtras::MapCache MCache; df::tile_designation des = MCache.designationAt(xy); df::tiletype tt = MCache.tiletypeAt(xy); df::tile_occupancy oc; if (checkbuilding) oc = MCache.occupancyAt(xy); source = (df::tile_traffic)des.bits.traffic; if(source == target) { out.printerr("This tile is already set to the target traffic type.\n"); return CR_FAILURE; } if(isWallTerrain(tt)) { out.printerr("This tile is a wall. Please select a passable tile.\n"); return CR_FAILURE; } if(checkpit && isOpenTerrain(tt)) { out.printerr("This tile is a hole. Please select a passable tile.\n"); return CR_FAILURE; } if(checkbuilding && oc.bits.building) { out.printerr("This tile contains a building. Please select an empty tile.\n"); return CR_FAILURE; } out.print("%d/%d/%d ... FILLING!\n", cx,cy,cz); //Naive four-way or six-way flood fill with possible tiles on a stack. stack <DFCoord> flood; flood.push(xy); while(!flood.empty()) { xy = flood.top(); flood.pop(); des = MCache.designationAt(xy); if (des.bits.traffic != source) continue; tt = MCache.tiletypeAt(xy); if(isWallTerrain(tt)) continue; if(checkpit && isOpenTerrain(tt)) continue; if (checkbuilding) { oc = MCache.occupancyAt(xy); if(oc.bits.building) continue; } //This tile is ready. Set its traffic level and add surrounding tiles. if (MCache.testCoord(xy)) { des.bits.traffic = target; MCache.setDesignationAt(xy,des); if (xy.x > 0) { flood.push(DFCoord(xy.x - 1, xy.y, xy.z)); } if (xy.x < int32_t(tx_max) - 1) { flood.push(DFCoord(xy.x + 1, xy.y, xy.z)); } if (xy.y > 0) { flood.push(DFCoord(xy.x, xy.y - 1, xy.z)); } if (xy.y < int32_t(ty_max) - 1) { flood.push(DFCoord(xy.x, xy.y + 1, xy.z)); } if (updown) { if (xy.z > 0 && LowPassable(tt)) { flood.push(DFCoord(xy.x, xy.y, xy.z - 1)); } if (xy.z < int32_t(z_max) && HighPassable(tt)) { flood.push(DFCoord(xy.x, xy.y, xy.z + 1)); } } } } MCache.WriteAll(); return CR_OK; }