293 game.cpp
@@ -13,7 +13,6 @@
#include "debug.h"
#include "bodypart.h"
#include "map.h"
#include "weather.h"

#include <map>
#include <algorithm>
@@ -89,34 +88,66 @@ game::~game()
}

void game::init_ui(){
clear(); // Clear the screen
intro(); // Print an intro screen, make sure we're at least 80x25

VIEWX = OPTIONS[OPT_VIEWPORT_X];
VIEWY = OPTIONS[OPT_VIEWPORT_Y];
if (VIEWX <= 0) {
VIEWX = 1;
}
if (VIEWY <= 0) {
VIEWY = 1;
}
TERRAIN_WINDOW_WIDTH = (VIEWX * 2) + 1;
TERRAIN_WINDOW_HEIGHT = (VIEWY * 2) + 1;
// Set up the main UI windows.
w_terrain = newwin(TERRAIN_WINDOW_HEIGHT, TERRAIN_WINDOW_WIDTH, 0, 0);
werase(w_terrain);
w_minimap = newwin(7, 7, 0, TERRAIN_WINDOW_WIDTH);
werase(w_minimap);
w_HP = newwin(14, 7, 7, TERRAIN_WINDOW_WIDTH);
werase(w_HP);
w_moninfo = newwin(12, 48, 0, VIEWX * 2 + 8);
werase(w_moninfo);
w_messages = newwin(8, 48, 12, VIEWX * 2 + 8);
werase(w_messages);
w_location = newwin(1, 48, 20, VIEWX * 2 + 8);
werase(w_location);
w_status = newwin(4, 55, 21, TERRAIN_WINDOW_WIDTH);
werase(w_status);
clear(); // Clear the screen
intro(); // Print an intro screen, make sure we're at least 80x25

#if (defined _WIN32 || defined __WIN32__)
TERMX = 55 + (OPTIONS[OPT_VIEWPORT_X] * 2 + 1);
TERMY = OPTIONS[OPT_VIEWPORT_Y] * 2 + 1;
VIEWX = OPTIONS[OPT_VIEWPORT_X];
VIEWY = OPTIONS[OPT_VIEWPORT_Y];
TERRAIN_WINDOW_WIDTH = (VIEWX * 2) + 1;
TERRAIN_WINDOW_HEIGHT = (VIEWY * 2) + 1;
#else
getmaxyx(stdscr, TERMY, TERMX);

//make sure TERRAIN_WINDOW_WIDTH and TERRAIN_WINDOW_HEIGHT are uneven
if (TERMX%2 == 1) {
TERMX--;
}

if (TERMY%2 == 0) {
TERMY--;
}

TERRAIN_WINDOW_WIDTH = TERMX - STATUS_WIDTH;
TERRAIN_WINDOW_HEIGHT = TERMY;
VIEWX = (TERRAIN_WINDOW_WIDTH - 1) / 2;
VIEWY = (TERRAIN_WINDOW_HEIGHT - 1) / 2;
#endif

if (VIEWX < 12) {
VIEWX = 12;
}

if (VIEWY < 12) {
VIEWY = 12;
}

// Set up the main UI windows.
w_terrain = newwin(TERRAIN_WINDOW_HEIGHT, TERRAIN_WINDOW_WIDTH, 0, 0);
werase(w_terrain);

w_minimap = newwin(MINIMAP_HEIGHT, MINIMAP_WIDTH, 0, TERMX - MONINFO_WIDTH - MINIMAP_WIDTH);
werase(w_minimap);

w_HP = newwin(HP_HEIGHT, HP_WIDTH, MINIMAP_HEIGHT, TERMX - MESSAGES_WIDTH - HP_WIDTH);
werase(w_HP);

w_moninfo = newwin(MONINFO_HEIGHT, MONINFO_WIDTH, 0, TERMX - MONINFO_WIDTH);
werase(w_moninfo);

w_messages = newwin(MESSAGES_HEIGHT, MESSAGES_WIDTH, MONINFO_HEIGHT, TERMX - MESSAGES_WIDTH);
werase(w_messages);

w_location = newwin(LOCATION_HEIGHT, LOCATION_WIDTH, MONINFO_HEIGHT+MESSAGES_HEIGHT, TERMX - LOCATION_WIDTH);
werase(w_location);

w_status = newwin(STATUS_HEIGHT, STATUS_WIDTH, MONINFO_HEIGHT+MESSAGES_HEIGHT+LOCATION_HEIGHT, TERMX - STATUS_WIDTH);
werase(w_status);

w_void = newwin(TERMY-(MONINFO_HEIGHT+MESSAGES_HEIGHT+LOCATION_HEIGHT+STATUS_HEIGHT), STATUS_WIDTH, MONINFO_HEIGHT+MESSAGES_HEIGHT+LOCATION_HEIGHT+STATUS_HEIGHT, TERMX - STATUS_WIDTH);
werase(w_void);
}

void game::setup()
@@ -781,166 +812,14 @@ bool game::do_turn()
refresh();
}

// update_bodytemp();
u.update_bodytemp(this);

rustCheck();
if (turn % 10 == 0)
u.update_morale();
return false;
}

/* Here lies the intended effects of body temperature
Assumption 1 : a naked person is comfortable at 31C/87.8F.
Assumption 2 : a "lightly clothed" person is comfortable at 25C/77F.
Assumption 3 : frostbite cannot happen above 0C temperature.*
* In the current model, a naked person can get frostbite at 1C. This isn't true, but it's a compromise with using nice whole numbers.
Here is a list of warmth values and the corresponding temperatures in which the player is comfortable, and in which the player is very cold.
Warmth Temperature (Comfortable) Temperature (Very cold) Notes
0 31C / 87.8F 1C / 33.8F * Naked
10 25C / 77.0F -5C / 23.0F * Lightly clothed
20 19C / 66.2F -11C / 12.2F
30 13C / 55.4F -17C / 1.4F
40 7C / 44.6F -23C / -9.4F
50 1C / 33.8F -29C / -20.2F
60 -5C / 23.0F -35C / -31.0F
70 -11C / 12.2F -41C / -41.8F
80 -17C / 1.4F -47C / -52.6F
90 -23C / -9.4F -53C / -63.4F
100 -29C / -20.2F -59C / -74.2F
*/

