Something went wrong on our end
-
Jake Feddersen authoredJake Feddersen authored
character.cpp 8.06 KiB
#include <ncurses.h>
#include <cstdint>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include "character.h"
#include "distance_map.h"
#include "draw_dungeon.h"
#include "heap.h"
#include "util.h"
const char *pickup_header_top_bottom = "************************************";
const char *pickup_header_middle = "* Pick up item (',' to select) *";
const char *pickup_no_items = "No items on the floor here.";
static int32_t character_turn_cmp(const void *key, const void *with) {
if (((character *) key)->next_turn == ((character *) with)->next_turn) {
return ((character *) key)->seq_num - ((character *) with)->seq_num;
} else {
return ((character *) key)->next_turn - ((character *) with)->next_turn;
}
}
char characteristics_symbols[] = "0123456789abcdef";
character::character(int speed, char symbol, int hitpoints) {
pos.x = 0;
pos.y = 0;
this->speed = speed;
seq_num = 0;
next_turn = 0;
alive = 1;
this->symbol = symbol;
this->hitpoints = hitpoints;
}
character::~character() { }
item::item(object_description *desc, dice *damage, int hit, int dodge, int defence, int weight, int speed, int attribute, int value) :
desc(desc), damage(damage), hit(hit), dodge(dodge), defence(defence), weight(weight), speed(speed), attribute(attribute), value(value) {
desc->instances++;
}
item::~item() {
desc->instances--;
}
int item::get_color() {
return (int)desc->get_color();
}
char item::get_symbol() {
return desc->get_symbol();
}
monster::monster(uint8_t x, uint8_t y, monster_description *desc, int speed, uint32_t abilities, int hitpoints, dice *damage, char symbol) : character(speed, symbol, hitpoints) {
this->desc = desc;
desc->instances++;
static int next_seq = 1;
this->characteristics = abilities;
seq_num = next_seq++;
symbol = characteristics_symbols[characteristics];
this->damage = damage;
color_index = 0;
pos.x = x;
pos.y = y;
last_seen.x = x;
last_seen.y = y;
}
monster::~monster() {
desc->instances--;
}
void monster::die() {
desc->invalidate();
}
int monster::next_color() {
color_index++;
color_index = (color_index % desc->color.size());
return desc->color[color_index];
}
bool monster::is_player() {
return false;
}
int monster::has_characteristic(int bit) {
return characteristics & bit;
}
// Erratic tunneling monsters don't care about anything but the border
position monster::next_monster_move_tunnel_erratic(dungeon &d) {
position next_move;
next_move = pos;
while(next_move.x == pos.x && next_move.y == pos.y) {
next_move.x = randrange(std::max(1, pos.x - 1), std::min(MAP_WIDTH-2, pos.x + 1));
next_move.y = randrange(std::max(1, pos.y - 1), std::min(MAP_HEIGHT-2, pos.y + 1));
}
return next_move;
}
// Erratic non-tunneling monsters have to pick a random open space
position monster::next_monster_move_erratic(dungeon &d) {
position next_move;
next_move = pos;
while(next_move.x == pos.x && next_move.y == pos.y && !d.hardness[next_move.y][next_move.x]) {
next_move.x = randrange(std::max(1, pos.x - 1), std::min(MAP_WIDTH-2, pos.x + 1));
next_move.y = randrange(std::max(1, pos.y - 1), std::min(MAP_HEIGHT-2, pos.y + 1));
}
return next_move;
}
// Smart tunneling monsters move toward the last known position of the player following
// the tunneling distance map. Smart non-tunneling monsters do the same but use the non-tunneling
// distance map
position monster::next_monster_move_smart(dungeon &d) {
int i, j;
uint32_t dist_to_target[MAP_HEIGHT][MAP_WIDTH];
dijkstra_from_pos(d, dist_to_target, (has_characteristic(NPC_TUNNEL)) ? 255 : 1, last_seen.x, last_seen.y);
position best = {pos.x, pos.y};
uint32_t best_dist = dist_to_target[pos.y][pos.x];
for (i = std::max(0, pos.y-1); i < std::min(MAP_WIDTH, pos.y+2); i++) {
for (j = std::max(0, pos.x-1); j < std::min(MAP_WIDTH, pos.x+2); j++) {
if (dist_to_target[i][j] < best_dist || (dist_to_target[i][j] == best_dist && i == pos.y)) {
best_dist = dist_to_target[i][j];
best.x = j;
best.y = i;
}
}
}
return best;
}
// Dumb tunneling monsters move directly toward the player position if they know it, and
// move erratically otherwise
// Dumb non-tunneling monsters do the same, but can't tunnel
position monster::next_monster_move_dumb(dungeon &d) {
if (last_seen.x == pos.x && last_seen.y == pos.y) {
if (has_characteristic(NPC_TUNNEL)) {
return next_monster_move_tunnel_erratic(d);
} else {
return next_monster_move_erratic(d);
}
} else {
position next_move;
next_move.x = pos.x + sign(last_seen.x - pos.x);
next_move.y = pos.y + sign(last_seen.y - pos.y);
return next_move;
}
}
position monster::monster_move(dungeon &d) {
if (is_player()) {
return pos;
}
// Handle telepathic monsters
// If the monster is telepathic, update the position of the PC
// Otherwise if it can currently see the PC then update the position
// Otherwise if it is not smart then forget the pc position
if (has_characteristic(NPC_TELE)) {
last_seen = d.player_pos;
} else if (d.pc_sight[pos.y][pos.x]) {
last_seen = d.player_pos;
} else if (!(has_characteristic(NPC_SMART))) {
last_seen = pos;
}
// Handle erratic monsters
// If the monster is erratic, then 50% chance to move randomly
// Movement based on whether it can tunnel or not
if (has_characteristic(NPC_ERRATIC) && randchance(0.5)) {
if (has_characteristic(NPC_TUNNEL)) {
return next_monster_move_tunnel_erratic(d);
} else {
return next_monster_move_erratic(d);
}
}
// Handle smart/dumb monsters
if (has_characteristic(NPC_SMART)) {
return next_monster_move_smart(d);
} else {
return next_monster_move_dumb(d);
}
return pos;
}
player_character::player_character(uint8_t x, uint8_t y) : character(10, '@', 100) {
pos.x = x;
pos.y = y;
int i;
for (i = 0; i < 12; i++) {
equipment[i] = 0;
}
for (i = 0; i < 10; i++) {
inventory[i] = 0;
}
}
bool player_character::is_player() {
return true;
}
int player_character::has_characteristic(int bit) {
return 0;
}
int player_character::next_color() {
return COLOR_WHITE;
}
int player_character::pick_up(std::vector<item *> &floor) {
int i, j, k;
int shouldExit = 0;
k = 0;
while(1) {
for (i = 2; i < 21; i++) {
for (j = 1; j < 79; j++) {
mvaddch(i, j, ' ');
}
}
mvaddstr(3, (80 - strlen(pickup_header_top_bottom)) / 2, pickup_header_top_bottom);
mvaddstr(4, (80 - strlen(pickup_header_middle)) / 2, pickup_header_middle);
mvaddstr(5, (80 - strlen(pickup_header_top_bottom)) / 2, pickup_header_top_bottom);
if (floor.size() == 0) {
mvaddstr(7, (80 - strlen(pickup_no_items)) / 2, pickup_no_items);
} else {
int top = std::max(0, k-2);
top = std::min(top, std::max(0, (int)floor.size()-5));
int bottom = std::min((int)floor.size()-1, top + 4);
int missing_top = top;
if (missing_top > 0) {
mvprintw(7, 38, "(+%d)", missing_top);
}
int missing_bottom = floor.size() - 1 - bottom;
if (missing_bottom > 0) {
mvprintw(19, 38, "(+%d)", missing_bottom);
}
for (i = top; i <= bottom; i++) {
int row = 9 + ((i - top) * 2);
if (i == k) {
mvprintw(row, 28, "*");
}
move(row, 30);
print_char(floor[i]->get_symbol(), floor[i]->get_color());
mvprintw(row, 32, floor[i]->desc->get_name().c_str());
}
}
int c = getch();
if (c == 'q' || c == 'Q') {
shouldExit = 1;
break;
}
if (c == 27) {
break;
}
if (floor.size() == 0) continue;
if (c == KEY_DOWN) {
k = std::max(0, std::min((int)floor.size()-1, k+1));
}
if (c == KEY_UP) {
k = std::max(0, k-1);
}
if (c == ',') {
for (i = 0; i < 10; i++) {
if (!inventory[i]) {
inventory[i] = floor[k];
floor.erase(floor.begin() + k);
break;
}
}
break;
}
}
return shouldExit;
}
void init_character_turn_heap(heap_t *h) {
heap_init(h, character_turn_cmp, NULL);
}