/* * Copyright (C) 2002-2007 The Warp Rogue Team * Part of the Warp Rogue Project * * This software is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License. * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY. * * See the license.txt file for more details. */ /* * Module Name: Sector * Description: - */ #define Uses_Area #define Uses_Character #define Uses_Object #define Uses_Terrain #define Uses_Random #define Uses_Death #define Uses_Perception #define Uses_Util #define Uses_ProgramManager #include "mheader.h" #include "sector.h" #define MAX_SECTOR_CLASSES 5 #define SECTOR_RANDOM_N_TRIES 256 typedef int N_TRIES; static bool out_of_sector_bounds(const AREA_POINT *); static bool sc_free(const AREA_POINT *); static bool sc_free_object_location(const AREA_POINT *); static bool sc_free_object_location_antiblock(const AREA_POINT *); static bool sc_move_target(const AREA_POINT *); static bool sc_move_target_safe(const AREA_POINT *); static bool antiblock_check_passed(const AREA_POINT *); static void sector_description_add_gore(char *, GORE_LEVEL); /* * sector class functions */ static bool (* const SectorClass[MAX_SECTOR_CLASSES])(const AREA_POINT *) = { sc_free, sc_free_object_location, sc_free_object_location_antiblock, sc_move_target, sc_move_target_safe }; /* * sector function bounds */ static AREA_SECTION SectorBounds; /* * Sector module init */ void sector_init(void) { sector_reset_bounds(); } /* * sets the bounds for the sector functions */ void sector_set_bounds(const AREA_SECTION *bounds) { SectorBounds = *bounds; } /* * sets the bounds for the sector functions back * to the default bounds */ void sector_reset_bounds(void) { SectorBounds = *area_bounds(); } /* * returns a string that describes a sector (used by the Look commmand) */ char * sector_description(char *description_string, const SECTOR *sector) { const CHARACTER *character; const OBJECT *object; const TERRAIN *terrain; character = sector->character; object = sector->object; terrain = sector->terrain; if (character != NULL && (!character->is_hostile || !character_unnoticed(character))) { strcpy(description_string, character->name); sprintf(description_string, "%s (%d/%d)", character->name, character->injury, injury_max(character) ); } else if (object != NULL) { const OBJECT_DATA *object_data; object_data = object_static_data(object); if (object->charge != CHARGE_NIL) { sprintf(description_string, "%s [%d]", object_data->name, object->charge ); } else { sprintf(description_string, "%s", object_data->name ); } sector_description_add_gore(description_string, object->gore_level ); } else if (terrain != NULL) { const TERRAIN_DATA *terrain_data; terrain_data = terrain_static_data(terrain); strcpy(description_string, terrain_data->name); sector_description_add_gore(description_string, terrain->gore_level ); } return description_string; } /* * returns true if the passed sector belongs to the passed class */ bool sector_is_class(const AREA_POINT *sector_location, SECTOR_CLASS sector_class ) { return (*SectorClass[sector_class])(sector_location); } /* * returns a randomly chosen sector of the wanted class */ SECTOR * sector_random(AREA_POINT *location, SECTOR_CLASS wanted_class) { AREA_POINT p, op; N_TRIES i; for (i = 0; i < SECTOR_RANDOM_N_TRIES; i++) { op.y = random_int(SectorBounds.top, SectorBounds.bottom); op.x = random_int(SectorBounds.left, SectorBounds.right); if (sector_is_class(&op, wanted_class)) { break; } } for (p.y = op.y; p.y <= SectorBounds.bottom; p.y++) { for (p.x = op.x; p.x <= SectorBounds.right; p.x++) { if (sector_is_class(&p, wanted_class)) { *location = p; return sector_at(location); } } } for (p.y = SectorBounds.top; p.y <= SectorBounds.bottom; p.y++) { for (p.x = SectorBounds.left; p.x <= SectorBounds.right; p.x++) { if (area_points_equal(&p, &op)) { return NULL; } if (sector_is_class(&p, wanted_class)) { *location = p; return sector_at(location); } } } /* NEVER REACHED */ return NULL; } /* * returns a sector located adjacent to the passed location */ SECTOR * sector_adjacent(AREA_POINT *location, SECTOR_CLASS wanted_class) { #define MAX_ADJACENT_SECTOR_MOD 99 int mod; for (mod = 1; mod <= MAX_ADJACENT_SECTOR_MOD; mod++) { AREA_POINT start, end, i; start.y = location->y - mod; start.x = location->x - mod; end.y = location->y + mod; end.x = location->x + mod; for (i.y = start.y; i.y <= end.y; i.y++) { int x_inc; if (i.y == start.y || i.y == end.y) { x_inc = 1; } else { x_inc = end.x - start.x; } for (i.x = start.x; i.x <= end.x; i.x += x_inc) { if (out_of_sector_bounds(&i)) { continue; } if (sector_is_class(&i, wanted_class)) { SECTOR *sector; *location = i; sector = sector_at(&i); return sector; } } } } return NULL; } /* * returns a sector which contains an elevator */ SECTOR * sector_elevator(AREA_POINT *location) { AREA_POINT p; for (p.y = SectorBounds.top; p.y <= SectorBounds.bottom; p.y++) { for (p.x = SectorBounds.left; p.x <= SectorBounds.right; p.x++) { OBJECT *object; object = object_at(&p); if (object == NULL) continue; if (object_static_data(object)->subtype == OSTYPE_ELEVATOR) { *location = p; return sector_at(location); } } } die("*** CORE ERROR *** no elevator found"); /* NEVER REACHED */ return NULL; } /* * returns a sector which contains a way up */ SECTOR * sector_way_up(AREA_POINT *location) { AREA_POINT p; for (p.y = SectorBounds.top; p.y <= SectorBounds.bottom; p.y++) { for (p.x = SectorBounds.left; p.x <= SectorBounds.right; p.x++) { OBJECT *object; object = object_at(&p); if (object == NULL) continue; if (object_static_data(object)->subtype == OSTYPE_WAY_UP) { *location = p; return sector_at(location); } } } die("*** CORE ERROR *** no way up found"); /* NEVER REACHED */ return NULL; } /* * returns a sector which contains a way down */ SECTOR * sector_way_down(AREA_POINT *location) { AREA_POINT p; for (p.y = SectorBounds.top; p.y <= SectorBounds.bottom; p.y++) { for (p.x = SectorBounds.left; p.x <= SectorBounds.right; p.x++) { OBJECT *object; object = object_at(&p); if (object == NULL) continue; if (object_static_data(object)->subtype == OSTYPE_WAY_DOWN) { *location = p; return sector_at(location); } } } die("*** CORE ERROR *** no way down found"); /* NEVER REACHED */ return NULL; } /* * returns true if the passed point is out of bounds */ static bool out_of_sector_bounds(const AREA_POINT *p) { if (p->y < SectorBounds.top || p->x < SectorBounds.left || p->y > SectorBounds.bottom || p->x > SectorBounds.right) { return true; } return false; } /* * sector class 'free' * i.e. a sector with plain terrain and without an object or a character */ static bool sc_free(const AREA_POINT *sector_location) { const SECTOR *sector; sector = sector_at(sector_location); if (sector->character != NULL) { return false; } if (sector->object != NULL) { return false; } if (terrain_has_attribute(sector->terrain, TA_IMPASSABLE)) { return false; } if (terrain_has_attribute(sector->terrain, TA_DANGEROUS)) { return false; } return true; } /* * sector class 'free object location' * i.e. one can place an object there */ static bool sc_free_object_location(const AREA_POINT *sector_location) { const SECTOR *sector; sector = sector_at(sector_location); if (sector->object != NULL) { return false; } if (terrain_has_attribute(sector->terrain, TA_IMPASSABLE)) { return false; } if (terrain_has_attribute(sector->terrain, TA_DANGEROUS)) { return false; } return true; } /* * sector class 'free object location antiblock' * i.e. one can place an impassable object there without blocking * access to other sectors */ static bool sc_free_object_location_antiblock(const AREA_POINT *sector_location) { if (sc_free_object_location(sector_location) && antiblock_check_passed(sector_location)) { return true; } return false; } /* * sector class 'move target' * i.e. a character can move there */ static bool sc_move_target(const AREA_POINT *sector_location) { const SECTOR *sector; sector = sector_at(sector_location); if (sector->character != NULL) { return false; } if (sector->object != NULL && object_has_attribute(sector->object, OA_IMPASSABLE)) { return false; } if (terrain_has_attribute(sector->terrain, TA_IMPASSABLE)) { return false; } return true; } /* * sector class 'move target safe' * i.e. a character can safely move there */ static bool sc_move_target_safe(const AREA_POINT *sector_location) { const SECTOR *sector; sector = sector_at(sector_location); if (sector->character != NULL) { return false; } if (sector->object != NULL && object_has_attribute(sector->object, OA_IMPASSABLE)) { return false; } if (terrain_has_attribute(sector->terrain, TA_IMPASSABLE)) { return false; } if (terrain_has_attribute(sector->terrain, TA_DANGEROUS)) { return false; } return true; } /* * antiblock check */ static bool antiblock_check_passed(const AREA_POINT *sector_location) { int y_mod, x_mod; for (y_mod = -1; y_mod <= 1; y_mod++) { for (x_mod = -1; x_mod <= 1; x_mod++) { AREA_POINT p; if (x_mod == 0 && y_mod == 0) { continue; } p.y = sector_location->y + y_mod; p.x = sector_location->x + x_mod; if (out_of_sector_bounds(&p)) { return false; } if (!sc_move_target(&p)) { return false; } } } return true; } /* * adds the gore description to a sector description */ static void sector_description_add_gore(char *description, GORE_LEVEL gore_level) { if (gore_level <= GORE_LEVEL_ZERO) { return; } strcat(description, " ("); strcat(description, gore_level_name(gore_level)); strcat(description, ")"); }