From 7010072f00a71418e0b433a04f7f804d3869083c Mon Sep 17 00:00:00 2001 From: Whales Date: Sat, 8 Oct 2011 22:09:09 -0400 Subject: [PATCH] Big update centered on mines. --- calendar.cpp | 21 +- calendar.h | 6 +- computer.cpp | 169 +++++++++++- computer.h | 7 + disease.h | 3 + event.cpp | 81 ++++++ event.h | 2 + field.cpp | 55 +++- game.cpp | 71 ++++- game.h | 3 +- itype.h | 5 +- itypedef.cpp | 24 +- iuse.cpp | 42 ++- iuse.h | 1 + map.cpp | 59 ++++- map.h | 4 +- mapdata.h | 26 +- mapgen.cpp | 658 ++++++++++++++++++++++++++++++++++++++++++++++- mapitems.h | 4 +- mapitemsdef.cpp | 26 +- monattack.cpp | 222 ++++++++++++++++ monattack.h | 3 + mondeath.cpp | 23 ++ mondeath.h | 2 + mongroup.h | 1 + mongroupdef.cpp | 3 + mtype.h | 7 +- mtypedef.cpp | 81 ++++++ newcharacter.cpp | 11 +- omdata.h | 13 + overmap.cpp | 65 ++++- overmap.h | 1 + player.cpp | 36 ++- player.h | 2 + pldata.h | 7 +- ranged.cpp | 2 +- weather.h | 1 + weather_data.h | 26 +- 38 files changed, 1671 insertions(+), 102 deletions(-) diff --git a/calendar.cpp b/calendar.cpp index ef266fdd91a1f..701488f81cfac 100644 --- a/calendar.cpp +++ b/calendar.cpp @@ -209,13 +209,15 @@ void calendar::standardize() int calendar::minutes_past_midnight() { - return minute + hour * 60; + //debugmsg("minute: %d hour: %d"); + int ret = minute + hour * 60; + return ret; } moon_phase calendar::moon() { int phase = day / (DAYS_IN_SEASON / 4); - phase %= 4; + //phase %= 4; Redundant? if (phase = 3) return MOON_HALF; else @@ -290,24 +292,25 @@ bool calendar::is_night() { calendar sunrise_time = sunrise(), sunset_time = sunset(); - int mins = minutes_past_midnight(), + int mins = minutes_past_midnight(), sunrise_mins = sunrise_time.minutes_past_midnight(), sunset_mins = sunset_time.minutes_past_midnight(); - return (mins > sunset_mins + TWILIGHT_MINUTES && mins < sunrise_mins); + return (mins > sunset_mins + TWILIGHT_MINUTES || mins < sunrise_mins); } int calendar::sunlight() { calendar sunrise_time = sunrise(), sunset_time = sunset(); - int mins = minutes_past_midnight(), - sunrise_mins = sunrise_time.minutes_past_midnight(), - sunset_mins = sunset_time.minutes_past_midnight(); + int mins = 0, sunrise_mins = 0, sunset_mins = 0; + mins = minutes_past_midnight(); + sunrise_mins = sunrise_time.minutes_past_midnight(); + sunset_mins = sunset_time.minutes_past_midnight(); - int moonlight = int(moon()) * MOONLIGHT_LEVEL; + int moonlight = 1 + int(moon()) * MOONLIGHT_LEVEL; - if (mins > sunset_mins + TWILIGHT_MINUTES && mins < sunrise_mins) // Night + if (mins > sunset_mins + TWILIGHT_MINUTES || mins < sunrise_mins) // Night return moonlight; else if (mins >= sunrise_mins && mins < sunrise_mins + TWILIGHT_MINUTES) { diff --git a/calendar.h b/calendar.h index 576739d3d00b6..b7ded55ad00e4 100644 --- a/calendar.h +++ b/calendar.h @@ -16,9 +16,9 @@ #define SUNRISE_SOLSTICE 6 #define SUNRISE_SUMMER 5 -#define SUNSET_WINTER 5 -#define SUNSET_SOLSTICE 7 -#define SUNSET_SUMMER 9 +#define SUNSET_WINTER 17 +#define SUNSET_SOLSTICE 19 +#define SUNSET_SUMMER 21 // How long, in minutes, does sunrise/sunset last? #define TWILIGHT_MINUTES 60 diff --git a/computer.cpp b/computer.cpp index 04d5e287b3286..40c7bea1c999b 100644 --- a/computer.cpp +++ b/computer.cpp @@ -117,7 +117,8 @@ void computer::use(game *g) bool done = false; // Are we done using the computer? do { - reset_terminal(); + //reset_terminal(); + print_line(""); print_line("%s - Root Menu", name.c_str()); for (int i = 0; i < options.size(); i++) print_line("%d - %s", i + 1, options[i].name.c_str()); @@ -248,8 +249,8 @@ void computer::activate_function(game *g, computer_action action) for (int x = 0; x < SEEX * 3; x++) { for (int y = 0; y < SEEY * 3; y++) { if (g->m.ter(x, y) == t_sewage_pump) { - for (int x1 = x - 1; x1 = x + 1; x1++) { - for (int y1 = y - 1; y1 = y + 1; y1++ ) { + for (int x1 = x - 1; x1 <= x + 1; x1++) { + for (int y1 = y - 1; y1 <= y + 1; y1++ ) { if (g->m.ter(x1, y1) == t_counter) { bool found_item = false; for (int i = 0; i < g->m.i_at(x1, y1).size(); i++) { @@ -367,7 +368,7 @@ void computer::activate_function(game *g, computer_action action) } } while(tmp.find_first_of('%') != 0 && getline(fin, tmp)); } - print_line(log.c_str()); + print_line(" %s", log.c_str()); print_line("Press any key..."); getch(); } break; @@ -430,6 +431,127 @@ void computer::activate_function(game *g, computer_action action) case COMPACT_MISS_DISARM: // TODO: This! break; + case COMPACT_LIST_BIONICS: { + std::vector names; + int more = 0; + for (int x = 0; x < SEEX * 3; x++) { + for (int y = 0; y < SEEY * 3; y++) { + for (int i = 0; i < g->m.i_at(x, y).size(); i++) { + if (g->m.i_at(x, y)[i].is_bionic()) { + if (names.size() < 9) + names.push_back(g->m.i_at(x, y)[i].name); + else + more++; + } + } + } + } + for (int i = 0; i < names.size(); i++) + print_line(names[i].c_str()); + if (more > 0) + print_line("%d OTHERS FOUND..."); + } break; + + case COMPACT_ELEVATOR_ON: + for (int x = 0; x < SEEX * 3; x++) { + for (int y = 0; y < SEEY * 3; y++) { + if (g->m.ter(x, y) == t_elevator_control_off) + g->m.ter(x, y) = t_elevator_control; + } + } + print_line("Elevator activated."); + break; + + case COMPACT_AMIGARA_LOG: // TODO: This is static, move to data file? + print_line("NEPower Mine(%d:%d) Log", g->levx, g->levy); + print_line("\ +ENTRY 47:\n\ +Our normal mining routine has unearthed a hollow chamber. This would not be\n\ +out of the ordinary, save for the odd, perfectly vertical faultline found.\n\ +This faultline has several odd concavities in it which have the more\n\ +superstitious crew members alarmed; they seem to be of human origin.\n\ +\n\ +ENTRY 48:\n\ +The concavities are between 10 and 20 feet tall, and run the length of the\n\ +faultline. Each one is vaguely human in shape, but with the proportions of\n\ +the limbs, neck and head greatly distended, all twisted and curled in on\n\ +themselves.\n"); + if (!query_ynq("Continue reading?")) + return; + reset_terminal(); + print_line("NEPower Mine(%d:%d) Log", g->levx, g->levy); + print_line("\ +ENTRY 49:\n\ +We've stopped mining operations in this area, obviously, until archaeologists\n\ +have the chance to inspect the area. This is going to set our schedule back\n\ +by at least a week. This stupid artifact-preservation law has been in place\n\ +for 50 years, and hasn't even been up for termination despite the fact that\n\ +these mining operations are the backbone of our economy.\n\ +\n\ +ENTRY 52:\n\ +Still waiting on the archaeologists. We've done a little light insepction of\n\ +the faultline; our sounding equipment is insufficient to measure the depth of\n\ +the concavities. The equipment is rated at 15 miles depth, but it isn't made\n\ +for such narrow tunnels, so it's hard to say exactly how far back they go.\n"); + if (!query_ynq("Continue reading?")) + return; + reset_terminal(); + print_line("NEPower Mine(%d:%d) Log", g->levx, g->levy); + print_line("\ +ENTRY 54:\n\ +I noticed a couple of the guys down in the chamber with a chisel, breaking\n\ +off a piece of the sheer wall. I'm looking the other way. It's not like\n\ +the eggheads are going to notice a little piece missing. Fuck em.\n\ +\n\ +ENTRY 55:\n\ +Well, the archaeologists are down there now with a couple of the boys as\n\ +guides. They're hardly Indiana Jones types; I doubt they been below 20\n\ +feet. I hate taking guys off assignment just to babysit the scientists, but\n\ +if they get hurt we'll be shut down for god knows how long.\n\ +\n\ +ENTRY 58:\n\ +They're bringing in ANOTHER CREW? Christ, it's just some cave carvings! I\n\ +know that's sort of a big deal, but come on, these guys can't handle it?\n"); + if (!query_ynq("Continue reading?")) + return; + reset_terminal(); + for (int i = 0; i < 10; i++) + print_gibberish_line(); + print_line(""); + print_line(""); + print_line(""); + print_line("AMIGARA PROJECT"); + print_line(""); + print_line(""); + if (!query_ynq("Continue reading?")) + return; + reset_terminal(); + print_line("\ +SITE %d%d%d%d%d\n\ +PERTINANT FOREMAN LOGS WILL BE PREPENDED TO NOTES", +g->cur_om.posx, g->cur_om.posy, g->levx, g->levy, abs(g->levz)); + print_line("\n\ +MINE OPERATIONS SUSPENDED; CONTROL TRANSFERRED TO AMIGARA PROJECT UNDER\n\ + IMPERATIVE 2:07B\n\ +FAULTLINE SOUNDING HAS PLACED DEPTH AT 30.09 KM\n\ +DAMAGE TO FAULTLINE DISCOVERED; NEPOWER MINE CREW PLACED UNDER ARREST FOR\n\ + VIOLATION OF REGULATION 87.08 AND TRANSFERRED TO LAB 89-C FOR USE AS\n\ + SUBJECTS\n\ +QUALITIY OF FAULTLINE NOT COMPROMISED\n\ +INITIATING STANDARD TREMOR TEST..."); + print_gibberish_line(); + print_gibberish_line(); + print_line(""); + print_error("FILE CORRUPTED, PRESS ANY KEY..."); + getch(); + reset_terminal(); + break; + + case COMPACT_AMIGARA_START: + g->add_event(EVENT_AMIGARA, int(g->turn) + 10, 0, 0, 0); + g->u.add_disease(DI_AMIGARA, -1, g); + break; + } // switch (action) } @@ -459,7 +581,7 @@ void computer::activate_failure(game *g, computer_failure fail) case COMPFAIL_ALARM: g->sound(g->u.posx, g->u.posy, 60, "An alarm sounds!"); if (g->levz > 0 && !g->event_queued(EVENT_WANTED)) - g->add_event(EVENT_WANTED, g->turn + 300, 0, g->levx, g->levy); + g->add_event(EVENT_WANTED, int(g->turn) + 300, 0, g->levx, g->levy); break; case COMPFAIL_MANHACKS: { @@ -481,7 +603,7 @@ void computer::activate_failure(game *g, computer_failure fail) } break; case COMPFAIL_SECUBOTS: { - int num_robots = rng(4, 8); + int num_robots = rng(1, 3); for (int i = 0; i < num_robots; i++) { int mx, my, tries = 0; do { @@ -544,6 +666,13 @@ void computer::activate_failure(game *g, computer_failure fail) } } break; + + case COMPFAIL_AMIGARA: + g->add_event(EVENT_AMIGARA, int(g->turn) + 5, 0, 0, 0); + g->u.add_disease(DI_AMIGARA, -1, g); + g->explosion(rng(0, SEEX * 3), rng(0, SEEY * 3), 10, 10, false); + g->explosion(rng(0, SEEX * 3), rng(0, SEEY * 3), 10, 10, false); + break; }// switch (fail) } @@ -576,8 +705,15 @@ void computer::print_line(const char *mes, ...) char buff[6000]; vsprintf(buff, mes, ap); va_end(ap); +// Replace any '\n' with "\n " to allow for the border + std::string message = buff; + size_t pos = 0; + while ((pos = message.find("\n", pos) != std::string::npos)) { + message.replace(pos, 1, "\n "); + pos += 2; + } // Print the line. - wprintz(w_terminal, c_green, " %s%s", buff, "\n"); + wprintz(w_terminal, c_green, "%s%s", message.c_str(), "\n"); // Reprint the border, in case we pushed a line over it wborder(w_terminal, LINE_XOXO, LINE_XOXO, LINE_OXOX, LINE_OXOX, LINE_OXXO, LINE_OOXX, LINE_XXOO, LINE_XOOX ); @@ -600,6 +736,25 @@ void computer::print_error(const char *mes, ...) wrefresh(w_terminal); } +void computer::print_gibberish_line() +{ + std::string gibberish; + int length = rng(50, 70); + for (int i = 0; i < length; i++) { + switch (rng(0, 4)) { + case 0: gibberish += '0' + rng(0, 9); break; + case 1: + case 2: gibberish += 'a' + rng(0, 25); break; + case 3: + case 4: gibberish += 'A' + rng(0, 25); break; + } + } + wprintz(w_terminal, c_yellow, gibberish.c_str()); + wborder(w_terminal, LINE_XOXO, LINE_XOXO, LINE_OXOX, LINE_OXOX, + LINE_OXXO, LINE_OOXX, LINE_XXOO, LINE_XOOX ); + wrefresh(w_terminal); +} + void computer::reset_terminal() { werase(w_terminal); diff --git a/computer.h b/computer.h index d9abf52afaaf0..62256254dbb90 100644 --- a/computer.h +++ b/computer.h @@ -24,6 +24,10 @@ enum computer_action COMPACT_MAPS, COMPACT_MISS_LAUNCH, COMPACT_MISS_DISARM, + COMPACT_LIST_BIONICS, + COMPACT_ELEVATOR_ON, + COMPACT_AMIGARA_LOG, + COMPACT_AMIGARA_START, NUM_COMPUTER_ACTIONS }; @@ -37,6 +41,7 @@ enum computer_failure COMPFAIL_DAMAGE, COMPFAIL_PUMP_EXPLODE, COMPFAIL_PUMP_LEAK, + COMPFAIL_AMIGARA, NUM_COMPUTER_FAILURES }; @@ -93,6 +98,8 @@ class computer void print_line(const char *text, ...); // For now, the same as print_line but in red (TODO: change this?) void print_error(const char *text, ...); +// Prints code-looking gibberish + void print_gibberish_line(); // Prints a line and waits for Y/N/Q char query_ynq(const char *text, ...); }; diff --git a/disease.h b/disease.h index 499782112fa8d..610e8141ee3fe 100644 --- a/disease.h +++ b/disease.h @@ -72,6 +72,9 @@ void dis_msg(game *g, dis_type type) case DI_BLIND: g->add_msg("You're blinded!"); break; + case DI_AMIGARA: + g->add_msg("You can't look away from the fautline..."); + break; } } diff --git a/event.cpp b/event.cpp index 89a7ef1e00cdf..a8d5c8b4894f9 100644 --- a/event.cpp +++ b/event.cpp @@ -6,6 +6,7 @@ void event::actualize(game *g) { switch (type) { + case EVENT_HELP: { npc tmp; int num = 1; @@ -26,6 +27,7 @@ void event::actualize(game *g) g->active_npc.push_back(tmp); } } break; + case EVENT_ROBOT_ATTACK: { if (rl_dist(g->levx, g->levy, map_point.x, map_point.y) <= 4) { mtype *robot_type = g->mtypes[mon_tripod]; @@ -38,6 +40,72 @@ void event::actualize(game *g) g->z.push_back(robot); } } break; + + case EVENT_SPAWN_WYRMS: { + if (g->levz >= 0) + return; + monster wyrm(g->mtypes[mon_dark_wyrm]); + int num_wyrms = rng(2, 6); + for (int i = 0; i < num_wyrms; i++) { + int tries = 0; + int monx = -1, mony = -1; + do { + monx = rng(0, SEEX * 3); + mony = rng(0, SEEY * 3); + tries++; + } while (tries < 10 && !g->is_empty(monx, mony) && + rl_dist(g->u.posx, g->u.posx, monx, mony) <= 2); + if (tries < 10) { + wyrm.spawn(monx, mony); + g->z.push_back(wyrm); + } + } + if (!one_in(15)) // They just keep coming! + g->add_event(EVENT_SPAWN_WYRMS, int(g->turn) + rng(10, 20)); + } break; + + case EVENT_AMIGARA: { + int num_horrors = rng(4, 8); + int faultx = -1, faulty = -1; + bool horizontal; + for (int x = 0; x < SEEX * 3 && faultx == -1; x++) { + for (int y = 0; y < SEEY * 3 && faulty == -1; y++) { + if (g->m.ter(x, y) == t_fault) { + faultx = x; + faulty = y; + if (g->m.ter(x - 1, y) == t_fault || g->m.ter(x + 1, y) == t_fault) + horizontal = true; + else + horizontal = false; + } + } + } + monster horror(g->mtypes[mon_amigara_horror]); + for (int i = 0; i < num_horrors; i++) { + int tries = 0; + int monx = -1, mony = -1; + do { + tries = 0; + if (horizontal) { + monx = rng(0, SEEX * 3); + for (int n = -1; n <= 1; n++) { + if (g->m.ter(monx, faulty + n) == t_rock_floor) + mony = faulty + n; + } + } else { // Vertical fault + mony = rng(0, SEEY * 3); + for (int n = -1; n <= 1; n++) { + if (g->m.ter(faultx + n, mony) == t_rock_floor) + monx = faultx + n; + } + } + tries++; + } while (!g->is_empty(monx, mony) && tries < 10); + horror.spawn(monx, mony); + g->z.push_back(horror); + } + } break; + default: break; // Nothing happens for other events } @@ -60,6 +128,19 @@ void event::per_turn(game *g) g->add_msg("An eyebot swoops down nearby!"); } } break; + + case EVENT_SPAWN_WYRMS: + if (g->levz >= 0) { + turn--; + return; + } + g->add_msg("You hear screeches from the rock above and around you!"); + break; + + case EVENT_AMIGARA: + g->add_msg("The entire cavern shakes!"); + break; + default: break; // Nothing happens for other events } diff --git a/event.h b/event.h index 1611ca0c8d27c..bca2b69a3264d 100644 --- a/event.h +++ b/event.h @@ -11,6 +11,8 @@ enum event_type { EVENT_HELP, EVENT_WANTED, EVENT_ROBOT_ATTACK, + EVENT_SPAWN_WYRMS, + EVENT_AMIGARA, NUM_EVENT_TYPES }; diff --git a/field.cpp b/field.cpp index 8a7a75a37dcea..868c9a6d96ad5 100644 --- a/field.cpp +++ b/field.cpp @@ -234,6 +234,51 @@ bool map::process_fields(game *g) } 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 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++) { @@ -251,6 +296,7 @@ bool map::process_fields(game *g) 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)) @@ -265,8 +311,9 @@ bool map::process_fields(game *g) field_at(p.x, p.y).density < 3) { field_at(p.x, p.y).density++; cur->density--; -// Nearby smoke & teargas is converted into nukegas +// 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. @@ -409,6 +456,12 @@ void map::step_in_field(int x, int y, game *g) if (cur->density > 1 || !one_in(3)) g->u.infect(DI_TEARGAS, bp_mouth, 5, 20, g); break; + case fd_toxic_gas: + if (cur->density == 2) + g->u.infect(DI_POISON, bp_mouth, 5, 30, g); + else if (cur->density == 3) + 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) { diff --git a/game.cpp b/game.cpp index 087861f45bb09..d12c827cf4132 100644 --- a/game.cpp +++ b/game.cpp @@ -649,7 +649,7 @@ void game::process_events() { for (int i = 0; i < events.size(); i++) { events[i].per_turn(this); - if (events[i].turn <= turn) { + if (events[i].turn <= int(turn)) { events[i].actualize(this); events.erase(events.begin() + i); i--; @@ -804,6 +804,13 @@ void game::update_weather() if (weather == WEATHER_SUNNY && turn.is_night()) weather = WEATHER_CLEAR; + if (weather_data[weather].dangerous) { + std::stringstream weather_text; + weather_text << "The weather changed to " << weather_data[weather].name << + "!"; + cancel_activity_query(weather_text.str()); + } + // Now update temperature if (!one_in(4)) { // 3 in 4 chance of respecting avg temp for the weather int average = weather_data[weather].avg_temperature[season]; @@ -1267,8 +1274,7 @@ void game::add_msg(const char* msg, ...) messages.push_back(s); } -void game::add_event(event_type type, int on_turn, int faction_id = -1, - int x = -1, int y = -1) +void game::add_event(event_type type, int on_turn, int faction_id, int x, int y) { event tmp(type, on_turn, faction_id, x, y); events.push_back(tmp); @@ -1968,6 +1974,10 @@ void game::hallucinate() unsigned char game::light_level() { int ret; +/* + debugmsg("mins: %d hour: %d past midnight: %d", turn.minute, turn.hour, + turn.minutes_past_midnight()); +*/ if (levz < 0) // Underground! ret = 1; else { @@ -3012,7 +3022,7 @@ void game::smash() if (m.has_flag(alarmed, u.posx + smashx, u.posy + smashy) && !event_queued(EVENT_WANTED)) { sound(u.posx, u.posy, 30, "An alarm sounds!"); - add_event(EVENT_WANTED, turn + 300, 0, levx, levy); + add_event(EVENT_WANTED, int(turn) + 300, 0, levx, levy); } if (smashx != -2 && smashy != -2) didit = m.bash(u.posx + smashx, u.posy + smashy, smashskill, bashsound); @@ -3170,6 +3180,25 @@ void game::examine() } } } + } else if (m.ter(examx, examy) == t_elevator_control && + query_yn("Activate elevator?")) { + int movez = (levz < 0 ? 2 : -2); + levz += movez; + cur_om.save(u.name); + m.save(&cur_om, turn, levx, levy); + overmap(this, cur_om.posx, cur_om.posy, -1); + cur_om = overmap(this, cur_om.posx, cur_om.posy, cur_om.posz + movez); + m.load(this, levx, levy); + update_map(u.posx, u.posy); + for (int x = 0; x < SEEX * 3; x++) { + for (int y = 0; y < SEEY * 3; y++) { + if (m.ter(x, y) == t_elevator) { + u.posx = x; + u.posy = y; + } + } + } + refresh_all(); } else if (m.ter(examx, examy) == t_gas_pump && query_yn("Pump gas?")) { item gas(itypes[itm_gasoline], turn); if (one_in(u.dex_cur)) { @@ -3212,7 +3241,18 @@ void game::examine() case 4: break; } + } else if (m.ter(examx, examy) == t_fault) { + popup("\ +This wall is perfectly vertical. Odd, twisted holes are set in it, leading\n\ +as far back into the solid rock as you can see. The holes are humanoid in\n\ +shape, but with long, twisted, distended limbs."); + } else if (m.ter(examx, examy) == t_pedestal_wyrm && + m.i_at(examx, examy).empty()) { + add_msg("The pedestal sinks into the ground..."); + m.ter(examx, examy) = t_rock_floor; + add_event(EVENT_SPAWN_WYRMS, int(turn) + rng(3, 5)); } + if (m.tr_at(examx, examy) != tr_null && traps[m.tr_at(examx, examy)]->difficulty < 99 && u.per_cur-u.encumb(bp_eyes) >= traps[m.tr_at(examx, examy)]->visibility && @@ -4461,9 +4501,30 @@ void game::plmove(int x, int y) } // Otherwise, actual movement, zomg + if (u.has_disease(DI_AMIGARA)) { + int curdist = 999, newdist = 999; + for (int cx = 0; cx < SEEX * 3; cx++) { + for (int cy = 0; cy < SEEY * 3; cy++) { + if (m.ter(cx, cy) == t_fault) { + int dist = rl_dist(cx, cy, u.posx, u.posy); + if (dist < curdist) + curdist = dist; + dist = rl_dist(cx, cy, x, y); + if (dist < newdist) + newdist = dist; + } + } + } + if (newdist > curdist) { + add_msg("You cannot pull yourself away from the faultline..."); + return; + } + } + if (u.has_disease(DI_IN_PIT)) { if (rng(0, 40) > u.str_cur + int(u.dex_cur / 2)) { add_msg("You try to escape the pit, but slip back in."); + u.moves -= 100; return; } else { add_msg("You escape the pit!"); @@ -4895,7 +4956,7 @@ void game::spawn_mon(int shiftx, int shifty) // (The area of the group's territory) in (population/square at this range) // chance of adding one monster; cap at the population OR 16 while (long((1.0 - double(dist / rad)) * pop) > rng(0, pow(rad, 2)) && - rng(1, 20) > group && group < pop && group < 16) + rng(0, 17) > group && group < pop && group < 16) group++; cur_om.zg[i].population -= group; if (group > 0) // If we spawned some zombies, advance the timer diff --git a/game.h b/game.h index 792fa8ab069b3..dba28a06a3112 100644 --- a/game.h +++ b/game.h @@ -59,7 +59,8 @@ class game void draw_ter(); void advance_nextinv(); // Increment the next inventory letter void add_msg(const char* msg, ...); - void add_event(event_type type, int on_turn, int faction_id, int x, int y); + void add_event(event_type type, int on_turn, int faction_id = -1, + int x = -1, int y = -1); bool event_queued(event_type type); // Sound at (x, y) of intensity (vol), described to the player is (description) void sound(int x, int y, int vol, std::string description); diff --git a/itype.h b/itype.h index af548412af1a3..54ce695d4ef4c 100644 --- a/itype.h +++ b/itype.h @@ -54,7 +54,8 @@ itm_wrapper, itm_syringe, itm_rag, itm_fur, itm_leather, itm_superglue, itm_pan, itm_knife_butter, itm_knife_steak, itm_knife_butcher, itm_knife_combat, itm_2x4, itm_muffler, itm_pipe, itm_bat, itm_machete, itm_katana, itm_spear_wood, itm_spear_knife, itm_baton, itm_bee_sting, - itm_wasp_sting, itm_chitin_piece, itm_canister_empty, itm_gold, + itm_wasp_sting, itm_chitin_piece, itm_canister_empty, itm_gold, itm_coal, + itm_petrified_eye, itm_spiral_stone, // Footwear itm_sneakers, itm_boots, itm_boots_steel, itm_boots_winter, itm_mocassins, itm_flip_flops, itm_dress_shoes, itm_heels, @@ -132,7 +133,7 @@ itm_lighter, itm_sewing_kit, itm_scissors, itm_hammer, itm_extinguisher, itm_smokebomb, itm_smokebomb_act, itm_molotov, itm_molotov_lit, itm_dynamite, itm_dynamite_act, itm_mininuke, itm_mininuke_act, itm_pheromone, itm_portal, itm_bot_manhack, itm_bot_turret, itm_UPS_off, itm_UPS_on, itm_tazer, itm_mp3, - itm_mp3_on, + itm_mp3_on, itm_vortex_stone, // Bionics containers itm_bionics_battery, itm_bionics_power, itm_bionics_tools, itm_bionics_neuro, itm_bionics_sensory, itm_bionics_aquatic, diff --git a/itypedef.cpp b/itypedef.cpp index e23acdbd4fde5..298de78058e91 100644 --- a/itypedef.cpp +++ b/itypedef.cpp @@ -806,9 +806,26 @@ An empty cansiter, which may have once held tear gas or other substances."); MELEE("gold bar", 10,3000,'/', c_yellow, STEEL, MNULL, 2, 60, 14, 0, -1, 0, "\ -A large bar of gold. Before the apocalypse, this wouldn't been worth a small\n\ +A large bar of gold. Before the apocalypse, this would've been worth a small\n\ fortune; now its value is greatly diminished."); +// NAME RAR PRC SYM COLOR MAT1 MAT2 +MELEE("coal pallet", 20, 600,'/', c_dkgray, STONE, MNULL, +// VOL WGT DAM CUT HIT FLAGS + 72,100, 8, 0, -5, 0, "\ +A large block of semi-processed coal."); + +MELEE("petrified eye", 1,2000,'*', c_dkgray, STONE, MNULL, + 2, 8, 10, 0, -1, 0, "\ +A fist-sized eyeball with a cross-shaped pupil. It seems to be made of\n\ +stone, but doesn't look like it was carved."); + +MELEE("spiral stone", 20, 200,'*', c_pink, STONE, MNULL, + 1, 3, 14, 0, -1, 0, "\ +A rock the size of your fist. It is covered with intricate spirals; it is\n\ +impossible to tell whether they are carved, naturally formed, or some kind of\n\ +fossil."); + // ARMOR #define ARMOR(name,rarity,price,color,mat1,mat2,volume,wgt,dam,to_hit,\ encumber,dmg_resist,cut_resist,env,warmth,storage,covers,des)\ @@ -2648,6 +2665,11 @@ This mp3 player is turned on and playing some great tunes, raising your\n\ morale steadily while on your person. It runs through batteries quickly; you\n\ can turn it off by using it. It also obscures your hearing."); +TOOL("vortex stone", 2,3000,';',c_pink, STONE, MNULL, + 2, 0, 6, 0, 0, 1, 1, 1, 0, AT_NULL, itm_null, &iuse::vortex,0,"\ +A stone with spirals all over it, and holes around its perimeter. Though it\n\ +is fairly large, it weighs next to nothing. Air seems to gather around it."); + // BIONICS // These are the modules used to install new bionics in the player. They're diff --git a/iuse.cpp b/iuse.cpp index b02854319c346..c517ab9d863ee 100644 --- a/iuse.cpp +++ b/iuse.cpp @@ -8,6 +8,9 @@ #include "player.h" #include +/* To mark an item as "removed from inventory", set its invlet to 0 + This is useful for traps (placed on ground), inactive bots, etc + */ void iuse::sewage(game *g, player *p, item *it, bool t) { p->vomit(g); @@ -510,7 +513,7 @@ void iuse::purifier(game *g, player *p, item *it, bool t) { std::vector valid; // Which flags the player has for (int i = 0; i < PF_MAX2; i++) { - if (p->has_trait(pl_flag(i)) && traits[i].curable) + if (p->has_mutation(pl_flag(i))|| p->has_trait(pl_flag(i)) && traits[i].curable) valid.push_back(i); } if (valid.size() == 0) { @@ -615,7 +618,7 @@ void iuse::sew(game *g, player *p, item *it, bool t) { char ch = g->inv("Repair what?"); item* fix = &(p->i_at(ch)); - if (fix == NULL || fix->is_null() == 0) { + if (fix == NULL || fix->is_null()) { g->add_msg("You do not have that item!"); it->charges++; return; @@ -1298,7 +1301,7 @@ That trap needs a 3x3 space to be clear, centered two tiles from you."); } } } - //p->i_rem(it->invlet); + it->invlet = 0; // Remove the trap from the player's inv } void iuse::geiger(game *g, player *p, item *it, bool t) @@ -1673,7 +1676,7 @@ void iuse::manhack(game *g, player *p, item *it, bool t) } int index = rng(0, valid.size() - 1); p->moves -= 60; - p->i_rem(it->invlet); // Remove the manhack from the player's inv + it->invlet = 0; // Remove the manhack from the player's inv monster manhack(g->mtypes[mon_manhack], valid[index].x, valid[index].y); if (rng(0, p->int_cur / 2) + p->sklevel[sk_electronics] / 2 + p->sklevel[sk_computer] < rng(0, 4)) @@ -1700,7 +1703,7 @@ void iuse::turret(game *g, player *p, item *it, bool t) g->add_msg("You cannot place a turret there."); return; } - p->i_rem(it->invlet); // Remove the turret from the player's inv + it->invlet = 0; // Remove the turret from the player's inv monster turret(g->mtypes[mon_turret], dirx, diry); if (rng(0, p->int_cur / 2) + p->sklevel[sk_electronics] / 2 + p->sklevel[sk_computer] < rng(0, 6)) @@ -1842,6 +1845,35 @@ void iuse::mp3_on(game *g, player *p, item *it, bool t) } } +void iuse::vortex(game *g, player *p, item *it, bool t) +{ + std::vector spawn; + for (int i = -3; i <= 3; i++) { + if (g->is_empty(p->posx - 3, p->posy + i)) + spawn.push_back( point(p->posx - 3, p->posy + i) ); + if (g->is_empty(p->posx + 3, p->posy + i)) + spawn.push_back( point(p->posx + 3, p->posy + i) ); + if (g->is_empty(p->posx + i, p->posy - 3)) + spawn.push_back( point(p->posx + i, p->posy - 3) ); + if (g->is_empty(p->posx + i, p->posy + 3)) + spawn.push_back( point(p->posx + i, p->posy + 3) ); + } + if (spawn.empty()) { + if (!p->is_npc()) + g->add_msg("Air swirls around you for a moment."); + it->make(g->itypes[itm_spiral_stone]); + return; + } + + g->add_msg("Air swirls all over..."); + int index = rng(0, spawn.size() - 1); + p->moves -= 100; + it->make(g->itypes[itm_spiral_stone]); + monster vortex(g->mtypes[mon_vortex], spawn[index].x, spawn[index].y); + vortex.friendly = -1; + g->z.push_back(vortex); +} + /* MACGUFFIN FUNCTIONS * These functions should refer to it->associated_mission for the particulars */ diff --git a/iuse.h b/iuse.h index 44340f45c6086..81565e9b7db30 100644 --- a/iuse.h +++ b/iuse.h @@ -86,6 +86,7 @@ class iuse void tazer (game *g, player *p, item *it, bool t); void mp3 (game *g, player *p, item *it, bool t); void mp3_on (game *g, player *p, item *it, bool t); + void vortex (game *g, player *p, item *it, bool t); // MACGUFFINS void mcg_note (game *g, player *p, item *it, bool t); // ARTIFACTS diff --git a/map.cpp b/map.cpp index 1b466a8964e46..5e14c248a8675 100644 --- a/map.cpp +++ b/map.cpp @@ -397,14 +397,10 @@ void map::destroy(game *g, int x, int y, bool makesound) void map::shoot(game *g, int x, int y, int &dam, bool hit_items, unsigned flags) { -/* - if (flags & mfb(IF_AMMO_FLAME) && has_flag(flammable, x, y)) - add_field(g, x, y, fd_fire, 2); -*/ if (has_flag(alarmed, x, y) && !g->event_queued(EVENT_WANTED)) { g->sound(g->u.posx, g->u.posy, 30, "An alarm sounds!"); - g->add_event(EVENT_WANTED, g->turn + 300, 0, g->levx, g->levy); + g->add_event(EVENT_WANTED, int(g->turn) + 300, 0, g->levx, g->levy); } switch (ter(x, y)) { @@ -535,6 +531,56 @@ void map::shoot(game *g, int x, int y, int &dam, bool hit_items, unsigned flags) } } +bool map::hit_with_acid(game *g, int x, int y) +{ + if (move_cost(x, y) != 0) + return false; // Didn't hit the tile! + + switch (ter(x, y)) { + case t_wall_glass_v: + case t_wall_glass_h: + case t_wall_glass_v_alarm: + case t_wall_glass_h_alarm: + case t_vat: + ter(x, y) = t_floor; + break; + + case t_door_c: + case t_door_locked: + case t_door_locked_alarm: + if (one_in(3)) + ter(x, y) = t_door_b; + break; + + case t_door_b: + if (one_in(4)) + ter(x, y) = t_door_frame; + else + return false; + break; + + case t_window: + case t_window_alarm: + ter(x, y) = t_window_empty; + break; + + case t_wax: + ter(x, y) = t_floor_wax; + break; + + case t_toilet: + case t_gas_pump: + case t_gas_pump_smashed: + return false; + + case t_card_reader: + ter(x, y) = t_card_reader_broken; + break; + } + + return true; +} + void map::marlossify(int x, int y) { int type = rng(1, 9); @@ -1424,6 +1470,9 @@ void map::spawn_monsters(game *g) tmp.spawnposy = my; tmp.spawnmapx = g->levx; tmp.spawnmapy = g->levy; +// TODO: this is a hacky hacky hack + if (tmp.type->id == mon_dog_thing) + tmp.friendly = -1; int fx = mx + gx * SEEX, fy = my + gy * SEEY; while ((!g->is_empty(fx, fy) || !tmp.can_move_to(g->m, fx, fy)) && diff --git a/map.h b/map.h index e70667af43cd7..52b6f2f3264cf 100644 --- a/map.h +++ b/map.h @@ -61,6 +61,7 @@ class map bool bash(int x, int y, int str, std::string &sound); void destroy(game *g, int x, int y, bool makesound); void shoot(game *g, int x, int y, int &dam, bool hit_items, unsigned flags); + bool hit_with_acid(game *g, int x, int y); void marlossify(int x, int y); // Radiation @@ -106,7 +107,8 @@ class map void saven(overmap *om, unsigned int turn, int x, int y, int gridx, int gridy); bool loadn(game *g, int x, int y, int gridx, int gridy); void draw_map(oter_id terrain_type, oter_id t_north, oter_id t_east, - oter_id t_south, oter_id t_west, oter_id t_above, int turn); + oter_id t_south, oter_id t_west, oter_id t_above, int turn, + game *g); void add_extra(map_extra type, game *g); void rotate(int turns);// Rotates the current map 90*turns degress clockwise // Useful for houses, shops, etc diff --git a/mapdata.h b/mapdata.h index d1e6bdb138e51..48d614a486b46 100644 --- a/mapdata.h +++ b/mapdata.h @@ -84,7 +84,7 @@ t_door_metal_c, t_door_metal_o, t_door_metal_locked, t_bulletin, t_portcullis, t_window, t_window_alarm, t_window_empty, t_window_frame, t_window_boarded, -t_rock, +t_rock, t_fault, t_paper, // Tree t_tree, t_tree_young, t_underbrush, @@ -115,7 +115,8 @@ t_vat, t_stairs_down, t_stairs_up, t_manhole, t_ladder, t_slope_down, t_slope_up, t_manhole_cover, // Special -t_card_reader, t_card_reader_broken, t_slot_machine, +t_card_reader, t_card_reader_broken, t_slot_machine, t_elevator_control, + t_elevator_control_off, t_elevator, t_pedestal_wyrm, num_terrain_types }; @@ -218,6 +219,8 @@ const ter_t terlist[num_terrain_types] = { // MUST match enum ter_id above! mfb(bashable)|mfb(flammable)|mfb(noitem)}, {"solid rock", '#', c_white, 0, mfb(noitem)}, +{"odd fault", '#', c_magenta, 0, + mfb(noitem)}, {"paper wall", '#', c_white, 0, mfb(bashable)|mfb(flammable)|mfb(noitem)}, {"tree", '7', c_green, 0, @@ -291,7 +294,7 @@ const ter_t terlist[num_terrain_types] = { // MUST match enum ter_id above! mfb(transparent)|mfb(console)|mfb(noitem)}, {"sewage pipe", '1', c_ltgray, 0, mfb(transparent)}, -{"sewage pump", '2', c_ltgray, 0, +{"sewage pump", '&', c_ltgray, 0, mfb(noitem)}, {"refrigerator", '{', c_ltcyan, 0, mfb(container)}, @@ -317,14 +320,22 @@ const ter_t terlist[num_terrain_types] = { // MUST match enum ter_id above! mfb(transparent)|mfb(goes_down)|mfb(container)}, {"upward slope", '<', c_brown, 2, mfb(transparent)|mfb(goes_up)|mfb(container)}, -{"manhole cover", '0', c_dkgray, 2, +{"manhole cover", '0', c_dkgray, 2, mfb(transparent)}, {"card reader", '6', c_pink, 0, mfb(noitem)}, -{"broken card reader",'6', c_ltgray, 0, +{"broken card reader",'6',c_ltgray, 0, mfb(noitem)}, {"slot machine", '6', c_green, 0, - mfb(bashable)|mfb(noitem)} + mfb(bashable)|mfb(noitem)}, +{"elevator controls",'6', c_ltblue, 0, + mfb(noitem)}, +{"powerless controls",'6',c_ltgray, 0, + mfb(noitem)}, +{"elevator", '.', c_magenta, 2, + 0}, +{"dark pedestal", '&', c_dkgray, 0, + mfb(transparent)} }; enum map_extra { @@ -380,6 +391,7 @@ enum field_id { fd_fire, fd_smoke, fd_tear_gas, + fd_toxic_gas, fd_nuke_gas, fd_electricity, fd_fatigue, @@ -403,6 +415,8 @@ const field_t fieldlist[] = { {c_yellow, c_ltred, c_red}, {true, true, true}, {true, true, true}, 2000}, {{"thin smoke", "smoke", "thick smoke"}, '8', {c_white, c_ltgray, c_dkgray}, {true, false, false},{false, true, true}, 400}, +{{"hazy cloud","toxic gas","thick toxic gas"}, '8', + {c_white, c_ltgreen, c_green}, {true, false, false},{false, true, true}, 900}, {{"hazy cloud","tear gas","thick tear gas"}, '8', {c_white, c_yellow, c_brown}, {true, false, false},{true, true, true}, 600}, {{"hazy cloud","radioactive gas", "thick radioactive gas"}, '8', diff --git a/mapgen.cpp b/mapgen.cpp index 85eb1f07480d4..31173daf11424 100644 --- a/mapgen.cpp +++ b/mapgen.cpp @@ -26,11 +26,17 @@ enum room_type { room_goo, room_cloning, room_vivisect, + room_bionics, room_dorm, room_living, room_bathroom, room_kitchen, room_bedroom, + room_mine_shaft, + room_mine_office, + room_mine_storage, + room_mine_fuel, + room_mine_housing, room_split }; @@ -39,6 +45,7 @@ void house_room(map *m, room_type type, int x1, int y1, int x2, int y2); void science_room(map *m, int x1, int y1, int x2, int y2, int rotate); void set_science_room(map *m, int x1, int y1, bool faces_right, int turn); void silo_rooms(map *m); +void build_mine_room(map *m, room_type type, int x1, int y1, int x2, int y2); map_extra random_map_extra(); void line(map *m, ter_id type, int x1, int y1, int x2, int y2); @@ -90,7 +97,7 @@ void map::generate(game *g, overmap *om, int x, int y, int turn) t_west = tmp.ter(overx - 1, overy); else t_west = om->ter(OMAPX - 1, overy); - draw_map(terrain_type, t_north, t_east, t_south, t_west, t_above, turn); + draw_map(terrain_type, t_north, t_east, t_south, t_west, t_above, turn, g); for (int i = 0; i < 2; i++) { for (int j = 0; j < 2; j++) saven(&tmp, turn, overx*2, overy*2, i, j); @@ -126,7 +133,7 @@ void map::generate(game *g, overmap *om, int x, int y, int turn) overmap tmp(g, om->posx - 1, om->posy, 0); t_west = tmp.ter(OMAPX - 1, overy); } - draw_map(terrain_type, t_north, t_east, t_south, t_west, t_above, turn); + draw_map(terrain_type, t_north, t_east, t_south, t_west, t_above, turn, g); if (oterlist[terrain_type].embellished && one_in(MAP_EXTRA_CHANCE)) add_extra(random_map_extra(), g); @@ -140,7 +147,8 @@ void map::generate(game *g, overmap *om, int x, int y, int turn) } void map::draw_map(oter_id terrain_type, oter_id t_north, oter_id t_east, - oter_id t_south, oter_id t_west, oter_id t_above, int turn) + oter_id t_south, oter_id t_west, oter_id t_above, int turn, + game *g) { // Big old switch statement with a case for each overmap terrain type. // Many of these can be copied from another type, then rotated; for instance, @@ -186,6 +194,7 @@ void map::draw_map(oter_id terrain_type, oter_id t_north, oter_id t_east, ter(i, j) = t_dirt; } } + place_items(mi_wreckage, 83, 0, 0, SEEX * 2 - 1, SEEY * 2 - 1, true, 0); break; case ot_field: @@ -2685,6 +2694,454 @@ void map::draw_map(oter_id terrain_type, oter_id t_north, oter_id t_east, } break; + case ot_mine_entrance: { + for (int i = 0; i < SEEX * 2; i++) { + for (int j = 0; j < SEEY * 2; j++) + ter(i, j) = grass_or_dirt(); + } + int tries = 0; + bool build_shaft = true; + do { + int x1 = rng(1, SEEX * 2 - 10), y1 = rng(1, SEEY * 2 - 10); + int x2 = x1 + rng(4, 9), y2 = y1 + rng(4, 9); + if (build_shaft) { + build_mine_room(this, room_mine_shaft, x1, y1, x2, y2); + build_shaft = false; + } else { + bool okay = true; + for (int x = x1; x <= x2 && okay; x++) { + for (int y = y1; y <= y2 && okay; y++) { + if (ter(x, y) != t_grass & ter(x, y) != t_dirt) + okay = false; + } + } + if (okay) { + room_type type = room_type( rng(room_mine_office, room_mine_housing) ); + build_mine_room(this, type, x1, y1, x2, y2); + tries = 0; + } else + tries++; + } + } while (tries < 5); + } break; + + case ot_mine_shaft: // Not intended to actually be inhabited! + for (int i = 0; i < SEEX * 2; i++) { + for (int j = 0; j < SEEY * 2; j++) { + if (i < SEEX - 1 || i > SEEX + 2 || j < SEEY - 1 || j > SEEY + 2) + ter(i, j) = t_rock; + else + ter(i, j) = t_hole; + } + } + break; + + case ot_mine: + case ot_mine_down: + if (t_north >= ot_mine && t_north <= ot_mine_finale) + n_fac = (one_in(10) ? 0 : -2); + else + n_fac = 4; + if (t_east >= ot_mine && t_east <= ot_mine_finale) + e_fac = (one_in(10) ? 0 : -2); + else + e_fac = 4; + if (t_south >= ot_mine && t_south <= ot_mine_finale) + s_fac = (one_in(10) ? 0 : -2); + else + s_fac = 4; + if (t_west >= ot_mine && t_west <= ot_mine_finale) + w_fac = (one_in(10) ? 0 : -2); + else + w_fac = 4; + + for (int i = 0; i < SEEX * 2; i++) { + for (int j = 0; j < SEEY * 2; j++) { + if (i >= w_fac + rng(0, 2) && i <= SEEX * 2 - 1 - e_fac - rng(0, 2) && + j >= n_fac + rng(0, 2) && j <= SEEY * 2 - 1 - s_fac - rng(0, 2) && + i + j >= 4 && (SEEX * 2 - i) + (SEEY * 2 - j) >= 6 ) + ter(i, j) = t_rock_floor; + else + ter(i, j) = t_rock; + } + } + + if (t_above == ot_mine_shaft) { // We need the entrance room + square(this, t_floor, 10, 10, 15, 15); + line(this, t_wall_h, 9, 9, 16, 9); + line(this, t_wall_h, 9, 16, 16, 16); + line(this, t_wall_v, 9, 10, 9, 15); + line(this, t_wall_v, 16, 10, 16, 15); + line(this, t_wall_h, 10, 11, 12, 11); + ter(10, 10) = t_elevator_control; + ter(11, 10) = t_elevator; + line(this, t_counter, 10, 15, 15, 15); + place_items(mi_mine_equipment, 86, 10, 15, 15, 15, false, 0); + if (one_in(2)) + ter(9, 12) = t_door_c; + else + ter(16, 12) = t_door_c; + + } else { // Not an entrance; maybe some hazards! + switch( rng(0, 6) ) { + case 0: break; // Nothing! Lucky! + + case 1: { // Toxic gas + int cx = rng(9, 14), cy = rng(9, 14); + for (int i = cx - 3; i < cx + 3; i++) { + for (int j = cy - 3; j < cy + 3; j++) + add_field(g, i, j, fd_toxic_gas, rng(1, 3)); + } + } break; + + case 2: { // Lava + int x1 = rng(6, SEEX), y1 = rng(6, SEEY), + x2 = rng(SEEX + 1, SEEX * 2 - 7), y2 = rng(SEEY + 1, SEEY * 2 - 7); + int num = rng(2, 4); + for (int i = 0; i < num; i++) { + int lx1 = x1 + rng(-1, 1), lx2 = x2 + rng(-1, 1), + ly1 = y1 + rng(-1, 1), ly2 = y2 + rng(-1, 1); + line(this, t_lava, lx1, ly1, lx2, ly2); + } + } break; + + case 3: { // Wrecked equipment + int x = rng(9, 14), y = rng(9, 14); + for (int i = x - 3; i < x + 3; i++) { + for (int j = y - 3; j < y + 3; j++) { + if (!one_in(4)) + ter(i, j) = t_wreckage; + } + } + place_items(mi_wreckage, 70, x - 3, y - 3, x + 2, y + 2, false, 0); + } break; + + case 4: { // Dead miners + int num_bodies = rng(4, 8); + for (int i = 0; i < num_bodies; i++) { + int tries = 0; + point body; + do { + body = point(-1, -1); + int x = rng(0, SEEX * 2 - 1), y = rng(0, SEEY * 2 - 1); + if (move_cost(x, y) == 2) + body = point(x, y); + else + tries++; + } while (body.x == -1 && tries < 10); + if (tries < 10) { + item miner; + miner.make_corpse(g->itypes[itm_corpse], g->mtypes[mon_null], 0); + add_item(body.x, body.y, miner); + place_items(mi_mine_equipment, 60, body.x, body.y, body.x, body.y, + false, 0); + } + } + } break; + + case 5: { // Dark worm! + int num_worms = rng(1, 5); + for (int i = 0; i < num_worms; i++) { + std::vector sides; + if (n_fac == 6) + sides.push_back(NORTH); + if (e_fac == 6) + sides.push_back(EAST); + if (s_fac == 6) + sides.push_back(SOUTH); + if (w_fac == 6) + sides.push_back(WEST); + if (sides.empty()) { + add_spawn(mon_dark_wyrm, 1, SEEX, SEEY); + i = num_worms; + } else { + direction side = sides[rng(0, sides.size() - 1)]; + point p; + switch (side) { + case NORTH: p = point(rng(1, SEEX * 2 - 2), rng(1, 5) );break; + case EAST: p = point(SEEX * 2 - rng(2, 6), rng(1, SEEY * 2 - 2));break; + case SOUTH: p = point(rng(1, SEEX * 2 - 2), SEEY * 2 - rng(2, 6));break; + case WEST: p = point(rng(1, 5) , rng(1, SEEY * 2 - 2));break; + } + ter(p.x, p.y) = t_rock_floor; + add_spawn(mon_dark_wyrm, 1, p.x, p.y); + } + } + } break; + + case 6: { // Spiral + int orx = rng(SEEX - 4, SEEX), ory = rng(SEEY - 4, SEEY); + line(this, t_rock, orx , ory , orx + 5, ory ); + line(this, t_rock, orx + 5, ory , orx + 5, ory + 5); + line(this, t_rock, orx + 1, ory + 5, orx + 5, ory + 5); + line(this, t_rock, orx + 1, ory + 2, orx + 1, ory + 4); + line(this, t_rock, orx + 1, ory + 2, orx + 3, ory + 2); + ter(orx + 3, ory + 3) = t_rock; + item miner; + miner.make_corpse(g->itypes[itm_corpse], g->mtypes[mon_null], 0); + add_item(orx + 2, ory + 3, miner); + place_items(mi_mine_equipment, 60, orx + 2, ory + 3, orx + 2, ory + 3, + false, 0); + } break; + } + } + + if (terrain_type == ot_mine_down) { // Don't forget to build a slope down! + std::vector open; + if (n_fac == 4) + open.push_back(NORTH); + if (e_fac == 4) + open.push_back(EAST); + if (s_fac == 4) + open.push_back(SOUTH); + if (w_fac == 4) + open.push_back(WEST); + + if (open.empty()) { // We'll have to build it in the center + int tries = 0; + point p; + bool okay = true; + do { + p.x = rng(SEEX - 6, SEEX + 1); + p.y = rng(SEEY - 6, SEEY + 1); + okay = true; + for (int i = p.x; i <= p.x + 5 && okay; i++) { + for (int j = p.y; j <= p.y + 5 && okay; j++) { + if (ter(i, j) != t_rock_floor) + okay = false; + } + } + if (!okay) + tries++; + } while (!okay && tries < 10); + if (tries == 10) // Clear the area around the slope down + square(this, t_rock_floor, p.x, p.y, p.x + 5, p.y + 5); + square(this, t_slope_down, p.x + 1, p.y + 1, p.x + 2, p.y + 2); + + } else { // We can build against a wall + direction side = open[rng(0, open.size() - 1)]; + switch (side) { + case NORTH: + square(this, t_rock_floor, SEEX - 3, 6, SEEX + 2, SEEY); + line(this, t_slope_down, SEEX - 2, 6, SEEX + 1, 6); + break; + case EAST: + square(this, t_rock_floor, SEEX + 1, SEEY - 3, SEEX * 2 - 7, SEEY + 2); + line(this, t_slope_down, SEEX * 2 - 7, SEEY - 2, SEEX * 2 - 7, SEEY + 1); + break; + case SOUTH: + square(this, t_rock_floor, SEEX - 3, SEEY + 1, SEEX + 2, SEEY * 2 - 7); + line(this, t_slope_down, SEEX - 2, SEEY * 2 - 7, SEEX + 1, SEEY * 2 - 7); + break; + case WEST: + square(this, t_rock_floor, 6, SEEY - 3, SEEX, SEEY + 2); + line(this, t_slope_down, 6, SEEY - 2, 6, SEEY + 1); + break; + } + } + } // Done building a slope down + + if (t_above == ot_mine_down) { // Don't forget to build a slope up! + std::vector open; + if (n_fac == 6 && ter(SEEX, 6) != t_slope_down) + open.push_back(NORTH); + if (e_fac == 6 && ter(SEEX * 2 - 7, SEEY) != t_slope_down) + open.push_back(EAST); + if (s_fac == 6 && ter(SEEX, SEEY * 2 - 7) != t_slope_down) + open.push_back(SOUTH); + if (w_fac == 6 && ter(6, SEEY) != t_slope_down) + open.push_back(WEST); + + if (open.empty()) { // We'll have to build it in the center + int tries = 0; + point p; + bool okay = true; + do { + p.x = rng(SEEX - 6, SEEX + 1); + p.y = rng(SEEY - 6, SEEY + 1); + okay = true; + for (int i = p.x; i <= p.x + 5 && okay; i++) { + for (int j = p.y; j <= p.y + 5 && okay; j++) { + if (ter(i, j) != t_rock_floor) + okay = false; + } + } + if (!okay) + tries++; + } while (!okay && tries < 10); + if (tries == 10) // Clear the area around the slope down + square(this, t_rock_floor, p.x, p.y, p.x + 5, p.y + 5); + square(this, t_slope_up, p.x + 1, p.y + 1, p.x + 2, p.y + 2); + + } else { // We can build against a wall + direction side = open[rng(0, open.size() - 1)]; + switch (side) { + case NORTH: + line(this, t_slope_up, SEEX - 2, 6, SEEX + 1, 6); + break; + case EAST: + line(this, t_slope_up, SEEX * 2 - 7, SEEY - 2, SEEX * 2 - 7, SEEY + 1); + break; + case SOUTH: + line(this, t_slope_up, SEEX - 2, SEEY * 2 - 7, SEEX + 1, SEEY * 2 - 7); + break; + case WEST: + line(this, t_slope_up, 6, SEEY - 2, 6, SEEY + 1); + break; + } + } + } // Done building a slope up + break; + + case ot_mine_finale: { +// Set up the basic chamber + for (int i = 0; i < SEEX * 2; i++) { + for (int j = 0; j < SEEY * 2; j++) { + if (i > rng(1, 3) && i < SEEX * 2 - rng(2, 4) && + j > rng(1, 3) && j < SEEY * 2 - rng(2, 4) ) + ter(i, j) = t_rock_floor; + else + ter(i, j) = t_rock; + } + } + std::vector face; // Which walls are solid, and can be a facing? +// Now draw the entrance(s) + if (t_north == ot_mine) + square(this, t_rock_floor, SEEX, 0, SEEX + 1, 3); + else + face.push_back(NORTH); + + if (t_east == ot_mine) + square(this, t_rock_floor, SEEX * 2 - 4, SEEY, SEEX * 2 - 1, SEEY + 1); + else + face.push_back(EAST); + + if (t_south == ot_mine) + square(this, t_rock_floor, SEEX, SEEY * 2 - 4, SEEX + 1, SEEY * 2 - 1); + else + face.push_back(SOUTH); + + if (t_west == ot_mine) + square(this, t_rock_floor, 0, SEEY, 3, SEEY + 1); + else + face.push_back(WEST); + +// Now, pick and generate a type of finale! + if (face.empty()) + rn = rng(1, 3); // Amigara fault is not valid + else + rn = rng(1, 4); + + switch (rn) { + case 1: { // Wyrms + int x = rng(SEEX, SEEX + 1), y = rng(SEEY, SEEY + 1); + ter(x, y) = t_pedestal_wyrm; + add_item(x, y, (*itypes)[itm_petrified_eye], 0); + } break; // That's it! game::examine handles the pedestal/wyrm spawns + + case 2: { // The Thing dog + item miner; + miner.make_corpse(g->itypes[itm_corpse], g->mtypes[mon_null], 0); + int num_bodies = rng(4, 8); + for (int i = 0; i < num_bodies; i++) { + int x = rng(4, SEEX * 2 - 5), y = rng(4, SEEX * 2 - 5); + add_item(x, y, miner); + place_items(mi_mine_equipment, 60, x, y, x, y, false, 0); + } + add_spawn(mon_dog_thing, 1, rng(SEEX, SEEX + 1), rng(SEEX, SEEX + 1)); + } break; + + case 3: { // Spiral down + line(this, t_rock, 5, 5, 5, 18); + line(this, t_rock, 5, 5, 18, 5); + line(this, t_rock, 18, 5, 18, 18); + line(this, t_rock, 8, 18, 18, 18); + line(this, t_rock, 8, 8, 8, 18); + line(this, t_rock, 8, 8, 15, 8); + line(this, t_rock, 15, 8, 15, 15); + line(this, t_rock, 10, 15, 15, 15); + line(this, t_rock, 10, 10, 10, 15); + line(this, t_rock, 10, 10, 13, 10); + line(this, t_rock, 13, 10, 13, 13); + ter(12, 13) = t_rock; + ter(12, 12) = t_slope_down; + ter(12, 11) = t_slope_down; + } break; + + case 4: { // Amigara fault + direction fault = face[rng(0, face.size() - 1)]; +// Construct the fault on the appropriate face + switch (fault) { + case NORTH: + square(this, t_rock, 0, 0, SEEX * 2 - 1, 4); + line(this, t_fault, 4, 4, SEEX * 2 - 5, 4); + break; + case EAST: + square(this, t_rock, SEEX * 2 - 5, 0, SEEY * 2 - 1, SEEX * 2 - 1); + line(this, t_fault, SEEX * 2 - 5, 4, SEEX * 2 - 5, SEEY * 2 - 5); + break; + case SOUTH: + square(this, t_rock, 0, SEEY * 2 - 5, SEEX * 2 - 1, SEEY * 2 - 1); + line(this, t_fault, 4, SEEY * 2 - 5, SEEX * 2 - 5, SEEY * 2 - 5); + break; + case WEST: + square(this, t_rock, 0, 0, 4, SEEY * 2 - 1); + line(this, t_fault, 4, 4, 4, SEEY * 2 - 5); + break; + } + + ter(SEEX, SEEY) = t_console; + tmpcomp = add_computer(SEEX, SEEY, "NEPowerOS", 0); + tmpcomp->add_option("Read Logs", COMPACT_AMIGARA_LOG, 0); + tmpcomp->add_option("Initiate Tremors", COMPACT_AMIGARA_START, 4); + tmpcomp->add_failure(COMPFAIL_AMIGARA); + } break; + } + } break; + + case ot_spiral_hub: + for (int i = 0; i < SEEX * 2; i++) { + for (int j = 0; j < SEEY * 2; j++) + ter(i, j) = t_rock_floor; + } + line(this, t_rock, 23, 0, 23, 23); + line(this, t_rock, 2, 23, 23, 23); + line(this, t_rock, 2, 4, 2, 23); + line(this, t_rock, 2, 4, 18, 4); + line(this, t_rock, 18, 4, 18, 18); // bad + line(this, t_rock, 6, 18, 18, 18); + line(this, t_rock, 6, 7, 6, 18); + line(this, t_rock, 6, 7, 15, 7); + line(this, t_rock, 15, 7, 15, 15); + line(this, t_rock, 8, 15, 15, 15); + line(this, t_rock, 8, 9, 8, 15); + line(this, t_rock, 8, 9, 13, 9); + line(this, t_rock, 13, 9, 13, 13); + line(this, t_rock, 10, 13, 13, 13); + line(this, t_rock, 10, 11, 10, 13); + square(this, t_slope_up, 11, 11, 12, 12); + rotate(rng(0, 3)); + break; + + case ot_spiral: { + for (int i = 0; i < SEEX * 2; i++) { + for (int j = 0; j < SEEY * 2; j++) + ter(i, j) = t_rock_floor; + } + int num_spiral = rng(1, 4); + for (int i = 0; i < num_spiral; i++) { + int orx = rng(SEEX - 4, SEEX), ory = rng(SEEY - 4, SEEY); + line(this, t_rock, orx , ory , orx + 5, ory ); + line(this, t_rock, orx + 5, ory , orx + 5, ory + 5); + line(this, t_rock, orx + 1, ory + 5, orx + 5, ory + 5); + line(this, t_rock, orx + 1, ory + 2, orx + 1, ory + 4); + line(this, t_rock, orx + 1, ory + 2, orx + 3, ory + 2); + ter(orx + 3, ory + 3) = t_rock; + ter(orx + 2, ory + 3) = t_rock_floor; + place_items(mi_spiral, 60, orx + 2, ory + 3, orx + 2, ory + 3, false, 0); + } + } break; + case ot_radio_tower: for (int i = 0; i < SEEX * 2; i++) { for (int j = 0; j < SEEY * 2; j++) @@ -4398,8 +4855,10 @@ void science_room(map *m, int x1, int y1, int x2, int y2, int rotate) valid_rooms.push_back(room_chemistry); if ((height > 7 || width > 7) && height > 2 && width > 2) valid_rooms.push_back(room_teleport); - if (height > 4 && width > 4) + if (height > 4 && width > 4) { valid_rooms.push_back(room_goo); + valid_rooms.push_back(room_bionics); + } if (height > 7 && width > 7) valid_rooms.push_back(room_cloning); if (area >= 9) @@ -4530,6 +4989,62 @@ void science_room(map *m, int x1, int y1, int x2, int y2, int rotate) } m->add_trap(int((x1 + x2) / 2), int((y1 + y2) / 2), tr_dissector); break; + + case room_bionics: + if (rotate % 2 == 0) { + int biox = x1, bioy = int((y1 + y2) / 2); + m->ter(biox , bioy - 1) = t_wall_h; + m->ter(biox + 1, bioy - 1) = t_wall_h; + m->ter(biox , bioy + 1) = t_wall_h; + m->ter(biox + 1, bioy + 1) = t_wall_h; + m->ter(biox , bioy ) = t_counter; + m->ter(biox + 1, bioy ) = t_reinforced_glass_v; + m->place_items(mi_bionics, 70, biox, bioy, biox, bioy, false, 0); + + biox = x2; + m->ter(biox , bioy - 1) = t_wall_h; + m->ter(biox - 1, bioy - 1) = t_wall_h; + m->ter(biox , bioy + 1) = t_wall_h; + m->ter(biox - 1, bioy + 1) = t_wall_h; + m->ter(biox , bioy ) = t_counter; + m->ter(biox - 1, bioy ) = t_reinforced_glass_v; + m->place_items(mi_bionics, 70, biox, bioy, biox, bioy, false, 0); + + int compx = int((x1 + x2) / 2), compy = int((y1 + y2) / 2); + m->ter(compx, compy) = t_console; + computer* tmpcomp = m->add_computer(compx, compy, "Bionic access", 4); + tmpcomp->add_option("Manifest", COMPACT_LIST_BIONICS, 0); + tmpcomp->add_option("Open Chambers", COMPACT_RELEASE, 2); + tmpcomp->add_failure(COMPFAIL_MANHACKS); + tmpcomp->add_failure(COMPFAIL_SECUBOTS); + } else { + int bioy = y1, biox = int((x1 + x2) / 2); + m->ter(biox - 1, bioy ) = t_wall_v; + m->ter(biox - 1, bioy + 1) = t_wall_v; + m->ter(biox + 1, bioy ) = t_wall_v; + m->ter(biox + 1, bioy + 1) = t_wall_v; + m->ter(biox , bioy ) = t_counter; + m->ter(biox , bioy + 1) = t_reinforced_glass_h; + m->place_items(mi_bionics, 70, biox, bioy, biox, bioy, false, 0); + + bioy = y2; + m->ter(biox - 1, bioy ) = t_wall_v; + m->ter(biox - 1, bioy - 1) = t_wall_v; + m->ter(biox + 1, bioy ) = t_wall_v; + m->ter(biox + 1, bioy - 1) = t_wall_v; + m->ter(biox , bioy ) = t_counter; + m->ter(biox , bioy - 1) = t_reinforced_glass_h; + m->place_items(mi_bionics, 70, biox, bioy, biox, bioy, false, 0); + + int compx = int((x1 + x2) / 2), compy = int((y1 + y2) / 2); + m->ter(compx, compy) = t_console; + computer* tmpcomp = m->add_computer(compx, compy, "Bionic access", 4); + tmpcomp->add_option("Manifest", COMPACT_LIST_BIONICS, 0); + tmpcomp->add_option("Open Chambers", COMPACT_RELEASE, 2); + tmpcomp->add_failure(COMPFAIL_MANHACKS); + tmpcomp->add_failure(COMPFAIL_SECUBOTS); + } + break; case room_dorm: if (rotate % 2 == 0) { for (int y = y1 + 1; y <= y2 - 1; y += 3) { @@ -4788,6 +5303,141 @@ void silo_rooms(map *m) } } +void build_mine_room(map *m, room_type type, int x1, int y1, int x2, int y2) +{ + direction door_side; + std::vector possibilities; + int midx = int( (x1 + x2) / 2), midy = int( (y1 + y2) / 2); + if (x2 < SEEX) + possibilities.push_back(EAST); + if (x1 > SEEX + 1) + possibilities.push_back(WEST); + if (y1 > SEEY + 1) + possibilities.push_back(NORTH); + if (y2 < SEEY) + possibilities.push_back(SOUTH); + + if (possibilities.empty()) { // We're in the middle of the map! + if (midx <= SEEX) + possibilities.push_back(EAST); + else + possibilities.push_back(WEST); + if (midy <= SEEY) + possibilities.push_back(SOUTH); + else + possibilities.push_back(NORTH); + } + + door_side = possibilities[rng(0, possibilities.size() - 1)]; + point door_point; + switch (door_side) { + case NORTH: + door_point.x = midx; + door_point.y = y1; + break; + case EAST: + door_point.x = x2; + door_point.y = midy; + break; + case SOUTH: + door_point.x = midx; + door_point.y = y2; + break; + case WEST: + door_point.x = x1; + door_point.y = midy; + break; + } + square(m, t_floor, x1, y1, x2, y2); + line(m, t_wall_h, x1, y1, x2, y1); + line(m, t_wall_h, x1, y2, x2, y2); + line(m, t_wall_v, x1, y1 + 1, x1, y2 - 1); + line(m, t_wall_v, x2, y1 + 1, x2, y2 - 1); +// Main build switch! + switch (type) { + case room_mine_shaft: { + m->ter(x1 + 1, y1 + 1) = t_console; + line(m, t_wall_h, x2 - 2, y1 + 2, x2 - 1, y1 + 2); + m->ter(x2 - 2, y1 + 1) = t_elevator; + m->ter(x2 - 1, y1 + 1) = t_elevator_control_off; + computer* tmpcomp = m->add_computer(x1 + 1, y1 + 1, "NEPowerOS", 2); + tmpcomp->add_option("Divert power to elevator", COMPACT_ELEVATOR_ON, 0); + tmpcomp->add_failure(COMPFAIL_ALARM); + } break; + + case room_mine_office: + line(m, t_counter, midx, y1 + 2, midx, y2 - 2); + line(m, t_window, midx - 1, y1, midx + 1, y1); + line(m, t_window, midx - 1, y2, midx + 1, y2); + line(m, t_window, x1, midy - 1, x1, midy + 1); + line(m, t_window, x2, midy - 1, x2, midy + 1); + m->place_items(mi_office, 80, x1 + 1, y1 + 1, x2 - 1, y2 - 1, false, 0); + break; + + case room_mine_storage: + m->place_items(mi_mine_storage,85, x1 + 2, y1 + 2, x2 - 2, y2 - 2, false, 0); + break; + + case room_mine_fuel: { + int spacing = rng(2, 4); + if (door_side == NORTH || door_side == SOUTH) { + int y = (door_side == NORTH ? y1 + 2 : y2 - 2); + for (int x = x1 + 1; x <= x2 - 1; x += spacing) + m->ter(x, y) = t_gas_pump; + } else { + int x = (door_side == EAST ? x2 - 2 : x1 + 2); + for (int y = y1 + 1; y <= y2 - 1; y += spacing) + m->ter(x, y) = t_gas_pump; + } + } break; + + case room_mine_housing: + if (door_side == NORTH || door_side == SOUTH) { + for (int y = y1 + 2; y <= y2 - 2; y += 2) { + m->ter(x1 , y) = t_window; + m->ter(x1 + 1, y) = t_bed; + m->ter(x1 + 2, y) = t_bed; + m->ter(x2 , y) = t_window; + m->ter(x2 - 1, y) = t_bed; + m->ter(x2 - 2, y) = t_bed; + m->ter(x1 + 1, y + 1) = t_dresser; + m->place_items(mi_dresser, 78, x1 + 1, y + 1, x1 + 1, y + 1, false, 0); + m->ter(x2 - 1, y + 1) = t_dresser; + m->place_items(mi_dresser, 78, x2 - 1, y + 1, x2 - 1, y + 1, false, 0); + } + } else { + for (int x = x1 + 2; x <= x2 - 2; x += 2) { + m->ter(x, y1 ) = t_window; + m->ter(x, y1 + 1) = t_bed; + m->ter(x, y1 + 2) = t_bed; + m->ter(x, y2 ) = t_window; + m->ter(x, y2 - 1) = t_bed; + m->ter(x, y2 - 2) = t_bed; + m->ter(x + 1, y1 + 1) = t_dresser; + m->place_items(mi_dresser, 78, x + 1, y1 + 1, x + 1, y1 + 1, false, 0); + m->ter(x + 1, y2 - 1) = t_dresser; + m->place_items(mi_dresser, 78, x + 1, y2 - 1, x + 1, y2 - 1, false, 0); + } + } + m->place_items(mi_bedroom, 65, x1 + 1, y1 + 1, x2 - 1, y2 - 1, false, 0); + break; + } + + if (type == room_mine_fuel) { // Fuel stations are open on one side + switch (door_side) { + case NORTH: line(m, t_floor, x1, y1 , x2, y1 ); break; + case EAST: line(m, t_floor, x2, y1 + 1, x2, y2 - 1); break; + case SOUTH: line(m, t_floor, x1, y2 , x2, y2 ); break; + case WEST: line(m, t_floor, x1, y1 + 1, x1, y2 - 1); break; + } + } else { + if (type == room_mine_storage) // Storage has a locked door + m->ter(door_point.x, door_point.y) = t_door_locked; + else + m->ter(door_point.x, door_point.y) = t_door_c; + } +} + map_extra random_map_extra() { int pick = 0; diff --git a/mapitems.h b/mapitems.h index e62b5e5f919cf..e524393cd8ee2 100644 --- a/mapitems.h +++ b/mapitems.h @@ -24,6 +24,8 @@ enum items_location { mi_hydro, mi_electronics, mi_monparts, mi_bionics, mi_weapons, mi_survival_armor, mi_survival_tools, mi_sewage_plant, + mi_mine_storage, mi_mine_equipment, + mi_spiral, mi_radio, mi_subway, mi_sewer, mi_cavern, @@ -33,7 +35,7 @@ enum items_location { mi_bugs, mi_bees, mi_wasps, mi_robots, // Map Extras mi_helicopter, mi_military, mi_science, mi_rare, mi_stash_food, mi_stash_ammo, - mi_stash_wood, mi_stash_drugs, + mi_stash_wood, mi_stash_drugs, mi_wreckage, // Shopkeeps &c mi_trader_avoid, num_itloc diff --git a/mapitemsdef.cpp b/mapitemsdef.cpp index d2c8999c27ebe..026aa3fa03377 100644 --- a/mapitemsdef.cpp +++ b/mapitemsdef.cpp @@ -426,7 +426,24 @@ void game::init_mapitems() itm_boots, itm_jumpsuit, itm_coat_lab, itm_gloves_rubber, itm_mask_filter, itm_glasses_safety, itm_hat_hard, itm_extinguisher, itm_flashlight, itm_water_purifier, itm_two_way_radio, - itm_bionics_tools, itm_bionics_hazmat, itm_bionics_construction, NULL); + itm_bionics_tools, itm_bionics_hazmat, NULL); + + setvector( + mapitems[mi_mine_storage], + itm_rock, itm_coal, NULL); + + setvector( + mapitems[mi_mine_equipment], + itm_water, itm_1st_aid, itm_rope_30, itm_chain, itm_boots_steel, + itm_jumpsuit, itm_gloves_leather, itm_mask_filter, itm_mask_gas, + itm_glasses_safety, itm_goggles_welding, itm_goggles_nv, itm_hat_hard, + itm_backpack, itm_battery, itm_flashlight, itm_two_way_radio, + itm_jackhammer, itm_dynamite, itm_UPS_off, itm_bionics_tools, + itm_bionics_construction, NULL); + + setvector( + mapitems[mi_spiral], + itm_spiral_stone, itm_vortex_stone, NULL); setvector( mapitems[mi_radio], @@ -525,8 +542,7 @@ void game::init_mapitems() itm_textbook_chemistry, itm_SICP, itm_textbook_robots, itm_soldering_iron, itm_geiger_off, itm_teleporter, itm_canister_goo, itm_EMPbomb, itm_pheromone, itm_portal, itm_bot_manhack, itm_UPS_off, - itm_tazer, itm_bionics_battery, itm_bionics_power, itm_bionics_tools, - itm_bionics_neuro, itm_bionics_hazmat, itm_bionics_medical, NULL); + itm_tazer, itm_bionics_hazmat, NULL); setvector( mapitems[mi_rare], @@ -556,6 +572,10 @@ void game::init_mapitems() itm_pills_sleep, itm_oxycodone, itm_xanax, itm_adderall, itm_weed, itm_coke, itm_meth, itm_heroin, NULL); + setvector( + mapitems[mi_wreckage], + itm_chain, itm_steel_chunk, itm_rock, NULL); + // This one kind of an inverted list; what a traveling salesman will NOT carry setvector( mapitems[mi_trader_avoid], diff --git a/monattack.cpp b/monattack.cpp index 102b0b9a5efe0..7e1bab84a618b 100644 --- a/monattack.cpp +++ b/monattack.cpp @@ -65,6 +65,15 @@ void mattack::acid(game *g, monster *z) z->sp_timeout = z->type->sp_freq; // Reset timer g->sound(z->posx, z->posy, 4, "a spitting noise."); int hitx = g->u.posx + rng(-2, 2), hity = g->u.posy + rng(-2, 2); + std::vector line = line_to(z->posx, z->posy, hitx, hity, junk); + for (int i = 0; i < line.size(); i++) { + if (g->m.hit_with_acid(g, line[i].x, line[i].y)) { + if (g->u_see(line[i].x, line[i].y, junk)) + g->add_msg("A glob of acid hits the %s!", + g->m.tername(line[i].x, line[i].y).c_str()); + return; + } + } for (int i = -3; i <= 3; i++) { for (int j = -3; j <= 3; j++) { if (g->m.move_cost(hitx + i, hity +j) > 0 && @@ -611,6 +620,219 @@ void mattack::formblob(game *g, monster *z) } } +void mattack::dogthing(game *g, monster *z) +{ + if (!one_in(3)) + return; + + int t; + if (g->u_see(z, t)) + g->add_msg("The %s's head explodes in a mass of roiling tentacles!", + z->name().c_str()); + + for (int x = z->posx - 2; x <= z->posx + 2; x++) { + for (int y = z->posy - 2; y <= z->posy + 2; y++) { + if (rng(0, 2) >= rl_dist(z->posx, z->posy, x, y)) + g->m.add_field(g, x, y, fd_blood, 2); + } + } + + z->friendly = 0; + z->poly(g->mtypes[mon_headless_dog_thing]); +} + +void mattack::tentacle(game *g, monster *z) +{ + int t; + if (!g->sees_u(z->posx, z->posy, t)) + return; + + g->add_msg("The %s lashes its tentacle at you!", z->name().c_str()); + z->moves -= 100; + z->sp_timeout = z->type->sp_freq; // Reset timer + + std::vector line = line_to(z->posx, z->posy, g->u.posx, g->u.posy, t); + for (int i = 0; i < line.size(); i++) { + int tmpdam = 20; + g->m.shoot(g, line[i].x, line[i].y, tmpdam, true, 0); + } + + if (rng(0, 20) > g->u.dodge() || one_in(g->u.dodge())) { + g->add_msg("You dodge it!"); + return; + } + body_part hit = random_body_part(); + int dam = rng(10, 20), side = rng(0, 1); + g->add_msg("Your %s is hit for %d damage!", body_part_name(hit, side).c_str(), + dam); + g->u.hit(g, hit, side, dam, 0); +} + +void mattack::vortex(game *g, monster *z) +{ + int t; +// Moves are NOT used up by this attack, as it is "passive" + z->sp_timeout = z->type->sp_freq; +// Before anything else, smash terrain! + for (int x = z->posx - 2; x <= z->posx + 2; x++) { + for (int y = z->posx - 2; y <= z->posy + 2; y++) { + if (x == z->posx && y == z->posy) // Don't throw us! + y++; + std::string sound; + g->m.bash(x, y, 14, sound); + g->sound(x, y, 8, sound); + } + } + for (int x = z->posx - 2; x <= z->posx + 2; x++) { + for (int y = z->posx - 2; y <= z->posy + 2; y++) { + if (x == z->posx && y == z->posy) // Don't throw us! + y++; + std::vector from_monster = line_to(z->posx, z->posy, x, y, 0); + while (!g->m.i_at(x, y).empty()) { + item thrown = g->m.i_at(x, y)[0]; + g->m.i_rem(x, y, 0); + int distance = 5 - (thrown.weight() / 15); + if (distance > 0) { + int dam = thrown.weight() / double(3 + double(thrown.volume() / 6)); + std::vector traj = continue_line(from_monster, distance); + for (int i = 0; i < traj.size() && dam > 0; i++) { + g->m.shoot(g, traj[i].x, traj[i].y, dam, false, 0); + int mondex = g->mon_at(traj[i].x, traj[i].y); + if (mondex != -1) { + if (g->z[mondex].hurt(dam)) + g->kill_mon(mondex); + dam = 0; + } + if (g->m.move_cost(traj[i].x, traj[i].y) == 0) { + dam = 0; + i--; + } else if (traj[i].x == g->u.posx && traj[i].y == g->u.posy) { + body_part hit = random_body_part(); + int side = rng(0, 1); + g->add_msg("A %s hits your %s for %d damage!", thrown.tname().c_str(), + body_part_name(hit, side).c_str(), dam); + g->u.hit(g, hit, side, dam, 0); + dam = 0; + } +// TODO: Hit NPCs + if (dam == 0 || i == traj.size() - 1) { + if (thrown.made_of(GLASS)) { + if (g->u_see(traj[i].x, traj[i].y, t)) + g->add_msg("The %s shatters!", thrown.tname().c_str()); + for (int n = 0; n < thrown.contents.size(); n++) + g->m.add_item(traj[i].x, traj[i].y, thrown.contents[n]); + g->sound(traj[i].x, traj[i].y, 16, "glass breaking!"); + } else + g->m.add_item(traj[i].x, traj[i].y, thrown); + } + } + } // Done throwing item + } // Done getting items +// Throw monsters + int mondex = g->mon_at(x, y); + if (mondex != -1) { + int distance = 0, damage = 0; + monster *thrown = &(g->z[mondex]); + switch (thrown->type->size) { + case MS_TINY: distance = 5; break; + case MS_SMALL: distance = 3; break; + case MS_MEDIUM: distance = 2; break; + case MS_LARGE: distance = 1; break; + case MS_HUGE: distance = 0; break; + } + damage = distance * 4; + switch (thrown->type->mat) { + case LIQUID: distance += 3; damage -= 10; break; + case VEGGY: distance += 1; damage -= 5; break; + case POWDER: distance += 4; damage -= 30; break; + case COTTON: + case WOOL: distance += 5; damage -= 40; break; + case LEATHER: distance -= 1; damage += 5; break; + case KEVLAR: distance -= 3; damage -= 20; break; + case STONE: distance -= 3; damage += 5; break; + case PAPER: distance += 6; damage -= 10; break; + case WOOD: distance += 1; damage += 5; break; + case PLASTIC: distance += 1; damage += 5; break; + case GLASS: distance += 2; damage += 20; break; + case IRON: distance -= 1; // fall through + case STEEL: + case SILVER: distance -= 3; damage -= 10; break; + } + if (distance > 0) { + if (g->u_see(thrown, t)) + g->add_msg("The %s is thrown by winds!", thrown->name().c_str()); + std::vector traj = continue_line(from_monster, distance); + bool hit_wall = false; + for (int i = 0; i < traj.size() && !hit_wall; i++) { + int monhit = g->mon_at(traj[i].x, traj[i].y); + if (i > 0 && monhit != -1 && !g->z[monhit].has_flag(MF_DIGS)) { + if (g->u_see(traj[i].x, traj[i].y, t)) + g->add_msg("The %s hits a %s!", thrown->name().c_str(), + g->z[monhit].name().c_str()); + if (g->z[monhit].hurt(damage)) + g->kill_mon(monhit); + hit_wall = true; + thrown->posx = traj[i - 1].x; + thrown->posy = traj[i - 1].y; + } else if (g->m.move_cost(traj[i].x, traj[i].y) == 0) { + hit_wall = true; + thrown->posx = traj[i - 1].x; + thrown->posy = traj[i - 1].y; + } + int damage_copy = damage; + g->m.shoot(g, traj[i].x, traj[i].y, damage_copy, false, 0); + if (damage_copy < damage) + thrown->hurt(damage - damage_copy); + } + if (hit_wall) + damage *= 2; + else { + thrown->posx = traj[traj.size() - 1].x; + thrown->posy = traj[traj.size() - 1].y; + } + if (thrown->hurt(damage)) + g->kill_mon(g->mon_at(thrown->posx, thrown->posy)); + } // if (distance > 0) + } // if (mondex != -1) + + if (g->u.posx == x && g->u.posy == y) { // Throw... the player?! D: + std::vector traj = continue_line(from_monster, rng(3, 4)); + bool hit_wall = false; + int damage = rng(5, 10); + for (int i = 0; i < traj.size() && !hit_wall; i++) { + int monhit = g->mon_at(traj[i].x, traj[i].y); + if (i > 0 && monhit != -1 && !g->z[monhit].has_flag(MF_DIGS)) { + if (g->u_see(traj[i].x, traj[i].y, t)) + g->add_msg("You hit a %s!", g->z[monhit].name().c_str()); + if (g->z[monhit].hurt(damage)) + g->kill_mon(monhit); + hit_wall = true; + g->u.posx = traj[i - 1].x; + g->u.posy = traj[i - 1].y; + } else if (g->m.move_cost(traj[i].x, traj[i].y) == 0) { + g->add_msg("You slam into a %s", + g->m.tername(traj[i].x, traj[i].y).c_str()); + hit_wall = true; + g->u.posx = traj[i - 1].x; + g->u.posy = traj[i - 1].y; + } + int damage_copy = damage; + g->m.shoot(g, traj[i].x, traj[i].y, damage_copy, false, 0); + if (damage_copy < damage) + g->u.hit(g, bp_torso, 0, damage - damage_copy, 0); + } + if (hit_wall) + damage *= 2; + else { + g->u.posx = traj[traj.size() - 1].x; + g->u.posy = traj[traj.size() - 1].y; + } + g->u.hit(g, bp_torso, 0, damage, 0); + } // Done with checking for player + } + } // Done with loop! +} + void mattack::gene_sting(game *g, monster *z) { int j; diff --git a/monattack.h b/monattack.h index 839e36f731fc6..c56ac3ff4e50f 100644 --- a/monattack.h +++ b/monattack.h @@ -24,6 +24,9 @@ class mattack void plant (game *g, monster *z); void disappear (game *g, monster *z); void formblob (game *g, monster *z); + void dogthing (game *g, monster *z); + void tentacle (game *g, monster *z); + void vortex (game *g, monster *z); void gene_sting (game *g, monster *z); void stare (game *g, monster *z); void fear_paralyze (game *g, monster *z); diff --git a/mondeath.cpp b/mondeath.cpp index 0e1fe6bc3d296..5ed50f20be768 100644 --- a/mondeath.cpp +++ b/mondeath.cpp @@ -184,6 +184,29 @@ void mdeath::melt(game *g, monster *z) g->add_msg("The %s melts away!", z->name().c_str()); } +void mdeath::amigara(game *g, monster *z) +{ + if (g->u.has_disease(DI_AMIGARA)) { + int count = 0; + for (int i = 0; i < g->z.size(); i++) { + if (g->z[i].type->id == mon_amigara_horror) + count++; + } + if (count <= 1) { // We're the last! + g->u.rem_disease(DI_AMIGARA); + g->add_msg("Your obsession with the fault fades away..."); + } + } + normal(g, z); +} + +void mdeath::thing(game *g, monster *z) +{ + monster thing(g->mtypes[mon_thing]); + thing.spawn(z->posx, z->posy); + g->z.push_back(thing); +} + void mdeath::explode(game *g, monster *z) { int size; diff --git a/mondeath.h b/mondeath.h index bdf36f196216a..6e7fa5be135b0 100644 --- a/mondeath.h +++ b/mondeath.h @@ -19,6 +19,8 @@ class mdeath void guilt (game *g, monster *z); // Morale penalty void blobsplit (game *g, monster *z); // Creates more blobs void melt (game *g, monster *z); // Normal death, but melts + void amigara (game *g, monster *z); // Removes hypnosis if last one + void thing (game *g, monster *z); // Turn into a full thing void explode (game *g, monster *z); // Damaging explosion }; diff --git a/mongroup.h b/mongroup.h index 1b34d48aaecd3..04e0eef346e33 100644 --- a/mongroup.h +++ b/mongroup.h @@ -19,6 +19,7 @@ enum moncat_id { mcat_swamp, mcat_lab, mcat_nether, + mcat_spiral, num_moncats }; diff --git a/mongroupdef.cpp b/mongroupdef.cpp index cbc7ada4d4fd5..3a1aa0022be7e 100644 --- a/mongroupdef.cpp +++ b/mongroupdef.cpp @@ -50,5 +50,8 @@ void game::init_moncats() moncats[mcat_nether], mon_flying_polyp, mon_hunting_horror, mon_mi_go, mon_yugg, mon_gelatin, mon_flaming_eye, mon_kreck, mon_blank, NULL); + setvector( + moncats[mcat_spiral], + mon_human_snail, mon_twisted_body, mon_vortex, NULL); } diff --git a/mtype.h b/mtype.h index 8bf4310e5ca0f..75cf88d2b355d 100644 --- a/mtype.h +++ b/mtype.h @@ -46,6 +46,11 @@ mon_mosquito, mon_dragonfly, mon_centipede, mon_frog, mon_slug, // SPIDERS mon_spider_wolf, mon_spider_web, mon_spider_jumping, mon_spider_trapdoor, mon_spider_widow, +// Unearthed Horrors +mon_dark_wyrm, mon_amigara_horror, mon_dog_thing, mon_headless_dog_thing, + mon_thing, +// Spiral monsters +mon_human_snail, mon_twisted_body, mon_vortex, // Subspace monsters mon_flying_polyp, mon_hunting_horror, mon_mi_go, mon_yugg, mon_gelatin, mon_flaming_eye, mon_kreck, mon_blank, mon_gozu, @@ -117,7 +122,7 @@ struct mtype { m_size size; material mat; // See enums.h for material list. Generally, flesh; veggy? - unsigned flags : MF_MAX; // Bitfield of m_flags + unsigned long flags : MF_MAX; // Bitfield of m_flags unsigned char frequency; // How often do these show up? 0 (never) to ?? int difficulty;// Used all over; 30 min + (diff-3)*30 min = earlist appearance diff --git a/mtypedef.cpp b/mtypedef.cpp index 68c11d34508f4..758d6f3958855 100644 --- a/mtypedef.cpp +++ b/mtypedef.cpp @@ -641,6 +641,87 @@ hourglass on its black carapace. It is\n\ known for its highly toxic venom." ); +// UNEARTHED HORRORS +mon("dark wyrm", 'S', c_blue, MS_LARGE, FLESH, + (mfb(MF_SMELLS)|mfb(MF_HEARS)|mfb(MF_GOODHEARING)|mfb(MF_DESTROYS)| + mfb(MF_POISON)|mfb(MF_SUNDEATH)|mfb(MF_ACIDPROOF)|mfb(MF_ACIDTRAIL)), +// frq dif agr spd msk mdi m## cut dge arm itm HP special freq + 1, 20, 5, 90, 8, 2, 6, 4, 4, 0, 0,100, 0, + &mdeath::normal, &mattack::none, "\ +A huge, black worm, its flesh glistening\n\ +with an acidic, blue slime. It has a gaping\n\ +round mouth lined with dagger-like teeth." +); + +mon("Amigara horror", 'H', c_white, MS_LARGE, FLESH, + (mfb(MF_SMELLS)|mfb(MF_HEARS)|mfb(MF_SEES)|mfb(MF_STUMBLES)| + mfb(MF_HARDTOSHOOT)), +// frq dif agr spd msk mdi m## cut dge arm itm HP special freq + 1, 30, 5, 70, 10, 2, 4, 0, 2, 0, 0,250, 0, + &mdeath::amigara, &mattack::fear_paralyze, "\ +A spindly body, standing at least 15 feet\n\ +tall. It looks vaguely human, but its face is\n\ +grotesquely stretched out, and its limbs are\n\ +distorted to the point of being tentacles." +); + +// This "dog" is, in fact, a disguised Thing +mon("dog", 'd', c_white, MS_SMALL, FLESH, + (mfb(MF_SEES)|mfb(MF_SMELLS)|mfb(MF_HEARS)|mfb(MF_ANIMAL)|mfb(MF_WARM)| + mfb(MF_FUR)|mfb(MF_FRIENDLY_SPECIAL)), +// frq dif agr spd msk mdi m## cut dge arm itm HP special freq + 3, 5, 1,150, 12, 2, 3, 3, 3, 0, 0, 25, 40, + &mdeath::thing, &mattack::dogthing, "\ +A medium-sized domesticated dog, gone feral." +); + +mon("tentacle dog", 'd', c_dkgray, MS_MEDIUM, FLESH, + (mfb(MF_SEES)|mfb(MF_SMELLS)|mfb(MF_HEARS)|mfb(MF_BASHES)), +// frq dif agr spd msk mdi m## cut dge arm itm HP special freq + 1, 14, 5,120, 12, 2, 4, 0, 3, 0, 0,120, 5, + &mdeath::thing, &mattack::tentacle, "\ +A dog's body with a mass of ropy, black\n\ +tentacles extending from its head." +); + +mon("Thing", 'H', c_dkgray, MS_LARGE, FLESH, + (mfb(MF_SMELLS)|mfb(MF_HEARS)|mfb(MF_NOHEAD)|mfb(MF_BASHES)| + mfb(MF_SWIMS)|mfb(MF_ATTACKMON)|mfb(MF_PLASTIC)|mfb(MF_ACIDPROOF)), +// frq dif agr spd msk mdi m## cut dge arm itm HP special freq + 1, 25, 5,135, 14, 2, 4, 0, 5, 0, 0,160, 5, + &mdeath::melt, &mattack::tentacle, "\ +An amorphous black creature which seems to\n\ +sprout tentacles rapidly." +); + +mon("human snail", 'h', c_green, MS_LARGE, FLESH, + (mfb(MF_SMELLS)|mfb(MF_HEARS)|mfb(MF_POISON)|mfb(MF_ACIDPROOF)| + mfb(MF_ACIDTRAIL)), +// frq dif agr spd msk mdi m## cut dge arm itm HP special freq + 20, 10, 3, 50, 4, 1, 5, 0, 0, 10, 0, 50, 5, + &mdeath::normal, &mattack::acid, "\ +A large snail, with an oddly human face." +); + +mon("twisted body", 'h', c_pink, MS_MEDIUM, FLESH, + (mfb(MF_SEES)|mfb(MF_HEARS)|mfb(MF_GOODHEARING)|mfb(MF_POISON)), +// frq dif agr spd msk mdi m## cut dge arm itm HP special freq + 5, 12, 5, 90, 5, 2, 4, 0, 6, 0, 0, 65, 0, + &mdeath::normal, &mattack::none, "\ +A human body, but with its limbs, neck, and\n\ +hair impossibly twisted." +); + +mon("vortex", 'v', c_white, MS_SMALL, POWDER, + (mfb(MF_HEARS)|mfb(MF_GOODHEARING)|mfb(MF_STUMBLES)|mfb(MF_NOHEAD)| + mfb(MF_HARDTOSHOOT)|mfb(MF_FLIES)|mfb(MF_PLASTIC)|mfb(MF_SHOCK)| + mfb(MF_FRIENDLY_SPECIAL)), +// frq dif agr spd msk mdi m## cut dge arm itm HP special freq + 2, 30, 5,120, 0, 0, 0, 0, 0, 0, 0, 20, 6, + &mdeath::melt, &mattack::vortex, "\ +A twisting spot in the air, with some kind\n\ +of morphing mass at its center." +); // NETHER WORLD INHABITANTS mon("flying polyp", 'H', c_dkgray, MS_HUGE, FLESH, diff --git a/newcharacter.cpp b/newcharacter.cpp index b01ebe05789b8..34c849319c19d 100644 --- a/newcharacter.cpp +++ b/newcharacter.cpp @@ -245,6 +245,9 @@ End of cheatery */ tmp = item(g->itypes[itm_inhaler], 0, 'a' + worn.size()); inv.push_back(tmp); } +// make sure we have no mutations +for (int i = 0; i < PF_MAX2; i++) + my_mutations[i] = false; return true; } @@ -934,7 +937,7 @@ int player::random_good_trait(character_type type) case 10: return PF_PACKMULE; case 11: if (!has_trait(PF_SAVANT)) return PF_FASTLEARNER; case 12: return PF_DEFT; - case 13: return PF_SUPERTASTER; + case 13: case 14: return PF_DISRESISTANT; case 15: return PF_INCONSPICUOUS; case 16: return PF_LIGHTSTEP; @@ -989,7 +992,7 @@ int player::random_good_trait(character_type type) case 16: return PF_THICKSKIN; case 17: case 18: - case 19: return PF_SUPERTASTER; + case 19: case 20: case 21: return PF_ANIMALEMPATH; case 22: return PF_TERRIFYING; @@ -1018,7 +1021,7 @@ int player::random_good_trait(character_type type) case 11: case 12: if (!has_trait(PF_SAVANT)) return PF_FASTLEARNER; case 13: return PF_GOURMAND; - case 14: return PF_SUPERTASTER; + case 14: case 15: case 16: return PF_INCONSPICUOUS; case 17: @@ -1035,7 +1038,7 @@ int player::random_good_trait(character_type type) case 6: return PF_POISRESIST; case 7: return PF_FASTREADER; case 8: if (!has_trait(PF_SAVANT)) return PF_FASTLEARNER; - case 9: return PF_SUPERTASTER; + case 9: case 10: case 11: case 12: return PF_DISRESISTANT; diff --git a/omdata.h b/omdata.h index 6950ddf29ce51..7bce73102f9f0 100644 --- a/omdata.h +++ b/omdata.h @@ -69,6 +69,8 @@ enum oter_id { ot_silo, ot_silo_finale, ot_temple, ot_temple_stairs, ot_temple_core, ot_temple_finale, // TODO ot_sewage_treatment, ot_sewage_treatment_hub, ot_sewage_treatment_under, + ot_mine_entrance, ot_mine_shaft, ot_mine, ot_mine_down, ot_mine_finale, + ot_spiral_hub, ot_spiral, ot_radio_tower, // Underground terrain ot_spider_pit_under, @@ -217,6 +219,13 @@ const oter_t oterlist[num_ter_types] = { {"sewage treatment", 'P', c_red, 5, false}, {"sewage treatment", 'P', c_green, 5, false}, {"sewage treatment", 'P', c_green, 5, false}, +{"mine entrance", 'M', c_ltgray, 5, false}, +{"mine shaft", 'O', c_dkgray, 5, false}, +{"mine", 'M', c_brown, 2, false}, +{"mine", 'M', c_brown, 2, false}, +{"mine", 'M', c_brown, 2, false}, +{"spiral cavern", '@', c_pink, 2, false}, +{"spiral cavern", '@', c_pink, 2, false}, {"radio tower", 'X', c_ltgray, 2, false}, {"cavern", '0', c_ltgray, 2, false}, {"anthill", '%', c_brown, 2, false}, @@ -327,6 +336,7 @@ enum omspec_id OMSPEC_SILO, OMSPEC_RADIO, OMSPEC_SEWAGE, + OMSPEC_MINE, OMSPEC_ANTHILL, OMSPEC_SPIDER, OMSPEC_SLIME, @@ -364,6 +374,9 @@ const overmap_special overmap_specials[NUM_OMSPECS] = { {ot_sewage_treatment, 10, 10, mcat_null, 0, 0, 0, 0, &omspec_place::land, mfb(OMS_FLAG_PARKING_LOT)}, +{ot_mine_entrance, 5, 15, mcat_null, 0, 0, 0, 0, + &omspec_place::wilderness, mfb(OMS_FLAG_PARKING_LOT)}, + {ot_anthill, 30, 10, mcat_ant, 10, 30, 1000, 2000, &omspec_place::wilderness, 0}, diff --git a/overmap.cpp b/overmap.cpp index 4098ad3a4f53b..5dd0e09c5c11c 100644 --- a/overmap.cpp +++ b/overmap.cpp @@ -505,6 +505,8 @@ void overmap::generate_sub(overmap* above) std::vector ant_points; std::vector goo_points; std::vector lab_points; + std::vector shaft_points; + std::vector mine_points; for (int i = 0; i < OMAPX; i++) { for (int j = 0; j < OMAPY; j++) { seen(i, j) = false; // Start by setting all squares to unseen @@ -554,7 +556,23 @@ void overmap::generate_sub(overmap* above) else if (above->ter(i, j) == ot_lab_stairs) ter(i, j) = ot_lab; - else if (above->ter(i, j) == ot_silo) { + else if (above->ter(i, j) == ot_mine_entrance) + shaft_points.push_back(city(i, j, 0)); + + else if (above->ter(i, j) == ot_mine_shaft || + above->ter(i, j) == ot_mine_down ) { + ter(i, j) = ot_mine; + mine_points.push_back(city(i, j, rng(6 + posz, 10 + posz))); + + } else if (above->ter(i, j) == ot_mine_finale) { + for (int x = i - 1; x <= i + 1; x++) { + for (int y = j - 1; y <= j + 1; y++) + ter(x, y) = ot_spiral; + } + ter(i, j) = ot_spiral_hub; + zg.push_back(mongroup(mcat_spiral, i * 2, j * 2, 2, 200)); + + } else if (above->ter(i, j) == ot_silo) { if (rng(2, 7) < abs(posz) || rng(2, 7) < abs(posz)) ter(i, j) = ot_silo_finale; else @@ -585,6 +603,8 @@ void overmap::generate_sub(overmap* above) above->cities[i].s * 3.5, above->cities[i].s * 70)); } place_rifts(); + for (int i = 0; i < mine_points.size(); i++) + build_mine(mine_points[i].x, mine_points[i].y, mine_points[i].s); polish(ot_subway_ns, ot_subway_nesw); polish(ot_ants_ns, ot_ants_nesw); // Basements done last so sewers, etc. don't overwrite them @@ -595,6 +615,8 @@ void overmap::generate_sub(overmap* above) ter(i, j) = ot_basement; } } + for (int i = 0; i < shaft_points.size(); i++) + ter(shaft_points[i].x, shaft_points[i].y) = ot_mine_shaft; } void overmap::make_tutorial() @@ -1344,11 +1366,13 @@ void overmap::build_lab(int x, int y, int s) } if (numstairs == 0) { // This is the bottom of the lab; We need a finale int finalex, finaley; + int tries = 0; do { finalex = rng(x - s, x + s); finaley = rng(y - s, y + s); - } while (ter(finalex, finaley) != ot_lab && - ter(finalex, finaley) != ot_lab_core); + tries++; + } while (tries < 15 && ter(finalex, finaley) != ot_lab && + ter(finalex, finaley) != ot_lab_core); ter(finalex, finaley) = ot_lab_finale; } zg.push_back(mongroup(mcat_lab, (x * 2), (y * 2), s, 60)); @@ -1426,6 +1450,33 @@ void overmap::build_slimepit(int x, int y, int s) } } +void overmap::build_mine(int x, int y, int s) +{ + bool finale = (s <= rng(1, 3)); + int built = 0; + if (s < 2) + s = 2; + while (built < s) { + ter(x, y) = ot_mine; + std::vector next; + for (int i = -1; i <= 1; i += 2) { + if (ter(x, y + i) == ot_rock) + next.push_back( point(x, y + i) ); + if (ter(x + i, y) == ot_rock) + next.push_back( point(x + i, y) ); + } + if (next.empty()) { // Dead end! Go down! + ter(x, y) = (finale ? ot_mine_finale : ot_mine_down); + return; + } + point p = next[ rng(0, next.size() - 1) ]; + x = p.x; + y = p.y; + built++; + } + ter(x, y) = (finale ? ot_mine_finale : ot_mine_down); +} + void overmap::place_rifts() { int num_rifts = rng(0, 2) * rng(0, 2); @@ -2045,14 +2096,6 @@ void overmap::place_mongroups() mongroup(mcat_plants, rng(0, OMAPX * 2 - 1), rng(0, OMAPY * 2 - 1), rng(30, 50), rng(800, 1300))); } -/* - numgroups = rng(0, 7); - for (int i = 0; i < numgroups; i++) { - zg.push_back( - mongroup(mcat_fungi, rng(0, OMAPX * 2 - 1), rng(0, OMAPY * 2 - 1), - rng(20, 30), rng(400, 800))); - } -*/ // Forest groups cover the entire map zg.push_back( mongroup(mcat_forest, 0, OMAPY, OMAPY, diff --git a/overmap.h b/overmap.h index 7f87e9ea2c0a1..ccd233fd9d829 100644 --- a/overmap.h +++ b/overmap.h @@ -110,6 +110,7 @@ class overmap void build_anthill(int x, int y, int s); void build_tunnel(int x, int y, int s, int dir); void build_slimepit(int x, int y, int s); + void build_mine(int x, int y, int s); void place_rifts(); // Connection highways void place_hiways(std::vector cities, oter_id base); diff --git a/player.cpp b/player.cpp index 9e26583a1a92a..a69c0a163ad35 100644 --- a/player.cpp +++ b/player.cpp @@ -50,6 +50,8 @@ player::player() } for (int i = 0; i < PF_MAX2; i++) my_traits[i] = false; + for (int i = 0; i < PF_MAX2; i++) + my_mutations[i] = false; } player::~player() @@ -93,6 +95,8 @@ player& player::operator= (player rhs) } for (int i = 0; i < PF_MAX2; i++) my_traits[i] = rhs.my_traits[i]; + for (int i = 0; i < PF_MAX2; i++) + my_mutations[i] = rhs.my_mutations[i]; inv.clear(); for (int i = 0; i < rhs.inv.size(); i++) @@ -317,6 +321,9 @@ void player::load_info(game *g, std::string data) for (int i = 0; i < PF_MAX2; i++) dump >> my_traits[i]; + for (int i = 0; i < PF_MAX2; i++) + dump >> my_mutations[i]; + for (int i = 0; i < num_hp_parts; i++) dump >> hp_cur[i] >> hp_max[i]; for (int i = 0; i < num_skill_types; i++) @@ -380,6 +387,8 @@ std::string player::save_info() for (int i = 0; i < PF_MAX2; i++) dump << my_traits[i] << " "; + for (int i = 0; i < PF_MAX2; i++) + dump << my_mutations[i] << " "; for (int i = 0; i < num_hp_parts; i++) dump << hp_cur[i] << " " << hp_max[i] << " "; for (int i = 0; i < num_skill_types; i++) @@ -1272,12 +1281,18 @@ void player::disp_status(WINDOW *w) bool player::has_trait(int flag) { - return my_traits[flag];// || my_mutations[flag]; + return my_traits[flag]; +} + +bool player::has_mutation(int flag) +{ + return my_mutations[flag]; } void player::toggle_trait(int flag) { my_traits[flag] = !my_traits[flag]; + my_mutations[flag] = !my_mutations[flag]; } bool player::has_bionic(bionic_id b) @@ -1478,15 +1493,16 @@ void player::mutate(game *g) } break; case 8: - if (!has_trait(PF_NIGHTVISION) && !has_trait(PF_NIGHTVISION2)) { - if (g->light_level() <= 2) + if (!has_trait(PF_NIGHTVISION)) { + if (g->light_level() <= 2 && !has_trait(PF_NIGHTVISION2)) + g->add_msg("The darkness seems clearer."); + else if (g->light_level() <= 4) g->add_msg("The darkness seems clearer."); toggle_trait(PF_NIGHTVISION); return; } else if (!has_trait(PF_NIGHTVISION2)) { if (g->light_level() <= 4) g->add_msg("The darkness seems clearer."); - toggle_trait(PF_NIGHTVISION); toggle_trait(PF_NIGHTVISION2); return; } @@ -1499,12 +1515,6 @@ void player::mutate(game *g) } break; case 10: - if (!has_trait(PF_SUPERTASTER)) { - g->add_msg("Your sense of taste feels acute."); - toggle_trait(PF_SUPERTASTER); - return; - } - break; case 11: if (has_trait(PF_MYOPIC)) { g->add_msg("Your vision feels sharper."); @@ -1878,7 +1888,7 @@ int player::sight_range(int light_level) if (has_trait(PF_NIGHTVISION) && ret < 12) ret += 1; if (has_trait(PF_NIGHTVISION2) && ret < 12) - ret += 3; + ret += 2; if (underwater && !has_bionic(bio_membrane) && !has_trait(PF_MEMBRANE) && !is_wearing(itm_goggles_swim)) ret = 1; @@ -2018,7 +2028,7 @@ int player::comprehension_percent(skill s, bool real_life) percent += 125 - 1000 / intel; if (has_trait(PF_FASTLEARNER)) - percent += 20.; + percent += 50.; return (int)(percent); } @@ -3549,7 +3559,7 @@ void player::use(game *g, char let) } else g->add_msg("Your %s has %d charges but needs %d.", used->tname(g).c_str(), used->charges, tool->charges_per_use); - if (replace_item && tool->use != &iuse::set_trap) + if (replace_item && used->invlet != 0) inv.add_item(copy); return; diff --git a/player.h b/player.h index 41b9cc5cbca4d..7a8416aee220e 100644 --- a/player.h +++ b/player.h @@ -47,6 +47,7 @@ class player { int swim_speed(); // Our speed when swimming bool has_trait(int flag); + bool has_mutation(int flag); void toggle_trait(int flag); bool has_bionic(bionic_id b); @@ -185,6 +186,7 @@ class player { std::string name; bool male; bool my_traits[PF_MAX2]; + bool my_mutations[PF_MAX2]; std::vector my_bionics; // Current--i.e. modified by disease, pain, etc. int str_cur, dex_cur, int_cur, per_cur; diff --git a/pldata.h b/pldata.h index dc6a7e55946fd..0a01ee4a60563 100644 --- a/pldata.h +++ b/pldata.h @@ -77,7 +77,7 @@ enum dis_type { // Traps DI_BEARTRAP, DI_IN_PIT, // Other - DI_TELEGLOW + DI_AMIGARA, DI_TELEGLOW }; enum add_type { @@ -146,7 +146,6 @@ enum pl_flag { PF_DEFT, // Less movement penalty for melee miss PF_DRUNKEN, // Having a drunk status improves melee combat PF_GOURMAND, // Faster eating, higher level of max satiated - PF_SUPERTASTER,// ALWAYS warned for rotten food PF_ANIMALEMPATH,// Animals run away more PF_TERRIFYING, // All creatures run away more PF_DISRESISTANT,// Less likely to succumb to low health; TODO: Implement this @@ -261,7 +260,7 @@ Your skin is tough. Cutting damage is slightly reduced for you."}, {"Packmule", 3, false, "\ You can manage to find space for anything! You can carry 40%% more volume."}, {"Fast Learner", 3, false, "\ -Your skill comprehension is 50%% higher, allowing you to learn skills much\n\ +Your skill comprehension is 20%% higher, allowing you to learn skills much\n\ faster than others. Note that this only applies to real-world experience,\n\ not to skill gain from other sources like books."}, {"Deft", 2, false, "\ @@ -275,8 +274,6 @@ considerably, especially unarmed combat."}, You eat faster, and can eat and drink more, than anyone else! You also enjoy\n\ food more; delicious food is better for your morale, and you don't mind some\n\ unsavory meals."}, -{"Supertaster", 1, false, "\ -You can tell if food is even a little spoiled before eating it."}, {"Animal Empathy", 1, false, "\ Peaceful animals will not run away from you, and even aggressive animals are\n\ less likely to attack. This only applies to natural animals such as woodland\n\ diff --git a/ranged.cpp b/ranged.cpp index ce8d3acdd17ca..b882d045cadb1 100644 --- a/ranged.cpp +++ b/ranged.cpp @@ -254,7 +254,7 @@ void game::throw_item(player &p, int tarx, int tary, item &thrown, if (thrown.made_of(GLASS) && !thrown.active && // active = molotov, etc. rng(0, thrown.volume() + 8) - rng(0, p.str_cur) < thrown.volume()) { if (u_see(tx, ty, tart)) - add_msg("The %s shatters!", thrown.tname().c_str()); + add_msg("The %s shatters!", thrown.tname().c_str()); for (int i = 0; i < thrown.contents.size(); i++) m.add_item(tx, ty, thrown.contents[i]); sound(tx, ty, 16, "glass breaking!"); diff --git a/weather.h b/weather.h index aa0b868a82801..33300053a6a26 100644 --- a/weather.h +++ b/weather.h @@ -53,6 +53,7 @@ struct weather_datum int avg_temperature[4]; // Spring, Summer, Winter, Fall int ranged_penalty; int sight_penalty; // Penalty to max sight range + bool dangerous; // If true, our activity gets interrupted void (weather_effect::*effect)(game *); }; diff --git a/weather_data.h b/weather_data.h index 9ee556a99aec9..9bec7a602a9ee 100644 --- a/weather_data.h +++ b/weather_data.h @@ -9,43 +9,43 @@ std::string season_name[4] = { weather_datum weather_data[NUM_WEATHER_TYPES] = { {"NULL Weather - BUG", c_magenta, - {0, 0, 0, 0}, 0, 0, + {0, 0, 0, 0}, 0, 0, false, &weather_effect::none}, {"Clear", c_cyan, - {55, 85, 30, 60}, 0, 0, + {55, 85, 30, 60}, 0, 0, false, &weather_effect::none}, {"Sunny", c_ltcyan, - {70, 100, 40, 70}, 0, 0, + {70, 100, 40, 70}, 0, 0, false, &weather_effect::glare}, {"Cloudy", c_ltgray, - {50, 75, 20, 60}, 0, 0, + {50, 75, 20, 60}, 0, 0, false, &weather_effect::none}, {"Drizzle", c_ltblue, - {45, 70, 35, 45}, 1, 1, + {45, 70, 35, 45}, 1, 1, true, &weather_effect::wet}, {"Rain", c_blue, - {42, 65, 35, 40}, 3, 5, + {42, 65, 35, 40}, 3, 5, true, &weather_effect::very_wet}, {"Thunder Storm", c_dkgray, - {42, 70, 35, 40}, 4, 7, + {42, 70, 35, 40}, 4, 7, true, &weather_effect::thunder}, {"Lightning Storm", c_yellow, - {45, 52, 38, 42}, 4, 8, + {45, 52, 38, 42}, 4, 8, true, &weather_effect::lightning}, {"Acidic Drizzle", c_ltgreen, - {45, 70, 35, 45}, 2, 3, + {45, 70, 35, 45}, 2, 3, true, &weather_effect::light_acid}, {"Acid Rain", c_green, - {45, 70, 35, 45}, 4, 6, + {45, 70, 35, 45}, 4, 6, true, &weather_effect::acid}, {"Flurries", c_white, - {30, 30, 20, 30}, 2, 4, + {30, 30, 20, 30}, 2, 4, true, &weather_effect::flurry}, {"Snowing", c_white, - {25, 25, 18, 25}, 4, 7, + {25, 25, 18, 25}, 4, 7, true, &weather_effect::snow}, {"Snowstorm", c_white, - {20, 20, 10, 20}, 6, 10, + {20, 20, 10, 20}, 6, 10, true, &weather_effect::snowstorm} };