// Calculate one step and max corrections double calculation(const int iterate) { // Vt + Vr + Vb + Vl - 4Vx = 0 int y, x; double maxStepSize, pde, stepSize; d("Calculation step (iterate: %d)", iterate); maxStepSize = 0; for (y = 1; y < segment_size_y - 1; ++y) { for (x = 1; x < segment_size_x - 1; ++x) { // We're using checkerboard to calculate values. if ((y + x + iterate) % 2 == 0 || is_outside(x, y) || is_wire(x, y) ) { continue; } pde = data[bottom_point(x, y)] + data[top_point(x, y)] + data[left_point(x, y)] + data[right_point(x, y)]; pde *= .25; stepSize = abs(pde - data[y * segment_size_x + x]); if (stepSize > maxStepSize) { maxStepSize = stepSize; } data[y * segment_size_x + x] = pde; } } return maxStepSize; }
int insert_tetri(t_tetri *tetri, int i, t_square *sq, int tetri_nb) { int x; int y; y = 0; while (y < sq->y_max) { x = 0; while (x < sq->x_max) { set_position(tetri, i, x, y); if (!is_outside(tetri[i], sq) && !is_overlapping(tetri, i)) { if (i == tetri_nb - 1) return (1); if (insert_tetri(tetri, i + 1, sq, tetri_nb)) return (1); } x++; } y++; } return (0); }
// Get the value from x,y void grid::set(const int& x, const int& y, const int value) { if(is_outside(x, y)) { return; } m_grid[y][x] = value; }
// Get the value from x,y inline int grid::get(const int& x, const int& y) const { if(is_outside(x, y)) { return -1; } return m_grid[y][x]; }
void print_board() { if (screen_width > 120 || screen_height > 120) { return; } printf("Board layout (%dx%d):\n", screen_width, screen_height); for (int y = 0; y < segment_size_y; ++y) { for (int x = 0; x < segment_size_x; ++x) { printf("%s ", is_wire(x, y) ? "x" : (is_outside(x, y) ? "o" : ".")); } printf("\n"); } }
void Character::spell_call_lightning (int sn, int lvl, void *vo) { Character *vch; if (!is_outside()) { send_to_char ("You must be out of doors.\r\n"); return; } if (!g_world->is_raining()) { send_to_char ("You need bad weather.\r\n"); return; } int dam = dice (lvl / 2, 8); send_to_char ("God's lightning strikes your foes!\r\n"); act ("$n calls God's lightning to strike $s foes!", NULL, NULL, TO_ROOM); CharIter c, next; for (c = char_list.begin(); c != char_list.end(); c = next) { vch = *c; next = ++c; if (vch->in_room == NULL) continue; if (vch->in_room == in_room) { if (vch != this && (is_npc () ? !vch->is_npc () : vch->is_npc ())) damage (this, vch, vch->saves_spell (lvl) ? dam / 2 : dam, sn); continue; } if (vch->in_room->area == in_room->area && vch->is_outside() && vch->is_awake ()) vch->send_to_char ("Lightning flashes in the sky.\r\n"); } return; }
void draw_point(t_graphics *g, t_point pos) { if (is_outside(g, pos)) return ; draw_point_bare(g, pos); }
unsigned get_pixel(t_graphics *g, t_point pos) { if (is_outside(g, pos)) return (0); return (g->fb[pos.h * g->dim.w + pos.w]); }
void map::generate_lightmap() { memset(lm, 0, sizeof(lm)); memset(sm, 0, sizeof(sm)); /* Bulk light sources wastefully cast rays into neighbors; a burning hospital can produce significant slowdown, so for stuff like fire and lava: * Step 1: Store the position and luminance in buffer via add_light_source, for efficient checking of neighbors. * Step 2: After everything else, iterate buffer and apply_light_source only in non-redundant directions * Step 3: Profit! */ memset(light_source_buffer, 0, sizeof(light_source_buffer)); constexpr int dir_x[] = { 0, -1 , 1, 0 }; // [0] constexpr int dir_y[] = { -1, 0 , 0, 1 }; // [1][X][2] constexpr int dir_d[] = { 180, 270, 0, 90 }; // [3] const bool u_is_inside = !is_outside(g->u.posx(), g->u.posy()); const float natural_light = g->natural_light_level(); const float hl = natural_light / 2; if (natural_light > LIGHT_SOURCE_BRIGHT) { // Apply sunlight, first light source so just assign for (int sx = DAYLIGHT_LEVEL - hl; sx < LIGHTMAP_CACHE_X - hl; ++sx) { for (int sy = DAYLIGHT_LEVEL - hl; sy < LIGHTMAP_CACHE_Y - hl; ++sy) { // In bright light indoor light exists to some degree if (!is_outside(sx, sy)) { lm[sx][sy] = LIGHT_AMBIENT_LOW; } else if (g->u.posx() == sx && g->u.posy() == sy ) { //Only apply daylight on square where player is standing to avoid flooding // the lightmap when in less than total sunlight. lm[sx][sy] = natural_light; } } } } apply_character_light( g->u ); for( auto &n : g->active_npc ) { apply_character_light( *n ); } // LIGHTMAP_CACHE_X = MAPSIZE * SEEX // LIGHTMAP_CACHE_Y = MAPSIZE * SEEY // Traverse the submaps in order for (int smx = 0; smx < my_MAPSIZE; ++smx) { for (int smy = 0; smy < my_MAPSIZE; ++smy) { auto const cur_submap = get_submap_at_grid( smx, smy ); for (int sx = 0; sx < SEEX; ++sx) { for (int sy = 0; sy < SEEY; ++sy) { const int x = sx + smx * SEEX; const int y = sy + smy * SEEY; // When underground natural_light is 0, if this changes we need to revisit // Only apply this whole thing if the player is inside, // buildings will be shadowed when outside looking in. if (natural_light > LIGHT_SOURCE_BRIGHT && u_is_inside && !is_outside(x, y)) { // Apply light sources for external/internal divide for(int i = 0; i < 4; ++i) { if (INBOUNDS(x + dir_x[i], y + dir_y[i]) && is_outside(x + dir_x[i], y + dir_y[i])) { lm[x][y] = natural_light; if (light_transparency(x, y) > LIGHT_TRANSPARENCY_SOLID) { apply_light_arc(x, y, dir_d[i], natural_light); } } } } if (cur_submap->lum[sx][sy]) { auto items = i_at(x, y); add_light_from_items(x, y, items.begin(), items.end()); } const ter_id terrain = cur_submap->ter[sx][sy]; if (terrain == t_lava) { add_light_source(x, y, 50 ); } else if (terrain == t_console) { add_light_source(x, y, 3 ); } else if (terrain == t_utility_light) { add_light_source(x, y, 35 ); } for( auto &fld : cur_submap->fld[sx][sy] ) { const field_entry *cur = &fld.second; // TODO: [lightmap] Attach light brightness to fields switch(cur->getFieldType()) { case fd_fire: if (3 == cur->getFieldDensity()) { add_light_source(x, y, 160); } else if (2 == cur->getFieldDensity()) { add_light_source(x, y, 60); } else { add_light_source(x, y, 16); } break; case fd_fire_vent: case fd_flame_burst: add_light_source(x, y, 8); break; case fd_electricity: case fd_plasma: if (3 == cur->getFieldDensity()) { add_light_source(x, y, 8); } else if (2 == cur->getFieldDensity()) { add_light_source(x, y, 1); } else { apply_light_source(x, y, LIGHT_SOURCE_LOCAL, trigdist); // kinda a hack as the square will still get marked } break; case fd_incendiary: if (3 == cur->getFieldDensity()) { add_light_source(x, y, 30); } else if (2 == cur->getFieldDensity()) { add_light_source(x, y, 16); } else { add_light_source(x, y, 8); } break; case fd_laser: apply_light_source(x, y, 1, trigdist); break; case fd_spotlight: add_light_source(x, y, 20); break; case fd_dazzling: add_light_source(x, y, 2); break; default: //Suppress warnings break; } } } } } } for (size_t i = 0; i < g->num_zombies(); ++i) { auto &critter = g->zombie(i); if(critter.is_hallucination()) { continue; } int mx = critter.posx(); int my = critter.posy(); if (INBOUNDS(mx, my)) { if (critter.has_effect("onfire")) { apply_light_source(mx, my, 3, trigdist); } // TODO: [lightmap] Attach natural light brightness to creatures // TODO: [lightmap] Allow creatures to have light attacks (ie: eyebot) // TODO: [lightmap] Allow creatures to have facing and arc lights if (critter.type->luminance > 0) { apply_light_source(mx, my, critter.type->luminance, trigdist); } } } // Apply any vehicle light sources VehicleList vehs = get_vehicles(); for( auto &vv : vehs ) { vehicle *v = vv.v; if(v->lights_on) { int dir = v->face.dir(); float veh_luminance = 0.0; float iteration = 1.0; std::vector<int> light_indices = v->all_parts_with_feature(VPFLAG_CONE_LIGHT); for( auto &light_indice : light_indices ) { veh_luminance += ( v->part_info( light_indice ).bonus / iteration ); iteration = iteration * 1.1; } if (veh_luminance > LL_LIT) { for( auto &light_indice : light_indices ) { int px = vv.x + v->parts[light_indice].precalc[0].x; int py = vv.y + v->parts[light_indice].precalc[0].y; if(INBOUNDS(px, py)) { add_light_source(px, py, SQRT_2); // Add a little surrounding light apply_light_arc( px, py, dir + v->parts[light_indice].direction, veh_luminance, 45 ); } } } } if(v->overhead_lights_on) { std::vector<int> light_indices = v->all_parts_with_feature(VPFLAG_CIRCLE_LIGHT); for( auto &light_indice : light_indices ) { if( ( calendar::turn % 2 && v->part_info( light_indice ).has_flag( VPFLAG_ODDTURN ) ) || ( !( calendar::turn % 2 ) && v->part_info( light_indice ).has_flag( VPFLAG_EVENTURN ) ) || ( !v->part_info( light_indice ).has_flag( VPFLAG_EVENTURN ) && !v->part_info( light_indice ).has_flag( VPFLAG_ODDTURN ) ) ) { int px = vv.x + v->parts[light_indice].precalc[0].x; int py = vv.y + v->parts[light_indice].precalc[0].y; if(INBOUNDS(px, py)) { add_light_source( px, py, v->part_info( light_indice ).bonus ); } } } } // why reinvent the [lightmap] wheel if(v->dome_lights_on) { std::vector<int> light_indices = v->all_parts_with_feature(VPFLAG_DOME_LIGHT); for( auto &light_indice : light_indices ) { int px = vv.x + v->parts[light_indice].precalc[0].x; int py = vv.y + v->parts[light_indice].precalc[0].y; if(INBOUNDS(px, py)) { add_light_source( px, py, v->part_info( light_indice ).bonus ); } } } if(v->aisle_lights_on) { std::vector<int> light_indices = v->all_parts_with_feature(VPFLAG_AISLE_LIGHT); for( auto &light_indice : light_indices ) { int px = vv.x + v->parts[light_indice].precalc[0].x; int py = vv.y + v->parts[light_indice].precalc[0].y; if(INBOUNDS(px, py)) { add_light_source( px, py, v->part_info( light_indice ).bonus ); } } } if(v->has_atomic_lights) { // atomic light is always on std::vector<int> light_indices = v->all_parts_with_feature(VPFLAG_ATOMIC_LIGHT); for( auto &light_indice : light_indices ) { int px = vv.x + v->parts[light_indice].precalc[0].x; int py = vv.y + v->parts[light_indice].precalc[0].y; if(INBOUNDS(px, py)) { add_light_source( px, py, v->part_info( light_indice ).bonus ); } } } for( size_t p = 0; p < v->parts.size(); ++p ) { int px = vv.x + v->parts[p].precalc[0].x; int py = vv.y + v->parts[p].precalc[0].y; if( !INBOUNDS( px, py ) ) { continue; } if( v->part_flag( p, VPFLAG_CARGO ) && !v->part_flag( p, "COVERED" ) ) { add_light_from_items( px, py, v->get_items(p).begin(), v->get_items(p).end() ); } } } /* Now that we have position and intensity of all bulk light sources, apply_ them This may seem like extra work, but take a 12x12 raging inferno: unbuffered: (12^2)*(160*4) = apply_light_ray x 92160 buffered: (12*4)*(160) = apply_light_ray x 7680 */ for(int sx = 0; sx < LIGHTMAP_CACHE_X; ++sx) { for(int sy = 0; sy < LIGHTMAP_CACHE_Y; ++sy) { if ( light_source_buffer[sx][sy] > 0. ) { apply_light_source(sx, sy, light_source_buffer[sx][sy], ( trigdist && light_source_buffer[sx][sy] > 3. ) ); } } } if (g->u.has_active_bionic("bio_night") ) { for(int sx = 0; sx < LIGHTMAP_CACHE_X; ++sx) { for(int sy = 0; sy < LIGHTMAP_CACHE_Y; ++sy) { if (rl_dist(sx, sy, g->u.posx(), g->u.posy()) < 15) { lm[sx][sy] = 0; } } } } }
bool map::process_fields_in_submap(game *g, int gridn) { bool found_field = false; field *cur; field_id curtype; for (int locx = 0; locx < SEEX; locx++) { for (int locy = 0; locy < SEEY; locy++) { cur = &(grid[gridn].fld[locx][locy]); int x = locx + SEEX * (gridn % my_MAPSIZE), y = locy + SEEY * int(gridn / my_MAPSIZE); curtype = cur->type; if (!found_field && curtype != fd_null) found_field = true; if (cur->density > 3 || cur->density < 1) debugmsg("Whoooooa density of %d", cur->density); if (cur->age == 0) // Don't process "newborn" fields curtype = fd_null; int part; vehicle *veh; switch (curtype) { case fd_null: break; // Do nothing, obviously. OBVIOUSLY. case fd_blood: case fd_bile: if (has_flag(swimmable, x, y)) // Dissipate faster in water cur->age += 250; break; case fd_acid: if (has_flag(swimmable, x, y)) // Dissipate faster in water cur->age += 20; for (int i = 0; i < i_at(x, y).size(); i++) { item *melting = &(i_at(x, y)[i]); if (melting->made_of(LIQUID) || melting->made_of(VEGGY) || melting->made_of(FLESH) || melting->made_of(POWDER) || melting->made_of(COTTON) || melting->made_of(WOOL) || melting->made_of(PAPER) || melting->made_of(PLASTIC) || (melting->made_of(GLASS) && !one_in(3)) || one_in(4)) { // Acid destructable objects here melting->damage++; if (melting->damage >= 5 || (melting->made_of(PAPER) && melting->damage >= 3)) { cur->age += melting->volume(); for (int m = 0; m < i_at(x, y)[i].contents.size(); m++) i_at(x, y).push_back( i_at(x, y)[i].contents[m] ); i_at(x, y).erase(i_at(x, y).begin() + i); i--; } } } break; case fd_sap: break; // It doesn't do anything. case fd_fire: { // Consume items as fuel to help us grow/last longer. bool destroyed = false; int vol = 0, smoke = 0, consumed = 0; for (int i = 0; i < i_at(x, y).size() && consumed < cur->density * 2; i++) { destroyed = false; vol = i_at(x, y)[i].volume(); item *it = &(i_at(x, y)[i]); if (it->is_ammo() && it->ammo_type() != AT_BATT && it->ammo_type() != AT_NAIL && it->ammo_type() != AT_BB && it->ammo_type() != AT_BOLT && it->ammo_type() != AT_ARROW) { cur->age /= 2; cur->age -= 600; destroyed = true; smoke += 6; consumed++; } else if (it->made_of(PAPER)) { destroyed = it->burn(cur->density * 3); consumed++; if (cur->density == 1) cur->age -= vol * 10; if (vol >= 4) smoke++; } else if ((it->made_of(WOOD) || it->made_of(VEGGY))) { if (vol <= cur->density * 10 || cur->density == 3) { cur->age -= 4; destroyed = it->burn(cur->density); smoke++; consumed++; } else if (it->burnt < cur->density) { destroyed = it->burn(1); smoke++; } } else if ((it->made_of(COTTON) || it->made_of(WOOL))) { if (vol <= cur->density * 5 || cur->density == 3) { cur->age--; destroyed = it->burn(cur->density); smoke++; consumed++; } else if (it->burnt < cur->density) { destroyed = it->burn(1); smoke++; } } else if (it->made_of(FLESH)) { if (vol <= cur->density * 5 || (cur->density == 3 && one_in(vol / 20))) { cur->age--; destroyed = it->burn(cur->density); smoke += 3; consumed++; } else if (it->burnt < cur->density * 5 || cur->density >= 2) { destroyed = it->burn(1); smoke++; } } else if (it->made_of(LIQUID)) { switch (it->type->id) { // TODO: Make this be not a hack. case itm_whiskey: case itm_vodka: case itm_rum: case itm_tequila: cur->age -= 300; smoke += 6; break; default: cur->age += rng(80 * vol, 300 * vol); smoke++; } destroyed = true; consumed++; } else if (it->made_of(POWDER)) { cur->age -= vol; destroyed = true; smoke += 2; } else if (it->made_of(PLASTIC)) { smoke += 3; if (it->burnt <= cur->density * 2 || (cur->density == 3 && one_in(vol))) { destroyed = it->burn(cur->density); if (one_in(vol + it->burnt)) cur->age--; } } if (destroyed) { for (int m = 0; m < i_at(x, y)[i].contents.size(); m++) i_at(x, y).push_back( i_at(x, y)[i].contents[m] ); i_at(x, y).erase(i_at(x, y).begin() + i); i--; } } veh = &(veh_at(x, y, part)); if (veh->type != veh_null && (veh->parts[part].flags & VHP_FUEL_TANK) && veh->fuel_type == AT_GAS) { if (cur->density > 1 && one_in (8) && veh->fuel > 0) veh->explode (g, x, y); } // Consume the terrain we're on if (has_flag(explodes, x, y)) { ter(x, y) = ter_id(int(ter(x, y)) + 1); cur->age = 0; cur->density = 3; g->explosion(x, y, 40, 0, true); } else if (has_flag(inflammable, x, y) && one_in(32 - cur->density * 10)) { cur->age -= cur->density * cur->density * 40; smoke += 15; if (cur->density == 3) ter(x, y) = t_ash; } else if (has_flag(flammable, x, y) && one_in(32 - cur->density * 10)) { cur->age -= cur->density * cur->density * 40; smoke += 15; if (cur->density == 3) ter(x, y) = t_rubble; } else if (has_flag(meltable, x, y) && one_in(32 - cur->density * 10)) { cur->age -= cur->density * cur->density * 40; if (cur->density == 3) ter(x, y) = t_b_metal; } else if (terlist[ter(x, y)].flags & mfb(swimmable)) cur->age += 800; // Flames die quickly on water // If we consumed a lot, the flames grow higher while (cur->density < 3 && cur->age < 0) { cur->age += 300; cur->density++; } // If the flames are in a pit, it can't spread to non-pit bool in_pit = (ter(x, y) == t_pit); // If the flames are REALLY big, they contribute to adjacent flames if (cur->density == 3 && cur->age < 0) { // Randomly offset our x/y shifts by 0-2, to randomly pick a square to spread to int starti = rng(0, 2); int startj = rng(0, 2); for (int i = 0; i < 3 && cur->age < 0; i++) { for (int j = 0; j < 3 && cur->age < 0; j++) { int fx = x + ((i + starti) % 3) - 1, fy = y + ((j + startj) % 3) - 1; if (field_at(fx, fy).type == fd_fire && field_at(fx, fy).density < 3 && (!in_pit || ter(fx, fy) == t_pit)) { field_at(fx, fy).density++; field_at(fx, fy).age = 0; cur->age = 0; } } } } // Consume adjacent fuel / terrain to spread. // Randomly offset our x/y shifts by 0-2, to randomly pick a square to spread to int starti = rng(0, 2); int startj = rng(0, 2); for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { int fx = x + ((i + starti) % 3) - 1, fy = y + ((j + startj) % 3) - 1; if (INBOUNDS(fx, fy)) { int spread_chance = 20 * (cur->density - 1) + 10 * smoke; if (has_flag(explodes, fx, fy) && one_in(8 - cur->density)) { ter(fx, fy) = ter_id(int(ter(fx, fy)) + 1); g->explosion(fx, fy, 40, 0, true); } else if ((i != 0 || j != 0) && rng(1, 100) < spread_chance && (!in_pit || ter(fx, fy) == t_pit) && ((cur->density == 3 && (has_flag(flammable, fx, fy) || one_in(20))) || flammable_items_at(fx, fy) || field_at(fx, fy).type == fd_web)) { if (field_at(fx, fy).type == fd_smoke || field_at(fx, fy).type == fd_web) field_at(fx, fy) = field(fd_fire, 1, 0); else add_field(g, fx, fy, fd_fire, 1); } else { bool nosmoke = true; for (int ii = -1; ii <= 1; ii++) { for (int jj = -1; jj <= 1; jj++) { if (field_at(x+ii, y+jj).type == fd_fire && field_at(x+ii, y+jj).density == 3) smoke++; else if (field_at(x+ii, y+jj).type == fd_smoke) nosmoke = false; } } // If we're not spreading, maybe we'll stick out some smoke, huh? if (move_cost(fx, fy) > 0 && (!one_in(smoke) || (nosmoke && one_in(40))) && rng(3, 35) < cur->density * 10 && cur->age < 1000) { smoke--; add_field(g, fx, fy, fd_smoke, rng(1, cur->density)); } } } } } } break; case fd_smoke: for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) g->scent(x+i, y+j) = 0; } if (is_outside(x, y)) cur->age += 50; if (one_in(2)) { std::vector <point> spread; for (int a = -1; a <= 1; a++) { for (int b = -1; b <= 1; b++) { if ((field_at(x+a, y+b).type == fd_smoke && field_at(x+a, y+b).density < 3 ) || (field_at(x+a, y+b).is_null() && move_cost(x+a, y+b) > 0)) spread.push_back(point(x+a, y+b)); } } if (cur->density > 0 && cur->age > 0 && spread.size() > 0) { point p = spread[rng(0, spread.size() - 1)]; if (field_at(p.x, p.y).type == fd_smoke && field_at(p.x, p.y).density < 3) { field_at(p.x, p.y).density++; cur->density--; } else if (cur->density > 0 && move_cost(p.x, p.y) > 0 && add_field(g, p.x, p.y, fd_smoke, 1)){ cur->density--; field_at(p.x, p.y).age = cur->age; } } } break; case fd_tear_gas: // Reset nearby scents to zero for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) g->scent(x+i, y+j) = 0; } if (is_outside(x, y)) cur->age += 30; // One in three chance that it spreads (less than smoke!) if (one_in(3)) { std::vector <point> spread; // Pick all eligible points to spread to for (int a = -1; a <= 1; a++) { for (int b = -1; b <= 1; b++) { if (((field_at(x+a, y+b).type == fd_smoke || field_at(x+a, y+b).type == fd_tear_gas) && field_at(x+a, y+b).density < 3 ) || (field_at(x+a, y+b).is_null() && move_cost(x+a, y+b) > 0)) spread.push_back(point(x+a, y+b)); } } // Then, spread to a nearby point if (cur->density > 0 && cur->age > 0 && spread.size() > 0) { point p = spread[rng(0, spread.size() - 1)]; // Nearby teargas grows thicker if (field_at(p.x, p.y).type == fd_tear_gas && field_at(p.x, p.y).density < 3) { field_at(p.x, p.y).density++; cur->density--; // Nearby smoke is converted into teargas } else if (field_at(p.x, p.y).type == fd_smoke) { field_at(p.x, p.y).type = fd_tear_gas; // Or, just create a new field. } else if (cur->density > 0 && move_cost(p.x, p.y) > 0 && add_field(g, p.x, p.y, fd_tear_gas, 1)) { cur->density--; field_at(p.x, p.y).age = cur->age; } } } break; case fd_toxic_gas: // Reset nearby scents to zero for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) g->scent(x+i, y+j) = 0; } if (is_outside(x, y)) cur->age += 40; if (one_in(2)) { std::vector <point> spread; // Pick all eligible points to spread to for (int a = -1; a <= 1; a++) { for (int b = -1; b <= 1; b++) { if (((field_at(x+a, y+b).type == fd_smoke || field_at(x+a, y+b).type == fd_tear_gas || field_at(x+a, y+b).type == fd_toxic_gas || field_at(x+a, y+b).type == fd_nuke_gas ) && field_at(x+a, y+b).density < 3 ) || (field_at(x+a, y+b).is_null() && move_cost(x+a, y+b) > 0)) spread.push_back(point(x+a, y+b)); } } // Then, spread to a nearby point if (cur->density > 0 && cur->age > 0 && spread.size() > 0) { point p = spread[rng(0, spread.size() - 1)]; // Nearby toxic gas grows thicker if (field_at(p.x, p.y).type == fd_toxic_gas && field_at(p.x, p.y).density < 3) { field_at(p.x, p.y).density++; cur->density--; // Nearby smoke & teargas is converted into toxic gas } else if (field_at(p.x, p.y).type == fd_smoke || field_at(p.x, p.y).type == fd_tear_gas) { field_at(p.x, p.y).type = fd_toxic_gas; // Or, just create a new field. } else if (cur->density > 0 && move_cost(p.x, p.y) > 0 && add_field(g, p.x, p.y, fd_toxic_gas, 1)) { cur->density--; field_at(p.x, p.y).age = cur->age; } } } break; case fd_nuke_gas: // Reset nearby scents to zero for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) g->scent(x+i, y+j) = 0; } if (is_outside(x, y)) cur->age += 40; // Increase long-term radiation in the land underneath radiation(x, y) += rng(0, cur->density); if (one_in(2)) { std::vector <point> spread; // Pick all eligible points to spread to for (int a = -1; a <= 1; a++) { for (int b = -1; b <= 1; b++) { if (((field_at(x+a, y+b).type == fd_smoke || field_at(x+a, y+b).type == fd_tear_gas || field_at(x+a, y+b).type == fd_toxic_gas || field_at(x+a, y+b).type == fd_nuke_gas ) && field_at(x+a, y+b).density < 3 ) || (field_at(x+a, y+b).is_null() && move_cost(x+a, y+b) > 0)) spread.push_back(point(x+a, y+b)); } } // Then, spread to a nearby point if (cur->density > 0 && cur->age > 0 && spread.size() > 0) { point p = spread[rng(0, spread.size() - 1)]; // Nearby nukegas grows thicker if (field_at(p.x, p.y).type == fd_nuke_gas && field_at(p.x, p.y).density < 3) { field_at(p.x, p.y).density++; cur->density--; // Nearby smoke, tear, and toxic gas is converted into nukegas } else if (field_at(p.x, p.y).type == fd_smoke || field_at(p.x, p.y).type == fd_toxic_gas || field_at(p.x, p.y).type == fd_tear_gas) { field_at(p.x, p.y).type = fd_nuke_gas; // Or, just create a new field. } else if (cur->density > 0 && move_cost(p.x, p.y) > 0 && add_field(g, p.x, p.y, fd_nuke_gas, 1)) { cur->density--; field_at(p.x, p.y).age = cur->age; } } } break; case fd_gas_vent: for (int i = x - 1; i <= x + 1; i++) { for (int j = y - 1; j <= y + 1; j++) { if (field_at(i, j).type == fd_toxic_gas && field_at(i, j).density < 3) field_at(i, j).density++; else add_field(g, i, j, fd_toxic_gas, 3); } } break; case fd_fire_vent: if (cur->density > 1) { if (one_in(3)) cur->density--; } else { cur->type = fd_flame_burst; cur->density = 3; } break; case fd_flame_burst: if (cur->density > 1) cur->density--; else { cur->type = fd_fire_vent; cur->density = 3; } break; case fd_electricity: if (!one_in(5)) { // 4 in 5 chance to spread std::vector<point> valid; if (move_cost(x, y) == 0 && cur->density > 1) { // We're grounded int tries = 0; while (tries < 10 && cur->age < 50) { int cx = x + rng(-1, 1), cy = y + rng(-1, 1); if (move_cost(cx, cy) != 0 && field_at(cx, cy).is_null()) { add_field(g, cx, cy, fd_electricity, 1); cur->density--; tries = 0; } else tries++; } } else { // We're not grounded; attempt to ground for (int a = -1; a <= 1; a++) { for (int b = -1; b <= 1; b++) { if (move_cost(x + a, y + b) == 0 && // Grounded tiles first field_at(x + a, y + b).is_null()) valid.push_back(point(x + a, y + b)); } } if (valid.size() == 0) { // Spread to adjacent space, then int px = x + rng(-1, 1), py = y + rng(-1, 1); if (move_cost(px, py) > 0 && field_at(px, py).type == fd_electricity && field_at(px, py).density < 3) field_at(px, py).density++; else if (move_cost(px, py) > 0) add_field(g, px, py, fd_electricity, 1); cur->density--; } while (valid.size() > 0 && cur->density > 0) { int index = rng(0, valid.size() - 1); add_field(g, valid[index].x, valid[index].y, fd_electricity, 1); cur->density--; valid.erase(valid.begin() + index); } } } break; case fd_fatigue: if (cur->density < 3 && int(g->turn) % 3600 == 0 && one_in(10)) cur->density++; else if (cur->density == 3 && one_in(600)) { // Spawn nether creature! mon_id type = mon_id(rng(mon_flying_polyp, mon_blank)); monster creature(g->mtypes[type]); creature.spawn(x + rng(-3, 3), y + rng(-3, 3)); g->z.push_back(creature); } break; } cur->age++; if (fieldlist[cur->type].halflife > 0) { if (cur->age > 0 && dice(3, cur->age) > dice(3, fieldlist[cur->type].halflife)) { cur->age = 0; cur->density--; } if (cur->density <= 0) { // Totally dissapated. grid[gridn].field_count--; grid[gridn].fld[locx][locy] = field(); } } } } return found_field; }
void map::generate_lightmap() { memset(lm, 0, sizeof(lm)); memset(sm, 0, sizeof(sm)); /* Bulk light sources wastefully cast rays into neighbors; a burning hospital can produce significant slowdown, so for stuff like fire and lava: * Step 1: Store the position and luminance in buffer via add_light_source, for efficient checking of neighbors. * Step 2: After everything else, iterate buffer and apply_light_source only in non-redundant directions * Step 3: Profit! */ memset(light_source_buffer, 0, sizeof(light_source_buffer)); const int dir_x[] = { 1, 0 , -1, 0 }; const int dir_y[] = { 0, 1 , 0, -1 }; const int dir_d[] = { 180, 270, 0, 90 }; const float held_luminance = g->u.active_light(); const float natural_light = g->natural_light_level(); if (natural_light > LIGHT_SOURCE_BRIGHT) { // Apply sunlight, first light source so just assign for(int sx = DAYLIGHT_LEVEL - (natural_light / 2); sx < LIGHTMAP_CACHE_X - (natural_light / 2); ++sx) { for(int sy = DAYLIGHT_LEVEL - (natural_light / 2); sy < LIGHTMAP_CACHE_Y - (natural_light / 2); ++sy) { // In bright light indoor light exists to some degree if (!is_outside(sx, sy)) { lm[sx][sy] = LIGHT_AMBIENT_LOW; } else if (g->u.posx == sx && g->u.posy == sy ) { //Only apply daylight on square where player is standing to avoid flooding // the lightmap when in less than total sunlight. lm[sx][sy] = natural_light; } } } } // Apply player light sources if (held_luminance > LIGHT_AMBIENT_LOW) { apply_light_source(g->u.posx, g->u.posy, held_luminance, trigdist); } for(int sx = 0; sx < LIGHTMAP_CACHE_X; ++sx) { for(int sy = 0; sy < LIGHTMAP_CACHE_Y; ++sy) { const ter_id terrain = ter(sx, sy); const std::vector<item> &items = i_at(sx, sy); field ¤t_field = field_at(sx, sy); // When underground natural_light is 0, if this changes we need to revisit // Only apply this whole thing if the player is inside, // buildings will be shadowed when outside looking in. if (natural_light > LIGHT_AMBIENT_LOW && !is_outside(g->u.posx, g->u.posy) ) { if (!is_outside(sx, sy)) { // Apply light sources for external/internal divide for(int i = 0; i < 4; ++i) { if (INBOUNDS(sx + dir_x[i], sy + dir_y[i]) && is_outside(sx + dir_x[i], sy + dir_y[i])) { lm[sx][sy] = natural_light; if (light_transparency(sx, sy) > LIGHT_TRANSPARENCY_SOLID) { apply_light_arc(sx, sy, dir_d[i], natural_light); } } } } } for( std::vector<item>::const_iterator itm = items.begin(); itm != items.end(); ++itm ) { float ilum = 0.0; // brightness int iwidth = 0; // 0-360 degrees. 0 is a circular light_source int idir = 0; // otherwise, it's a light_arc pointed in this direction if ( itm->getlight(ilum, iwidth, idir ) ) { if ( iwidth > 0 ) { apply_light_arc( sx, sy, idir, ilum, iwidth ); } else { add_light_source(sx, sy, ilum); } } } if(terrain == t_lava) { add_light_source(sx, sy, 50 ); } if(terrain == t_console) { add_light_source(sx, sy, 3 ); } if(terrain == t_emergency_light) { add_light_source(sx, sy, 3 ); } if(terrain == t_utility_light) { add_light_source(sx, sy, 35 ); } field_entry *cur = NULL; for(std::map<field_id, field_entry *>::iterator field_list_it = current_field.getFieldStart(); field_list_it != current_field.getFieldEnd(); ++field_list_it) { cur = field_list_it->second; if(cur == NULL) { continue; } // TODO: [lightmap] Attach light brightness to fields switch(cur->getFieldType()) { case fd_fire: if (3 == cur->getFieldDensity()) { add_light_source(sx, sy, 160); } else if (2 == cur->getFieldDensity()) { add_light_source(sx, sy, 60); } else { add_light_source(sx, sy, 16); } break; case fd_fire_vent: case fd_flame_burst: add_light_source(sx, sy, 8); break; case fd_electricity: case fd_plasma: if (3 == cur->getFieldDensity()) { add_light_source(sx, sy, 8); } else if (2 == cur->getFieldDensity()) { add_light_source(sx, sy, 1); } else { apply_light_source(sx, sy, LIGHT_SOURCE_LOCAL, trigdist); // kinda a hack as the square will still get marked } break; case fd_incendiary: if (3 == cur->getFieldDensity()) { add_light_source(sx, sy, 30); } else if (2 == cur->getFieldDensity()) { add_light_source(sx, sy, 16); } else { add_light_source(sx, sy, 8); } break; case fd_laser: apply_light_source(sx, sy, 1, trigdist); break; case fd_spotlight: add_light_source(sx, sy, 20); break; case fd_dazzling: add_light_source(sx, sy, 2); break; default: //Suppress warnings break; } } } } for (size_t i = 0; i < g->num_zombies(); ++i) { int mx = g->zombie(i).posx(); int my = g->zombie(i).posy(); if (INBOUNDS(mx, my)) { if (g->zombie(i).has_effect("onfire")) { apply_light_source(mx, my, 3, trigdist); } // TODO: [lightmap] Attach natural light brightness to creatures // TODO: [lightmap] Allow creatures to have light attacks (ie: eyebot) // TODO: [lightmap] Allow creatures to have facing and arc lights if (g->zombie(i).type->luminance > 0) { apply_light_source(mx, my, g->zombie(i).type->luminance, trigdist); } } } // Apply any vehicle light sources VehicleList vehs = get_vehicles(); for( size_t v = 0; v < vehs.size(); ++v ) { if(vehs[v].v->lights_on) { int dir = vehs[v].v->face.dir(); float veh_luminance = 0.0; float iteration = 1.0; std::vector<int> light_indices = vehs[v].v->all_parts_with_feature(VPFLAG_CONE_LIGHT); for (std::vector<int>::iterator part = light_indices.begin(); part != light_indices.end(); ++part) { veh_luminance += ( vehs[v].v->part_info(*part).bonus / iteration ); iteration = iteration * 1.1; } if (veh_luminance > LL_LIT) { for (std::vector<int>::iterator part = light_indices.begin(); part != light_indices.end(); ++part) { int px = vehs[v].x + vehs[v].v->parts[*part].precalc_dx[0]; int py = vehs[v].y + vehs[v].v->parts[*part].precalc_dy[0]; if(INBOUNDS(px, py)) { apply_light_arc(px, py, dir + vehs[v].v->parts[*part].direction, veh_luminance, 45); } } } } if(vehs[v].v->overhead_lights_on) { std::vector<int> light_indices = vehs[v].v->all_parts_with_feature(VPFLAG_CIRCLE_LIGHT); for (std::vector<int>::iterator part = light_indices.begin(); part != light_indices.end(); ++part) { if((calendar::turn % 2 && vehs[v].v->part_info(*part).has_flag(VPFLAG_ODDTURN)) || (!(calendar::turn % 2) && vehs[v].v->part_info(*part).has_flag(VPFLAG_EVENTURN)) || (!vehs[v].v->part_info(*part).has_flag(VPFLAG_EVENTURN) && !vehs[v].v->part_info(*part).has_flag(VPFLAG_ODDTURN))) { int px = vehs[v].x + vehs[v].v->parts[*part].precalc_dx[0]; int py = vehs[v].y + vehs[v].v->parts[*part].precalc_dy[0]; if(INBOUNDS(px, py)) { add_light_source( px, py, vehs[v].v->part_info(*part).bonus ); } } } } } /* Now that we have position and intensity of all bulk light sources, apply_ them This may seem like extra work, but take a 12x12 raging inferno: unbuffered: (12^2)*(160*4) = apply_light_ray x 92160 buffered: (12*4)*(160) = apply_light_ray x 7680 */ for(int sx = 0; sx < LIGHTMAP_CACHE_X; ++sx) { for(int sy = 0; sy < LIGHTMAP_CACHE_Y; ++sy) { if ( light_source_buffer[sx][sy] > 0. ) { apply_light_source(sx, sy, light_source_buffer[sx][sy], ( trigdist && light_source_buffer[sx][sy] > 3. ) ); } } } if (g->u.has_active_bionic("bio_night") ) { for(int sx = 0; sx < LIGHTMAP_CACHE_X; ++sx) { for(int sy = 0; sy < LIGHTMAP_CACHE_Y; ++sy) { if (rl_dist(sx, sy, g->u.posx, g->u.posy) < 15) { lm[sx][sy] = 0; } } } } }
std::vector<tripoint> map::route( const tripoint &f, const tripoint &t, const pathfinding_settings &settings, const std::set<tripoint> &pre_closed ) const { /* TODO: If the origin or destination is out of bound, figure out the closest * in-bounds point and go to that, then to the real origin/destination. */ std::vector<tripoint> ret; if( f == t || !inbounds( f ) ) { return ret; } if( !inbounds( t ) ) { tripoint clipped = t; clip_to_bounds( clipped ); return route( f, clipped, settings, pre_closed ); } // First, check for a simple straight line on flat ground // Except when the line contains a pre-closed tile - we need to do regular pathing then static const auto non_normal = PF_SLOW | PF_WALL | PF_VEHICLE | PF_TRAP; if( f.z == t.z ) { const auto line_path = line_to( f, t ); const auto &pf_cache = get_pathfinding_cache_ref( f.z ); // Check all points for any special case (including just hard terrain) if( std::all_of( line_path.begin(), line_path.end(), [&pf_cache]( const tripoint & p ) { return !( pf_cache.special[p.x][p.y] & non_normal ); } ) ) { const std::set<tripoint> sorted_line( line_path.begin(), line_path.end() ); if( is_disjoint( sorted_line, pre_closed ) ) { return line_path; } } } // If expected path length is greater than max distance, allow only line path, like above if( rl_dist( f, t ) > settings.max_dist ) { return ret; } int max_length = settings.max_length; int bash = settings.bash_strength; int climb_cost = settings.climb_cost; bool doors = settings.allow_open_doors; bool trapavoid = settings.avoid_traps; const int pad = 16; // Should be much bigger - low value makes pathfinders dumb! int minx = std::min( f.x, t.x ) - pad; int miny = std::min( f.y, t.y ) - pad; int minz = std::min( f.z, t.z ); // TODO: Make this way bigger int maxx = std::max( f.x, t.x ) + pad; int maxy = std::max( f.y, t.y ) + pad; int maxz = std::max( f.z, t.z ); // Same TODO as above clip_to_bounds( minx, miny, minz ); clip_to_bounds( maxx, maxy, maxz ); pathfinder pf( minx, miny, maxx, maxy ); // Make NPCs not want to path through player // But don't make player pathing stop working for( const auto &p : pre_closed ) { if( p.x >= minx && p.x < maxx && p.y >= miny && p.y < maxy ) { pf.close_point( p ); } } // Start and end must not be closed pf.unclose_point( f ); pf.unclose_point( t ); pf.add_point( 0, 0, f, f ); bool done = false; do { auto cur = pf.get_next(); const int parent_index = flat_index( cur.x, cur.y ); auto &layer = pf.get_layer( cur.z ); auto &cur_state = layer.state[parent_index]; if( cur_state == ASL_CLOSED ) { continue; } if( layer.gscore[parent_index] > max_length ) { // Shortest path would be too long, return empty vector return std::vector<tripoint>(); } if( cur == t ) { done = true; break; } cur_state = ASL_CLOSED; const auto &pf_cache = get_pathfinding_cache_ref( cur.z ); const auto cur_special = pf_cache.special[cur.x][cur.y]; // 7 3 5 // 1 . 2 // 6 4 8 constexpr std::array<int, 8> x_offset{{ -1, 1, 0, 0, 1, -1, -1, 1 }}; constexpr std::array<int, 8> y_offset{{ 0, 0, -1, 1, -1, 1, -1, 1 }}; for( size_t i = 0; i < 8; i++ ) { const tripoint p( cur.x + x_offset[i], cur.y + y_offset[i], cur.z ); const int index = flat_index( p.x, p.y ); // @todo: Remove this and instead have sentinels at the edges if( p.x < minx || p.x >= maxx || p.y < miny || p.y >= maxy ) { continue; } if( layer.state[index] == ASL_CLOSED ) { continue; } // Penalize for diagonals or the path will look "unnatural" int newg = layer.gscore[parent_index] + ( ( cur.x != p.x && cur.y != p.y ) ? 1 : 0 ); const auto p_special = pf_cache.special[p.x][p.y]; // @todo: De-uglify, de-huge-n if( !( p_special & non_normal ) ) { // Boring flat dirt - the most common case above the ground newg += 2; } else { int part = -1; const maptile &tile = maptile_at_internal( p ); const auto &terrain = tile.get_ter_t(); const auto &furniture = tile.get_furn_t(); const vehicle *veh = veh_at_internal( p, part ); const int cost = move_cost_internal( furniture, terrain, veh, part ); // Don't calculate bash rating unless we intend to actually use it const int rating = ( bash == 0 || cost != 0 ) ? -1 : bash_rating_internal( bash, furniture, terrain, false, veh, part ); if( cost == 0 && rating <= 0 && ( !doors || !terrain.open ) && veh == nullptr && climb_cost <= 0 ) { layer.state[index] = ASL_CLOSED; // Close it so that next time we won't try to calculate costs continue; } newg += cost; if( cost == 0 ) { if( climb_cost > 0 && p_special & PF_CLIMBABLE ) { // Climbing fences newg += climb_cost; } else if( doors && terrain.open && ( !terrain.has_flag( "OPENCLOSE_INSIDE" ) || !is_outside( cur ) ) ) { // Only try to open INSIDE doors from the inside // To open and then move onto the tile newg += 4; } else if( veh != nullptr ) { part = veh->obstacle_at_part( part ); int dummy = -1; if( doors && veh->part_flag( part, VPFLAG_OPENABLE ) && ( !veh->part_flag( part, "OPENCLOSE_INSIDE" ) || veh_at_internal( cur, dummy ) == veh ) ) { // Handle car doors, but don't try to path through curtains newg += 10; // One turn to open, 4 to move there } else if( part >= 0 && bash > 0 ) { // Car obstacle that isn't a door // @todo: Account for armor int hp = veh->parts[part].hp(); if( hp / 20 > bash ) { // Threshold damage thing means we just can't bash this down layer.state[index] = ASL_CLOSED; continue; } else if( hp / 10 > bash ) { // Threshold damage thing means we will fail to deal damage pretty often hp *= 2; } newg += 2 * hp / bash + 8 + 4; } else if( part >= 0 ) { if( !doors || !veh->part_flag( part, VPFLAG_OPENABLE ) ) { // Won't be openable, don't try from other sides layer.state[index] = ASL_CLOSED; } continue; } } else if( rating > 1 ) { // Expected number of turns to bash it down, 1 turn to move there // and 5 turns of penalty not to trash everything just because we can newg += ( 20 / rating ) + 2 + 10; } else if( rating == 1 ) { // Desperate measures, avoid whenever possible newg += 500; } else { // Unbashable and unopenable from here if( !doors || !terrain.open ) { // Or anywhere else for that matter layer.state[index] = ASL_CLOSED; } continue; } } if( trapavoid && p_special & PF_TRAP ) { const auto &ter_trp = terrain.trap.obj(); const auto &trp = ter_trp.is_benign() ? tile.get_trap_t() : ter_trp; if( !trp.is_benign() ) { // For now make them detect all traps if( has_zlevels() && terrain.has_flag( TFLAG_NO_FLOOR ) ) { // Special case - ledge in z-levels // Warning: really expensive, needs a cache if( valid_move( p, tripoint( p.x, p.y, p.z - 1 ), false, true ) ) { tripoint below( p.x, p.y, p.z - 1 ); if( !has_flag( TFLAG_NO_FLOOR, below ) ) { // Otherwise this would have been a huge fall auto &layer = pf.get_layer( p.z - 1 ); // From cur, not p, because we won't be walking on air pf.add_point( layer.gscore[parent_index] + 10, layer.score[parent_index] + 10 + 2 * rl_dist( below, t ), cur, below ); } // Close p, because we won't be walking on it layer.state[index] = ASL_CLOSED; continue; } } else if( trapavoid ) { // Otherwise it's walkable newg += 500; } } } } // If not visited, add as open // If visited, add it only if we can do so with better score if( layer.state[index] == ASL_NONE || newg < layer.gscore[index] ) { pf.add_point( newg, newg + 2 * rl_dist( p, t ), cur, p ); } } if( !has_zlevels() || !( cur_special & PF_UPDOWN ) || !settings.allow_climb_stairs ) { // The part below is only for z-level pathing continue; } const maptile &parent_tile = maptile_at_internal( cur ); const auto &parent_terrain = parent_tile.get_ter_t(); if( settings.allow_climb_stairs && cur.z > minz && parent_terrain.has_flag( TFLAG_GOES_DOWN ) ) { tripoint dest( cur.x, cur.y, cur.z - 1 ); dest = vertical_move_destination<TFLAG_GOES_UP>( *this, dest ); if( inbounds( dest ) ) { auto &layer = pf.get_layer( dest.z ); pf.add_point( layer.gscore[parent_index] + 2, layer.score[parent_index] + 2 * rl_dist( dest, t ), cur, dest ); } } if( settings.allow_climb_stairs && cur.z < maxz && parent_terrain.has_flag( TFLAG_GOES_UP ) ) { tripoint dest( cur.x, cur.y, cur.z + 1 ); dest = vertical_move_destination<TFLAG_GOES_DOWN>( *this, dest ); if( inbounds( dest ) ) { auto &layer = pf.get_layer( dest.z ); pf.add_point( layer.gscore[parent_index] + 2, layer.score[parent_index] + 2 * rl_dist( dest, t ), cur, dest ); } } if( cur.z < maxz && parent_terrain.has_flag( TFLAG_RAMP ) && valid_move( cur, tripoint( cur.x, cur.y, cur.z + 1 ), false, true ) ) { auto &layer = pf.get_layer( cur.z + 1 ); for( size_t it = 0; it < 8; it++ ) { const tripoint above( cur.x + x_offset[it], cur.y + y_offset[it], cur.z + 1 ); pf.add_point( layer.gscore[parent_index] + 4, layer.score[parent_index] + 4 + 2 * rl_dist( above, t ), cur, above ); } } } while( !done && !pf.empty() ); if( done ) { ret.reserve( rl_dist( f, t ) * 2 ); tripoint cur = t; // Just to limit max distance, in case something weird happens for( int fdist = max_length; fdist != 0; fdist-- ) { const int cur_index = flat_index( cur.x, cur.y ); const auto &layer = pf.get_layer( cur.z ); const tripoint &par = layer.parent[cur_index]; if( cur == f ) { break; } ret.push_back( cur ); // Jumps are acceptable on 1 z-level changes // This is because stairs teleport the player too if( rl_dist( cur, par ) > 1 && abs( cur.z - par.z ) != 1 ) { debugmsg( "Jump in our route! %d:%d:%d->%d:%d:%d", cur.x, cur.y, cur.z, par.x, par.y, par.z ); return ret; } cur = par; } std::reverse( ret.begin(), ret.end() ); } return ret; }
std::vector<tripoint> map::route( const tripoint &f, const tripoint &t, const int bash, const int maxdist ) const { /* TODO: If the origin or destination is out of bound, figure out the closest * in-bounds point and go to that, then to the real origin/destination. */ int linet1 = 0, linet2 = 0; if( !inbounds( f ) || !inbounds( t ) ) { // Note: The creature needs to understand not-moving upwards // or else the plans can cause it to do so. if( sees( f, t, -1, linet1, linet2 ) ) { return line_to( f, t, linet1, linet2 ); } else { std::vector<tripoint> empty; return empty; } } // First, check for a simple straight line on flat ground // Except when the player is on the line - we need to do regular pathing then const tripoint &pl_pos = g->u.pos(); if( f.z == t.z && clear_path( f, t, -1, 2, 2, linet1, linet2 ) ) { const auto line_path = line_to( f, t, linet1, linet2 ); if( pl_pos.z != f.z ) { // Player on different z-level, certainly not on the line return line_path; } if( std::find( line_path.begin(), line_path.end(), pl_pos ) == line_path.end() ) { return line_path; } } const int pad = 8; // Should be much bigger - low value makes pathfinders dumb! int minx = std::min( f.x, t.x ) - pad; int miny = std::min( f.y, t.y ) - pad; int minz = std::min( f.z, t.z ); // TODO: Make this way bigger int maxx = std::max( f.x, t.x ) + pad; int maxy = std::max( f.y, t.y ) + pad; int maxz = std::max( f.z, t.z ); // Same TODO as above clip_to_bounds( minx, miny, minz ); clip_to_bounds( maxx, maxy, maxz ); pathfinder pf( minx, miny, maxx, maxy ); pf.add_point( 0, 0, f, f ); // Make NPCs not want to path through player // But don't make player pathing stop working if( f != pl_pos && t != pl_pos ) { pf.close_point( pl_pos ); } bool done = false; do { auto cur = pf.get_next(); const int parent_index = flat_index( cur.x, cur.y ); auto &layer = pf.get_layer( cur.z ); auto &cur_state = layer.state[parent_index]; if( cur_state == ASL_CLOSED ) { continue; } if( layer.gscore[parent_index] > maxdist ) { // Shortest path would be too long, return empty vector return std::vector<tripoint>(); } if( cur == t ) { done = true; break; } cur_state = ASL_CLOSED; std::vector<tripoint> neighbors = closest_tripoints_first( 1, cur ); for( const auto &p : neighbors ) { const int index = flat_index( p.x, p.y ); // TODO: Remove this and instead have sentinels at the edges if( p.x < minx || p.x >= maxx || p.y < miny || p.y >= maxy ) { continue; } if( layer.state[index] == ASL_CLOSED ) { continue; } int part = -1; const maptile &tile = maptile_at_internal( p ); const auto &terrain = tile.get_ter_t(); const auto &furniture = tile.get_furn_t(); const vehicle *veh = veh_at_internal( p, part ); const int cost = move_cost_internal( furniture, terrain, veh, part ); // Don't calculate bash rating unless we intend to actually use it const int rating = ( bash == 0 || cost != 0 ) ? -1 : bash_rating_internal( bash, furniture, terrain, false, veh, part ); if( cost == 0 && rating <= 0 && terrain.open.empty() ) { layer.state[index] = ASL_CLOSED; // Close it so that next time we won't try to calc costs continue; } int newg = layer.gscore[parent_index] + cost + ( (cur.x != p.x && cur.y != p.y ) ? 1 : 0); if( cost == 0 ) { // Handle all kinds of doors // Only try to open INSIDE doors from the inside if( !terrain.open.empty() && ( !terrain.has_flag( "OPENCLOSE_INSIDE" ) || !is_outside( cur ) ) ) { newg += 4; // To open and then move onto the tile } else if( veh != nullptr ) { part = veh->obstacle_at_part( part ); int dummy = -1; if( !veh->part_flag( part, "OPENCLOSE_INSIDE" ) || veh_at_internal( cur, dummy ) == veh ) { // Handle car doors, but don't try to path through curtains newg += 10; // One turn to open, 4 to move there } else { // Car obstacle that isn't a door newg += veh->parts[part].hp / bash + 8 + 4; } } else if( rating > 1 ) { // Expected number of turns to bash it down, 1 turn to move there // and 2 turns of penalty not to trash everything just because we can newg += ( 20 / rating ) + 2 + 4; } else if( rating == 1 ) { // Desperate measures, avoid whenever possible newg += 500; } else { continue; // Unbashable and unopenable from here } } // If not visited, add as open // If visited, add it only if we can do so with better score if( layer.state[index] == ASL_NONE || newg < layer.gscore[index] ) { pf.add_point( newg, newg + 2 * rl_dist( p, t ), cur, p ); } } if( !has_zlevels() ) { // The part below is only for z-level pathing continue; } const maptile &parent_tile = maptile_at_internal( cur ); const auto &parent_terrain = parent_tile.get_ter_t(); if( cur.z > minz && parent_terrain.has_flag( TFLAG_GOES_DOWN ) ) { tripoint dest( cur.x, cur.y, cur.z - 1 ); dest = vertical_move_destination<TFLAG_GOES_UP>( *this, dest ); if( inbounds( dest ) ) { auto &layer = pf.get_layer( dest.z ); pf.add_point( layer.gscore[parent_index] + 2, layer.score[parent_index] + 2 * rl_dist( dest, t ), cur, dest ); } } if( cur.z < maxz && parent_terrain.has_flag( TFLAG_GOES_UP ) ) { tripoint dest( cur.x, cur.y, cur.z + 1 ); dest = vertical_move_destination<TFLAG_GOES_DOWN>( *this, dest ); if( inbounds( dest ) ) { auto &layer = pf.get_layer( dest.z ); pf.add_point( layer.gscore[parent_index] + 2, layer.score[parent_index] + 2 * rl_dist( dest, t ), cur, dest ); } } } while( !done && !pf.empty() ); std::vector<tripoint> ret; ret.reserve( rl_dist( f, t ) * 2 ); if( done ) { tripoint cur = t; // Just to limit max distance, in case something weird happens for( int fdist = maxdist; fdist != 0; fdist-- ) { const int cur_index = flat_index( cur.x, cur.y ); const auto &layer = pf.get_layer( cur.z ); const tripoint &par = layer.parent[cur_index]; if( cur == f ) { break; } ret.push_back( cur ); // Jumps are acceptable on 1 z-level changes // This is because stairs teleport the player too if( rl_dist( cur, par ) > 1 && abs( cur.z - par.z ) != 1 ) { debugmsg( "Jump in our route! %d:%d:%d->%d:%d:%d", cur.x, cur.y, cur.z, par.x, par.y, par.z ); return ret; } cur = par; } std::reverse( ret.begin(), ret.end() ); } return ret; }
// Moves the tiles in the direction // TODO: Refactor, works for now bool grid::move(direction dir) { bool movement = false; if(dir == direction::SOUTH) { for(int x = 0; x < grid_size; ++x) { for(int y = grid_size - 1; y >= 0; --y) { // Empty slots dont move if(m_grid[y][x] == 0) { continue; } int newY = y; int nextY = y + 1; while(m_grid[nextY][x] == 0 && !is_outside(x, nextY)) { newY = nextY; ++nextY; } if(newY != y) { movement = true; } int value = m_grid[y][x]; m_grid[y][x] = 0; m_grid[newY][x] = value; } } } if(dir == direction::NORTH) { for(int x = 0; x < grid_size; ++x) { for(int y = 0; y < grid_size; ++y) { // Empty slots dont move if(m_grid[y][x] == 0) { continue; } int newY = y; int nextY = y - 1; while(m_grid[nextY][x] == 0 && !is_outside(x, nextY)) { newY = nextY; --nextY; } if(newY != y) { movement = true; } int value = m_grid[y][x]; m_grid[y][x] = 0; m_grid[newY][x] = value; } } } if(dir == direction::WEST) { for(int x = 0; x < grid_size; ++x) { for(int y = 0; y < grid_size; ++y) { // Empty slots dont move if(m_grid[y][x] == 0) { continue; } int newX = x; int nextX = x - 1; while(m_grid[y][nextX] == 0 && !is_outside(nextX, y)) { newX = nextX; --nextX; } if(newX != x) { movement = true; } int value = m_grid[y][x]; m_grid[y][x] = 0; m_grid[y][newX] = value; } } } if(dir == direction::EAST) { for(int x = grid_size - 1; x >= 0; --x) { for(int y = 0; y < grid_size; ++y) { // Empty slots dont move if(m_grid[y][x] == 0) { continue; } int newX = x; int nextX = x + 1; while(m_grid[y][nextX] == 0 && !is_outside(nextX, y)) { newX = nextX; ++nextX; } if(newX != x) { movement = true; } int value = m_grid[y][x]; m_grid[y][x] = 0; m_grid[y][newX] = value; } } } return movement; }
void light_map::generate(game* g, int x, int y, float natural_light, float luminance) { build_light_cache(g, x, y); memset(lm, 0, sizeof(lm)); memset(sm, 0, sizeof(sm)); int dir_x[] = { 1, 0 , -1, 0 }; int dir_y[] = { 0, 1 , 0, -1 }; int dir_d[] = { 180, 270, 0, 90 }; //g->add_msg("natural_light %f", natural_light); //CAT-mgs: this is just for indoors to initialize it at low ambient light if(natural_light > LIGHT_AMBIENT_LOW) // LIGHT_SOURCE_BRIGHT { for(int sx = x - CAT_VX; sx <= x + CAT_VX; ++sx) { for(int sy = y - CAT_VY; sy <= y + CAT_VY; ++sy) { if(!is_outside(sx - x + g->u.view_offset_x, sy - y + g->u.view_offset_y)) lm[sx - x + CAT_VX][sy - y + CAT_VY] = LIGHT_AMBIENT_LOW; } } } if(luminance > LIGHT_AMBIENT_LOW) apply_light_source(g->u.posx, g->u.posy, x, y, luminance); for(int sx = x - LIGHTMAP_RANGE_X; sx <= x + LIGHTMAP_RANGE_X; ++sx) { for(int sy = y - LIGHTMAP_RANGE_Y; sy <= y + LIGHTMAP_RANGE_Y; ++sy) { const ter_id terrain = g->m.ter(sx, sy); const std::vector<item> items = g->m.i_at(sx, sy); const field current_field = g->m.field_at(sx, sy); // When underground natural_light is 0, if this changes we need to revisit if (natural_light > LIGHT_AMBIENT_LOW) { int lx= sx - x + g->u.view_offset_x; int ly= sy - y + g->u.view_offset_y; if(!is_outside(lx, ly)) { // Apply light sources for external/internal divide for(int i = 0; i < 4; ++i) { if(INBOUNDS_LARGE(lx + dir_x[i], ly + dir_y[i]) && is_outside(lx + dir_x[i], ly + dir_y[i])) { //CAT-mgs: I did that above, didn't I? //... no, yes, what's this for anyway? if(INBOUNDS(sx - x, sy - y) && is_outside(0, 0)) lm[sx - x + CAT_VX][sy - y + CAT_VY]= natural_light; if(c[sx - x + LIGHTMAP_RANGE_X][sy - y + LIGHTMAP_RANGE_Y].transparency > LIGHT_TRANSPARENCY_SOLID) apply_light_arc(sx, sy, dir_d[i], x, y, natural_light); } } } } //CAT-g: if(items.size() > 0) { if(items[0].type->id == itm_flashlight_on || items[items.size()-1].type->id == itm_flashlight_on) apply_light_source(sx, sy, x, y, 48); if(items[0].type->id == itm_torch_lit || items[items.size()-1].type->id == itm_torch_lit) apply_light_source(sx, sy, x, y, 20); if(items[0].type->id == itm_candle_lit || items[items.size()-1].type->id == itm_candle_lit) apply_light_source(sx, sy, x, y, 9); } if(terrain == t_lava) apply_light_source(sx, sy, x, y, 48); if(terrain == t_console) apply_light_source(sx, sy, x, y, 3); if(terrain == t_emergency_light) apply_light_source(sx, sy, x, y, 3); // TODO: [lightmap] Attach light brightness to fields switch(current_field.type) { case fd_fire: if(current_field.density > 5) apply_light_source(sx, sy, x, y, 48); else if (current_field.density > 2) apply_light_source(sx, sy, x, y, 20); else apply_light_source(sx, sy, x, y, 9); break; case fd_fire_vent: apply_light_source(sx, sy, x, y, 3); case fd_flame_burst: apply_light_source(sx, sy, x, y, 8); break; case fd_electricity: if (3 == current_field.density) apply_light_source(sx, sy, x, y, 9); else if (2 == current_field.density) apply_light_source(sx, sy, x, y, 1); else apply_light_source(sx, sy, x, y, LIGHT_SOURCE_LOCAL); // kinda a hack as the square will still get marked break; } // Apply any vehicle light sources if (c[sx - x + LIGHTMAP_RANGE_X][sy - y + LIGHTMAP_RANGE_Y].veh && c[sx - x + LIGHTMAP_RANGE_X][sy - y + LIGHTMAP_RANGE_Y].veh_light > LL_DARK) { if (c[sx - x + LIGHTMAP_RANGE_X][sy - y + LIGHTMAP_RANGE_Y].veh_light > LL_LIT) { int dir = c[sx - x + LIGHTMAP_RANGE_X][sy - y + LIGHTMAP_RANGE_Y].veh->face.dir(); //CAT-g: longer range headlights // float luminance = c[sx - x + LIGHTMAP_RANGE_X][sy - y + LIGHTMAP_RANGE_Y].veh_light; float luminance = 4000 + c[sx - x + LIGHTMAP_RANGE_X][sy - y + LIGHTMAP_RANGE_Y].veh_light; apply_light_arc(sx, sy, dir, x, y, luminance); } } if (c[sx - x + LIGHTMAP_RANGE_X][sy - y + LIGHTMAP_RANGE_Y].mon >= 0) { if (g->z[c[sx - x + LIGHTMAP_RANGE_X][sy - y + LIGHTMAP_RANGE_Y].mon].has_effect(ME_ONFIRE)) apply_light_source(sx, sy, x, y, 3); // TODO: [lightmap] Attach natural light brightness to creatures // TODO: [lightmap] Allow creatures to have light attacks (ie: eyebot) // TODO: [lightmap] Allow creatures to have facing and arc lights switch(g->z[c[sx - x + LIGHTMAP_RANGE_X][sy - y + LIGHTMAP_RANGE_Y].mon].type->id) { case mon_zombie_electric: apply_light_source(sx, sy, x, y, 1); break; case mon_turret: apply_light_source(sx, sy, x, y, 2); break; case mon_flaming_eye: apply_light_source(sx, sy, x, y, LIGHT_SOURCE_BRIGHT); break; case mon_manhack: apply_light_source(sx, sy, x, y, LIGHT_SOURCE_LOCAL); break; } } } } }
void GLWidget::draw_function (functions index, bool draw_mesh) { functions old_index = function_index; function_index = index; const double step_x = (MAX_X - MIN_X) / (double) steps; const double step_y = (MAX_Y - MIN_Y) / (double) steps; GLfloat u[3], v[3], n[3]; for (double x = MIN_X; x <= MAX_X; x += step_x) { for (double y = MIN_Y; y <= MAX_Y; y += step_y) { /// Upper triangle if (!(is_outside (x, y) || is_outside (x, y + step_y) || is_outside (x + step_x, y + step_y))) { glBegin (GL_TRIANGLES); { u[0] = 0.0f; u[1] = step_y; u[2] = function (x + u[0], y + u[1]) - function (x, y); v[0] = step_x; v[1] = step_y; v[2] = function (x + v[0], y + v[1]) - function (x, y); cross_product (v, u, n); glNormal3fv (n); glVertex3f(x, y, function (x, y)); glVertex3f(x, y + step_y, function (x, y + step_y)); glVertex3f(x + step_x, y + step_y, function (x + step_x, y + step_y)); } glEnd (); } /// Lower triangle if (!(is_outside (x, y) || is_outside (x + step_x, y) || is_outside (x + step_x, y + step_y))) { glBegin (GL_TRIANGLES); { u[0] = step_x; u[1] = step_y; u[2] = function (x + u[0], y + u[1]) - function (x, y); v[0] = step_x; v[1] = 0; v[2] = function (x + v[0], y + v[1]) - function (x, y); cross_product (v, u, n); glNormal3fv (n); glVertex3f(x, y, function (x, y)); glVertex3f(x + step_x, y + step_y, function (x + step_x, y + step_y)); glVertex3f(x + step_x, y, function (x + step_x, y)); } glEnd (); } } } if (draw_mesh) { glDisable (GL_LIGHTING); glColor3f (1, 1, 1); for (double x = MIN_X; x <= MAX_X; x += step_x) { for (double y = MIN_Y; y <= MAX_Y; y += step_y) { /// Upper triangle if (!(is_outside (x, y) || is_outside (x, y + step_y) || is_outside (x + step_x, y + step_y))) { glBegin (GL_LINE_STRIP); { glVertex3f(x, y, function (x, y)); glVertex3f(x, y + step_y, function (x, y + step_y)); glVertex3f(x + step_x, y + step_y, function (x + step_x, y + step_y)); glVertex3f(x, y, function (x, y)); } glEnd (); } /// Lower triangle if (!(is_outside (x, y) || is_outside (x + step_x, y) || is_outside (x + step_x, y + step_y))) { glBegin (GL_LINE_STRIP); { glVertex3f(x, y, function (x, y)); glVertex3f(x + step_x, y + step_y, function (x + step_x, y + step_y)); glVertex3f(x + step_x, y, function (x + step_x, y)); glVertex3f(x, y, function (x, y)); } glEnd (); } } } glEnable (GL_LIGHTING); } function_index = old_index; }
/* * Get echo messages according to time changes... * some echoes depend upon the weather so an echo must be * found for each area */ void send_time_echo( void ) { AREA_DATA *pArea; DESCRIPTOR_DATA *d; for ( pArea = first_area; pArea; pArea = pArea->next ) { char echo[MSL]; int color = AT_GREY; int n; int pindex; if ( !pArea->weather ) continue; echo[0] = '\0'; n = number_bits( 2 ); pindex = ( pArea->weather->precip + (meteo.weath_unit*3) - 1 ) / meteo.weath_unit; switch ( calendar.hour ) { case HOUR_SUNRISE-1: { char *echo_strings[4] = { "Comincia un nuovo giorno.\r\n", "E' un nuovo giorno.\r\n", "Il cielo lentamente si rischiara, nell'alba di un nuovo giorno.\r\n", "S'affaccia adagio il sole, al giorno appena nato.\r\n" }; calendar.sunlight = SUN_RISE; strcpy( echo, echo_strings[n] ); color = AT_YELLOW; break; } case HOUR_SUNRISE: { char *echo_strings[4] = { "Il sole nasce di raggi tiepidi, sorgendo ad est..\r\n", "L'Oriente si rischiara: il sole sta sorgendo.\r\n", "Un sole fosco alza lo sguardo sul piatto dell'orizzonte..\r\n", "Un giorno nuovo saluta il mondo all'ascesa di un pallido sole..\r\n" }; calendar.sunlight = SUN_LIGHT; strcpy( echo, echo_strings[n] ); color = AT_ORANGE; break; } case HOUR_NOON: { if ( pindex > 0 ) strcpy( echo, "E' mezzogiorno.\r\n" ); else { char *echo_strings[2] = { "Il sole è alto nel cielo, l'intensità del suo diadema infuocato annuncia il mezzogiorno di luce..\r\n", "La luce del sole è vigorosa, nel cielo disegna un bagliore acceso: è mezzogiorno.\r\n" }; strcpy( echo, echo_strings[n%2] ); } calendar.sunlight = SUN_LIGHT; color = AT_WHITE; break; } case HOUR_SUNSET: { char *echo_strings[4] = { "L'occidente riluce dell'abbraccio infuocato del sole che tramonta..\r\n", "L'orizzonte è tagliato dalla corona rossa del sole in tramonto..\r\n", "Il cielo si dipinge d'oro rosso brillante, il sole ruotando adagio tramonta oltre lo sguardo.. \r\n", "Il sole finisce il suo viaggio portando i raggi splendenti nel sonno del tramonto..\r\n" }; calendar.sunlight = SUN_SET; strcpy( echo, echo_strings[n] ); color = AT_RED; break; } case HOUR_SUNSET+1: { if ( pindex > 0 ) { char *echo_strings[2] = { "Cala la sera.\r\n", "Avanza lento il crepuscolo..\r\n" }; strcpy( echo, echo_strings[n%2] ); } else { char *echo_strings[2] = { "Il chiarore gentile della luna si diffonde attraverso il cielo, annunciando la sera..\r\n", "Mille punti di luci tenui occhieggiano nel cielo serale, contornando una pallida luna..\r\n" }; strcpy( echo, echo_strings[n%2] ); } calendar.sunlight = SUN_DARK; color = AT_BLUE; break; } } /* chiude lo switch su calendar.hour */ for ( d = first_descriptor; d; d = d->next ) { if ( d->connected != CON_PLAYING ) continue; if ( !is_outside(d->character) ) continue; if ( !is_awake(d->character) ) continue; if ( !d->character->in_room ) continue; if ( d->character->in_room->area != pArea ) continue; /* Se è stato creato un'echo viene inviato */ if ( VALID_STR(echo) ) { set_char_color( color, d->character ); send_to_char( d->character, echo ); } #ifdef T2_MSP /* Invia i suoni */ if ( calendar.hour == HOUR_SUNSET ) send_audio( d, "sunset.wav", TO_CHAR ); #endif } /* ciclo for dei descrittori */ } /* ciclo for delle aree */ }
bool map::process_fields(game *g) { bool found_field = false; field *cur; field_id curtype; for (int x = 0; x < SEEX * 3; x++) { for (int y = 0; y < SEEY * 3; y++) { cur = &field_at(x, y); curtype = cur->type; if (!found_field && curtype != fd_null) found_field = true; if (cur->density > 3) debugmsg("Whoooooa density of %d", cur->density); if (cur->age == 0) // Don't process "newborn" fields curtype = fd_null; switch (curtype) { case fd_null: break; // Do nothing, obviously. OBVIOUSLY. case fd_blood: case fd_bile: if (has_flag(swimmable, x, y)) // Dissipate faster in water cur->age += 250; break; case fd_acid: if (has_flag(swimmable, x, y)) // Dissipate faster in water cur->age += 20; for (int i = 0; i < i_at(x, y).size(); i++) { item *melting = &(i_at(x, y)[i]); if (melting->made_of(LIQUID) || melting->made_of(VEGGY) || melting->made_of(FLESH) || melting->made_of(POWDER) || melting->made_of(COTTON) || melting->made_of(WOOL) || melting->made_of(PAPER) || melting->made_of(PLASTIC) || (melting->made_of(GLASS) && !one_in(3)) || one_in(4)) { // Acid destructable objects here melting->damage++; if (melting->damage >= 5 || (melting->made_of(PAPER) && melting->damage >= 3)) { cur->age += melting->volume(); for (int m = 0; m < i_at(x, y)[i].contents.size(); m++) i_at(x, y).push_back( i_at(x, y)[i].contents[m] ); i_at(x, y).erase(i_at(x, y).begin() + i); i--; } } } break; case fd_fire: // Consume items as fuel to help us grow/last longer. bool destroyed; int vol; for (int i = 0; i < i_at(x, y).size(); i++) { destroyed = false; vol = i_at(x, y)[i].volume(); if (i_at(x, y)[i].is_ammo()) { cur->age /= 2; cur->age -= 300; destroyed = true; } else if (i_at(x, y)[i].made_of(PAPER)) { cur->age -= vol * 10; destroyed = true; } else if ((i_at(x, y)[i].made_of(WOOD) || i_at(x, y)[i].made_of(VEGGY)) && (vol <= cur->density*10-(cur->age>0 ? rng(0,cur->age/10) : 0) || cur->density == 3)) { cur->age -= vol * 10; destroyed = true; } else if ((i_at(x, y)[i].made_of(COTTON) || i_at(x, y)[i].made_of(FLESH)|| i_at(x, y)[i].made_of(WOOL)) && (vol <= cur->density*2 || (cur->density == 3 && one_in(vol)))) { cur->age -= vol * 5; destroyed = true; } else if (i_at(x, y)[i].made_of(LIQUID) || i_at(x, y)[i].made_of(POWDER)|| i_at(x, y)[i].made_of(PLASTIC)|| (cur->density >= 2 && i_at(x, y)[i].made_of(GLASS)) || (cur->density == 3 && i_at(x, y)[i].made_of(IRON))) { switch (i_at(x, y)[i].type->id) { // TODO: Make this be not a hack. case itm_whiskey: case itm_vodka: case itm_rum: case itm_tequila: cur->age -= 220; break; } destroyed = true; } if (destroyed) { for (int m = 0; m < i_at(x, y)[i].contents.size(); m++) i_at(x, y).push_back( i_at(x, y)[i].contents[m] ); i_at(x, y).erase(i_at(x, y).begin() + i); i--; } } // Consume the terrain we're on if (terlist[ter(x, y)].flags & mfb(flammable) && one_in(8 - cur->density)) { cur->age -= cur->density * cur->density * 40; if (cur->density == 3) ter(x, y) = t_rubble; } else if (terlist[ter(x, y)].flags & mfb(explodes)) { ter(x, y) = ter_id(int(ter(x, y)) + 1); cur->age = 0; cur->density = 3; g->explosion(x, y, 40, 0, true); } else if (terlist[ter(x, y)].flags & mfb(swimmable)) cur->age += 800; // Flames die quickly on water // If we consumed a lot, the flames grow higher while (cur->density < 3 && cur->age < 0) { cur->age += 300; cur->density++; } // If the flames are REALLY big, they contribute to adjacent flames if (cur->density == 3 && cur->age < 0) { // If the flames are in a pit, it can't spread to non-pit bool in_pit = (ter(x, y) == t_pit); // Randomly offset our x/y shifts by 0-2, to randomly pick a square to spread to int starti = rng(0, 2); int startj = rng(0, 2); for (int i = 0; i < 3 && cur->age < 0; i++) { for (int j = 0; j < 3 && cur->age < 0; j++) { if (field_at(x+((i+starti)%3), y+((j+startj)%3)).type == fd_fire && field_at(x+((i+starti)%3), y+((j+startj)%3)).density < 3 && (!in_pit || ter(x+((i+starti)%3), y+((j+startj)%3)) == t_pit)) { field_at(x+((i+starti)%3), y+((j+startj)%3)).density++; field_at(x+((i+starti)%3), y+((j+startj)%3)).age = 0; cur->age = 0; } } } } // Consume adjacent fuel / terrain to spread. for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) { if (x+i >= 0 && y+j >= 0 && x+i < SEEX * 3 && y+j <= SEEY * 3) { if (has_flag(explodes, x + i, y + j) && one_in(8 - cur->density)) { ter(x + i, y + i) = ter_id(int(ter(x + i, y + i)) + 1); g->explosion(x+i, y+j, 40, 0, true); } else if ((i != 0 || j != 0) && (i_at(x+i, y+j).size() > 0 || rng(15, 120) < cur->density * 10)) { if (field_at(x+i, y+j).type == fd_smoke) field_at(x+i, y+j) = field(fd_fire, 1, 0); // Fire in pits can only spread to adjacent pits else if (ter(x, y) != t_pit || ter(x + i, y + j) == t_pit) add_field(g, x+i, y+j, fd_fire, 1); // If we're not spreading, maybe we'll stick out some smoke, huh? } else if (move_cost(x+i, y+j) > 0 && rng(7, 40) < cur->density * 10 && cur->age < 1000) { add_field(g, x+i, y+j, fd_smoke, rng(1, cur->density)); } } } } break; case fd_smoke: for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) g->scent(x+i, y+j) = 0; } if (is_outside(x, y)) cur->age += 50; if (one_in(2)) { std::vector <point> spread; for (int a = -1; a <= 1; a++) { for (int b = -1; b <= 1; b++) { if ((field_at(x+a, y+b).type == fd_smoke && field_at(x+a, y+b).density < 3) || (field_at(x+a, y+b).is_null() && move_cost(x+a, y+b) > 0)) spread.push_back(point(x+a, y+b)); } } if (cur->density > 0 && cur->age > 0 && spread.size() > 0) { point p = spread[rng(0, spread.size() - 1)]; if (field_at(p.x, p.y).type == fd_smoke && field_at(p.x, p.y).density < 3) { field_at(p.x, p.y).density++; cur->density--; } else if (cur->density > 0 && move_cost(p.x, p.y) > 0 && add_field(g, p.x, p.y, fd_smoke, 1)){ cur->density--; field_at(p.x, p.y).age = cur->age; } } } break; case fd_tear_gas: // Reset nearby scents to zero for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) g->scent(x+i, y+j) = 0; } if (is_outside(x, y)) cur->age += 30; // One in three chance that it spreads (less than smoke!) if (one_in(3)) { std::vector <point> spread; // Pick all eligible points to spread to for (int a = -1; a <= 1; a++) { for (int b = -1; b <= 1; b++) { if (((field_at(x+a, y+b).type == fd_smoke || field_at(x+a, y+b).type == fd_tear_gas) && field_at(x+a, y+b).density < 3 ) || (field_at(x+a, y+b).is_null() && move_cost(x+a, y+b) > 0)) spread.push_back(point(x+a, y+b)); } } // Then, spread to a nearby point if (cur->density > 0 && cur->age > 0 && spread.size() > 0) { point p = spread[rng(0, spread.size() - 1)]; // Nearby teargas grows thicker if (field_at(p.x, p.y).type == fd_tear_gas && field_at(p.x, p.y).density < 3) { field_at(p.x, p.y).density++; cur->density--; // Nearby smoke is converted into teargas } else if (field_at(p.x, p.y).type == fd_smoke) { field_at(p.x, p.y).type = fd_tear_gas; // Or, just create a new field. } else if (cur->density > 0 && move_cost(p.x, p.y) > 0 && add_field(g, p.x, p.y, fd_tear_gas, 1)) { cur->density--; field_at(p.x, p.y).age = cur->age; } } } break; case fd_nuke_gas: // Reset nearby scents to zero for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) g->scent(x+i, y+j) = 0; } if (is_outside(x, y)) cur->age += 40; // Increase long-term radiation in the land underneath radiation(x, y) += rng(0, cur->density); if (one_in(2)) { std::vector <point> spread; // Pick all eligible points to spread to for (int a = -1; a <= 1; a++) { for (int b = -1; b <= 1; b++) { if (((field_at(x+a, y+b).type == fd_smoke || field_at(x+a, y+b).type == fd_tear_gas || field_at(x+a, y+b).type == fd_nuke_gas ) && field_at(x+a, y+b).density < 3 ) || (field_at(x+a, y+b).is_null() && move_cost(x+a, y+b) > 0)) spread.push_back(point(x+a, y+b)); } } // Then, spread to a nearby point if (cur->density > 0 && cur->age > 0 && spread.size() > 0) { point p = spread[rng(0, spread.size() - 1)]; // Nearby nukegas grows thicker if (field_at(p.x, p.y).type == fd_nuke_gas && field_at(p.x, p.y).density < 3) { field_at(p.x, p.y).density++; cur->density--; // Nearby smoke & teargas is converted into nukegas } else if (field_at(p.x, p.y).type == fd_smoke || field_at(p.x, p.y).type == fd_tear_gas) { field_at(p.x, p.y).type = fd_nuke_gas; // Or, just create a new field. } else if (cur->density > 0 && move_cost(p.x, p.y) > 0 && add_field(g, p.x, p.y, fd_nuke_gas, 1)) { cur->density--; field_at(p.x, p.y).age = cur->age; } } } break; case fd_electricity: if (!one_in(5)) { // 4 in 5 chance to spread std::vector<point> valid; if (move_cost(x, y) == 0 && cur->density > 1) { // We're grounded int tries = 0; while (tries < 10 && cur->age < 50) { int cx = x + rng(-1, 1), cy = y + rng(-1, 1); if (move_cost(cx, cy) != 0 && field_at(cx, cy).is_null()) { add_field(g, cx, cy, fd_electricity, 1); cur->density--; tries = 0; } else tries++; } } else { // We're not grounded; attempt to ground for (int a = -1; a <= 1; a++) { for (int b = -1; b <= 1; b++) { if (move_cost(x + a, y + b) == 0 && // Grounded tiles first field_at(x + a, y + b).is_null()) valid.push_back(point(x + a, y + b)); } } if (valid.size() == 0) { // Spread to adjacent space, then int px = x + rng(-1, 1), py = y + rng(-1, 1); if (move_cost(px, py) > 0 && field_at(px, py).type == fd_electricity && field_at(px, py).density < 3) field_at(px, py).density++; else if (move_cost(px, py) > 0) add_field(g, px, py, fd_electricity, 1); cur->density--; } while (valid.size() > 0 && cur->density > 0) { int index = rng(0, valid.size() - 1); add_field(g, valid[index].x, valid[index].y, fd_electricity, 1); cur->density--; valid.erase(valid.begin() + index); } } } break; case fd_fatigue: if (cur->density < 3 && g->turn % 3600 == 0 && one_in(10)) cur->density++; else if (cur->density == 3 && one_in(3600)) { // Spawn nether creature! mon_id type = mon_id(rng(mon_flying_polyp, mon_blank)); monster creature(g->mtypes[type]); creature.spawn(x + rng(-3, 3), y + rng(-3, 3)); g->z.push_back(creature); } break; } if (fieldlist[cur->type].halflife > 0) { cur->age++; if (cur->age > 0 && dice(3, cur->age) > dice(3, fieldlist[cur->type].halflife)) { cur->age = 0; cur->density--; } if (cur->density <= 0) // Totally dissapated. field_at(x, y) = field(); } } } return found_field; }
/* * Update the time */ void update_calendar( void ) { calendar.hour++; if ( calendar.hour == HOUR_MIDNIGHT ) { DESCRIPTOR_DATA *d; calendar.hour = 0; calendar.day++; if ( calendar.day >= DAYS_IN_MONTH ) { calendar.day = 0; calendar.month++; if ( calendar.month >= MONTHS_IN_YEAR ) { calendar.month = 0; calendar.year++; } } for ( d = first_descriptor; d; d = d->next ) { ANNIVERSARY_DATA *anniversary; bool msend = FALSE; /* Indica se ha inviato un messaggio al pg evitando di visualizzare il cielo */ if ( d->connected != CON_PLAYING ) continue; if ( !d->character->in_room ) continue; for ( anniversary = first_anniversary; anniversary; anniversary = anniversary->next ) { if ( anniversary->month != calendar.month ) continue; if ( anniversary->day != calendar.day ) continue; if ( HAS_BIT(anniversary->races, d->character->race) ) ch_printf( d->character, "%s\r\n", anniversary->message ); } if ( calendar.day == 0 ) { if ( calendar.month == 0 ) { set_char_color( AT_GREEN, d->character ); send_to_char( d->character, "Oggi è il primo dell'anno!\r\n" ); } else { set_char_color( AT_DGREEN, d->character ); ch_printf( d->character, "Oggi è il primo giorno del mese %s\r\n", month_name[calendar.month] ); } msend = TRUE; } if ( !is_outside(d->character) ) continue; if ( !is_awake(d->character) ) continue; #ifdef T2_MSP send_audio( d, "midnight.wav", TO_CHAR ); #endif if ( !msend && d->character->in_room->area && d->character->in_room->area->weather ) { int precip; precip = ( d->character->in_room->area->weather->precip + (meteo.weath_unit*3) - 1 ) / meteo.weath_unit; if ( precip <= 1 ) send_command( d->character, "sky", CO ); } } } /* Invia l'echo sulla base dell'ora trascorsa e del weather dell'area */ send_time_echo( ); /* Salva il calendario ogni ora-mud */ save_calendar( ); }