bool estimate_underground(color_ostream &out, EmbarkTileLayout &tile, df::world_region_details *details, int x, int y) { // Find actual biome int bv = clip_range(details->biome[x][y] & 15, 1, 9); tile.biome_off = biome_delta[bv-1]; df::world_data *data = &world->world_data; int bx = clip_range(details->pos.x + tile.biome_off.x, 0, data->world_width-1); int by = clip_range(details->pos.y + tile.biome_off.y, 0, data->world_height-1); tile.biome_pos = coord2d(bx, by); tile.biome = &data->region_map[bx][by]; tile.geo_biome = df::world_geo_biome::find(tile.biome->geo_index); // Compute surface elevation tile.elevation = details->elevation[x][y]; tile.max_soil_depth = std::max((154-tile.elevation)/5,1); tile.penalty.clear(); // Special biome adjustments if (!tile.biome->flags.is_set(region_map_entry_flags::is_lake)) { // Mountain biome if (tile.biome->elevation >= 150) tile.max_soil_depth = 0; // Ocean biome else if (tile.biome->elevation < 100) { if (tile.elevation == 99) tile.elevation = 98; if (tile.geo_biome && (tile.geo_biome->unk1 == 4 || tile.geo_biome->unk1 == 5)) { auto b_details = get_details(data, tile.biome_pos); if (b_details && b_details->unk12e8 < 500) tile.max_soil_depth = 0; } } } tile.base_z = tile.elevation-1; return true; }
rgbf lightThread::lightUpCell(rgbf power,int dx,int dy,int tx,int ty) { int h=dispatch.getH(); if(isInRect(coord2d(tx,ty),dispatch.viewPort)) { size_t tile=tx*h+ty; int dsq=dx*dx+dy*dy; float dt=1; if(dsq == 1) dt=1; else if(dsq == 2) dt = RootTwo; else if(dsq == 0) dt = 0; else dt=sqrt((float)dsq); rgbf& v=dispatch.occlusion[tile]; lightSource& ls=dispatch.lights[tile]; bool wallhack=false; if(v.r+v.g+v.b==0) wallhack=true; if (dsq>0 && !wallhack) { power*=v.pow(dt); } if(ls.radius>0 && dsq>0) { if(power<=ls.power) return rgbf(); } //float dt=sqrt(dsq); rgbf oldCol=canvas[tile]; rgbf ncol=blendMax(power,oldCol); canvas[tile]=ncol; if(wallhack) return rgbf(); return power; } else return rgbf(); }
" If called during the embark selection screen, displays\n" " an estimate of layer stone availability. If the 'all'\n" " option is specified, also estimates veins.\n" " The estimate is computed either for 1 embark tile of the\n" " blinking biome, or for all tiles of the embark rectangle.\n" )); return CR_OK; } DFhackCExport command_result plugin_shutdown ( color_ostream &out ) { return CR_OK; } static coord2d biome_delta[] = { coord2d(-1,1), coord2d(0,1), coord2d(1,1), coord2d(-1,0), coord2d(0,0), coord2d(1,0), coord2d(-1,-1), coord2d(0,-1), coord2d(1,-1) }; struct EmbarkTileLayout { coord2d biome_off, biome_pos; df::region_map_entry *biome; df::world_geo_biome *geo_biome; int elevation, max_soil_depth; int min_z, base_z; std::map<int, float> penalty; }; static df::world_region_details *get_details(df::world_data *data, df::coord2d pos) {
bool estimate_underground(color_ostream &out, EmbarkTileLayout &tile, df::world_region_details *details, int x, int y) { // Find actual biome int bv = clip_range(details->biome[x][y] & 15, 1, 9); tile.biome_off = biome_delta[bv-1]; df::world_data *data = world->world_data; int bx = clip_range(details->pos.x + tile.biome_off.x, 0, data->world_width-1); int by = clip_range(details->pos.y + tile.biome_off.y, 0, data->world_height-1); tile.biome_pos = coord2d(bx, by); tile.biome = &data->region_map[bx][by]; tile.geo_biome = df::world_geo_biome::find(tile.biome->geo_index); // Compute surface elevation tile.elevation = details->elevation[x][y]; tile.max_soil_depth = std::max((154-tile.elevation)/5,1); tile.penalty.clear(); // Special biome adjustments if (!tile.biome->flags.is_set(region_map_entry_flags::is_lake)) { // Mountain biome if (tile.biome->elevation >= 150) tile.max_soil_depth = 0; // Ocean biome else if (tile.biome->elevation < 100) { if (tile.elevation == 99) tile.elevation = 98; if (tile.geo_biome && (tile.geo_biome->unk1 == 4 || tile.geo_biome->unk1 == 5)) { auto b_details = get_details(data, tile.biome_pos); if (b_details && b_details->unk12e8 < 500) tile.max_soil_depth = 0; } } } tile.base_z = tile.elevation-1; auto &features = details->features[x][y]; // Collect global feature layer depths and apply penalties std::map<int, int> layer_bottom, layer_top; bool sea_found = false; for (size_t i = 0; i < features.size(); i++) { auto feature = features[i]; auto layer = df::world_underground_region::find(feature->layer); if (!layer || feature->min_z == -30000) continue; layer_bottom[layer->layer_depth] = feature->min_z; layer_top[layer->layer_depth] = feature->max_z; tile.base_z = std::min(tile.base_z, (int)feature->min_z); float penalty = 1.0f; switch (layer->type) { case df::world_underground_region::Cavern: penalty = 0.75f; break; case df::world_underground_region::MagmaSea: sea_found = true; tile.min_z = feature->min_z; for (int i = feature->min_z; i <= feature->max_z; i++) tile.penalty[i] = 0.2 + 0.6f*(i-feature->min_z)/(feature->max_z-feature->min_z+1); break; case df::world_underground_region::Underworld: penalty = 0.0f; break; } if (penalty != 1.0f) { for (int i = feature->min_z; i <= feature->max_z; i++) tile.penalty[i] = penalty; } } if (!sea_found) { out.printerr("Could not find magma sea; depth may be incorrect.\n"); tile.min_z = tile.base_z; } // Scan for big local features and apply their penalties for (size_t i = 0; i < features.size(); i++) { auto feature = features[i]; auto lfeature = Maps::getLocalInitFeature(details->pos, feature->feature_idx); if (!lfeature) continue; switch (lfeature->getType()) { case feature_type::pit: case feature_type::magma_pool: case feature_type::volcano: for (int i = layer_bottom[lfeature->end_depth]; i <= layer_top[lfeature->start_depth]; i++) tile.penalty[i] = std::min(0.4f, map_find(tile.penalty, i, 1.0f)); break; default: break; } } return true; }
void lightingEngineViewscreen::doOcupancyAndLights() { float daycol; if(dayHour<0) { int length=1200/daySpeed; daycol= (*df::global::cur_year_tick % length)/ (float)length; } else daycol= fmod(dayHour,24.0f)/24.0f; //1->12h 0->24h rgbf sky_col=getSkyColor(daycol); lightSource sky(sky_col, -1);//auto calculate best size MapExtras::MapCache cache; doSun(sky,cache); int window_x=*df::global::window_x; int window_y=*df::global::window_y; coord2d window2d(window_x,window_y); int window_z=*df::global::window_z; rect2d vp=getMapViewport(); coord2d vpSize=rect_size(vp); rect2d blockVp; blockVp.first=coord2d(window_x,window_y)/16; blockVp.second=(window2d+vpSize)/16; blockVp.second.x=std::min(blockVp.second.x,(int16_t)df::global::world->map.x_count_block); blockVp.second.y=std::min(blockVp.second.y,(int16_t)df::global::world->map.y_count_block); for(int blockX=blockVp.first.x;blockX<=blockVp.second.x;blockX++) for(int blockY=blockVp.first.y;blockY<=blockVp.second.y;blockY++) { MapExtras::Block* b=cache.BlockAt(DFCoord(blockX,blockY,window_z)); MapExtras::Block* bDown=cache.BlockAt(DFCoord(blockX,blockY,window_z-1)); if(!b) continue; //empty blocks fixed by sun propagation for(int block_x = 0; block_x < 16; block_x++) for(int block_y = 0; block_y < 16; block_y++) { df::coord2d pos; pos.x = blockX*16+block_x; pos.y = blockY*16+block_y; df::coord2d gpos=pos; pos=worldToViewportCoord(pos,vp,window2d); if(!isInRect(pos,vp)) continue; int tile=getIndex(pos.x,pos.y); rgbf& curCell=ocupancy[tile]; curCell=matAmbience.transparency; df::tiletype type = b->tiletypeAt(gpos); df::tile_designation d = b->DesignationAt(gpos); if(d.bits.hidden) { curCell=rgbf(0,0,0); continue; // do not process hidden stuff, TODO other hidden stuff } //df::tile_occupancy o = b->OccupancyAt(gpos); df::tiletype_shape shape = ENUM_ATTR(tiletype,shape,type); df::tiletype_shape_basic basic_shape = ENUM_ATTR(tiletype_shape, basic_shape, shape); df::tiletype_material tileMat= ENUM_ATTR(tiletype,material,type); DFHack::t_matpair mat=b->staticMaterialAt(gpos); matLightDef* lightDef=getMaterialDef(mat.mat_type,mat.mat_index); if(!lightDef || !lightDef->isTransparent) lightDef=&matWall; if(shape==df::tiletype_shape::BROOK_BED ) { curCell=rgbf(0,0,0); } else if(shape==df::tiletype_shape::WALL) { if(tileMat==df::tiletype_material::FROZEN_LIQUID) applyMaterial(tile,matIce); else applyMaterial(tile,*lightDef); } else if(!d.bits.liquid_type && d.bits.flow_size>0 ) { applyMaterial(tile,matWater, (float)d.bits.flow_size/7.0f, (float)d.bits.flow_size/7.0f); } if(d.bits.liquid_type && d.bits.flow_size>0) { applyMaterial(tile,matLava,(float)d.bits.flow_size/7.0f,(float)d.bits.flow_size/7.0f); } else if(shape==df::tiletype_shape::EMPTY || shape==df::tiletype_shape::RAMP_TOP || shape==df::tiletype_shape::STAIR_DOWN || shape==df::tiletype_shape::STAIR_UPDOWN) { if(bDown) { df::tile_designation d2=bDown->DesignationAt(gpos); if(d2.bits.liquid_type && d2.bits.flow_size>0) { applyMaterial(tile,matLava); } } } } df::map_block* block=b->getRaw(); if(!block) continue; //flows for(int i=0;i<block->flows.size();i++) { df::flow_info* f=block->flows[i]; if(f && f->density>0 && f->type==df::flow_type::Dragonfire || f->type==df::flow_type::Fire) { df::coord2d pos=f->pos; pos=worldToViewportCoord(pos,vp,window2d); int tile=getIndex(pos.x,pos.y); if(isInRect(pos,vp)) { rgbf fireColor; if(f->density>60) { fireColor=rgbf(0.98f,0.91f,0.30f); } else if(f->density>30) { fireColor=rgbf(0.93f,0.16f,0.16f); } else { fireColor=rgbf(0.64f,0.0f,0.0f); } lightSource fire(fireColor,f->density/5); addLight(tile,fire); } } } //plants for(int i=0;i<block->plants.size();i++) { df::plant* cPlant=block->plants[i]; if (cPlant->grow_counter <180000) //todo maybe smaller light/oclusion? continue; df::coord2d pos=cPlant->pos; pos=worldToViewportCoord(pos,vp,window2d); int tile=getIndex(pos.x,pos.y); if(isInRect(pos,vp)) { applyMaterial(tile,419,cPlant->material); } } //blood and other goo for(int i=0;i<block->block_events.size();i++) { df::block_square_event* ev=block->block_events[i]; df::block_square_event_type ev_type=ev->getType(); if(ev_type==df::block_square_event_type::material_spatter) { df::block_square_event_material_spatterst* spatter=static_cast<df::block_square_event_material_spatterst*>(ev); matLightDef* m=getMaterialDef(spatter->mat_type,spatter->mat_index); if(!m) continue; if(!m->isEmiting) continue; for(int x=0;x<16;x++) for(int y=0;y<16;y++) { df::coord2d pos; pos.x = blockX*16+x; pos.y = blockY*16+y; int16_t amount=spatter->amount[x][y]; if(amount<=0) continue; pos=worldToViewportCoord(pos,vp,window2d); if(isInRect(pos,vp)) { addLight(getIndex(pos.x,pos.y),m->makeSource((float)amount/100)); } } } } } if(df::global::cursor->x>-30000) { int wx=df::global::cursor->x-window_x+vp.first.x; int wy=df::global::cursor->y-window_y+vp.first.y; int tile=getIndex(wx,wy); applyMaterial(tile,matCursor); } //citizen only emit light, if defined //or other creatures if(matCitizen.isEmiting || creatureDefs.size()>0) for (int i=0;i<df::global::world->units.active.size();++i) { df::unit *u = df::global::world->units.active[i]; coord2d pos=worldToViewportCoord(coord2d(u->pos.x,u->pos.y),vp,window2d); if(u->pos.z==window_z && isInRect(pos,vp)) { if (DFHack::Units::isCitizen(u) && !u->counters.unconscious) addLight(getIndex(pos.x,pos.y),matCitizen.makeSource()); creatureLightDef *def=getCreatureDef(u); if(def && !u->flags1.bits.dead) { addLight(getIndex(pos.x,pos.y),def->light.makeSource()); } } } //items if(itemDefs.size()>0) { std::vector<df::item*>& vec=df::global::world->items.other[items_other_id::IN_PLAY]; for(size_t i=0;i<vec.size();i++) { df::item* curItem=vec[i]; df::coord itemPos=DFHack::Items::getPosition(curItem); coord2d pos=worldToViewportCoord(itemPos,vp,window2d); itemLightDef* mat=0; if( itemPos.z==window_z && isInRect(pos,vp) && (mat=getItemDef(curItem)) ) { if( ((mat->equiped || mat->haul ||mat->inBuilding ||mat->inContainer) && curItem->flags.bits.in_inventory)|| //TODO split this up (mat->onGround && curItem->flags.bits.on_ground) ) { if(mat->light.isEmiting) addLight(getIndex(pos.x,pos.y),mat->light.makeSource()); if(!mat->light.isTransparent) addOclusion(getIndex(pos.x,pos.y),mat->light.transparency,1); } } } } //buildings for(size_t i = 0; i < df::global::world->buildings.all.size(); i++) { df::building *bld = df::global::world->buildings.all[i]; if(window_z!=bld->z) continue; if(bld->getBuildStage()<bld->getMaxBuildStage()) //only work if fully built continue; df::coord2d p1(bld->x1,bld->y1); df::coord2d p2(bld->x2,bld->y2); p1=worldToViewportCoord(p1,vp,window2d); p2=worldToViewportCoord(p2,vp,window2d); if(isInRect(p1,vp)||isInRect(p2,vp)) { int tile; if(isInRect(p1,vp)) tile=getIndex(p1.x,p1.y); //TODO multitile buildings. How they would work? else tile=getIndex(p2.x,p2.y); df::building_type type = bld->getType(); buildingLightDef* def=getBuildingDef(bld); if(!def) continue; if(type==df::enums::building_type::Door) { df::building_doorst* door=static_cast<df::building_doorst*>(bld); if(!door->door_flags.bits.closed) continue; } else if(type==df::enums::building_type::Floodgate) { df::building_floodgatest* gate=static_cast<df::building_floodgatest*>(bld); if(!gate->gate_flags.bits.closed) continue; } if(def->useMaterial) { matLightDef* mat=getMaterialDef(bld->mat_type,bld->mat_index); if(!mat)mat=&matWall; if(!def->poweredOnly || !bld->isUnpowered()) //not powered. Add occlusion only. { if(def->light.isEmiting) { addLight(tile,def->light.makeSource(def->size)); } else if(mat->isEmiting) { addLight(tile,mat->makeSource(def->size)); } } if(def->light.isTransparent) { addOclusion(tile,def->light.transparency,def->size); } else { addOclusion(tile,mat->transparency,def->size); } } else { if(!def->poweredOnly || !bld->isUnpowered())//not powered. Add occlusion only. addOclusion(tile,def->light.transparency,def->size); else applyMaterial(tile,def->light,def->size,def->thickness); } } } }