From: Martin Read Date: Sun, 9 Feb 2014 02:31:58 +0000 (+0000) Subject: Big-ball-of-mud commit because of a UI landmark X-Git-Tag: printmsg-purged~3 X-Git-Url: http://git.blackswordsonics.com/?a=commitdiff_plain;h=bfd4803d7a4242d027e0bb53abde857168f4fa04;p=victrix-abyssi Big-ball-of-mud commit because of a UI landmark --- diff --git a/MANIFEST b/MANIFEST index 9825dbe..f8cff9b 100644 --- a/MANIFEST +++ b/MANIFEST @@ -12,6 +12,7 @@ display.hh display-nc.cc fov.cc fov.hh +log.cc main.cc map.cc map.hh diff --git a/Makefile b/Makefile index d84034e..b7843f6 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ include features.mk GENERATED_OBJS:=permobj.o GENERATED_SOURCE:=permobj.cc pobj_id.hh dirs.mk features.mk -HANDWRITTEN_OBJS:=combat.o display-nc.o fov.o main.o map.o misc.o monsters.o mon2.o notify-local-tty.o objects.o permons.o pmon2.o rng.o sorcery.o u.o +HANDWRITTEN_OBJS:=combat.o display-nc.o fov.o log.o main.o map.o misc.o monsters.o mon2.o notify-local-tty.o objects.o permons.o pmon2.o rng.o sorcery.o u.o OBJS:=$(GENERATED_OBJS) $(HANDWRITTEN_OBJS) GAME:=victrix-abyssi MAJVERS:=1 @@ -41,6 +41,9 @@ my-debworkflow: debianize-archive cp -R debian archive-test/$(ARCHIVEDIR)/debian (cd archive-test/$(ARCHIVEDIR) && debuild -uc -us) +my-debclean: + -rm -rf archive-test + clean: -rm -rf $(ARCHIVEDIR) -rm -f *.o $(GAME) *.tar.gz @@ -48,6 +51,9 @@ clean: code-docs: doxygen Doxyfile +code-docs-clean: + -rm -rf html latex + install: all echo "man6dir is $(man6dir)" install -D $(GAME) $(DESTDIR)$(gamesdir)/$(GAME) diff --git a/combat.cc b/combat.cc index 0d87f00..ca028f4 100644 --- a/combat.cc +++ b/combat.cc @@ -225,20 +225,23 @@ test_unaffected: switch (dtype) { case DT_PHYS: + case DT_HELLFIRE: + case DT_KNOCKBACK: debug_player_resists_phys(); unaffected = 0; /* Turn off the player's resistance, because they're * not supposed to have it! */ - u.resistances[DT_PHYS] = 0; + u.resistances[dtype] = 0; goto test_unaffected; case DT_FIRE: - print_msg("The flames seem pleasantly warm.\n"); - break; case DT_COLD: - print_msg("Its touch seems pleasantly cool.\n"); - break; case DT_NECRO: - print_msg("Its touch makes you feel no deader.\n"); + case DT_POISON: + case DT_DROWNING: + case DT_ELEC: + /* these are the only damage types you should be completely + * ignoring right now */ + notify_player_ignore_damage(dtype); break; default: debug_bad_damage_type(dtype); @@ -247,22 +250,7 @@ test_unaffected: } else { - switch (dtype) - { - default: - case DT_PHYS: - break; - case DT_FIRE: - print_msg("You are engulfed in flames.\n"); - break; - case DT_COLD: - print_msg("You are covered in frost.\n"); - break; - case DT_NECRO: - print_msg("You feel your life force slipping away.\n"); - break; - } - print_msg("You take %d damage.\n", damage); + notify_player_touch_effect(dtype); if ((mptr->mon_id == PM_VAMPIRE) && !u.resists(DT_NECRO)) { heal_mon(mon, damage * 2 / 5, 1); @@ -341,7 +329,6 @@ int mshootu(int mon) if (!unaffected) { damage = one_die(mptr->rdam); - print_msg("You take %d damage.\n", damage); damage_u(damage, DEATH_KILLED_MON, permons[mptr->mon_id].name); } display_update(); @@ -402,84 +389,5 @@ int mshootu(int mon) return 0; } -#if 0 -static void flask_effect_weakness(Mon *mptr) -{ - if (!pmon_is_undead(mptr->mon_id)) - { - print_msg("Your foe shrivels and twists horribly.\n"); - mptr->hpmax = (mptr->hpmax + 1) / 2; - mptr->hpcur = (mptr->hpcur + 1) / 2; - } - else - { - print_msg("What does not live cannot degenerate.\n"); - } -} - -static void flask_effect_poison(Mon *mptr) -{ - print_msg("Your foe is drenched in contact poison.\n"); - if (!pmon_resists_poison(mptr->mon_id)) - { - damage_mon(mptr - monsters, inclusive_flat((mptr->hpmax + 5) / 6, (mptr->hpmax + 2) / 3), true); - } - else - { - print_msg("... to little effect.\n"); - } -} -static void flask_effect_fire(Mon *mptr) -{ - print_msg("Your foe is drenched in burning oil.\n"); - if (!pmon_resists_fire(mptr->mon_id)) - { - damage_mon(mptr - monsters, 15 + dice(5, 4), true); - } - else - { - print_msg("... to little effect.\n"); - } -} -#endif - -Action_cost throw_flask(int obj, Offset step) -{ - int i; - Coord c; - int mon; - void (*flask_effect)(Mon *); - switch (objects[obj].obj_id) - { -#if 0 - case PO_FLASK_WEAKNESS: - flask_effect = flask_effect_weakness; - break; - case PO_FLASK_POISON: - flask_effect = flask_effect_poison; - break; - case PO_FLASK_FIRE: - flask_effect = flask_effect_fire; - break; -#endif - default: - debug_throw_non_flask(); - return Cost_none; - } - for ((i = 1), (c = u.pos + step); i < 10; ++i, (c += step)) - { - mon = lvl.mon_at(c); - if ((mon != NO_MON) && (monsters[mon].used)) - { - Mon *mptr = monsters + mon; - flask_effect(mptr); - consume_obj(obj); - return Cost_std; - } - } - notify_no_flask_target(); - return Cost_std; -} - /* combat.cc */ // vim:cindent diff --git a/default.permobjs b/default.permobjs index 74640d4..e519f96 100644 --- a/default.permobjs +++ b/default.permobjs @@ -354,6 +354,8 @@ POWER2 10 DEPTH 24 DRESS +# Mr Read would like to note that he invented the following item and its +# game mechanics *before* becoming aware of the anime series _Kill la Kill_. ARMOUR set of ribbons PLURAL sets of ribbons DESC These ribbons, arranged as if to form an alleged garment, make your fingers tingle with magic. diff --git a/display-nc.cc b/display-nc.cc index c5af9f0..87d9128 100644 --- a/display-nc.cc +++ b/display-nc.cc @@ -49,18 +49,18 @@ WINDOW *status_window; WINDOW *world_window; WINDOW *message_window; WINDOW *inventory_window; +WINDOW *fullscreen_window; /* discrete from stdscr because *twitch* */ PANEL *status_panel; PANEL *world_panel; PANEL *message_panel; PANEL *inventory_panel; +PANEL *fullscreen_panel; bool force_ascii; -int wall_colour; -int you_colour; -int status_updated; -int map_updated; -int show_terrain; -int hard_redraw; +bool status_updated; +bool map_updated; +bool show_terrain; +bool hard_redraw; struct Attr_wrapper { @@ -112,12 +112,26 @@ static cchar_t const *monster_char(int monster_id); static cchar_t const *terrain_char(Terrain terrain_type); static void draw_status_line(void); static void draw_world(void); +static void draw_main_menu(void); +static void run_main_menu(void); /* Static funcs */ +static void draw_main_menu(void) +{ + mvwprintw(fullscreen_window, 1, 18, "----====< Victrix Abyssi >====----\n"); + mvwprintw(fullscreen_window, 3, 20, "A roguelike game by Martin Read\n"); + mvwprintw(fullscreen_window, 10, 25, "S)tart new game\n"); + mvwprintw(fullscreen_window, 11, 25, "R)esume existing game\n"); + mvwprintw(fullscreen_window, 12, 25, "Q)uit\n"); + show_panel(fullscreen_panel); + update_panels(); + doupdate(); +} + static void draw_status_line(void) { mvwprintw(status_window, 0, 0, "%-16.16s", u.name); - mvwprintw(status_window, 0, 17, "HP: %03d/%03d", u.hpcur, u.hpmax); + mvwprintw(status_window, 0, 17, "HP: %3d/%03d", u.hpcur, u.hpmax); mvwprintw(status_window, 0, 30, "BOD: %02d/%02d", u.body - u.bdam, u.body); mvwprintw(status_window, 0, 62, "Exp: %d/%d", u.level, u.experience); mvwprintw(status_window, 1, 0, "DEF: %2d", u.defence); @@ -377,7 +391,7 @@ void display_update(void) * * \todo Rename this and put appropriate glue elsewhere to make it work */ -int display_init(void) +int launch_user_interface(void) { int i; int j; @@ -433,8 +447,6 @@ int display_init(void) init_pair(Gcol_d_grey, COLOR_BLACK, COLOR_BLACK); init_pair(Gcol_purple, COLOR_MAGENTA, COLOR_BLACK); init_pair(Gcol_cyan, COLOR_CYAN, COLOR_BLACK); - wall_colour = Gcol_brown; - you_colour = Gcol_white; for (i = 0; i < DISP_HEIGHT; ++i) { for (j = 0; j < DISP_WIDTH; ++j) @@ -459,30 +471,41 @@ int display_init(void) message_panel = new_panel(message_window); inventory_window = newwin(DISP_HEIGHT, 58, 0, DISP_WIDTH + 1); inventory_panel = new_panel(inventory_window); + fullscreen_window = newwin(24, 80, 0, 0); + fullscreen_panel = new_panel(fullscreen_window); wattr_set(inventory_window, colour_data[Gcol_l_grey].attr, colour_data[Gcol_l_grey].cpair, NULL); hide_panel(inventory_panel); clear(); + curs_set(0); wclear(status_window); wclear(world_window); wclear(message_window); wclear(inventory_window); + wclear(fullscreen_window); scrollok(status_window, FALSE); scrollok(world_window, FALSE); scrollok(message_window, TRUE); scrollok(inventory_window, FALSE); + scrollok(fullscreen_window, FALSE); idcok(status_window, FALSE); idcok(world_window, FALSE); idcok(message_window, FALSE); idcok(inventory_window, FALSE); - mvwprintw(world_window, 7, 4, "Victrix Abyssi"); - //mvwprintw(world_window, 9, 2, "Seven-Day Roguelike"); - mvwprintw(world_window, 11, 4, "By Martin Read"); + idcok(fullscreen_window, FALSE); wmove(message_window, 0, 0); map_updated = FALSE; status_updated = FALSE; - update_panels(); - doupdate(); + draw_main_menu(); + run_main_menu(); + if (!game_finished) + { + status_updated = 1; + map_updated = 1; + hard_redraw = 1; + display_update(); + // WIP launch the game logic. + } return 0; } @@ -491,10 +514,12 @@ int display_init(void) int read_input(char *buffer, int length) { echo(); + curs_set(1); display_update(); buffer[0] = '\0'; wgetnstr(message_window, buffer, length); noecho(); + curs_set(0); return strlen(buffer); } @@ -974,10 +999,6 @@ void get_player_action(Action *act) } } break; - /*case 't': - // nobbling flask-throwing for now, as part of the general - // revisting of ranged combat. - return THROW_FLASK;*/ case 'w': { int slot = inv_select(POCLASS_WEAPON, "wield", 1); @@ -1179,7 +1200,6 @@ void print_help(void) print_msg("W wear armour\n"); print_msg("T take off armour\n"); print_msg("w wield a weapon\n"); - print_msg("t throw a flask\n"); print_msg("r read a scroll\n"); print_msg("q quaff a potion\n"); print_msg("e eat something edible\n"); @@ -1247,5 +1267,85 @@ void touch_one_screen(Coord c) } } +bool query_wizmode_death(void) +{ + int yn = getyn("Really die?"); + if (yn != 1) + { + print_msg("You survived that attempt on your life."); + return false; + } + return true; +} + +static void show_game_view(void) +{ + hide_panel(fullscreen_panel); + update_panels(); + doupdate(); + display_update(); +} + +static void run_main_menu(void) +{ + bool done = false; + char name[16]; + do + { + int ch = wgetch(fullscreen_window); + switch (ch) + { + case 's': + case 'S': + mvwprintw(fullscreen_window, 10, 1, "\n"); + mvwprintw(fullscreen_window, 11, 1, "\n"); + mvwprintw(fullscreen_window, 12, 1, "\n"); + mvwprintw(fullscreen_window, 13, 15, "Welcome, Princess. Remind me of your name?\n"); + mvwprintw(fullscreen_window, 14, 1, "\n"); + wmove(fullscreen_window, 14, 30); + update_panels(); + doupdate(); + echo(); + curs_set(1); + mvwgetnstr(fullscreen_window, 14, 30, name, 16); + curs_set(0); + noecho(); + new_game(name); + done = true; + show_game_view(); + break; + case 'c': + case 'C': + // TODO implement configuration management + break; + case 'r': + case 'R': + // TODO implement loading properly. + { + int i; + i = load_game(); + if (i != -1) + { + done = true; + } + else + { + mvwprintw(fullscreen_window, 14, 30, "No saved game found.\n"); + } + } + break; + case 'q': + case 'Q': + done = true; + game_finished = true; + break; + } + } while (!done); + if (!game_finished) + { + show_game_view(); + } +} + /* display-nc.cc */ // vim:cindent diff --git a/display.hh b/display.hh index 0a61376..5ca99b2 100644 --- a/display.hh +++ b/display.hh @@ -32,7 +32,7 @@ extern void print_msg(char const *fmt, ...); extern int read_input(char *buffer, int length); extern void print_help(void); -extern int display_init(void); +extern int launch_user_interface(void); extern void display_update(void); extern int display_shutdown(void); extern void newsym(Coord c); @@ -46,13 +46,14 @@ extern void press_enter(void); extern void pressanykey(void); extern void show_discoveries(void); extern void touch_one_screen(Coord c); +extern bool query_wizmode_death(void); /* "I've changed things that need to be redisplayed" flags. */ -extern int hard_redraw; -extern int status_updated; -extern int map_updated; +extern bool hard_redraw; +extern bool status_updated; +extern bool map_updated; /* "Show the player the terrain only" flag. */ -extern int show_terrain; +extern bool show_terrain; #endif diff --git a/log.cc b/log.cc new file mode 100644 index 0000000..86f60b4 --- /dev/null +++ b/log.cc @@ -0,0 +1,75 @@ +/* \file log.cc + * \brief death log handler for Victrix Abyssi + */ + +/* Copyright 2014 Martin Read + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "victrix-abyssi.hh" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void log_death(Death d, char const *what) +{ + FILE *fp; + fp = fopen("victrix-abyssi.log", "a"); + if (fp) + { + switch (d) + { + case DEATH_KILLED: + fprintf(fp, "%s was killed by %s.\n", u.name, what); + break; + case DEATH_KILLED_MON: + fprintf(fp, "%s was killed by a nasty %s.\n", u.name, what); + break; + case DEATH_BODY: + fprintf(fp, "%s's heart was stopped by %s.\n", u.name, what); + break; + case DEATH_AGILITY: + fprintf(fp, "%s's nerves were destroyed by %s.\n", u.name, what); + break; + case DEATH_LASH: + fprintf(fp, "%s tasted the lash one time too many.\n", u.name); + break; + case DEATH_RIBBONS: + fprintf(fp, "%s looked good in ribbons.\n", u.name); + break; + } + fprintf(fp, " %s died after %d ticks, with %d XP, on dungeon level %d.\n\n", u.name, game_tick, u.experience, depth); + fflush(fp); + fclose(fp); + } +} + +/* log.cc */ +// vim:cindent:expandtab diff --git a/main.cc b/main.cc index 82fc23b..e94aa5a 100644 --- a/main.cc +++ b/main.cc @@ -52,8 +52,6 @@ Offset const Northwest = { -1, -1 }; Offset const Stationary = { 0, 0 }; void save_game(void); -static void load_game(void); -static void new_game(void); static void rebuild_mapobjs(void); static void rebuild_mapmons(void); static void main_loop(void); @@ -112,18 +110,16 @@ void save_game(void) FILE *fp; uint32_t tmp; fp = fopen("victrix-abyssi.sav", "wb"); - serialize_level(fp, &lvl); - fwrite(permobjs, NUM_OF_PERMOBJS, sizeof (Permobj), fp); - fwrite(monsters, MONSTERS_IN_PLAY, sizeof (Mon), fp); - fwrite(objects, OBJECTS_IN_PLAY, sizeof (Obj), fp); - /* Write out the depth */ - tmp = htonl(depth); - fwrite(&tmp, 1, sizeof tmp, fp); /* Write out the player. */ fwrite(&u, 1, sizeof u, fp); - /* Write out the tick number */ + tmp = htonl(depth); + fwrite(&tmp, 1, sizeof tmp, fp); tmp = htonl(game_tick); fwrite(&tmp, 1, sizeof tmp, fp); + serialize_level(fp, &lvl); + fwrite(permobjs, sizeof (Permobj), NUM_OF_PERMOBJS, fp); + fwrite(monsters, sizeof (Mon), MONSTERS_IN_PLAY, fp); + fwrite(objects, sizeof (Obj), OBJECTS_IN_PLAY, fp); /* Clean up */ fflush(fp); fclose(fp); @@ -131,28 +127,32 @@ void save_game(void) return; } -void load_game(void) +int load_game(void) { FILE *fp; fp = fopen("victrix-abyssi.sav", "rb"); + if (!fp) + { + return -1; + } + wrapped_fread(&u, 1, sizeof u, fp); + wrapped_fread(&depth, 1, sizeof depth, fp); + depth = ntohl(depth); + wrapped_fread(&game_tick, 1, sizeof game_tick, fp); + game_tick = ntohl(game_tick); deserialize_level(fp, &lvl); wrapped_fread(permobjs, sizeof (Permobj), NUM_OF_PERMOBJS, fp); wrapped_fread(monsters, sizeof (Mon), MONSTERS_IN_PLAY, fp); wrapped_fread(objects, sizeof (Obj), OBJECTS_IN_PLAY, fp); rebuild_mapobjs(); rebuild_mapmons(); - wrapped_fread(&depth, 1, sizeof depth, fp); - depth = ntohl(depth); - wrapped_fread(&u, 1, sizeof u, fp); - wrapped_fread(&game_tick, 1, sizeof game_tick, fp); - game_tick = ntohl(game_tick); fclose(fp); look_around_you(); status_updated = 1; map_updated = 1; hard_redraw = 1; - print_msg("Game loaded.\n"); unlink("victrix-abyssi.sav"); + return 0; } Offset random_step(void) @@ -179,15 +179,10 @@ Offset random_step(void) return Stationary; } -void new_game(void) +void new_game(char const *name) { - u_init(); - flavours_init(); + u_init(name); make_new_level(); - status_updated = 1; - map_updated = 1; - hard_redraw = 1; - print_msg("Initialisation complete.\n"); } std::deque past_actions; @@ -198,6 +193,7 @@ enum Combo_phase Combo_link, Combo_finisher }; + /* \brief Process action combos * * This function is responsible for detecting whether the most recently @@ -476,33 +472,49 @@ void main_loop(void) } xdgHandle victrix_xdg_handle; +char const * victrix_data_home; +char const * victrix_config_home; +std::string our_data_dir; +std::string our_config_dir; -int main(void) +void setup_dirs(void) { - struct stat s; - int i; xdgHandle * foo = xdgInitHandle(&victrix_xdg_handle); if (!foo) { - //// welp. Should probably throw a wobbly. + fputs("FATAL ERROR: libxdg-basedir handle initialization failed\n", stderr); + exit(1); } - // TODO actually have a config file! - display_init(); - rng_init(); - /* Do we have a saved game? */ - i = stat("victrix-abyssi.sav.gz", &s); - if (!i) + victrix_data_home = xdgDataHome(foo); + victrix_config_home = xdgConfigHome(foo); + if (!victrix_data_home || !victrix_config_home) { - /* Yes! */ - print_msg("Loading...\n"); - load_game(); + fputs("FATAL ERROR: libxdg-basedir functions failed to provide data and config homes\n", stderr); + exit(1); } - else + our_data_dir = victrix_data_home; + our_data_dir += "/com.blackswordsonics/victrix-abyssi"; + /* TODO Make sure the directory exists. */ + our_config_dir = victrix_config_home; + our_config_dir += "/com.blackswordsonics/victrix-abyssi"; + /* TODO Make sure the directory exists. */ +} + +void load_config(void) +{ + // TODO actually do something here. +} + +int main(void) +{ + setup_dirs(); + load_config(); + rng_init(); + launch_user_interface(); + if (!game_finished) { - /* No! */ - new_game(); + main_loop(); } - main_loop(); display_shutdown(); return 0; } diff --git a/misc.cc b/misc.cc index 3e7a02c..fd75a0a 100644 --- a/misc.cc +++ b/misc.cc @@ -30,14 +30,15 @@ char const *damtype_names[DT_COUNT] = { "physical damage", - "fire", "cold", - "electricity", + "fire", "necromantic force", + "electricity", + "hellfire", "poison", "knockback", "drowning", }; -/* main.c */ +/* misc.cc */ // vim:cindent diff --git a/notify-local-tty.cc b/notify-local-tty.cc index c14f3da..b3890af 100644 --- a/notify-local-tty.cc +++ b/notify-local-tty.cc @@ -40,6 +40,56 @@ #include #include +void notify_player_heal(int amount, int boost, bool loud) +{ + status_updated = 1; + if (loud) + { + /* Tell the player how much better they feel. */ + if (u.hpcur == u.hpmax) + { + print_msg("You feel great.\n"); + } + else + { + print_msg("You feel %sbetter.\n", amount > 10 ? "much " : ""); + } + } + else + { + /* Update the display. */ + display_update(); + } +} + +void notify_death(Death d, char const *what) +{ + print_msg("\n\nTHOU ART SLAIN!\n\n"); + switch (d) + { + case DEATH_KILLED: + print_msg("You were killed by %s.\n", what); + break; + case DEATH_KILLED_MON: + print_msg("You were killed by a nasty %s.\n", what); + break; + case DEATH_BODY: + print_msg("Your heart was stopped by %s.\n", what); + break; + case DEATH_AGILITY: + print_msg("Your nerves were destroyed by %s.\n", what); + break; + case DEATH_LASH: + print_msg("You tasted the lash one time too many.\n"); + break; + case DEATH_RIBBONS: + print_msg("You looked good in ribbons.\n"); + break; + } + print_msg("Your game lasted %d ticks.\n", game_tick); + print_msg("You killed monsters worth %d experience.\n", u.experience); +} + /*! \brief The player has gained a level */ void notify_level_gain(void) @@ -74,6 +124,30 @@ void notify_hp_gain(int amount) print_msg("You gained %d hit points.\n", amount); } +void notify_body_restore(void) +{ + status_updated = 1; + print_msg("You feel less feeble.\n"); +} + +void notify_agility_restore(void) +{ + status_updated = 1; + print_msg("You feel less clumsy.\n"); +} + +void notify_agility_drain(int amount) +{ + status_updated = 1; + print_msg("You feel clumsy!\n"); +} + +void notify_body_drain(int amount) +{ + status_updated = 1; + print_msg("You feel weaker!\n"); +} + void notify_player_teleport(void) { print_msg("You are whisked away!\n"); @@ -360,6 +434,86 @@ void notify_armour_equip(void) } } +void notify_player_touch_effect(Damtyp dt) +{ + switch (dt) + { + default: + case DT_PHYS: + break; + case DT_FIRE: + print_msg("You are engulfed in flames.\n"); + break; + case DT_COLD: + print_msg("You are covered in frost.\n"); + break; + case DT_NECRO: + print_msg("You feel your life force slipping away.\n"); + break; + case DT_ELEC: + print_msg("Your muscles spasm agonizingly.\n"); + break; + } +} + +void notify_player_damage_taken(int amount) +{ + print_msg("You take %d damage.\n", amount); +} + +void notify_player_ignore_damage(Damtyp dt) +{ + switch (dt) + { + case DT_FIRE: + print_msg("The feel a pleasant warmth.\n"); + break; + case DT_COLD: + print_msg("You feel a pleasant chill.\n"); + break; + case DT_ELEC: + print_msg("You feel a pleasant tingle.\n"); + break; + case DT_NECRO: + print_msg("You feel no deader than before.\n"); + break; + case DT_DROWNING: + print_msg("TODO: Find a good way to express not-drowning.\n"); + break; + case DT_POISON: + print_msg("You feel vaguely queasy for a moment.\n"); + break; + default: + print_msg("BUG: You shouldn't be resisting damage type %d\n", (int) dt); + break; + } +} + +void notify_item_explodes_flames(int obj) +{ + print_msg("The %s explodes in flames!\n", permobjs[objects[obj].obj_id].name); +} + +void notify_read_scroll_protection(void) +{ + print_msg("You feel like something is helping you.\n"); +} + +void notify_dress_shredded(void) +{ + print_msg("Your dress has been reduced to a tattered wreck.\n"); +} + +void notify_eat_food(bool ravenous) +{ + print_msg(ravenous ? "You ravenously devour your food!\n" : "You eat some food.\n"); +} + +void notify_quaff_potion_restoration(void) +{ + print_msg("This potion makes you feel warm all over.\n"); +} + /* Debugging notifications */ void debug_bad_monspell(int spell) @@ -422,5 +576,20 @@ void debug_put_on_uncarried_ring(void) print_msg("BUG: attempting to put on uncarried ring.\n"); } -/* display-nc.cc */ +void debug_read_non_scroll(void) +{ + print_msg("Impossible: reading non-scroll\n"); +} + +void debug_eat_non_food(int obj) +{ + print_msg("Error: attempt to eat non-food (%d)!\n", objects[obj].obj_id); +} + +void debug_quaff_non_potion(void) +{ + print_msg("Impossible: quaffing non-potion\n"); +} + +/* notify-local-tty.cc */ // vim:cindent diff --git a/notify.hh b/notify.hh index 5139973..847f5e3 100644 --- a/notify.hh +++ b/notify.hh @@ -30,9 +30,15 @@ #define NOTIFY_HH // Player status changes +void notify_death(Death d, char const *what); +void notify_player_heal(int amount, int boost, bool loud); void notify_level_gain(void); void notify_agility_gain(int amount); void notify_body_gain(int amount); +void notify_agility_restore(void); +void notify_body_restore(void); +void notify_agility_drain(int amount); +void notify_body_drain(int amount); void notify_hp_gain(int amount); void notify_player_teleport(void); void notify_player_telefail(void); @@ -44,6 +50,8 @@ void notify_wither_recovered(void); void notify_protection_lost(void); void notify_wasted_gain(void); +// Resistance notifications + // Player movement notifications void notify_start_lavawalk(void); void notify_blocked_lava(void); @@ -53,6 +61,7 @@ void notify_cant_go(void); // Unsorted notifications void notify_obj_at(Coord c); +void notify_dress_shredded(void); // Item manipulation notifications void notify_magic_no_ring(void); @@ -60,6 +69,14 @@ void notify_emanate_no_armour(void); void notify_zap_no_weapon(void); void notify_armour_equip(void); +// Magic item use releated notifications +void notify_read_scroll_protection(void); + +void notify_quaff_potion_restoration(void); + +void notify_item_explodes_flames(int obj); +void notify_eat_food(bool ravenous); + // combat notifications void notify_swing_bow(void); void notify_no_attackee(void); @@ -74,6 +91,9 @@ void notify_player_shot_terrain(int obj, Coord c); void notify_mon_hit_armour(int mon); void notify_mon_missed_player(int mon); void notify_mon_hit_player(int mon); +void notify_player_damage_taken(int amount); +void notify_player_touch_effect(Damtyp dt); +void notify_player_ignore_damage(Damtyp dt); // Sorcery notifications void notify_summon_help(int mon, bool success); @@ -94,13 +114,15 @@ void debug_agility_gain(int amount); void debug_bad_monspell(int spell); void debug_player_resists_phys(void); void debug_bad_damage_type(int dt); -void debug_throw_non_flask(void); void debug_unimplemented(void); void debug_wear_while_wearing(void); void debug_wear_uncarried_armour(void); void debug_remove_no_ring(void); void debug_put_on_second_ring(void); void debug_put_on_uncarried_ring(void); +void debug_eat_non_food(int obj); +void debug_read_non_scroll(void); +void debug_quaff_non_potion(void); #endif diff --git a/objects.cc b/objects.cc index d2ea374..aa843da 100644 --- a/objects.cc +++ b/objects.cc @@ -64,22 +64,14 @@ Action_cost read_scroll(int obj) teleport_u(); break; case PO_FIRE_SCROLL: - print_msg("The scroll explodes in flames!\n"); + notify_item_explodes_flames(obj); if (u.resistances[DT_FIRE]) { - if (objects[u.ring].obj_id == PO_FIRE_RING) - { - print_msg("Your ring glows, and the flames seem cool.\n"); - break; - } + notify_player_ignore_damage(DT_FIRE); } else { - i = damage_u(dice(4, 10), DEATH_KILLED, "searing flames"); - if (!i) - { - print_msg("That hurt!\n"); - } + damage_u(dice(4, 10), DEATH_KILLED, "searing flames"); } for (i = 0; i < MONSTERS_IN_PLAY; ++i) { @@ -87,15 +79,15 @@ Action_cost read_scroll(int obj) { if (!pmon_resists_fire(monsters[i].mon_id)) { - print_mon_name(i, 3); - print_msg(" is burned.\n"); + //print_mon_name(i, 3); + //print_msg(" is burned.\n"); damage_mon(i, dice(4, 10), true); } } } break; case PO_PROTECTION_SCROLL: - print_msg("You feel like something is helping you.\n"); + notify_read_scroll_protection(); if (!u.protection) { /* Do not prolong existing protection, only grant @@ -104,22 +96,23 @@ Action_cost read_scroll(int obj) } if (u.withering) { - print_msg("Your limbs straighten.\n"); u.withering = 0; + notify_wither_recovered(); } if (u.armourmelt) { - print_msg("Your armour regains its strength.\n"); u.armourmelt = 0; + notify_armourmelt_recovered(); } if (u.leadfoot) { - print_msg("You shed your feet of lead.\n"); u.leadfoot = 0; + notify_leadfoot_recovered(); } + recalc_defence(); break; default: - print_msg("Impossible: reading non-scroll\n"); + debug_read_non_scroll(); return Cost_none; } consume_obj(obj); @@ -144,7 +137,7 @@ bool consume_obj(int obj) objects[obj].used = true; objects[obj].durability = 50 + zero_die(51); objects[obj].obj_id = PO_RAGGED_SHIFT; - print_msg("Your dress has been reduced to a tattered wreck.\n"); + notify_dress_shredded(); } else { @@ -184,7 +177,7 @@ Action_cost eat_food(int obj) Obj *optr = objects + obj; if (permobjs[optr->obj_id].poclass != POCLASS_FOOD) { - print_msg("Error: attempt to eat non-food (%d)!\n", optr->obj_id); + debug_eat_non_food(obj); return Cost_none; } if (optr->obj_id == PO_DEVIL_SPLEEN) @@ -198,14 +191,12 @@ Action_cost eat_food(int obj) { gain_agility(1); } - } - else if (u.food < 0) - { - print_msg("You ravenously devour your food!\n"); + // TODO + // gain_power(Power_demonic); } else { - print_msg("You eat some food.\n"); + notify_eat_food(u.food < 0); } u.food += 1500; status_updated = 1; @@ -233,37 +224,21 @@ Action_cost quaff_potion(int obj) } break; case PO_RESTORATION_POTION: - print_msg("This potion makes you feel warm all over.\n"); + notify_quaff_potion_restoration(); status_updated = 1; - if (!zero_die(2)) + if (u.bdam && ((!u.adam) || zero_die(2))) { - if (u.adam) - { - u.adam = 0; - print_msg("You feel less clumsy.\n"); - } - else if (u.bdam) - { - u.bdam = 0; - print_msg("You feel less feeble.\n"); - } + u.bdam = 0; + notify_body_restore(); } - else + else if (u.adam) { - if (u.bdam) - { - u.bdam = 0; - print_msg("You feel less feeble.\n"); - } - else if (u.adam) - { - u.adam = 0; - print_msg("You feel less clumsy.\n"); - } + u.adam = 0; + notify_agility_restore(); } break; default: - print_msg("Impossible: quaffing non-potion\n"); + debug_quaff_non_potion(); return Cost_none; } consume_obj(obj); diff --git a/u.cc b/u.cc index 931bdf6..910830d 100644 --- a/u.cc +++ b/u.cc @@ -202,7 +202,6 @@ int gain_body(int amount) int drain_body(int amount, char const *what, int permanent) { - print_msg("You feel weaker!\n"); if (permanent) { u.body -= amount; @@ -211,10 +210,9 @@ int drain_body(int amount, char const *what, int permanent) { u.bdam += amount; } - status_updated = 1; + notify_body_drain(amount); if ((u.body - u.bdam) < 0) { - print_msg("Your heart is too weak to beat.\n"); return do_death(DEATH_BODY, what); } display_update(); @@ -242,14 +240,13 @@ int gain_agility(int amount) } else { - print_msg("You feel disappointed.\n"); + notify_wasted_gain(); return 0; } } int drain_agility(int amount, char const *what, int permanent) { - print_msg("You feel clumsy!\n"); if (permanent) { u.agility -= amount; @@ -258,10 +255,9 @@ int drain_agility(int amount, char const *what, int permanent) { u.adam += amount; } - status_updated = 1; + notify_agility_drain(amount); if ((u.agility - u.adam) < 0) { - print_msg("You forget how to breathe.\n"); return do_death(DEATH_AGILITY, what); } recalc_defence(); @@ -272,6 +268,7 @@ int damage_u(int amount, Death d, char const *what) { u.hpcur -= amount; status_updated = 1; + notify_player_damage_taken(amount); if (u.hpcur < 0) { u.hpcur = 0; @@ -286,115 +283,47 @@ void heal_u(int amount, int boost, int loud) { if (boost) { + boost = 1; u.hpmax++; } amount = u.hpmax - u.hpcur; } - u.hpcur += amount; - /* Touch the status line */ - status_updated = 1; - if (loud) - { - /* Tell the player how much better they feel. */ - if (u.hpcur == u.hpmax) - { - print_msg("You feel great.\n"); - } - else - { - print_msg("You feel %sbetter.\n", amount > 10 ? "much " : ""); - } - } else { - /* Update the display. */ - display_update(); + boost = 0; + u.hpcur += amount; } + notify_player_heal(amount, boost, loud); return; } int do_death(Death d, char const *what) { - // GRATIUTOUS INITIALIZATION: The idiot bastard compiler's inadequate - // control flow analysis shits itself when using debuild/dh's default - // CXXFLAGS settings. - FILE *fp = 0; - int really = 0; + bool really = true; if (wizard_mode) { - really = getyn("Really die? "); - if (really != 1) - { - u.hpcur = u.hpmax; - u.adam = 0; - u.bdam = 0; - status_updated = 1; - print_msg("You survived that attempt on your life."); - return 0; - } + really = query_wizmode_death(); } - if (!wizard_mode) + if (!really) { - fp = fopen("victrix-abyssi.log", "a"); + heal_u(1000, 0, true); + u.adam = 0; + u.bdam = 0; + recalc_defence(); + status_updated = 1; + display_update(); } - print_msg("\n\nTHOU ART SLAIN!\n\n"); - game_finished = 1; - switch (d) + else { - case DEATH_KILLED: - print_msg("You were killed by %s.\n", what); - if (!wizard_mode) - { - fprintf(fp, "%s was killed by %s.\n", u.name, what); - } - break; - case DEATH_KILLED_MON: - print_msg("You were killed by a nasty %s.\n", what); - if (!wizard_mode) - { - fprintf(fp, "%s was killed by a nasty %s.\n", u.name, what); - } - break; - case DEATH_BODY: - print_msg("Your heart was stopped by %s.\n", what); - if (!wizard_mode) - { - fprintf(fp, "%s's heart was stopped by %s.\n", u.name, what); - } - break; - case DEATH_AGILITY: - print_msg("Your nerves were destroyed by %s.\n", what); + game_finished = 1; if (!wizard_mode) { - fprintf(fp, "%s's nerves were destroyed by %s.\n", u.name, what); + log_death(d, what); } - break; - case DEATH_LASH: - print_msg("You tasted the lash one time too many.\n"); - if (!wizard_mode) - { - fprintf(fp, "%s tasted the lash one time too many.\n", u.name); - } - break; - case DEATH_RIBBONS: - print_msg("You looked good in ribbons.\n"); - if (!wizard_mode) - { - fprintf(fp, "%s looked good in ribbons.\n", u.name); - } - break; + notify_death(d, what); } - if (!wizard_mode) - { - fprintf(fp, " %s died after %d ticks, with %d XP, on dungeon level %d.\n\n", u.name, game_tick, u.experience, depth); - fflush(fp); - fclose(fp); - } - print_msg("Your game lasted %d ticks.\n", game_tick); - print_msg("You killed monsters worth %d experience.\n", u.experience); - - return 1; + return really; } void write_char_dump(void) @@ -427,46 +356,16 @@ void write_char_dump(void) fclose(fp); } -void u_init(void) +void u_init(char const *name) { - char * hasslash = NULL; - int i; - u.name[16] = '\0'; - do { - print_msg("What is your name, stranger?\n"); - i = read_input(u.name, 16); - if (i == 0) - { - /* Default name is Matilda, in honour of Matilda of England - * (aka Empress Matilda) and of Matilda di Canossa, Margravine - * regnant of Tuscany. */ - strcpy(u.name, "Matilda"); - } - else - { - hasslash = strchr(u.name, '/'); - /* Now that we create a named dump file, we must not - * permit the player's name to contain a slash, colon, - * or backslash. */ - if (hasslash) - { - print_msg("No slashes permitted.\n"); - continue; - } - hasslash = strchr(u.name, '\\'); - if (hasslash) - { - print_msg("No backslashes permitted.\n"); - continue; - } - hasslash = strchr(u.name, ':'); - if (hasslash) - { - print_msg("No colons permitted.\n"); - continue; - } - } - } while (hasslash != NULL); + if (!name || !*name) + { + strcpy(u.name, "Matilda"); + } + else + { + strncpy(u.name, name, 16); + } u.body = 10; u.bdam = 0; u.agility = 10; diff --git a/victrix-abyssi.hh b/victrix-abyssi.hh index ce8c7de..70e87a6 100644 --- a/victrix-abyssi.hh +++ b/victrix-abyssi.hh @@ -102,7 +102,8 @@ enum Gamecolour /* XXX enum damtyp - types of damage. */ enum Damtyp { DT_PHYS = 0, DT_COLD, DT_FIRE, DT_NECRO, - DT_ELEC, DT_POISON, DT_KNOCKBACK, DT_DROWNING + DT_ELEC, DT_HELLFIRE, DT_POISON, + DT_KNOCKBACK, DT_DROWNING }; #define DT_COUNT (1 + DT_DROWNING) @@ -203,6 +204,8 @@ extern int game_tick; extern bool wizard_mode; extern void wrapped_system(char const *cmd); extern void wrapped_fread(void *buf, size_t size, size_t nmemb, FILE *stream); +extern void new_game(char const *name); +extern int load_game(void); /* XXX misc.c data and funcs */ extern char const *damtype_names[DT_COUNT]; @@ -212,8 +215,10 @@ extern bool po_is_stackable(int po); extern void damage_obj(int obj); extern int evasion_penalty(int obj); +/* XXX log.cc */ +void log_death(Death d, char const *what); /* XXX u.c data and funcs */ -extern void u_init(void); +extern void u_init(char const *name); extern void write_char_dump(void); extern int do_death(Death d, char const *what); extern void heal_u(int amount, int boost, int loud);