void map::step_in_field(int x, int y, game *g) { field *cur = &field_at(x, y); int veh_part; vehicle *veh = NULL; bool inside = false; int adjusted_intensity; if (g->u.in_vehicle) { veh = g->m.veh_at(x, y, veh_part); inside = (veh && veh->is_inside(veh_part)); } switch (cur->type) { case fd_null: case fd_blood: // It doesn't actually do anything case fd_bile: // Ditto return; case fd_web: { if (!g->u.has_trait(PF_WEB_WALKER) && !g->u.in_vehicle) { int web = cur->density * 5 - g->u.disease_level(DI_WEBBED); if (web > 0) g->u.add_disease(DI_WEBBED, web, g); remove_field(x, y); } } break; case fd_acid: if (cur->density == 3 && !inside) { g->add_msg("The acid burns your legs and feet!"); g->u.hit(g, bp_feet, 0, 0, rng(4, 10)); g->u.hit(g, bp_feet, 1, 0, rng(4, 10)); g->u.hit(g, bp_legs, 0, 0, rng(2, 8)); g->u.hit(g, bp_legs, 1, 0, rng(2, 8)); } break; case fd_sap: if( g->u.in_vehicle ) break; g->add_msg("The sap sticks to you!"); g->u.add_disease(DI_SAP, cur->density * 2, g); if (cur->density == 1) remove_field(x, y); else cur->density--; break; case fd_fire: adjusted_intensity = cur->density; if( g->u.in_vehicle ) if( inside ) adjusted_intensity -= 2; else adjusted_intensity -= 1; if (!g->u.has_active_bionic(bio_heatsink)) { if (adjusted_intensity == 1) { g->add_msg("You burn your legs and feet!"); g->u.hit(g, bp_feet, 0, 0, rng(2, 6)); g->u.hit(g, bp_feet, 1, 0, rng(2, 6)); g->u.hit(g, bp_legs, 0, 0, rng(1, 4)); g->u.hit(g, bp_legs, 1, 0, rng(1, 4)); } else if (adjusted_intensity == 2) { g->add_msg("You're burning up!"); g->u.hit(g, bp_legs, 0, 0, rng(2, 6)); g->u.hit(g, bp_legs, 1, 0, rng(2, 6)); g->u.hit(g, bp_torso, 0, 4, rng(4, 9)); } else if (adjusted_intensity == 3) { g->add_msg("You're set ablaze!"); g->u.hit(g, bp_legs, 0, 0, rng(2, 6)); g->u.hit(g, bp_legs, 1, 0, rng(2, 6)); g->u.hit(g, bp_torso, 0, 4, rng(4, 9)); g->u.add_disease(DI_ONFIRE, 5, g); } if (adjusted_intensity == 2) g->u.infect(DI_SMOKE, bp_mouth, 5, 20, g); else if (adjusted_intensity == 3) g->u.infect(DI_SMOKE, bp_mouth, 7, 30, g); } break; case fd_smoke: if (cur->density == 3 && !inside) g->u.infect(DI_SMOKE, bp_mouth, 4, 15, g); break; case fd_tear_gas: if ((cur->density > 1 || !one_in(3)) && !inside || inside && one_in(3)) g->u.infect(DI_TEARGAS, bp_mouth, 5, 20, g); if (cur->density > 1 && !inside || inside && one_in(3)) g->u.infect(DI_BLIND, bp_eyes, cur->density * 2, 10, g); break; case fd_toxic_gas: if (cur->density == 2 && !inside || cur->density == 3 && inside ) g->u.infect(DI_POISON, bp_mouth, 5, 30, g); else if (cur->density == 3 && !inside) g->u.infect(DI_BADPOISON, bp_mouth, 5, 30, g); break; case fd_nuke_gas: g->u.radiation += rng(0, cur->density * (cur->density + 1)); if (cur->density == 3) { g->add_msg("This radioactive gas burns!"); g->u.hurtall(rng(1, 3)); } break; case fd_flame_burst: if (inside) break; if (!g->u.has_active_bionic(bio_heatsink)) { g->add_msg("You're torched by flames!"); g->u.hit(g, bp_legs, 0, 0, rng(2, 6)); g->u.hit(g, bp_legs, 1, 0, rng(2, 6)); g->u.hit(g, bp_torso, 0, 4, rng(4, 9)); } else g->add_msg("These flames do not burn you."); break; case fd_electricity: if (g->u.has_artifact_with(AEP_RESIST_ELECTRICITY)) g->add_msg("The electricity flows around you."); else { g->add_msg("You're electrocuted!"); g->u.hurtall(rng(1, cur->density)); if (one_in(8 - cur->density) && !one_in(30 - g->u.str_cur)) { g->add_msg("You're paralyzed!"); g->u.moves -= rng(cur->density * 50, cur->density * 150); } } break; case fd_fatigue: if (rng(0, 2) < cur->density) { g->add_msg("You're violently teleported!"); g->u.hurtall(cur->density); g->teleport(); } break; case fd_shock_vent: case fd_acid_vent: remove_field(x, y); break; } }
void map::mon_in_field(int x, int y, game *g, monster *z) { if (z->has_flag(MF_DIGS)) return; // Digging monsters are immune to fields field *cur = &field_at(x, y); int dam = 0; switch (cur->type) { case fd_null: case fd_blood: // It doesn't actually do anything case fd_bile: // Ditto break; case fd_web: if (!z->has_flag(MF_WEBWALK)) { z->speed *= .8; remove_field(x, y); } break; case fd_acid: if (!z->has_flag(MF_DIGS) && !z->has_flag(MF_FLIES) && !z->has_flag(MF_ACIDPROOF)) { if (cur->density == 3) dam = rng(4, 10) + rng(2, 8); else dam = rng(cur->density, cur->density * 4); } break; case fd_sap: z->speed -= cur->density * 5; if (cur->density == 1) remove_field(x, y); else cur->density--; break; case fd_fire: if (z->made_of(FLESH)) dam = 3; if (z->made_of(VEGGY)) dam = 12; if (z->made_of(PAPER) || z->made_of(LIQUID) || z->made_of(POWDER) || z->made_of(WOOD) || z->made_of(COTTON) || z->made_of(WOOL)) dam = 20; if (z->made_of(STONE) || z->made_of(KEVLAR) || z->made_of(STEEL)) dam = -20; if (z->has_flag(MF_FLIES)) dam -= 15; if (cur->density == 1) dam += rng(2, 6); else if (cur->density == 2) { dam += rng(6, 12); if (!z->has_flag(MF_FLIES)) { z->moves -= 20; if (!z->made_of(LIQUID) && !z->made_of(STONE) && !z->made_of(KEVLAR) && !z->made_of(STEEL) && !z->has_flag(MF_FIREY)) z->add_effect(ME_ONFIRE, rng(3, 8)); } } else if (cur->density == 3) { dam += rng(10, 20); if (!z->has_flag(MF_FLIES) || one_in(3)) { z->moves -= 40; if (!z->made_of(LIQUID) && !z->made_of(STONE) && !z->made_of(KEVLAR) && !z->made_of(STEEL) && !z->has_flag(MF_FIREY)) z->add_effect(ME_ONFIRE, rng(8, 12)); } } // Drop through to smoke case fd_smoke: if (cur->density == 3) z->speed -= rng(10, 20); if (z->made_of(VEGGY)) // Plants suffer from smoke even worse z->speed -= rng(1, cur->density * 12); break; case fd_tear_gas: if (z->made_of(FLESH) || z->made_of(VEGGY)) { z->add_effect(ME_BLIND, cur->density * 8); if (cur->density == 3) { z->add_effect(ME_STUNNED, rng(10, 20)); dam = rng(4, 10); } else if (cur->density == 2) { z->add_effect(ME_STUNNED, rng(5, 10)); dam = rng(2, 5); } else z->add_effect(ME_STUNNED, rng(1, 5)); if (z->made_of(VEGGY)) { z->speed -= rng(cur->density * 5, cur->density * 12); dam += cur->density * rng(8, 14); } } break; case fd_toxic_gas: dam = cur->density; z->speed -= cur->density; break; case fd_nuke_gas: if (cur->density == 3) { z->speed -= rng(60, 120); dam = rng(30, 50); } else if (cur->density == 2) { z->speed -= rng(20, 50); dam = rng(10, 25); } else { z->speed -= rng(0, 15); dam = rng(0, 12); } if (z->made_of(VEGGY)) { z->speed -= rng(cur->density * 5, cur->density * 12); dam *= cur->density; } break; case fd_flame_burst: if (z->made_of(FLESH)) dam = 3; if (z->made_of(VEGGY)) dam = 12; if (z->made_of(PAPER) || z->made_of(LIQUID) || z->made_of(POWDER) || z->made_of(WOOD) || z->made_of(COTTON) || z->made_of(WOOL)) dam = 50; if (z->made_of(STONE) || z->made_of(KEVLAR) || z->made_of(STEEL)) dam = -25; dam += rng(0, 8); z->moves -= 20; break; case fd_electricity: dam = rng(1, cur->density); if (one_in(8 - cur->density)) z->moves -= cur->density * 150; break; case fd_fatigue: if (rng(0, 2) < cur->density) { dam = cur->density; int tries = 0; int newposx, newposy; do { newposx = rng(z->posx - SEEX, z->posx + SEEX); newposy = rng(z->posy - SEEY, z->posy + SEEY); tries++; } while (g->m.move_cost(newposx, newposy) == 0 && tries != 10); if (tries == 10) g->explode_mon(g->mon_at(z->posx, z->posy)); else { int mon_hit = g->mon_at(newposx, newposy), t; if (mon_hit != -1) { if (g->u_see(z, t)) g->add_msg("The %s teleports into a %s, killing them both!", z->name().c_str(), g->z[mon_hit].name().c_str()); g->explode_mon(mon_hit); } else { z->posx = newposx; z->posy = newposy; } } } break; } if (dam > 0) z->hurt(dam); }
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 && it->ammo_type() != AT_NULL) { 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) veh->damage (part, cur->density * 10, false); // If the flames are in a brazier, they're fully contained, so skip consuming terrain if(tr_brazier != tr_at(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(flammable, x, y) && one_in(32 - cur->density * 10)) { cur->age -= cur->density * cur->density * 40; smoke += 15; if (cur->density == 3) g->m.destroy(g, x, y, false); } else if (has_flag(flammable2, 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(l_flammable, x, y) && one_in(62 - cur->density * 10)) { cur->age -= cur->density * cur->density * 30; smoke += 10; if (cur->density == 3) g->m.destroy(g, x, y, false); } 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 && tr_brazier != tr_at(x, y)) { // 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 / webs 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 (field_at(fx, fy).type == fd_web) spread_chance = 50 + spread_chance / 2; if (has_flag(explodes, fx, fy) && one_in(8 - cur->density) && tr_brazier != tr_at(x, y)) { 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 && tr_brazier != tr_at(x, y) && (in_pit == (ter(fx, fy) == t_pit)) && ((cur->density == 3 && (has_flag(flammable, fx, fy) || one_in(20))) || (cur->density == 3 && (has_flag(l_flammable, fx, fy) && one_in(10))) || 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; case fd_push_items: { std::vector<item> *it = &(i_at(x, y)); for (int i = 0; i < it->size(); i++) { if ((*it)[i].type->id != itm_rock || (*it)[i].bday >= int(g->turn) - 1) i++; else { item tmp = (*it)[i]; tmp.bday = int(g->turn); it->erase(it->begin() + i); i--; std::vector<point> valid; for (int xx = x - 1; xx <= x + 1; xx++) { for (int yy = y - 1; yy <= y + 1; yy++) { if (field_at(xx, yy).type == fd_push_items) valid.push_back( point(xx, yy) ); } } if (!valid.empty()) { point newp = valid[rng(0, valid.size() - 1)]; add_item(newp.x, newp.y, tmp); if (g->u.posx == newp.x && g->u.posy == newp.y) { g->add_msg("A %s hits you!", tmp.tname().c_str()); g->u.hit(g, random_body_part(), rng(0, 1), 6, 0); } int npcdex = g->npc_at(newp.x, newp.y), mondex = g->mon_at(newp.x, newp.y); if (npcdex != -1) { int junk; npc *p = &(g->active_npc[npcdex]); p->hit(g, random_body_part(), rng(0, 1), 6, 0); if (g->u_see(newp.x, newp.y, junk)) g->add_msg("A %s hits %s!", tmp.tname().c_str(), p->name.c_str()); } if (mondex != -1) { int junk; monster *mon = &(g->z[mondex]); mon->hurt(6 - mon->armor_bash()); if (g->u_see(newp.x, newp.y, junk)) g->add_msg("A %s hits the %s!", tmp.tname().c_str(), mon->name().c_str()); } } } } } break; case fd_shock_vent: if (cur->density > 1) { if (one_in(5)) cur->density--; } else { cur->density = 3; int num_bolts = rng(3, 6); for (int i = 0; i < num_bolts; i++) { int xdir = 0, ydir = 0; while (xdir == 0 && ydir == 0) { xdir = rng(-1, 1); ydir = rng(-1, 1); } int dist = rng(4, 12); int boltx = x, bolty = y; for (int n = 0; n < dist; n++) { boltx += xdir; bolty += ydir; add_field(g, boltx, bolty, fd_electricity, rng(2, 3)); if (one_in(4)) { if (xdir == 0) xdir = rng(0, 1) * 2 - 1; else xdir = 0; } if (one_in(4)) { if (ydir == 0) ydir = rng(0, 1) * 2 - 1; else ydir = 0; } } } } break; case fd_acid_vent: if (cur->density > 1) { if (cur->age >= 10) { cur->density--; cur->age = 0; } } else { cur->density = 3; for (int i = x - 5; i <= x + 5; i++) { for (int j = y - 5; j <= y + 5; j++) { if (field_at(i, j).type == fd_null || field_at(i, j).density == 0) { int newdens = 3 - (rl_dist(x, y, i, j) / 2) + (one_in(3) ? 1 : 0); if (newdens > 3) newdens = 3; if (newdens > 0) add_field(g, i, j, fd_acid, newdens); } } } } break; } // switch (curtype) 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); } } } } } add_light_from_items( sx, sy, items ); 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( 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 (std::vector<int>::iterator part = light_indices.begin(); part != light_indices.end(); ++part) { veh_luminance += ( 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 = vv.x + v->parts[*part].precalc_dx[0]; int py = vv.y + v->parts[*part].precalc_dy[0]; if(INBOUNDS(px, py)) { apply_light_arc(px, py, dir + v->parts[*part].direction, veh_luminance, 45); } } } } if(v->overhead_lights_on) { std::vector<int> light_indices = 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 && v->part_info(*part).has_flag(VPFLAG_ODDTURN)) || (!(calendar::turn % 2) && v->part_info(*part).has_flag(VPFLAG_EVENTURN)) || (!v->part_info(*part).has_flag(VPFLAG_EVENTURN) && !v->part_info(*part).has_flag(VPFLAG_ODDTURN))) { int px = vv.x + v->parts[*part].precalc_dx[0]; int py = vv.y + v->parts[*part].precalc_dy[0]; if(INBOUNDS(px, py)) { add_light_source( px, py, v->part_info(*part).bonus ); } } } } for( size_t p = 0; p < v->parts.size(); ++p ) { int px = vv.x + v->parts[p].precalc_dx[0]; int py = vv.y + v->parts[p].precalc_dy[0]; if( !INBOUNDS( px, py ) ) { continue; } if( v->part_flag( p, VPFLAG_CARGO ) && !v->part_flag( p, "COVERED" ) ) { add_light_from_items( px, py, v->parts[p].items ); } } } /* 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(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; }
void map::step_in_field(int x, int y, game *g) { field *cur = &field_at(x, y); switch (cur->type) { case fd_null: case fd_blood: // It doesn't actually do anything case fd_bile: // Ditto return; case fd_acid: if (cur->density == 3) { g->add_msg("The acid burns your legs and feet!"); g->u.hit(g, bp_feet, 0, 0, rng(4, 10)); g->u.hit(g, bp_feet, 1, 0, rng(4, 10)); g->u.hit(g, bp_legs, 0, 0, rng(2, 8)); g->u.hit(g, bp_legs, 1, 0, rng(2, 8)); } else { g->add_msg("The acid burns your feet!"); g->u.hit(g, bp_feet, 0, 0, rng(cur->density, 4 * cur->density)); g->u.hit(g, bp_feet, 1, 0, rng(cur->density, 4 * cur->density)); } break; case fd_fire: if (!g->u.has_active_bionic(bio_heatsink)) { if (cur->density == 1) { g->add_msg("You burn your legs and feet!"); g->u.hit(g, bp_feet, 0, 0, rng(2, 6)); g->u.hit(g, bp_feet, 1, 0, rng(2, 6)); g->u.hit(g, bp_legs, 0, 0, rng(1, 4)); g->u.hit(g, bp_legs, 1, 0, rng(1, 4)); } else if (cur->density == 2) { g->add_msg("You're burning up!"); g->u.hit(g, bp_legs, 0, 0, rng(2, 6)); g->u.hit(g, bp_legs, 1, 0, rng(2, 6)); g->u.hit(g, bp_torso, 0, 4, rng(4, 9)); g->u.infect(DI_SMOKE, bp_mouth, 5, 20, g); } else if (cur->density == 3) { g->add_msg("You're set ablaze!"); g->u.hit(g, bp_legs, 0, 0, rng(2, 6)); g->u.hit(g, bp_legs, 1, 0, rng(2, 6)); g->u.hit(g, bp_torso, 0, 4, rng(4, 9)); g->u.add_disease(DI_ONFIRE, 5, g); g->u.infect(DI_SMOKE, bp_mouth, 7, 30, g); } } break; case fd_smoke: if (cur->density == 3) g->u.infect(DI_SMOKE, bp_mouth, 4, 15, g); break; case fd_tear_gas: if (cur->density > 1 || !one_in(3)) g->u.infect(DI_TEARGAS, bp_mouth, 5, 20, g); break; case fd_nuke_gas: g->u.radiation += rng(0, cur->density * (cur->density + 1)); if (cur->density == 3) { g->add_msg("This radioactive gas burns!"); g->u.hurtall(rng(1, 3)); } break; case fd_electricity: g->add_msg("You're electrocuted!"); g->u.hurtall(rng(1, cur->density)); if (one_in(8 - cur->density) && !one_in(30 - g->u.str_cur)) { g->add_msg("You're paralyzed!"); g->u.moves -= cur->density * 150; } break; case fd_fatigue: if (rng(0, 2) < cur->density) { g->add_msg("You're violently teleported!"); g->u.hurtall(cur->density); g->teleport(); } break; } }
void map::drawsq(WINDOW* w, player &u, int x, int y, bool invert, bool show_items) { if (!inbounds(x, y)) return; // Out of bounds int k = x + SEEX - u.posx; int j = y + SEEY - u.posy; nc_color tercol; char sym = terlist[ter(x, y)].sym; bool hi = false; if (u.has_disease(DI_BOOMERED)) tercol = c_magenta; else if ((u.is_wearing(itm_goggles_nv) && u.has_active_item(itm_UPS_on)) || u.has_active_bionic(bio_night_vision)) tercol = c_ltgreen; else tercol = terlist[ter(x, y)].color; if (move_cost(x, y) == 0 && has_flag(swimmable, x, y) && !u.underwater) show_items = false; // Can only see underwater items if WE are underwater // If there's a trap here, and we have sufficient perception, draw that instead if (tr_at(x, y) != tr_null && u.per_cur - u.encumb(bp_eyes) >= (*traps)[tr_at(x, y)]->visibility) { tercol = (*traps)[tr_at(x, y)]->color; if ((*traps)[tr_at(x, y)]->sym == '%') { switch(rng(1, 5)) { case 1: sym = '*'; break; case 2: sym = '0'; break; case 3: sym = '8'; break; case 4: sym = '&'; break; case 5: sym = '+'; break; } } else sym = (*traps)[tr_at(x, y)]->sym; } // If there's a field here, draw that instead (unless its symbol is %) if (field_at(x, y).type != fd_null) { tercol = fieldlist[field_at(x, y).type].color[field_at(x, y).density - 1]; if (fieldlist[field_at(x, y).type].sym == '*') { switch (rng(1, 5)) { case 1: sym = '*'; break; case 2: sym = '0'; break; case 3: sym = '8'; break; case 4: sym = '&'; break; case 5: sym = '+'; break; } } else if (fieldlist[field_at(x, y).type].sym != '%') sym = fieldlist[field_at(x, y).type].sym; } // If there's items here, draw those instead if (show_items && i_at(x, y).size() > 0 && field_at(x, y).is_null()) { if ((terlist[ter(x, y)].sym != '.')) hi = true; else { tercol = i_at(x, y)[i_at(x, y).size() - 1].color(); if (i_at(x, y).size() > 1) invert = !invert; sym = i_at(x, y)[i_at(x, y).size() - 1].symbol(); } } if (invert) mvwputch_inv(w, j, k, tercol, sym); else if (hi) mvwputch_hi (w, j, k, tercol, sym); else mvwputch (w, j, k, tercol, sym); }