void game::update_bodytemp() // TODO bionics, diseases and humidity (not in yet) can affect body temp.
{
// NOTE : visit weather.h for some details on the numbers used
// Converts temperature to Celsius/10(Wito plans on using degrees Kelvin later)
int Ctemperature = 100*(temperature - 32) * 5/9;
// Temperature norms
const int ambient_norm = 3100;
// This adjusts the temperature scale to match the bodytemp scale
int adjusted_temp = (Ctemperature - ambient_norm);
// Creative thinking for clean morale penalties: this gets incremented in the for loop and applied after the loop
int morale_pen = 0;
// Fetch the morale value of wetness for bodywetness
int bodywetness = 0;
for (int i = 0; bodywetness == 0 && i < u.morale.size(); i++)
if( u.morale[i].type == MORALE_WET ) {
bodywetness = abs(u.morale[i].bonus); // Make it positive, less confusing
break;
}
// Current temperature and converging temperature calculations
for (int i = 0 ; i < num_bp ; i++){
if (i == bp_eyes) continue; // Skip eyes
// Represents the fact that the body generates heat when it is cold. TODO : should this increase hunger?
float homeostasis_adjustement = (u.temp_cur[i] > BODYTEMP_NORM ? 40.0 : 60.0);
int clothing_warmth_adjustement = homeostasis_adjustement * (float)u.warmth(body_part(i)) * (1.0 - (float)bodywetness / 100.0);
// Disease name shorthand
int blister_pen = dis_type(DI_BLISTERS) + 1 + i, hot_pen = dis_type(DI_HOT) + 1 + i;
int cold_pen = dis_type(DI_COLD)+ 1 + i, frost_pen = dis_type(DI_FROSTBITE) + 1 + i;
// Convergeant temperature is affected by ambient temperature, clothing warmth, and body wetness.
signed int temp_conv = BODYTEMP_NORM + adjusted_temp + clothing_warmth_adjustement;
// Fatigue also affects convergeant temperature
if (!u.has_disease(DI_SLEEP)) temp_conv -= 10*u.fatigue/6;
else {
int vpart = -1;
vehicle *veh = m.veh_at (u.posx, u.posy, vpart);
if (m.ter(u.posx, u.posy) == t_bed) temp_conv += 1000;
else if (m.ter(u.posx, u.posy) == t_makeshift_bed) temp_conv += 500;
else if (m.tr_at(u.posx, u.posy) == tr_cot) temp_conv -= 500;
else if (m.tr_at(u.posx, u.posy) == tr_rollmat) temp_conv -= 1000;
else if (veh && veh->part_with_feature (vpart, vpf_seat) >= 0) temp_conv += 200;
else if (veh && veh->part_with_feature (vpart, vpf_bed) >= 0) temp_conv += 300;
else temp_conv -= 2000;
}
// Convection heat sources : generates body heat, helps fight frostbite
int blister_count = 0; // If the counter is high, your skin starts to burn
for (int j = -6 ; j <= 6 ; j++){
for (int k = -6 ; k <= 6 ; k++){
// Bizarre workaround for u_see() and friends not taking const arguments.
int l = std::max(j, k);
int heat_intensity = 0;
if(m.field_at(u.posx + j, u.posy + k).type == fd_fire)
heat_intensity = m.field_at(u.posx + j, u.posy + k).density;
else if (m.tr_at(u.posx + j, u.posy + k) == tr_lava )
heat_intensity = 3;
if (heat_intensity > 0 && u_see(u.posx + j, u.posy + k, l)) {
// Ensure fire_dist >=1 to avoid divide-by-zero errors.
int fire_dist = std::max(1, std::max(j, k));
if (u.frostbite_timer[i] > 0) u.frostbite_timer[i] -= heat_intensity - fire_dist / 2;
temp_conv += 50 * heat_intensity / (fire_dist * fire_dist);
blister_count += heat_intensity / (fire_dist * fire_dist);
}
}
}
// TODO Balance bionics
// Bionic "Internal Climate Control" says it is effective from 0F to 140F, these are the corresponding bodytemp values
if (u.has_bionic(bio_climate) && temperature > 0 && temperature < 140)
temp_conv = (9*BODYTEMP_NORM + temp_conv)/10; // Bionic "eases" the effects
// Bionic "Thermal Dissapation" says it prevents fire damage up to 2000F. 500 is picked at random...
if (u.has_bionic(bio_heatsink) && blister_count < 500)
blister_count = 0;
// Skin gets blisters from intense heat exposure.
if (blister_count - 10*u.resist(body_part(i)) > 20) u.add_disease(dis_type(blister_pen), 1, this);
// Increments current body temperature towards convergant.
int temp_difference = u.temp_cur[i] - temp_conv;
int temp_before = u.temp_cur[i];
// Bodytemp equalization code
if (i == bp_torso){u.temp_equalizer(bp_torso, bp_arms); u.temp_equalizer(bp_torso, bp_legs); u.temp_equalizer(bp_torso, bp_head);}
else if (i == bp_head) {u.temp_equalizer(bp_head, bp_eyes); u.temp_equalizer(bp_head, bp_mouth);}
else if (i == bp_arms) u.temp_equalizer(bp_arms, bp_hands);
else if (i == bp_legs) u.temp_equalizer(bp_legs, bp_feet);
if (u.temp_cur[i] != temp_conv) u.temp_cur[i] = temp_difference*exp(-0.002) + temp_conv; // It takes half an hour for bodytemp to converge half way to its convergeance point (think half-life)
int temp_after = u.temp_cur[i];
// Penalties
if (u.temp_cur[i] < BODYTEMP_FREEZING) {u.add_disease(dis_type(cold_pen), 1, this, 3, 3); u.frostbite_timer[i] += 3;}
else if (u.temp_cur[i] < BODYTEMP_VERY_COLD) {u.add_disease(dis_type(cold_pen), 1, this, 2, 3); u.frostbite_timer[i] += 2;}
else if (u.temp_cur[i] < BODYTEMP_COLD) {u.add_disease(dis_type(cold_pen), 1, this, 1, 3); u.frostbite_timer[i] += 1;} // Frostbite timer does not go down if you are still cold.
else if (u.temp_cur[i] > BODYTEMP_SCORCHING) {u.add_disease(dis_type(hot_pen), 1, this, 3, 3); } // If body temp rises over 15000, disease.cpp (DI_HOT_HEAD) acts weird and the player will die
else if (u.temp_cur[i] > BODYTEMP_VERY_HOT) {u.add_disease(dis_type(hot_pen), 1, this, 2, 3); }
else if (u.temp_cur[i] > BODYTEMP_HOT) {u.add_disease(dis_type(hot_pen), 1, this, 1, 3); }
// Morale penalties : a negative morale_pen means the player is cold
// Intensity multiplier is negative for cold, positive for hot
int intensity_mult = -u.disease_intensity(dis_type(cold_pen)) + u.disease_intensity(dis_type(hot_pen));
if (u.has_disease(dis_type(cold_pen)) > 0 || u.has_disease(dis_type(hot_pen)) > 0) {
switch (i) {
case bp_head :
case bp_torso :
case bp_mouth : morale_pen += 2*intensity_mult;
case bp_arms :
case bp_legs : morale_pen += 1*intensity_mult;
case bp_hands:
case bp_feet : morale_pen += 1*intensity_mult;
}
}
// Frostbite (level 1 after 2 hours, level 2 after 4 hours)
if (u.frostbite_timer[i] > 0) u.frostbite_timer[i]--;
if (u.frostbite_timer[i] >= 240) {
if (u.disease_intensity(dis_type(frost_pen)) < 2 && i == bp_mouth) add_msg("Your %s hardens from the frostbite!", body_part_name(body_part(i), -1).c_str());
else if (u.disease_intensity(dis_type(frost_pen)) < 2 && (i == bp_hands || i == bp_feet)) add_msg("Your %s harden from the frostbite!", body_part_name(body_part(i), -1).c_str());
u.add_disease(dis_type(frost_pen), 1, this, 2, 2);}
else if (u.frostbite_timer[i] >= 120) {
if (!u.has_disease(dis_type(frost_pen))) add_msg("You lose sensation in your %s.", body_part_name(body_part(i), -1).c_str());
u.add_disease(dis_type(frost_pen), 1, this, 1, 2);}
// Warn the player if condition worsens
if (temp_before > BODYTEMP_FREEZING && temp_after < BODYTEMP_FREEZING) add_msg("You feel your %s beginning to go numb from the cold!", body_part_name(body_part(i), -1).c_str());
else if (temp_before > BODYTEMP_VERY_COLD && temp_after < BODYTEMP_VERY_COLD) add_msg("You feel your %s getting very cold.", body_part_name(body_part(i), -1).c_str());
else if (temp_before > BODYTEMP_COLD && temp_after < BODYTEMP_COLD) add_msg("You feel your %s getting cold.", body_part_name(body_part(i), -1).c_str());
else if (temp_before < BODYTEMP_SCORCHING && temp_after > BODYTEMP_SCORCHING) add_msg("You feel your %s getting red hot from the heat!", body_part_name(body_part(i), -1).c_str());
else if (temp_before < BODYTEMP_VERY_HOT && temp_after > BODYTEMP_VERY_HOT) add_msg("You feel your %s getting very hot.", body_part_name(body_part(i), -1).c_str());
else if (temp_before < BODYTEMP_HOT && temp_after > BODYTEMP_HOT) add_msg("You feel your %s getting hot.", body_part_name(body_part(i), -1).c_str());

// Debug
//add_msg("%s temperature : %d", body_part_name(body_part(i), -1).c_str(), u.temp_cur[i]);

}
// Morale penalties
if (morale_pen < 0) u.add_morale(MORALE_COLD, -1, -abs(morale_pen));
if (morale_pen > 0) u.add_morale(MORALE_HOT, -1, -abs(morale_pen));
}

void game::rustCheck() {
if (OPTIONS[OPT_SKILL_RUST] == 2)
return;
@@ -1930,6 +1809,10 @@ bool game::handle_action()
list_missions();
break;

case ACTION_KILLS:
disp_kills();
break;

case ACTION_FACTIONS:
list_factions();
break;
@@ -2055,37 +1938,25 @@ bool game::is_game_over()

void game::death_screen()
{
gamemode->game_over(this);
std::stringstream playerfile;
playerfile << "save/" << u.name << ".sav";
unlink(playerfile.str().c_str());
int num_kills = 0;
for (int i = 0; i < num_monsters; i++)
num_kills += kills[i];
gamemode->game_over(this);
std::stringstream playerfile;
playerfile << "save/" << u.name << ".sav";
unlink(playerfile.str().c_str());

WINDOW* w_death = newwin(25, 80, 0, 0);
mvwprintz(w_death, 0, 35, c_red, "GAME OVER - Press Spacebar to Quit");
mvwprintz(w_death, 2, 0, c_white, "Number of kills: %d", num_kills);
int line = 0, mon = 0;
while (line < 40 && mon < num_monsters) {
if (kills[mon] > 0) {
int y = line % 20 + 3, x = int(line / 20) * 40 + 1;
mvwprintz(w_death, y, x, c_white, "%s: %d", mtypes[mon]->name.c_str(),
kills[mon]);
line++;
}
mon++;
}
WINDOW* w_death = newwin(TERMY, TERMX, 0, 0);
mvwprintz(w_death, 0, 35, c_red, "GAME OVER - Press Spacebar to Quit");
wrefresh(w_death);
refresh();
InputEvent input;
do
input = get_input();
while(input != Cancel && input != Close && input != Confirm);
delwin(w_death);

wrefresh(w_death);
refresh();
InputEvent input;
do
input = get_input();
while(input != Cancel && input != Close && input != Confirm);
delwin(w_death);
disp_kills();
}


bool game::load_master()
{
std::ifstream fin;
@@ -2629,7 +2500,7 @@ void game::draw_overmap()

void game::disp_kills()
{
WINDOW* w = newwin(25, 80, 0, 0);
WINDOW* w = newwin(TERMY, TERMX, 0, 0);
std::vector<mtype *> types;
std::vector<int> count;
for (int i = 0; i < num_monsters; i++) {
@@ -3023,6 +2894,8 @@ void game::refresh_all()
draw_HP();
wrefresh(w_moninfo);
wrefresh(w_messages);
werase(w_void);
wrefresh(w_void);
refresh();
}

21 game.h
@@ -27,6 +27,20 @@
#include <map>
#include <stdarg.h>

// Fixed window sizes
#define HP_HEIGHT 14
#define HP_WIDTH 7
#define MINIMAP_HEIGHT 7
#define MINIMAP_WIDTH 7
#define MONINFO_HEIGHT 12
#define MONINFO_WIDTH 48
#define MESSAGES_HEIGHT 8
#define MESSAGES_WIDTH 48
#define LOCATION_HEIGHT 1
#define LOCATION_WIDTH 48
#define STATUS_HEIGHT 4
#define STATUS_WIDTH 55

#define LONG_RANGE 10
#define BLINK_SPEED 300
#define BULLET_SPEED 10000000
@@ -243,6 +257,8 @@ class game
// Display data... TODO: Make this more portable?
int VIEWX;
int VIEWY;
int TERMX;
int TERMY;
int TERRAIN_WINDOW_WIDTH;
int TERRAIN_WINDOW_HEIGHT;

@@ -253,6 +269,7 @@ class game
WINDOW *w_messages;
WINDOW *w_location;
WINDOW *w_status;
WINDOW *w_void; //space unter status if viewport Y > 12
overmap *om_hori, *om_vert, *om_diag; // Adjacent overmaps

private:
@@ -298,7 +315,7 @@ class game
void close(); // Close a door 'c'
void smash(); // Smash terrain
void craft(); // See crafting.cpp
void recraft(); // See crafting.cpp
void recraft(); // See crafting.cpp
void try_and_make(recipe *r);
bool can_make(recipe *r);
void make_craft(recipe *making); // See crafting.cpp
@@ -344,6 +361,7 @@ class game
void chat(); // Talk to a nearby NPC 'C'
void plthrow(char chInput = '.'); // Throw an item 't'
void help(); // Help screen '?'
void show_options(); // Options screen '?'

// Target is an interactive function which allows the player to choose a nearby
// square. It display information on any monster/NPC on that square, and also
@@ -367,7 +385,6 @@ class game
void cleanup_dead(); // Delete any dead NPCs/monsters
void monmove(); // Monster movement
void rustCheck(); // Degrades practice levels
void update_bodytemp(); // Maintains body temperature
void process_events(); // Processes and enacts long-term events
void process_activity(); // Processes and enacts the player's activity
void update_weather(); // Updates the temperature and weather patten
595 help.cpp

Large diffs are not rendered by default.

@@ -196,7 +196,7 @@ itm_bag_plastic, itm_bottle_plastic, itm_bottle_glass,
// Tools
itm_lighter, itm_sewing_kit, itm_scissors, itm_hammer, itm_extinguisher,
itm_flashlight, itm_flashlight_on, itm_hotplate, itm_soldering_iron,
itm_water_purifier, itm_two_way_radio, itm_radio, itm_radio_on, itm_crowbar,
itm_water_purifier, itm_two_way_radio, itm_radio, itm_radio_on, itm_roadmap, itm_crowbar,
itm_hoe, itm_shovel, itm_chainsaw_off, itm_chainsaw_on, itm_jackhammer, itm_jacqueshammer,
itm_bubblewrap, itm_beartrap, itm_board_trap, itm_tripwire, itm_crossbow_trap,
itm_shotgun_trap, itm_blade_trap, itm_landmine, itm_geiger_off, itm_geiger_on,
@@ -274,7 +274,7 @@ itm_bio_claws, itm_bio_fusion, itm_bio_blaster,
// Unarmed Combat Styles
itm_style_karate, itm_style_aikido, itm_style_judo, itm_style_tai_chi,
itm_style_capoeira, itm_style_krav_maga, itm_style_muay_thai,
itm_style_ninjutsu, itm_style_taekwando, itm_style_tiger, itm_style_crane,
itm_style_ninjutsu, itm_style_taekwondo, itm_style_tiger, itm_style_crane,
itm_style_leopard, itm_style_snake, itm_style_dragon, itm_style_centipede,
itm_style_venom_snake, itm_style_scorpion, itm_style_lizard, itm_style_toad,
itm_style_zui_quan,
@@ -1411,7 +1411,7 @@ ARMOR("steeltoed boots",5, 135,C_SHOES, LEATHER, STEEL,
Leather boots with a steel toe. Extremely durable.\n\
These boots are a perfect fit for you.");

ARMOR("winter boots", 5, 140,C_SHOES, PLASTIC, WOOL,
ARMOR("winter boots", 5, 140,C_SHOES, WOOL, PLASTIC,
8, 7, 0, -1, 1, 0, 2, 1, 85, 0, mfb(bp_feet), "\
Cumbersome boots designed for warmth.\n\
These boots are a perfect fit for you.");
@@ -1466,7 +1466,7 @@ ARMOR("ski pants", 60, 300,C_PANTS, COTTON, MNULL,
A pair of pants meant for alpine skiing.");

ARMOR("long underwear", 40, 200,C_PANTS, COTTON, MNULL,
4, 2, -3, 0, 0, 0, 0, 0, 30, 12, mfb(bp_legs), "\
4, 2, -3, 0, 0, 0, 0, 0, 30, 0, mfb(bp_legs), "\
A pair of long underwear that help to maintain body temperature.");

ARMOR("skirt", 75, 120,C_PANTS, COTTON, MNULL,
@@ -3599,6 +3599,11 @@ TOOL("radio (on)", 0, 420,';', c_yellow, PLASTIC, IRON,
This radio is turned on, and continually draining its batteries. It is\n\
playing the broadcast being sent from any nearby radio towers.");

TOOL("road map", 40, 10, ';', c_yellow, MNULL, MNULL,
1, 0, 0, 0, -1, 0, 0, 0, 0, AT_NULL, itm_null, &iuse::roadmap, 0, "\
A road map. Use it to read points of interest, including, but not\n\
limited to, location(s) of hospital(s) nearby.");

TOOL("crowbar", 18, 130,';', c_ltblue, IRON, MNULL,
4, 9, 16, 3, 2, 0, 0, 0, 0, AT_NULL, itm_null, &iuse::crowbar, 0,"\
A prying tool. Use it to open locked doors without destroying them, or to\n\
@@ -1118,7 +1118,7 @@ void iuse::two_way_radio(game *g, player *p, item *it, bool t)
// > Report something to a faction
// > Call another player
mvwprintz(w, 1, 1, c_white, "1: Radio a faction for help...");
mvwprintz(w, 2, 1, c_white, "2: Call Acquaitance...");
mvwprintz(w, 2, 1, c_white, "2: Call Acquaintance...");
mvwprintz(w, 3, 1, c_white, "3: General S.O.S.");
mvwprintz(w, 4, 1, c_white, "0: Cancel");
wrefresh(w);
@@ -1241,6 +1241,39 @@ void iuse::radio_on(game *g, player *p, item *it, bool t)
}
}

void iuse::roadmap(game *g, player *p, item *it, bool t)
{
roadmap_a_target(g, p, it, t, (int)ot_hospital);
}

void iuse::roadmap_a_target(game *g, player *p, item *it, bool t, int target)
{
int dist = 0;
oter_t oter_target = oterlist[target];
point place = g->cur_om.find_closest(g->om_location(), (oter_id)target, 1, dist,
false);

int pomx = (g->levx + int(MAPSIZE / 2)) / 2; //overmap loc
int pomy = (g->levy + int(MAPSIZE / 2)) / 2; //overmap loc

if (g->debugmon) debugmsg("Map: %s at %d,%d found! You @ %d %d",oter_target.name.c_str(), place.x, place.y, pomx,pomy);

if (place.x >= 0 && place.y >= 0) {
for (int x = place.x - 3; x <= place.x + 3; x++) {
for (int y = place.y - 3; y <= place.y + 3; y++)
g->cur_om.seen(x, y) = true;
}

direction to_hospital = direction_from(pomx,pomy, place.x, place.y);
int distance = trig_dist(pomx,pomy, place.x, place.y);

g->add_msg_if_player(p, "You add a %s location to your map.", oterlist[target].name.c_str());
g->add_msg_if_player(p, "It's %d squares to the %s", distance, direction_name(to_hospital).c_str());
} else {
g->add_msg_if_player(p, "You can't find a hospital near your location.");
}
}

void iuse::picklock(game *g, player *p, item *it, bool t)
{
//CAT:
@@ -1297,7 +1330,7 @@ void iuse::picklock(game *g, player *p, item *it, bool t)
std::string sStatus = "damage";
if (it->damage >= 5) {
sStatus = "destroy";
p->i_rem(it->invlet);
it->invlet = 0; // no copy to inventory in player.cpp:4472 ->
}

g->add_msg_if_player(p,("The lock stumps your efforts to pick it, and you " + sStatus + " your tool.").c_str());
2 iuse.h
@@ -59,6 +59,8 @@ class iuse
void two_way_radio (game *g, player *p, item *it, bool t);
void radio_off (game *g, player *p, item *it, bool t);
void radio_on (game *g, player *p, item *it, bool t);
void roadmap (game *g, player *p, item *it, bool t);
void roadmap_a_target (game *g, player *p, item *it, bool t, int target);
void picklock (game *g, player *p, item *it, bool t);
void crowbar (game *g, player *p, item *it, bool t);
void makemound (game *g, player *p, item *it, bool t);
@@ -208,6 +208,7 @@ player_data @\n\
map m :\n\
missions M\n\
factions #\n\
kills )\n\
morale %\n\
messages P\n\
help ?\n\
@@ -54,11 +54,11 @@ void game::init_mapitems()
itm_whiskey, itm_bleach, itm_ammonia, itm_flour, itm_sugar, itm_salt,
itm_tea_raw, itm_coffee_raw,
NULL);

setvector(
mapitems[mi_knifeblock],
itm_knife_steak, itm_knife_butcher, itm_knife_combat, itm_pockknife,
NULL);

setvector(
mapitems[mi_knifeblock],
itm_knife_steak, itm_knife_butcher, itm_knife_combat, itm_pockknife,
NULL);

setvector(
mapitems[mi_fridge],
@@ -138,7 +138,7 @@ void game::init_mapitems()
mapitems[mi_behindcounter],
itm_aspirin, itm_caffeine, itm_cig, itm_cigar, itm_battery,
itm_shotgun_sawn, itm_mag_porn, itm_lighter, itm_flashlight,
itm_extinguisher, itm_tazer, itm_mp3, NULL);
itm_extinguisher, itm_tazer, itm_mp3, itm_roadmap, NULL);

setvector(
mapitems[mi_magazines],
@@ -988,7 +988,7 @@ void player::perform_defensive_technique(
// Special reductions for certain styles
if (weapon.typeId() == itm_style_tai_chi)
reduction -= double(0.08 * double(per_cur - 6));
if (weapon.typeId() == itm_style_taekwando)
if (weapon.typeId() == itm_style_taekwondo)
reduction -= double(0.08 * double(str_cur - 6));
if (reduction > 1.0)
reduction = 1.0;
@@ -20,6 +20,7 @@ std::string morale_data[NUM_MORALE_TYPES] = {
"Crack Cocaine Craving",

"Disliked %i",
"Ate Human Flesh",
"Ate Meat",
"Wet",
"Cold",
@@ -212,7 +212,7 @@ End of cheatery */
itype_id ma_type;
do {
int choice = menu("Pick your style:",
"Karate", "Judo", "Aikido", "Tai Chi", "Taekwando", NULL);
"Karate", "Judo", "Aikido", "Tai Chi", "Taekwondo", NULL);
if (choice == 1)
ma_type = itm_style_karate;
if (choice == 2)
@@ -222,7 +222,7 @@ End of cheatery */
if (choice == 4)
ma_type = itm_style_tai_chi;
if (choice == 5)
ma_type = itm_style_taekwando;
ma_type = itm_style_taekwondo;
item tmpitem = item(g->itypes[ma_type], 0);
full_screen_popup(tmpitem.info(true).c_str());
} while (!query_yn("Use this style?"));
@@ -820,17 +820,23 @@ To save this character as a template, press !.");


if (ch == '>') {
if (points > 0)
mvwprintz(w, 3, 2, c_red, "\
Points left: %d You must use the rest of your points!", points);
else if (u->name.size() == 0) {
if (points > 0 && query_yn("Remaining points will be discarded, are you sure you want to proceed?")) {
if (u->name.size() == 0) {
mvwprintz(w, 6, 8, h_ltgray, "______NO NAME ENTERED!!!!_____");
noname = true;
wrefresh(w);
if (query_yn("Are you SURE you're finished? Your name will be randomly generated."))
u->pick_name();
return 1;
} else
return 1;
} else if (u->name.size() == 0)
mvwprintz(w, 6, 8, h_ltgray, "______NO NAME ENTERED!!!!_____");
noname = true;
wrefresh(w);
if (query_yn("Are you SURE you're finished? Your name will be randomly generated.")){
u->pick_name();
return 1;
}
} else if (query_yn("Are you SURE you're finished?"))
return 1;
else
@@ -1,3 +1,4 @@
#include "game.h"
#include "options.h"
#include "output.h"
#include "keypress.h"
@@ -13,22 +14,32 @@ bool option_is_bool(option_key id);
void create_default_options();
std::string options_header();

void show_options()
void game::show_options()
{
erase();
int iMaxX = (VIEWX < 12) ? 80 : (VIEWX*2)+56;
int iMaxY = (VIEWY < 12) ? 25 : (VIEWY*2)+1;

WINDOW* w_options_border = newwin(25, 80, (iMaxY > 25) ? (iMaxY-25)/2 : 0, (iMaxX > 80) ? (iMaxX-80)/2 : 0);
WINDOW* w_options = newwin(23, 78, 1 + (int)((iMaxY > 25) ? (iMaxY-25)/2 : 0), 1 + (int)((iMaxX > 80) ? (iMaxX-80)/2 : 0));

int offset = 1;
int line = 0;
char ch = ' ';
bool changed_options = false;
bool needs_refresh = true;
do {
wborder(w_options_border, LINE_XOXO, LINE_XOXO, LINE_OXOX, LINE_OXOX,
LINE_OXXO, LINE_OOXX, LINE_XXOO, LINE_XOOX);
mvwprintz(w_options_border, 0, 36, c_ltred, " OPTIONS ");
wrefresh(w_options_border);

// TODO: change instructions
if (needs_refresh) {
erase();
mvprintz(0, 40, c_white, "Use up/down keys to scroll through");
mvprintz(1, 40, c_white, "available options.");
mvprintz(2, 40, c_white, "Use left/right keys to toggle.");
mvprintz(3, 40, c_white, "Press ESC or q to return. ");
werase(w_options);
mvwprintz(w_options, 0, 40, c_white, "Use up/down keys to scroll through");
mvwprintz(w_options, 1, 40, c_white, "available options.");
mvwprintz(w_options, 2, 40, c_white, "Use left/right keys to toggle.");
mvwprintz(w_options, 3, 40, c_white, "Press ESC or q to return. ");
// highlight options for option descriptions
std::string tmp = option_desc(option_key(offset + line));
std::string out;
@@ -37,7 +48,7 @@ void show_options()
do {
pos = tmp.find_first_of('\n');
out = tmp.substr(0, pos);
mvprintz(displayline, 40, c_white, out.c_str());
mvwprintz(w_options, displayline, 40, c_white, out.c_str());
tmp = tmp.substr(pos + 1);
displayline++;
} while (pos != std::string::npos && displayline < 12);
@@ -46,32 +57,32 @@ void show_options()

// Clear the lines
for (int i = 0; i < 25; i++)
mvprintz(i, 0, c_black, " ");
mvwprintz(w_options, i, 0, c_black, " ");
int valid_option_count = 0;

// display options
for (int i = 0; i < 25 && offset + i < NUM_OPTION_KEYS; i++)
{
valid_option_count++;
mvprintz(i, 0, c_white, "%s: ",
mvwprintz(w_options, i, 0, c_white, "%s: ",
option_name( option_key(offset + i) ).c_str());

if (option_is_bool(option_key(offset + i)))
{
bool on = OPTIONS[ option_key(offset + i) ];
if (i == line)
mvprintz(i, 30, hilite(c_ltcyan), (on ? "True" : "False"));
mvwprintz(w_options, i, 30, hilite(c_ltcyan), (on ? "True" : "False"));
else
mvprintz(i, 30, (on ? c_ltgreen : c_ltred), (on ? "True" : "False"));
} else
{
mvwprintz(w_options, i, 30, (on ? c_ltgreen : c_ltred), (on ? "True" : "False"));
} else {
char option_val = OPTIONS[ option_key(offset + i) ];
if (i == line)
mvprintz(i, 30, hilite(c_ltcyan), "%d", option_val );
mvwprintz(w_options, i, 30, hilite(c_ltcyan), "%d", option_val );
else
mvprintz(i, 30, c_ltgreen, "%d", option_val );
mvwprintz(w_options, i, 30, c_ltgreen, "%d", option_val );
}
}
wrefresh(w_options);
refresh();
ch = input();
needs_refresh = true;
@@ -117,7 +128,7 @@ void show_options()

if (changed_options && query_yn("Save changes?"))
save_options();
erase();
werase(w_options);
}

void load_options()
@@ -269,8 +280,8 @@ std::string option_desc(option_key key)
case OPT_DELETE_WORLD: return "Delete saves upon player death\n0 - no\n1 - yes\n2 - query";
case OPT_INITIAL_POINTS: return "Initial points available on character\ngeneration. Default is 6";
case OPT_INITIAL_TIME: return "Initial starting time of day on character\ngeneration. Default is 8:00";
case OPT_VIEWPORT_X: return "Set the expansion of the viewport along\nthe X axis. Must restart for changes\nto take effect. Default is 12";
case OPT_VIEWPORT_Y: return "Set the expansion of the viewport along\nthe Y axis. Must restart for changes\nto take effect. Default is 12";
case OPT_VIEWPORT_X: return "WINDOWS ONLY: Set the expansion of the viewport along\nthe X axis. Must restart for changes\nto take effect. Default is 12. POSIX\nsystems will use terminal size at startup.";
case OPT_VIEWPORT_Y: return "WINDOWS ONLY: Set the expansion of the viewport along\nthe Y axis. Must restart for changes\nto take effect. Default is 12. POSIX\nsystems will use terminal size at startup.";
case OPT_STATIC_SPAWN: return "Spawn zombies at game start instead of\nduring game. Must delete save directory\nafter changing for it to take effect.\nDefault is F";
default: return " ";
}
@@ -64,5 +64,6 @@ long special_symbol (char sym);
std::string word_rewrap (const std::string &in, int width);

void draw_tab(WINDOW *w, int iOffsetX, std::string sText, bool bSelected);
void clear_window(WINDOW* w);

#endif
@@ -345,6 +345,153 @@ void player::update_morale()
}
}

/* Here lies the intended effects of body temperature

Assumption 1 : a naked person is comfortable at 31C/87.8F.
Assumption 2 : a "lightly clothed" person is comfortable at 25C/77F.
Assumption 3 : frostbite cannot happen above 0C temperature.*
* In the current model, a naked person can get frostbite at 1C. This isn't true, but it's a compromise with using nice whole numbers.

Here is a list of warmth values and the corresponding temperatures in which the player is comfortable, and in which the player is very cold.

Warmth Temperature (Comfortable) Temperature (Very cold) Notes
0 31C / 87.8F 1C / 33.8F * Naked
10 25C / 77.0F -5C / 23.0F * Lightly clothed
20 19C / 66.2F -11C / 12.2F
30 13C / 55.4F -17C / 1.4F
40 7C / 44.6F -23C / -9.4F
50 1C / 33.8F -29C / -20.2F
60 -5C / 23.0F -35C / -31.0F
70 -11C / 12.2F -41C / -41.8F
80 -17C / 1.4F -47C / -52.6F
90 -23C / -9.4F -53C / -63.4F
100 -29C / -20.2F -59C / -74.2F

*/

void player::update_bodytemp(game *g) // TODO bionics, diseases and humidity (not in yet) can affect body temp.
{
// NOTE : visit weather.h for some details on the numbers used
// Converts temperature to Celsius/10(Wito plans on using degrees Kelvin later)
int Ctemperature = 100*(g->temperature - 32) * 5/9;
// Temperature norms
const int ambient_norm = 3100;
// This adjusts the temperature scale to match the bodytemp scale
int adjusted_temp = (Ctemperature - ambient_norm);
// Creative thinking for clean morale penalties: this gets incremented in the for loop and applied after the loop
int morale_pen = 0;
// Fetch the morale value of wetness for bodywetness
int bodywetness = 0;
for (int i = 0; bodywetness == 0 && i < morale.size(); i++)
if( morale[i].type == MORALE_WET ) {
bodywetness = abs(morale[i].bonus); // Make it positive, less confusing
break;
}
// Current temperature and converging temperature calculations
for (int i = 0 ; i < num_bp ; i++){
if (i == bp_eyes) continue; // Skip eyes
// Represents the fact that the body generates heat when it is cold. TODO : should this increase hunger?
float homeostasis_adjustement = (temp_cur[i] > BODYTEMP_NORM ? 40.0 : 60.0);
int clothing_warmth_adjustement = homeostasis_adjustement * (float)warmth(body_part(i)) * (1.0 - (float)bodywetness / 100.0);
// Disease name shorthand
int blister_pen = dis_type(DI_BLISTERS) + 1 + i, hot_pen = dis_type(DI_HOT) + 1 + i;
int cold_pen = dis_type(DI_COLD)+ 1 + i, frost_pen = dis_type(DI_FROSTBITE) + 1 + i;
// Convergeant temperature is affected by ambient temperature, clothing warmth, and body wetness.
signed int temp_conv = BODYTEMP_NORM + adjusted_temp + clothing_warmth_adjustement;
// Fatigue also affects convergeant temperature
if (!has_disease(DI_SLEEP)) temp_conv -= 10*fatigue/6;
else {
int vpart = -1;
vehicle *veh = g->m.veh_at (posx, posy, vpart);
if (g->m.ter(posx, posy) == t_bed) temp_conv += 1000;
else if (g->m.ter(posx, posy) == t_makeshift_bed) temp_conv += 500;
else if (g->m.tr_at(posx, posy) == tr_cot) temp_conv -= 500;
else if (g->m.tr_at(posx, posy) == tr_rollmat) temp_conv -= 1000;
else if (veh && veh->part_with_feature (vpart, vpf_seat) >= 0) temp_conv += 200;
else if (veh && veh->part_with_feature (vpart, vpf_bed) >= 0) temp_conv += 300;
else temp_conv -= 2000;
}
// Convection heat sources : generates body heat, helps fight frostbite
int blister_count = 0; // If the counter is high, your skin starts to burn
for (int j = -6 ; j <= 6 ; j++){
for (int k = -6 ; k <= 6 ; k++){
// Bizarre workaround for g->u_see() and friends not taking const arguments.
int l = std::max(j, k);
int heat_intensity = 0;
if(g->m.field_at(posx + j, posy + k).type == fd_fire)
heat_intensity = g->m.field_at(posx + j, posy + k).density;
else if (g->m.tr_at(posx + j, posy + k) == tr_lava )
heat_intensity = 3;
if (heat_intensity > 0 && g->u_see(posx + j, posy + k, l)) {
// Ensure fire_dist >=1 to avoid divide-by-zero errors.
int fire_dist = std::max(1, std::max(j, k));
if (frostbite_timer[i] > 0) frostbite_timer[i] -= heat_intensity - fire_dist / 2;
temp_conv += 50 * heat_intensity / (fire_dist * fire_dist);
blister_count += heat_intensity / (fire_dist * fire_dist);
}
}
}
// TODO Balance bionics
// Bionic "Internal Climate Control" says it is effective from 0F to 140F, these are the corresponding bodytemp values
if (has_bionic(bio_climate) && g->temperature > 0 && g->temperature < 140)
temp_conv = (9*BODYTEMP_NORM + temp_conv)/10; // Bionic "eases" the effects
// Bionic "Thermal Dissapation" says it prevents fire damage up to 2000F. 500 is picked at random...
if (has_bionic(bio_heatsink) && blister_count < 500)
blister_count = 0;
// Skin gets blisters from intense heat exposure.
if (blister_count - 10*resist(body_part(i)) > 20) add_disease(dis_type(blister_pen), 1, g);
// Increments current body temperature towards convergant.
int temp_difference = temp_cur[i] - temp_conv;
int temp_before = temp_cur[i];
// Bodytemp equalization code
if (i == bp_torso){temp_equalizer(bp_torso, bp_arms); temp_equalizer(bp_torso, bp_legs); temp_equalizer(bp_torso, bp_head);}
else if (i == bp_head) {temp_equalizer(bp_head, bp_eyes); temp_equalizer(bp_head, bp_mouth);}
else if (i == bp_arms) temp_equalizer(bp_arms, bp_hands);
else if (i == bp_legs) temp_equalizer(bp_legs, bp_feet);
if (temp_cur[i] != temp_conv) temp_cur[i] = temp_difference*exp(-0.002) + temp_conv; // It takes half an hour for bodytemp to converge half way to its convergeance point (think half-life)
int temp_after = temp_cur[i];
// Penalties
if (temp_cur[i] < BODYTEMP_FREEZING) {add_disease(dis_type(cold_pen), 1, g, 3, 3); frostbite_timer[i] += 3;}
else if (temp_cur[i] < BODYTEMP_VERY_COLD) {add_disease(dis_type(cold_pen), 1, g, 2, 3); frostbite_timer[i] += 2;}
else if (temp_cur[i] < BODYTEMP_COLD) {add_disease(dis_type(cold_pen), 1, g, 1, 3); frostbite_timer[i] += 1;} // Frostbite timer does not go down if you are still cold.
else if (temp_cur[i] > BODYTEMP_SCORCHING) {add_disease(dis_type(hot_pen), 1, g, 3, 3); } // If body temp rises over 15000, disease.cpp (DI_HOT_HEAD) acts weird and the player will die
else if (temp_cur[i] > BODYTEMP_VERY_HOT) {add_disease(dis_type(hot_pen), 1, g, 2, 3); }
else if (temp_cur[i] > BODYTEMP_HOT) {add_disease(dis_type(hot_pen), 1, g, 1, 3); }
// Morale penalties : a negative morale_pen means the player is cold
// Intensity multiplier is negative for cold, positive for hot
int intensity_mult = -disease_intensity(dis_type(cold_pen)) + disease_intensity(dis_type(hot_pen));
if (has_disease(dis_type(cold_pen)) > 0 || has_disease(dis_type(hot_pen)) > 0) {
switch (i) {
case bp_head :
case bp_torso :
case bp_mouth : morale_pen += 2*intensity_mult;
case bp_arms :
case bp_legs : morale_pen += 1*intensity_mult;
case bp_hands:
case bp_feet : morale_pen += 1*intensity_mult;
}
}
// Frostbite (level 1 after 2 hours, level 2 after 4 hours)
if (frostbite_timer[i] > 0) frostbite_timer[i]--;
if (frostbite_timer[i] >= 240) {
if (disease_intensity(dis_type(frost_pen)) < 2 && i == bp_mouth) g->add_msg("Your %s hardens from the frostbite!", body_part_name(body_part(i), -1).c_str());
else if (disease_intensity(dis_type(frost_pen)) < 2 && (i == bp_hands || i == bp_feet)) g->add_msg("Your %s harden from the frostbite!", body_part_name(body_part(i), -1).c_str());
add_disease(dis_type(frost_pen), 1, g, 2, 2);}
else if (frostbite_timer[i] >= 120) {
if (!has_disease(dis_type(frost_pen))) g->add_msg("You lose sensation in your %s.", body_part_name(body_part(i), -1).c_str());
add_disease(dis_type(frost_pen), 1, g, 1, 2);}
// Warn the player if condition worsens
if (temp_before > BODYTEMP_FREEZING && temp_after < BODYTEMP_FREEZING) g->add_msg("You feel your %s beginning to go numb from the cold!", body_part_name(body_part(i), -1).c_str());
else if (temp_before > BODYTEMP_VERY_COLD && temp_after < BODYTEMP_VERY_COLD) g->add_msg("You feel your %s getting very cold.", body_part_name(body_part(i), -1).c_str());
else if (temp_before > BODYTEMP_COLD && temp_after < BODYTEMP_COLD) g->add_msg("You feel your %s getting cold.", body_part_name(body_part(i), -1).c_str());
else if (temp_before < BODYTEMP_SCORCHING && temp_after > BODYTEMP_SCORCHING) g->add_msg("You feel your %s getting red hot from the heat!", body_part_name(body_part(i), -1).c_str());
else if (temp_before < BODYTEMP_VERY_HOT && temp_after > BODYTEMP_VERY_HOT) g->add_msg("You feel your %s getting very hot.", body_part_name(body_part(i), -1).c_str());
else if (temp_before < BODYTEMP_HOT && temp_after > BODYTEMP_HOT) g->add_msg("You feel your %s getting hot.", body_part_name(body_part(i), -1).c_str());
}
// Morale penalties, updated at the same rate morale is
if (morale_pen < 0 && int(g->turn) % 10 == 0) add_morale(MORALE_COLD, -2, -abs(morale_pen));
if (morale_pen > 0 && int(g->turn) % 10 == 0) add_morale(MORALE_HOT, -2, -abs(morale_pen));
}

void player::temp_equalizer(body_part bp1, body_part bp2)
{
@@ -62,6 +62,7 @@ class player {

void reset(game *g = NULL);// Resets movement points, stats, applies effects
void update_morale(); // Ticks down morale counters and removes them
void update_bodytemp(game *g); // Maintains body temperature
int current_speed(game *g = NULL); // Number of movement points we get a turn
int run_cost(int base_cost); // Adjust base_cost
int swim_speed(); // Our speed when swimming
@@ -26,18 +26,18 @@ enum dis_type {
DI_NULL,
// Weather
DI_GLARE, DI_WET,
// Temperature, the order is important
// Temperature, the order is important (dependant on bodypart.h)
DI_COLD,
DI_COLD_HEAD, DI_COLD_EYES, DI_COLD_MOUTH, DI_COLD_TORSO,
DI_COLD_TORSO, DI_COLD_HEAD, DI_COLD_EYES, DI_COLD_MOUTH,
DI_COLD_ARMS, DI_COLD_HANDS, DI_COLD_LEGS, DI_COLD_FEET,
DI_FROSTBITE,
DI_FROSTBITE_HEAD, DI_FROSTBITE_EYES, DI_FROSTBITE_MOUTH, DI_FROSTBITE_TORSO,
DI_FROSTBITE_TORSO, DI_FROSTBITE_HEAD, DI_FROSTBITE_EYES, DI_FROSTBITE_MOUTH,
DI_FROSTBITE_ARMS, DI_FROSTBITE_HANDS, DI_FROSTBITE_LEGS, DI_FROSTBITE_FEET,
DI_HOT,
DI_HOT_HEAD, DI_HOT_EYES, DI_HOT_MOUTH, DI_HOT_TORSO,
DI_HOT_TORSO, DI_HOT_HEAD, DI_HOT_EYES, DI_HOT_MOUTH,
DI_HOT_ARMS, DI_HOT_HANDS, DI_HOT_LEGS, DI_HOT_FEET,
DI_BLISTERS,
DI_BLISTERS_HEAD, DI_BLISTERS_EYES, DI_BLISTERS_MOUTH, DI_BLISTERS_TORSO,
DI_BLISTERS_TORSO, DI_BLISTERS_HEAD, DI_BLISTERS_EYES, DI_BLISTERS_MOUTH,
DI_BLISTERS_ARMS, DI_BLISTERS_HANDS, DI_BLISTERS_LEGS, DI_BLISTERS_FEET,
// Diseases
DI_INFECTION,
@@ -445,8 +445,8 @@ For your whole life you've been forbidden from indulging in your peculiar\n\
tastes. Now the world's ended, and you'll be damned if anyone is going to\n\
tell you you can't eat people."},
{"Martial Arts Training", 3, 0, 0, "\
You have receives some martial arts training at a local dojo. You will start\n\
with your choice of karate, judo, aikido, tai chi, or taekwando."},
You have received some martial arts training at a local dojo. You will start\n\
with your choice of karate, judo, aikido, tai chi, or taekwondo."},

{"NULL", 0, 0, 0, " -------------------------------------------------- "},