display-nc.cc
fov.cc
fov.hh
+log.cc
main.cc
map.cc
map.hh
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
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
code-docs:
doxygen Doxyfile
+code-docs-clean:
+ -rm -rf html latex
+
install: all
echo "man6dir is $(man6dir)"
install -D $(GAME) $(DESTDIR)$(gamesdir)/$(GAME)
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);
}
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);
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();
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
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.
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
{
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);
*
* \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;
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)
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;
}
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);
}
}
}
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);
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");
}
}
+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
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);
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
--- /dev/null
+/* \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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <limits.h>
+#include <deque>
+#include <basedir.h>
+
+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
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);
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);
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)
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<Action> past_actions;
Combo_link,
Combo_finisher
};
+
/* \brief Process action combos
*
* This function is responsible for detecting whether the most recently
}
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;
}
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
#include <locale.h>
#include <langinfo.h>
+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)
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");
}
}
+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)
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
#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);
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);
// Unsorted notifications
void notify_obj_at(Coord c);
+void notify_dress_shredded(void);
// Item manipulation notifications
void notify_magic_no_ring(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);
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);
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
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)
{
{
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
}
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);
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
{
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)
{
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;
}
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);
int drain_body(int amount, char const *what, int permanent)
{
- print_msg("You feel weaker!\n");
if (permanent)
{
u.body -= amount;
{
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();
}
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;
{
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();
{
u.hpcur -= amount;
status_updated = 1;
+ notify_player_damage_taken(amount);
if (u.hpcur < 0)
{
u.hpcur = 0;
{
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)
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;
/* 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)
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];
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);