Skip to content
Snippets Groups Projects
Commit 528a546b authored by Jake Feddersen's avatar Jake Feddersen
Browse files

Play around with Dr. Sheaffer's verson of the final game

parent 36831646
No related branches found
No related tags found
No related merge requests found
Showing
with 2567 additions and 0 deletions
#ifndef IO_H
# define IO_H
class dungeon;
void io_init_terminal(void);
void io_reset_terminal(void);
void io_display(dungeon *d);
void io_handle_input(dungeon *d);
void io_queue_message(const char *format, ...);
#endif
#include "move.h"
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include "dungeon.h"
#include "heap.h"
#include "move.h"
#include "npc.h"
#include "pc.h"
#include "character.h"
#include "utils.h"
#include "path.h"
#include "event.h"
#include "io.h"
#include "npc.h"
#include "object.h"
void do_combat(dungeon *d, character *atk, character *def)
{
uint32_t damage, i;
const char *organs[] = {
"liver",
"pancreas",
"heart",
"brain",
"eye",
"arm",
"leg",
"intestines",
"gall bladder",
"lungs",
"hand",
"foot",
"spinal cord",
"pituitary gland",
"thyroid",
"tongue",
"bladder",
"diaphram",
"frontal lobe",
"hippocampus",
"stomach",
"pharynx",
"esophagus",
"trachea",
"urethra",
"spleen",
"cerebellum",
"ganglia",
"ear",
"subcutaneous tissue",
"prefrontal cortex"
};
const char *attacks[] = {
"punches",
"kicks",
"stabs",
"impales",
"slashes",
"massages",
"soothes",
"bites",
"jabs",
"coerces",
"threatens",
"manipulates",
"arm locks",
"conquers",
"buries the hatchet in",
"indicates displeasure with",
"quarrels with",
"scrimmages with",
"tickles",
"engages in fisticuffs with",
"strikes",
"belts",
"wallops",
"gives the old one-two to",
"bumps into",
"behaves inappropriately with",
"smacks",
"body slams",
"fondues",
"flambes",
"pokes",
"anoints",
};
if (character_is_alive(def)) {
if (atk != d->PC) {
damage = atk->damage->roll();
io_queue_message("%s%s %s your %s for %d.", is_unique(atk) ? "" : "The ",
atk->name, attacks[rand() % (sizeof (attacks) /
sizeof (attacks[0]))],
organs[rand() % (sizeof (organs) /
sizeof (organs[0]))], damage);
} else {
for (i = damage = 0; i < num_eq_slots; i++) {
if (i == eq_slot_weapon && !d->PC->eq[i]) {
damage += atk->damage->roll();
} else if (d->PC->eq[i]) {
damage += d->PC->eq[i]->roll_dice();
}
}
io_queue_message("You hit %s%s for %d.", is_unique(def) ? "" : "the ",
def->name, damage);
}
if (damage >= def->hp) {
if (atk != d->PC) {
io_queue_message("You die.");
io_queue_message("As %s%s eats your %s,", is_unique(atk) ? "" : "the ",
atk->name, organs[rand() % (sizeof (organs) /
sizeof (organs[0]))]);
io_queue_message(" ...you wonder if there is an afterlife.");
/* Queue an empty message, otherwise the game will not pause for *
* player to see above. */
io_queue_message("");
} else {
io_queue_message("%s%s dies.", is_unique(def) ? "" : "The ", def->name);
}
def->hp = 0;
def->alive = 0;
character_increment_dkills(atk);
character_increment_ikills(atk, (character_get_dkills(def) +
character_get_ikills(def)));
if (def != d->PC) {
d->num_monsters--;
}
charpair(def->position) = NULL;
} else {
def->hp -= damage;
}
}
}
void move_character(dungeon *d, character *c, pair_t next)
{
int can_see_atk, can_see_def;
pair_t displacement;
uint32_t found_cell;
pair_t order[9] = {
{ -1, -1 },
{ -1, 0 },
{ -1, 1 },
{ 0, -1 },
{ 0, 0 },
{ 0, 1 },
{ 1, -1 },
{ 1, 0 },
{ 1, 1 },
};
uint32_t s, i;
if (charpair(next) &&
((next[dim_y] != c->position[dim_y]) ||
(next[dim_x] != c->position[dim_x]))) {
if ((charpair(next) == d->PC) ||
c == d->PC) {
do_combat(d, c, charpair(next));
} else {
/* Easiest way for a monster to displace another monster is *
* to swap them. This could lead to some strangeness where *
* two monsters of the exact same speed continually *
* displace each other and never make progress, but I don't *
* have any real problem with that. When we have better *
* game balance, weaker monsters should not be able to *
* displace stronger monsters. */
/* Turns out I don't like swapping them after all. We'll *
* instead select a random square from the 8 surrounding *
* the target cell. Keep doing it until either we swap or *
* find an empty one for the displacement. */
for (s = rand() % 9, found_cell = i = 0;
i < 9 && !found_cell; i++) {
displacement[dim_y] = next[dim_y] + order[s % 9][dim_y];
displacement[dim_x] = next[dim_x] + order[s % 9][dim_x];
if (((npc *) charpair(next))->characteristics & NPC_PASS_WALL) {
if (!charpair(displacement) ||
(charpair(displacement) == c)) {
found_cell = 1;
}
} else {
if ((!charpair(displacement) &&
(mappair(displacement) >= ter_floor)) ||
(charpair(displacement) == c)) {
found_cell = 1;
}
}
}
if (!found_cell) {
return;
}
assert(charpair(next));
can_see_atk = can_see(d, character_get_pos(d->PC),
character_get_pos(c), 1, 0);
can_see_def = can_see(d, character_get_pos(d->PC),
character_get_pos(charpair(next)), 1, 0);
if (can_see_atk && can_see_def) {
io_queue_message("%s%s pushes %s%s out of the way. How rude.",
is_unique(c) ? "" : "The ", c->name,
is_unique(charpair(next)) ? "" : "the ",
charpair(next)->name);
} else if (can_see_atk) {
io_queue_message("%s%s angrily shoves something out of the way.",
is_unique(c) ? "" : "The ", c->name);
} else if (can_see_def) {
io_queue_message("Something slams %s%s out of the way.",
is_unique(charpair(next)) ? "" : "the ",
charpair(next)->name);
}
charpair(c->position) = NULL;
charpair(displacement) = charpair(next);
charpair(next) = c;
charpair(displacement)->position[dim_y] = displacement[dim_y];
charpair(displacement)->position[dim_x] = displacement[dim_x];
c->position[dim_y] = next[dim_y];
c->position[dim_x] = next[dim_x];
}
} else {
/* No character in new position. */
d->character_map[c->position[dim_y]][c->position[dim_x]] = NULL;
c->position[dim_y] = next[dim_y];
c->position[dim_x] = next[dim_x];
d->character_map[c->position[dim_y]][c->position[dim_x]] = c;
}
if (c == d->PC) {
pc_reset_visibility((pc *) c);
pc_observe_terrain((pc *) c, d);
}
}
void do_moves(dungeon *d)
{
pair_t next;
character *c;
event *e;
/* Remove the PC when it is PC turn. Replace on next call. This allows *
* use to completely uninit the heap when generating a new level without *
* worrying about deleting the PC. */
if (pc_is_alive(d)) {
/* The PC always goes first one a tie, so we don't use new_event(). *
* We generate one manually so that we can set the PC sequence *
* number to zero. */
e = (event *) malloc(sizeof (*e));
e->type = event_character_turn;
/* Hack: New dungeons are marked. Unmark and ensure PC goes at d->time, *
* otherwise, monsters get a turn before the PC. */
if (d->is_new) {
d->is_new = 0;
e->time = d->time;
} else {
e->time = d->time + (1000 / d->PC->speed);
}
e->sequence = 0;
e->c = d->PC;
heap_insert(&d->events, e);
}
while (pc_is_alive(d) &&
(e = (event *) heap_remove_min(&d->events)) &&
((e->type != event_character_turn) || (e->c != d->PC))) {
d->time = e->time;
if (e->type == event_character_turn) {
c = e->c;
}
if (!c->alive) {
if (d->character_map[c->position[dim_y]][c->position[dim_x]] == c) {
d->character_map[c->position[dim_y]][c->position[dim_x]] = NULL;
}
if (c != d->PC) {
event_delete(e);
}
continue;
}
npc_next_pos(d, (npc *) c, next);
move_character(d, (npc *) c, next);
heap_insert(&d->events, update_event(d, e, 1000 / c->speed));
}
io_display(d);
if (pc_is_alive(d) && e->c == d->PC) {
c = e->c;
d->time = e->time;
/* Kind of kludgey, but because the PC is never in the queue when *
* we are outside of this function, the PC event has to get deleted *
* and recreated every time we leave and re-enter this function. */
e->c = NULL;
event_delete(e);
io_handle_input(d);
}
}
void dir_nearest_wall(dungeon *d, character *c, pair_t dir)
{
dir[dim_x] = dir[dim_y] = 0;
if (c->position[dim_x] != 1 && c->position[dim_x] != DUNGEON_X - 2) {
dir[dim_x] = (c->position[dim_x] > DUNGEON_X - c->position[dim_x] ? 1 : -1);
}
if (c->position[dim_y] != 1 && c->position[dim_y] != DUNGEON_Y - 2) {
dir[dim_y] = (c->position[dim_y] > DUNGEON_Y - c->position[dim_y] ? 1 : -1);
}
}
uint32_t against_wall(dungeon *d, character *c)
{
return ((mapxy(c->position[dim_x] - 1,
c->position[dim_y] ) == ter_wall_immutable) ||
(mapxy(c->position[dim_x] + 1,
c->position[dim_y] ) == ter_wall_immutable) ||
(mapxy(c->position[dim_x] ,
c->position[dim_y] - 1) == ter_wall_immutable) ||
(mapxy(c->position[dim_x] ,
c->position[dim_y] + 1) == ter_wall_immutable));
}
uint32_t in_corner(dungeon *d, character *c)
{
uint32_t num_immutable;
num_immutable = 0;
num_immutable += (mapxy(c->position[dim_x] - 1,
c->position[dim_y] ) == ter_wall_immutable);
num_immutable += (mapxy(c->position[dim_x] + 1,
c->position[dim_y] ) == ter_wall_immutable);
num_immutable += (mapxy(c->position[dim_x] ,
c->position[dim_y] - 1) == ter_wall_immutable);
num_immutable += (mapxy(c->position[dim_x] ,
c->position[dim_y] + 1) == ter_wall_immutable);
return num_immutable > 1;
}
static void new_dungeon_level(dungeon *d, uint32_t dir)
{
/* Eventually up and down will be independantly meaningful. *
* For now, simply generate a new dungeon. */
switch (dir) {
case '<':
io_queue_message("You go up the stairs.");
io_queue_message(""); /* To force "more" */
io_display(d); /* To force queue flush */
new_dungeon(d);
break;
case '>':
io_queue_message("You go down the stairs.");
io_queue_message(""); /* To force "more" */
io_display(d); /* To force queue flush */
new_dungeon(d);
break;
default:
break;
}
}
uint32_t move_pc(dungeon *d, uint32_t dir)
{
pair_t next;
uint32_t was_stairs = 0;
const char *wallmsg[] = {
"There's a wall in the way.",
"BUMP!",
"Ouch!",
"You stub your toe.",
"You can't go that way.",
"You admire the engravings.",
"Are you drunk?"
};
next[dim_y] = d->PC->position[dim_y];
next[dim_x] = d->PC->position[dim_x];
switch (dir) {
case 1:
case 2:
case 3:
next[dim_y]++;
break;
case 4:
case 5:
case 6:
break;
case 7:
case 8:
case 9:
next[dim_y]--;
break;
}
switch (dir) {
case 1:
case 4:
case 7:
next[dim_x]--;
break;
case 2:
case 5:
case 8:
break;
case 3:
case 6:
case 9:
next[dim_x]++;
break;
case '<':
if (mappair(d->PC->position) == ter_stairs_up) {
was_stairs = 1;
new_dungeon_level(d, '<');
}
break;
case '>':
if (mappair(d->PC->position) == ter_stairs_down) {
was_stairs = 1;
new_dungeon_level(d, '>');
}
break;
}
if (was_stairs) {
return 0;
}
if ((dir != '>') && (dir != '<') && (mappair(next) >= ter_floor)) {
move_character(d, d->PC, next);
dijkstra(d);
dijkstra_tunnel(d);
d->PC->pick_up(d);
return 0;
} else if (mappair(next) < ter_floor) {
io_queue_message(wallmsg[rand() % (sizeof (wallmsg) /
sizeof (wallmsg[0]))]);
io_display(d);
}
return 1;
}
#ifndef MOVE_H
# define MOVE_H
# include <stdint.h>
# include "dims.h"
class character;
class dungeon;
void next_move(dungeon *d,
character *c,
pair_t goal_pos,
pair_t next_pos);
void do_moves(dungeon *d);
void dir_nearest_wall(dungeon *d, character *c, pair_t dir);
uint32_t in_corner(dungeon *d, character *c);
uint32_t against_wall(dungeon *d, character *c);
uint32_t move_pc(dungeon *d, uint32_t dir);
void move_character(dungeon *d, character *c, pair_t next);
#endif
This diff is collapsed.
#ifndef NPC_H
# define NPC_H
# include <stdint.h>
# include "dims.h"
# include "character.h"
# define NPC_SMART 0x00000001
# define NPC_TELEPATH 0x00000002
# define NPC_TUNNEL 0x00000004
# define NPC_ERRATIC 0x00000008
# define NPC_PASS_WALL 0x00000010
# define NPC_DESTROY_OBJ 0x00000020
# define NPC_PICKUP_OBJ 0x00000040
# define NPC_UNIQ 0x00000080
# define NPC_BOSS 0x00000100
# define NPC_BIT09 0x00000200
# define NPC_BIT10 0x00000400
# define NPC_BIT11 0x00000800
# define NPC_BIT12 0x00001000
# define NPC_BIT13 0x00002000
# define NPC_BIT14 0x00004000
# define NPC_BIT15 0x00008000
# define NPC_BIT16 0x00010000
# define NPC_BIT17 0x00020000
# define NPC_BIT18 0x00040000
# define NPC_BIT19 0x00080000
# define NPC_BIT20 0x00100000
# define NPC_BIT21 0x00200000
# define NPC_BIT22 0x00400000
# define NPC_BIT23 0x00800000
# define NPC_BIT24 0x01000000
# define NPC_BIT25 0x02000000
# define NPC_BIT26 0x04000000
# define NPC_BIT27 0x08000000
# define NPC_BIT28 0x10000000
# define NPC_BIT29 0x20000000
# define NPC_BIT30 0x40000000
# define NPC_BIT31 0x80000000
# define has_characteristic(character, bit) \
(((npc *) character)->characteristics & NPC_##bit)
# define is_unique(character) has_characteristic(character, UNIQ)
# define is_boss(character) has_characteristic(character, BOSS)
class monster_description;
typedef uint32_t npc_characteristics_t;
class npc : public character {
public:
npc(dungeon *d, monster_description &m);
~npc();
npc_characteristics_t characteristics;
uint32_t have_seen_pc;
pair_t pc_last_known_position;
const char *description;
monster_description &md;
};
void gen_monsters(dungeon *d);
void npc_delete(npc *n);
void npc_next_pos(dungeon *d, npc *c, pair_t next);
uint32_t dungeon_has_npcs(dungeon *d);
bool boss_is_alive(dungeon *d);
#endif
This diff is collapsed.
#ifndef OBJECT_H
# define OBJECT_H
# include <string>
# include "descriptions.h"
# include "dims.h"
class object {
private:
const std::string &name;
const std::string &description;
object_type_t type;
uint32_t color;
pair_t position;
const dice &damage;
int32_t hit, dodge, defence, weight, speed, attribute, value;
bool seen;
object *next;
object_description &od;
public:
object(object_description &o, pair_t p, object *next);
~object();
inline int32_t get_damage_base() const
{
return damage.get_base();
}
inline int32_t get_damage_number() const
{
return damage.get_number();
}
inline int32_t get_damage_sides() const
{
return damage.get_sides();
}
char get_symbol();
uint32_t get_color();
const char *get_name();
int32_t get_speed();
int32_t roll_dice();
int32_t get_type();
bool have_seen() { return seen; }
void has_been_seen() { seen = true; }
int16_t *get_position() { return position; }
void pick_up() { od.find(); }
uint32_t is_equipable();
uint32_t is_removable();
uint32_t is_dropable();
uint32_t is_destructable();
int32_t get_eq_slot_index();
void to_pile(dungeon *d, pair_t location);
inline object *get_next() { return next; }
inline void set_next(object *n) { next = n; }
const char *get_description() { return description.c_str(); }
};
void gen_objects(dungeon *d);
char object_get_symbol(object *o);
void destroy_objects(dungeon *d);
#endif
This diff is collapsed.
#ifndef PATH_H
# define PATH_H
# define HARDNESS_PER_TURN 85
class dungeon;
void dijkstra(dungeon *d);
void dijkstra_tunnel(dungeon *d);
#endif
This diff is collapsed.
#ifndef PC_H
# define PC_H
# include <stdint.h>
# include "dims.h"
# include "character.h"
# include "dungeon.h"
typedef enum eq_slot {
eq_slot_weapon,
eq_slot_offhand,
eq_slot_ranged,
eq_slot_light,
eq_slot_armor,
eq_slot_helmet,
eq_slot_cloak,
eq_slot_gloves,
eq_slot_boots,
eq_slot_amulet,
eq_slot_lring,
eq_slot_rring,
num_eq_slots
} eq_slot_t;
extern const char *eq_slot_name[num_eq_slots];
class pc : public character {
private:
void recalculate_speed();
uint32_t has_open_inventory_slot();
int32_t get_first_open_inventory_slot();
object *from_pile(dungeon *d, pair_t pos);
public:
pc();
~pc();
object *eq[num_eq_slots];
object *in[MAX_INVENTORY];
uint32_t wear_in(uint32_t slot);
uint32_t remove_eq(uint32_t slot);
uint32_t drop_in(dungeon *d, uint32_t slot);
uint32_t destroy_in(uint32_t slot);
uint32_t pick_up(dungeon *d);
terrain_type known_terrain[DUNGEON_Y][DUNGEON_X];
uint8_t visible[DUNGEON_Y][DUNGEON_X];
};
void pc_delete(pc *pc);
uint32_t pc_is_alive(dungeon *d);
void config_pc(dungeon *d);
uint32_t pc_next_pos(dungeon *d, pair_t dir);
void place_pc(dungeon *d);
uint32_t pc_in_room(dungeon *d, uint32_t room);
void pc_learn_terrain(pc *p, pair_t pos, terrain_type ter);
terrain_type pc_learned_terrain(pc *p, int16_t y, int16_t x);
void pc_init_known_terrain(pc *p);
void pc_observe_terrain(pc *p, dungeon *d);
int32_t is_illuminated(pc *p, int16_t y, int16_t x);
void pc_reset_visibility(pc *p);
#endif
This diff is collapsed.
File added
File added
File added
File added
File added
File added
File added
File added
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment