bool monster::make_fungus(game *g) { switch (mon_id(type->id)) { case mon_ant: case mon_ant_soldier: case mon_ant_queen: case mon_fly: case mon_bee: case mon_dermatik: poly(g->mtypes[mon_ant_fungus]); return true; case mon_zombie: case mon_zombie_shrieker: case mon_zombie_electric: case mon_zombie_spitter: case mon_zombie_fast: case mon_zombie_brute: case mon_zombie_hulk: poly(g->mtypes[mon_zombie_fungus]); return true; case mon_boomer: poly(g->mtypes[mon_boomer_fungus]); return true; case mon_triffid: case mon_triffid_young: case mon_triffid_queen: poly(g->mtypes[mon_fungaloid]); return true; default: return true; } return false; }
void monster::die(game *g) { if (!dead) dead = true; if (!no_extra_death_drops) { drop_items_on_death(g); } // If we're a queen, make nearby groups of our type start to die out if (has_flag(MF_QUEEN)) { // Do it for overmap above/below too for(int z = 0; z >= -1; --z) { std::vector<mongroup*> groups = g->cur_om->monsters_at(g->levx, g->levy, z); for (int i = 0; i < groups.size(); i++) { if (MonsterGroupManager::IsMonsterInGroup(groups[i]->type, mon_id(type->id))) groups[i]->dying = true; } } } // If we're a mission monster, update the mission if (mission_id != -1) { mission_type *misstype = g->find_mission_type(mission_id); if (misstype->goal == MGOAL_FIND_MONSTER) g->fail_mission(mission_id); if (misstype->goal == MGOAL_KILL_MONSTER) g->mission_step_complete(mission_id, 1); } // Also, perform our death function mdeath md; (md.*type->dies)(g, this); // If our species fears seeing one of our own die, process that int anger_adjust = 0, morale_adjust = 0; for (int i = 0; i < type->anger.size(); i++) { if (type->anger[i] == MTRIG_FRIEND_DIED) anger_adjust += 15; } for (int i = 0; i < type->placate.size(); i++) { if (type->placate[i] == MTRIG_FRIEND_DIED) anger_adjust -= 15; } for (int i = 0; i < type->fear.size(); i++) { if (type->fear[i] == MTRIG_FRIEND_DIED) morale_adjust -= 15; } if (anger_adjust != 0 && morale_adjust != 0) { int light = g->light_level(); for (int i = 0; i < g->z.size(); i++) { int t = 0; if (g->m.sees(g->z[i].posx, g->z[i].posy, posx, posy, light, t)) { g->z[i].morale += morale_adjust; g->z[i].anger += anger_adjust; } } } }
bool monster::make_fungus(game *g) { switch (mon_id(type->id)) { case mon_ant: case mon_ant_soldier: case mon_ant_queen: case mon_bee: case mon_dermatik: poly(g->mtypes[mon_ant_fungus]); return true; case mon_zombie: case mon_zombie_shrieker: case mon_zombie_electric: case mon_zombie_spitter: case mon_zombie_fast: case mon_zombie_brute: case mon_zombie_hulk: poly(g->mtypes[mon_zombie_fungus]); return true; case mon_boomer: poly(g->mtypes[mon_boomer_fungus]); return true; case mon_triffid: case mon_triffid_young: case mon_triffid_queen: poly(g->mtypes[mon_fungaloid]); return true; case mon_fungaloid: case mon_spore: case mon_ant_fungus: case mon_zombie_fungus: case mon_boomer_fungus: return true; // Fungusy creatures are immune case mon_eyebot: case mon_manhack: case mon_skitterbot: case mon_secubot: case mon_molebot: case mon_tripod: case mon_chickenbot: case mon_tankbot: case mon_turret: return true; // Robots are immune default: return false; // All others die } return false; }
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 monster::die(game *g) { if (!dead) dead = true; // Drop goodies int total_chance = 0, total_it_chance, cur_chance, selected_location, selected_item; bool animal_done = false; std::vector<items_location_and_chance> it = g->monitems[type->id]; std::vector<itype_id> mapit; if (type->item_chance != 0 && it.size() == 0) debugmsg("Type %s has item_chance %d but no items assigned!", type->name.c_str(), type->item_chance); else { for (int i = 0; i < it.size(); i++) total_chance += it[i].chance; while (rng(0, 99) < abs(type->item_chance) && !animal_done) { cur_chance = rng(1, total_chance); selected_location = -1; while (cur_chance > 0) { selected_location++; cur_chance -= it[selected_location].chance; } total_it_chance = 0; mapit = g->mapitems[it[selected_location].loc]; for (int i = 0; i < mapit.size(); i++) total_it_chance += g->itypes[mapit[i]]->rarity; cur_chance = rng(1, total_it_chance); selected_item = -1; while (cur_chance > 0) { selected_item++; cur_chance -= g->itypes[mapit[selected_item]]->rarity; } g->m.add_item(posx, posy, g->itypes[mapit[selected_item]], 0); if (type->item_chance < 0) animal_done = true; // Only drop ONE item. } } // Done dropping items // If we're a queen, make nearby groups of our type start to die out if (has_flag(MF_QUEEN)) { // Do it for overmap above/below too for(int z = 0; z >= -1; --z) { std::vector<mongroup*> groups = g->cur_om.monsters_at(g->levx, g->levy, z); for (int i = 0; i < groups.size(); i++) { if (MonsterGroupManager::IsMonsterInGroup(groups[i]->type, mon_id(type->id))) groups[i]->dying = true; } } } // If we're a mission monster, update the mission if (mission_id != -1) { mission_type *misstype = g->find_mission_type(mission_id); if (misstype->goal == MGOAL_FIND_MONSTER) g->fail_mission(mission_id); if (misstype->goal == MGOAL_KILL_MONSTER) g->mission_step_complete(mission_id, 1); } // Also, perform our death function mdeath md; (md.*type->dies)(g, this); // If our species fears seeing one of our own die, process that int anger_adjust = 0, morale_adjust = 0; for (int i = 0; i < type->anger.size(); i++) { if (type->anger[i] == MTRIG_FRIEND_DIED) anger_adjust += 15; } for (int i = 0; i < type->placate.size(); i++) { if (type->placate[i] == MTRIG_FRIEND_DIED) anger_adjust -= 15; } for (int i = 0; i < type->fear.size(); i++) { if (type->fear[i] == MTRIG_FRIEND_DIED) morale_adjust -= 15; } if (anger_adjust != 0 && morale_adjust != 0) { int light = g->light_level(); for (int i = 0; i < g->z.size(); i++) { int t = 0; if (g->m.sees(g->z[i].posx, g->z[i].posy, posx, posy, light, t)) { g->z[i].morale += morale_adjust; g->z[i].anger += anger_adjust; } } } }
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; }