#include <cstdint> #include <iostream> #include <cstdlib> #include "character.h" #include "heap.h" #include "util.h" #include "distance_map.h" 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(uint8_t x, uint8_t y) { pos.x = x; pos.y = y; speed = 0; seq_num = 0; next_turn = 0; alive = 1; symbol = ' '; } character::~character() { } bool character::is_player() { return false; } int character::has_characteristic(int bit) { return 0; } monster::monster(uint8_t x, uint8_t y) : character(x, y) { static int next_seq = 1; uint32_t characteristics = rand() & 0x0000000f; this->characteristics = characteristics; // Initialize the starting "last seen" position to the position of the monster // This will keep smart monsters from moving until they actually see the player last_seen.x = x; last_seen.y = y; speed = randrange(5, 20); seq_num = next_seq++; symbol = characteristics_symbols[characteristics]; } 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(max(1, pos.x - 1), min(MAP_WIDTH-2, pos.x + 1)); next_move.y = randrange(max(1, pos.y - 1), 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(max(1, pos.x - 1), min(MAP_WIDTH-2, pos.x + 1)); next_move.y = randrange(max(1, pos.y - 1), 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(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 = max(0, pos.y-1); i < min(MAP_WIDTH, pos.y+2); i++) { for (j = max(0, pos.x-1); j < 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(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(TELE)) { last_seen = d.player_pos; } else if (d.pc_sight[pos.y][pos.x]) { last_seen = d.player_pos; } else if (!(has_characteristic(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(ERRATIC) && randchance(0.5)) { if (has_characteristic(TUNNEL)) { return next_monster_move_tunnel_erratic(d); } else { return next_monster_move_erratic(d); } } // Handle smart/dumb monsters if (has_characteristic(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(x, y){ speed = 10; seq_num = 0; symbol = '@'; } bool player_character::is_player() { return true; } int player_character::has_characteristic(int bit) { return 0; } void init_character_turn_heap(heap_t *h) { heap_init(h, character_turn_cmp, NULL); }