Initial commit
This commit is contained in:
823
engines/ultima/nuvie/core/map.cpp
Normal file
823
engines/ultima/nuvie/core/map.cpp
Normal file
@@ -0,0 +1,823 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/shared/std/string.h"
|
||||
|
||||
#include "ultima/nuvie/core/nuvie_defs.h"
|
||||
#include "ultima/nuvie/files/nuvie_io_file.h"
|
||||
|
||||
#include "ultima/nuvie/conf/configuration.h"
|
||||
#include "ultima/nuvie/core/game.h"
|
||||
#include "ultima/nuvie/core/tile_manager.h"
|
||||
#include "ultima/nuvie/actors/actor_manager.h"
|
||||
#include "ultima/nuvie/core/map.h"
|
||||
#include "ultima/nuvie/gui/widgets/map_window.h"
|
||||
|
||||
#include "ultima/nuvie/misc/u6_misc.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
Map::Map(const Configuration *cfg) : config(cfg), tile_manager(nullptr),
|
||||
obj_manager(nullptr), actor_manager(nullptr), surface(nullptr),
|
||||
roof_surface(nullptr) {
|
||||
ARRAYCLEAR(dungeons);
|
||||
|
||||
config->value(config_get_game_key(config) + "/roof_mode", roof_mode, false);
|
||||
}
|
||||
|
||||
Map::~Map() {
|
||||
if (surface == nullptr)
|
||||
return;
|
||||
|
||||
free(surface);
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
free(dungeons[i]);
|
||||
|
||||
if (roof_surface)
|
||||
free(roof_surface);
|
||||
}
|
||||
|
||||
|
||||
byte *Map::get_map_data(uint8 level) {
|
||||
if (level == 0)
|
||||
return surface;
|
||||
|
||||
if (level > 5)
|
||||
return nullptr;
|
||||
|
||||
return dungeons[level - 1];
|
||||
}
|
||||
|
||||
uint16 *Map::get_roof_data(uint8 level) {
|
||||
if (level == 0)
|
||||
return roof_surface;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const Tile *Map::get_tile(uint16 x, uint16 y, uint8 level, bool original_tile) {
|
||||
if (level > 5)
|
||||
return nullptr;
|
||||
|
||||
WRAP_COORD(x, level);
|
||||
WRAP_COORD(y, level);
|
||||
|
||||
const uint8 *ptr = get_map_data(level);
|
||||
const Tile *map_tile;
|
||||
if (original_tile)
|
||||
map_tile = tile_manager->get_original_tile(ptr[y * get_width(level) + x]);
|
||||
else
|
||||
map_tile = tile_manager->get_tile(ptr[y * get_width(level) + x]);
|
||||
|
||||
return map_tile;
|
||||
}
|
||||
|
||||
uint16 Map::get_width(uint8 level) const {
|
||||
if (level == 0)
|
||||
return 1024; // surface
|
||||
|
||||
return 256; // dungeon
|
||||
}
|
||||
|
||||
bool Map::is_passable(uint16 x, uint16 y, uint8 level) {
|
||||
WRAP_COORD(x, level);
|
||||
WRAP_COORD(y, level);
|
||||
|
||||
uint8 obj_status = obj_manager->is_passable(x, y, level);
|
||||
if (obj_status == OBJ_NOT_PASSABLE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//special case for bridges, hacked doors and dungeon entrances etc.
|
||||
if (obj_status != OBJ_NO_OBJ && obj_manager->is_forced_passable(x, y, level))
|
||||
return true;
|
||||
|
||||
const uint8 *ptr = get_map_data(level);
|
||||
const Tile *map_tile = tile_manager->get_original_tile(ptr[y * get_width(level) + x]);
|
||||
|
||||
return map_tile->passable;
|
||||
}
|
||||
|
||||
/***
|
||||
* Can we enter this map location by traveling in a given direction?
|
||||
* Used by MD
|
||||
*/
|
||||
bool Map::is_passable(uint16 x, uint16 y, uint8 level, NuvieDir dir) {
|
||||
if (is_passable_from_dir(x, y, level, get_reverse_direction(dir))) {
|
||||
sint16 rel_x, rel_y;
|
||||
uint16 tx, ty;
|
||||
get_relative_dir(get_reverse_direction(dir), &rel_x, &rel_y);
|
||||
tx = wrap_signed_coord((sint16)x + rel_x, level);
|
||||
ty = wrap_signed_coord((sint16)y + rel_y, level);
|
||||
return is_passable_from_dir(tx, ty, level, dir);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Map::is_passable_from_dir(uint16 x, uint16 y, uint8 level, NuvieDir dir) {
|
||||
WRAP_COORD(x, level);
|
||||
WRAP_COORD(y, level);
|
||||
|
||||
uint8 obj_status = obj_manager->is_passable(x, y, level);
|
||||
if (obj_status == OBJ_NOT_PASSABLE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//special case for bridges, hacked doors and dungeon entrances etc.
|
||||
if (obj_status != OBJ_NO_OBJ && obj_manager->is_forced_passable(x, y, level))
|
||||
return true;
|
||||
|
||||
const uint8 *ptr = get_map_data(level);
|
||||
const Tile *map_tile = tile_manager->get_original_tile(ptr[y * get_width(level) + x]);
|
||||
|
||||
if (!map_tile->passable && !(map_tile->flags1 & TILEFLAG_WALL)) {
|
||||
switch (dir) {
|
||||
case NUVIE_DIR_W :
|
||||
return (map_tile->flags1 & TILEFLAG_WALL_WEST);
|
||||
case NUVIE_DIR_S :
|
||||
return (map_tile->flags1 & TILEFLAG_WALL_SOUTH);
|
||||
case NUVIE_DIR_E :
|
||||
return (map_tile->flags1 & TILEFLAG_WALL_EAST);
|
||||
case NUVIE_DIR_N :
|
||||
return (map_tile->flags1 & TILEFLAG_WALL_NORTH);
|
||||
case NUVIE_DIR_NE :
|
||||
return !(!(map_tile->flags1 & TILEFLAG_WALL_NORTH) || !(map_tile->flags1 & TILEFLAG_WALL_EAST));
|
||||
case NUVIE_DIR_NW :
|
||||
return !(!(map_tile->flags1 & TILEFLAG_WALL_NORTH) || !(map_tile->flags1 & TILEFLAG_WALL_WEST));
|
||||
case NUVIE_DIR_SE :
|
||||
return !(!(map_tile->flags1 & TILEFLAG_WALL_SOUTH) || !(map_tile->flags1 & TILEFLAG_WALL_EAST));
|
||||
case NUVIE_DIR_SW :
|
||||
return !(!(map_tile->flags1 & TILEFLAG_WALL_SOUTH) || !(map_tile->flags1 & TILEFLAG_WALL_WEST));
|
||||
default:
|
||||
error("Invalid direction in Map::is_passable_from_dir");
|
||||
}
|
||||
}
|
||||
|
||||
return map_tile->passable;
|
||||
}
|
||||
|
||||
/* Returns true if an entire area is_passable(). */
|
||||
bool Map::is_passable(uint16 x1, uint16 y1, uint16 x2, uint16 y2, uint8 level) {
|
||||
for (int x = x1; x <= x2; x++)
|
||||
for (int y = y1; y <= y2; y++)
|
||||
if (!is_passable((uint16)x, (uint16)y, level))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Map::is_boundary(uint16 x, uint16 y, uint8 level) {
|
||||
WRAP_COORD(x, level);
|
||||
WRAP_COORD(y, level);
|
||||
|
||||
uint8 *ptr = get_map_data(level);
|
||||
Tile *map_tile = tile_manager->get_tile(ptr[y * get_width(level) + x]);
|
||||
|
||||
if (map_tile->boundary && obj_manager->is_forced_passable(x, y, level) == false)
|
||||
return true;
|
||||
|
||||
if (obj_manager->is_boundary(x, y, level))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Map::is_missile_boundary(uint16 x, uint16 y, uint8 level, Obj *excluded_obj) {
|
||||
WRAP_COORD(x, level);
|
||||
WRAP_COORD(y, level);
|
||||
|
||||
uint8 *ptr = get_map_data(level);
|
||||
Tile *map_tile = tile_manager->get_tile(ptr[y * get_width(level) + x]);
|
||||
|
||||
if ((map_tile->flags2 & TILEFLAG_MISSILE_BOUNDARY) != 0 && obj_manager->is_forced_passable(x, y, level) == false)
|
||||
return true;
|
||||
|
||||
if (obj_manager->is_boundary(x, y, level, TILEFLAG_MISSILE_BOUNDARY, excluded_obj))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Map::is_water(uint16 x, uint16 y, uint16 level, bool ignore_objects) {
|
||||
WRAP_COORD(x, level);
|
||||
WRAP_COORD(y, level);
|
||||
|
||||
if (!ignore_objects) {
|
||||
const Obj *obj = obj_manager->get_obj(x, y, level);
|
||||
if (obj != nullptr)
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint8 *ptr = get_map_data(level);
|
||||
const Tile *map_tile = tile_manager->get_original_tile(ptr[y * get_width(level) + x]);
|
||||
|
||||
if (map_tile->water)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Map::is_damaging(uint16 x, uint16 y, uint8 level, bool ignore_objects) {
|
||||
const uint8 *ptr = get_map_data(level);
|
||||
|
||||
WRAP_COORD(x, level);
|
||||
WRAP_COORD(y, level);
|
||||
|
||||
const Tile *map_tile = tile_manager->get_original_tile(ptr[y * get_width(level) + x]);
|
||||
|
||||
if (map_tile->damages)
|
||||
return true;
|
||||
|
||||
if (!ignore_objects) {
|
||||
if (obj_manager->is_damaging(x, y, level))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Map::can_put_obj(uint16 x, uint16 y, uint8 level) {
|
||||
LineTestResult lt;
|
||||
|
||||
if (lineTest(x, y, x, y, level, LT_HitActors | LT_HitUnpassable, lt)) {
|
||||
if (lt.hitObj) {
|
||||
// We can place an object on a bench or table. Or on any other object if
|
||||
// the object is passable and not on a boundary.
|
||||
|
||||
Tile *obj_tile = obj_manager->get_obj_tile(lt.hitObj->obj_n, lt.hitObj->frame_n);
|
||||
if ((obj_tile->flags3 & TILEFLAG_CAN_PLACE_ONTOP) ||
|
||||
(obj_tile->passable && !is_boundary(lt.hit_x, lt.hit_y, lt.hit_level))) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_missile_boundary(x, y, level))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8 Map::get_impedance(uint16 x, uint16 y, uint8 level, bool ignore_objects) {
|
||||
uint8 *ptr = get_map_data(level);
|
||||
WRAP_COORD(x, level);
|
||||
WRAP_COORD(y, level);
|
||||
const Tile *map_tile = tile_manager->get_original_tile(ptr[y * get_width(level) + x]);
|
||||
uint8 impedance = 0;
|
||||
|
||||
if (!ignore_objects) {
|
||||
const U6LList *obj_list = obj_manager->get_obj_list(x, y, level);
|
||||
if (obj_list) {
|
||||
for (const U6Link *link = obj_list->start(); link != nullptr; link = link->next) {
|
||||
const Obj *obj = (Obj *)link->data;
|
||||
if (obj != nullptr) {
|
||||
uint8 tile_flag = obj_manager->get_obj_tile(obj->obj_n, obj->frame_n)->flags1;
|
||||
if ((tile_flag & TILEFLAG_BLOCKING) == 0) {
|
||||
impedance += (tile_flag & TILEFLAG_IMPEDANCE) >> TILEFLAG_IMPEDANCE_SHIFT;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((map_tile->flags1 & TILEFLAG_BLOCKING) == 0)
|
||||
impedance += (map_tile->flags1 & TILEFLAG_IMPEDANCE) >> TILEFLAG_IMPEDANCE_SHIFT;
|
||||
|
||||
return impedance;
|
||||
}
|
||||
|
||||
const Tile *Map::get_dmg_tile(uint16 x, uint16 y, uint8 level) {
|
||||
const Tile *tile = get_tile(x, y, level);
|
||||
|
||||
if (tile->damages)
|
||||
return tile;
|
||||
|
||||
return obj_manager->get_obj_dmg_tile(x, y, level);
|
||||
}
|
||||
|
||||
bool Map::actor_at_location(uint16 x, uint16 y, uint8 level, bool inc_surrounding_objs) {
|
||||
WRAP_COORD(x, level);
|
||||
WRAP_COORD(y, level);
|
||||
//check for blocking Actor at location.
|
||||
if (actor_manager->get_actor(x, y, level, inc_surrounding_objs) != nullptr)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Return pointer to actor standing at map coordinates.
|
||||
*/
|
||||
Actor *Map::get_actor(uint16 x, uint16 y, uint8 z, bool inc_surrounding_objs) {
|
||||
WRAP_COORD(x, z);
|
||||
WRAP_COORD(y, z);
|
||||
return (actor_manager->get_actor(x, y, z, inc_surrounding_objs));
|
||||
}
|
||||
|
||||
|
||||
const char *Map::look(uint16 x, uint16 y, uint8 level) {
|
||||
unsigned char *ptr;
|
||||
uint16 qty = 0;
|
||||
|
||||
if (level == 0) {
|
||||
ptr = surface;
|
||||
} else
|
||||
ptr = dungeons[level - 1];
|
||||
|
||||
WRAP_COORD(x, level);
|
||||
WRAP_COORD(y, level);
|
||||
Obj *obj = obj_manager->get_obj(x, y, level);
|
||||
if (obj != nullptr && !(obj->status & OBJ_STATUS_INVISIBLE) //only show visible objects.
|
||||
&& !Game::get_game()->get_map_window()->tile_is_black(obj->x, obj->y, obj)) {
|
||||
// tile = tile_manager->get_original_tile(obj_manager->get_obj_tile_num(obj->obj_n)+obj->frame_n);
|
||||
// tile_num = tile->tile_num;
|
||||
// qty = obj->qty;
|
||||
return obj_manager->look_obj(obj);
|
||||
}
|
||||
uint16 tile_num = ptr[y * get_width(level) + x];
|
||||
return tile_manager->lookAtTile(tile_num, qty, true);
|
||||
}
|
||||
|
||||
|
||||
bool Map::loadMap(TileManager *tm, ObjManager *om) {
|
||||
Common::Path filename;
|
||||
NuvieIOFileRead map_file;
|
||||
NuvieIOFileRead chunks_file;
|
||||
|
||||
uint8 i;
|
||||
|
||||
tile_manager = tm;
|
||||
obj_manager = om;
|
||||
|
||||
config_get_path(config, "map", filename);
|
||||
if (map_file.open(filename) == false)
|
||||
return false;
|
||||
|
||||
config_get_path(config, "chunks", filename);
|
||||
if (chunks_file.open(filename) == false)
|
||||
return false;
|
||||
|
||||
unsigned char *map_data = map_file.readAll();
|
||||
if (map_data == nullptr)
|
||||
return false;
|
||||
|
||||
unsigned char *chunk_data = chunks_file.readAll();
|
||||
if (chunk_data == nullptr)
|
||||
return false;
|
||||
|
||||
unsigned char *map_ptr = map_data;
|
||||
|
||||
surface = (unsigned char *)malloc(1024 * 1024);
|
||||
if (surface == nullptr)
|
||||
return false;
|
||||
|
||||
for (i = 0; i < 64; i++) {
|
||||
insertSurfaceSuperChunk(map_ptr, chunk_data, i);
|
||||
map_ptr += 384;
|
||||
}
|
||||
|
||||
for (i = 0; i < 5; i++) {
|
||||
dungeons[i] = (unsigned char *)malloc(256 * 256);
|
||||
if (dungeons[i] == nullptr)
|
||||
return false;
|
||||
|
||||
insertDungeonSuperChunk(map_ptr, chunk_data, i);
|
||||
map_ptr += 1536;
|
||||
}
|
||||
|
||||
free(map_data);
|
||||
free(chunk_data);
|
||||
|
||||
if (roof_mode)
|
||||
loadRoofData();
|
||||
|
||||
/* ERIC Useful for testing map wrapping
|
||||
I plan to add a map patch function
|
||||
to allow custom map changes to be
|
||||
loaded easily into nuvie.
|
||||
|
||||
uint16 mx,my;
|
||||
for(my=100;my<130;my++)
|
||||
for(mx=0;mx<30;mx++)
|
||||
surface[my * 1024 + mx] = 1;
|
||||
|
||||
for(my=100;my<130;my++)
|
||||
for(mx=1000;mx<1024;mx++)
|
||||
surface[my * 1024 + mx] = 111;
|
||||
*/
|
||||
/*
|
||||
printf("\n\n\n\n\n\n\n");
|
||||
|
||||
uint16 mx,my;
|
||||
for(my=0;my<1024;my++)
|
||||
{
|
||||
for(mx=0;mx<1024;mx++)
|
||||
{
|
||||
printf("%3d,", surface[my * 1024 + mx]+1);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Map::has_roof(uint16 x, uint16 y, uint8 level) const {
|
||||
if (!roof_mode || level != 0)
|
||||
return false;
|
||||
|
||||
if (roof_surface[y * 1024 + x] != 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Common::Path Map::getRoofDataFilename() const {
|
||||
Std::string game_type, tmp;
|
||||
Common::Path datadir, path, mapfile;
|
||||
|
||||
config->value("config/datadir", tmp, "");
|
||||
config->value("config/GameID", game_type);
|
||||
|
||||
datadir = Common::Path(tmp);
|
||||
build_path(datadir, "maps", path);
|
||||
datadir = path;
|
||||
build_path(datadir, game_type, path);
|
||||
datadir = path;
|
||||
build_path(datadir, "roof_map_00.dat", mapfile);
|
||||
|
||||
return mapfile;
|
||||
}
|
||||
|
||||
Common::Path Map::getRoofTilesetFilename() const {
|
||||
Std::string tmp;
|
||||
Common::Path datadir;
|
||||
Common::Path imagefile;
|
||||
Common::Path path;
|
||||
|
||||
config->value("config/datadir", tmp, "");
|
||||
|
||||
datadir = Common::Path(tmp);
|
||||
build_path(datadir, "images", path);
|
||||
datadir = path;
|
||||
build_path(datadir, "roof_tiles.bmp", imagefile);
|
||||
return imagefile;
|
||||
}
|
||||
|
||||
void Map::set_roof_mode(bool roofs) {
|
||||
roof_mode = roofs;
|
||||
if (roof_mode) {
|
||||
if (roof_surface)
|
||||
return;
|
||||
else
|
||||
loadRoofData();
|
||||
} else {
|
||||
if (roof_surface) {
|
||||
free(roof_surface);
|
||||
roof_surface = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Map::loadRoofData() {
|
||||
NuvieIOFileRead file;
|
||||
roof_surface = (uint16 *)malloc(1024 * 1024 * 2);
|
||||
|
||||
if (file.open(getRoofDataFilename())) {
|
||||
memset(roof_surface, 0, 1024 * 1024 * 2);
|
||||
uint16 *ptr = roof_surface;
|
||||
while (!file.is_eof()) {
|
||||
uint16 offset = file.read2();
|
||||
ptr += offset;
|
||||
uint8 run_len = file.read1();
|
||||
for (uint8 i = 0; i < run_len; i++) {
|
||||
*ptr = file.read2();
|
||||
ptr++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (roof_surface) {
|
||||
free(roof_surface);
|
||||
roof_surface = nullptr;
|
||||
}
|
||||
roof_mode = false;
|
||||
}
|
||||
}
|
||||
|
||||
void Map::saveRoofData() {
|
||||
NuvieIOFileWrite file;
|
||||
uint32 prev_offset = 0;
|
||||
uint32 cur_offset = 0;
|
||||
uint16 run_length = 0;
|
||||
|
||||
if (roof_surface && file.open(getRoofDataFilename())) {
|
||||
for (; cur_offset < 1048576;) {
|
||||
for (; cur_offset < prev_offset + 65535 && cur_offset < 1048576;) {
|
||||
if (roof_surface[cur_offset] != 0) {
|
||||
file.write2((uint16)(cur_offset - prev_offset));
|
||||
for (run_length = 0; run_length < 256; run_length++) {
|
||||
if (roof_surface[cur_offset + run_length] == 0)
|
||||
break;
|
||||
}
|
||||
if (run_length == 256)
|
||||
run_length--;
|
||||
|
||||
file.write1((uint8)run_length);
|
||||
for (uint8 i = 0; i < run_length; i++) {
|
||||
file.write2(roof_surface[cur_offset + i]);
|
||||
}
|
||||
cur_offset += run_length;
|
||||
break;
|
||||
}
|
||||
|
||||
cur_offset++;
|
||||
if (cur_offset == prev_offset + 65535) {
|
||||
//write blank.
|
||||
file.write2(65535);
|
||||
file.write1(0);
|
||||
}
|
||||
}
|
||||
|
||||
prev_offset = cur_offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Map::insertSurfaceSuperChunk(const unsigned char *schunk, const unsigned char *chunk_data, uint8 schunk_num) {
|
||||
uint16 world_x, world_y;
|
||||
uint16 c1, c2;
|
||||
uint8 i, j;
|
||||
|
||||
world_x = schunk_num % 8;
|
||||
world_y = (schunk_num - world_x) / 8;
|
||||
|
||||
world_x *= 128;
|
||||
world_y *= 128;
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
for (j = 0; j < 16; j += 2) {
|
||||
c1 = ((schunk[1] & 0xf) << 8) | schunk[0];
|
||||
c2 = (schunk[2] << 4) | (schunk[1] >> 4);
|
||||
|
||||
insertSurfaceChunk(&chunk_data[c1 * 64], world_x + j * 8, world_y + i * 8);
|
||||
insertSurfaceChunk(&chunk_data[c2 * 64], world_x + (j + 1) * 8, world_y + i * 8);
|
||||
|
||||
schunk += 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Map::insertSurfaceChunk(const unsigned char *chunk, uint16 x, uint16 y) {
|
||||
unsigned char *map_ptr;
|
||||
|
||||
map_ptr = &surface[y * 1024 + x];
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
memcpy(map_ptr, chunk, 8);
|
||||
map_ptr += 1024;
|
||||
chunk += 8;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Map::insertDungeonSuperChunk(const unsigned char *schunk, const unsigned char *chunk_data, uint8 level) {
|
||||
uint16 c1, c2;
|
||||
uint8 i, j;
|
||||
|
||||
for (i = 0; i < 32; i++) {
|
||||
for (j = 0; j < 32; j += 2) {
|
||||
c1 = ((schunk[1] & 0xf) << 8) | schunk[0];
|
||||
c2 = (schunk[2] << 4) | (schunk[1] >> 4);
|
||||
|
||||
insertDungeonChunk(&chunk_data[c1 * 64], j * 8, i * 8, level);
|
||||
insertDungeonChunk(&chunk_data[c2 * 64], (j + 1) * 8, i * 8, level);
|
||||
|
||||
schunk += 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Map::insertDungeonChunk(const unsigned char *chunk, uint16 x, uint16 y, uint8 level) {
|
||||
unsigned char *map_ptr;
|
||||
|
||||
map_ptr = &dungeons[level][y * 256 + x];
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
memcpy(map_ptr, chunk, 8);
|
||||
map_ptr += 256;
|
||||
chunk += 8;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Get absolute coordinates for relative destination from MapCoord.
|
||||
*/
|
||||
MapCoord MapCoord::abs_coords(sint16 dx, sint16 dy) const {
|
||||
// uint16 pitch = Map::get_width(z); cannot call function without object
|
||||
uint16 pitch = (z == 0) ? 1024 : 256;
|
||||
dx += x;
|
||||
dy += y;
|
||||
// wrap on map boundary for MD
|
||||
if (dx < 0)
|
||||
dx = pitch + dx;
|
||||
else if (dx >= pitch)
|
||||
dx = pitch - dx;
|
||||
if (dy < 0)
|
||||
dy = 0;
|
||||
else if (dy >= pitch)
|
||||
dy = pitch - 1;
|
||||
return (MapCoord(dx, dy, z));
|
||||
}
|
||||
|
||||
|
||||
/* Returns true if this map coordinate is visible in the game window.
|
||||
*/
|
||||
bool MapCoord::is_visible() const {
|
||||
return (Game::get_game()->get_map_window()->in_window(x, y, z));
|
||||
}
|
||||
|
||||
|
||||
bool Map::testIntersection(int x, int y, uint8 level, uint8 flags, LineTestResult &Result, Obj *excluded_obj) {
|
||||
/* more checks added, may need more testing (SB-X) */
|
||||
#if 0
|
||||
if (flags & LT_HitUnpassable) {
|
||||
if (!is_passable(x, y, level)) {
|
||||
Result.init(x, y, level, nullptr, obj_manager->get_obj(x, y, level, true));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & LT_HitForcedPassable) {
|
||||
if (obj_manager->is_forced_passable(x, y, level)) {
|
||||
Result.init(x, y, level, nullptr, obj_manager->get_obj(x, y, level, true));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & LT_HitActors) {
|
||||
// TODO:
|
||||
}
|
||||
|
||||
return false;
|
||||
#else
|
||||
if (flags & LT_HitUnpassable) {
|
||||
if (!is_passable(x, y, level)) {
|
||||
Obj *obj_hit = obj_manager->get_obj(x, y, level);
|
||||
if (!obj_hit || !excluded_obj || obj_hit != excluded_obj) {
|
||||
Result.init(x, y, level, nullptr, obj_manager->get_obj(x, y, level, true));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & LT_HitMissileBoundary) {
|
||||
if (is_missile_boundary(x, y, level, excluded_obj)) {
|
||||
Result.init(x, y, level, nullptr, obj_manager->get_obj(x, y, level, true));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & LT_HitForcedPassable) {
|
||||
if (obj_manager->is_forced_passable(x, y, level)) {
|
||||
Result.init(x, y, level, nullptr, obj_manager->get_obj(x, y, level, true));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & LT_HitActors) {
|
||||
if (actor_manager->get_actor(x, y, level)) {
|
||||
Result.init(x, y, level, actor_manager->get_actor(x, y, level), nullptr);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ((flags & LT_HitLocation) && Result.loc_to_hit) {
|
||||
if (x == Result.loc_to_hit->x && y == Result.loc_to_hit->y) {
|
||||
Result.init(x, y, level, nullptr, nullptr);
|
||||
Result.loc_to_hit->z = level;
|
||||
Result.hitLoc = Result.loc_to_hit;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & LT_HitObjects) {
|
||||
if (obj_manager->get_obj(x, y, level)) {
|
||||
Result.init(x, y, level, nullptr, obj_manager->get_obj(x, y, level, true));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
// returns true if a line hits something travelling from (start_x, start_y) to
|
||||
// (end_x, end_y). If a hit occurs Result is filled in with the relevant info.
|
||||
// If want_screen_space is true input tile coordinates are multiplied by 16 for
|
||||
// line calculation and scaled back down before testing for collisions. The
|
||||
// original game does this for projectiles.
|
||||
bool Map::lineTest(int start_x, int start_y, int end_x, int end_y, uint8 level,
|
||||
uint8 flags, LineTestResult &Result, uint32 skip, Obj *excluded_obj,
|
||||
bool want_screen_space) {
|
||||
// standard Bresenham's algorithm.
|
||||
uint8 scale_factor_log2 = 0;
|
||||
if (want_screen_space)
|
||||
scale_factor_log2 = 4; // set scale factor to 16
|
||||
int deltax = abs(end_x - start_x) << scale_factor_log2;
|
||||
int deltay = abs(end_y - start_y) << scale_factor_log2;
|
||||
int x = (start_x << scale_factor_log2);
|
||||
int y = (start_y << scale_factor_log2);
|
||||
x += ((1 << scale_factor_log2) >> 1); // start at the center of the tile
|
||||
y += ((1 << scale_factor_log2) >> 1); // when in screen space
|
||||
int d;
|
||||
int xinc1, xinc2;
|
||||
int yinc1, yinc2;
|
||||
int dinc1, dinc2;
|
||||
uint32 count;
|
||||
int xtile = start_x;
|
||||
int ytile = start_y;
|
||||
int xtile_prev = xtile;
|
||||
int ytile_prev = ytile;
|
||||
|
||||
|
||||
if (deltax >= deltay) {
|
||||
d = (deltay << 1) - deltax;
|
||||
|
||||
count = deltax + 1;
|
||||
dinc1 = deltay << 1;
|
||||
dinc2 = (deltay - deltax) << 1;
|
||||
xinc1 = 1;
|
||||
xinc2 = 1;
|
||||
yinc1 = 0;
|
||||
yinc2 = 1;
|
||||
} else {
|
||||
d = (deltax << 1) - deltay;
|
||||
|
||||
count = deltay + 1;
|
||||
dinc1 = deltax << 1;
|
||||
dinc2 = (deltax - deltay) << 1;
|
||||
xinc1 = 0;
|
||||
xinc2 = 1;
|
||||
yinc1 = 1;
|
||||
yinc2 = 1;
|
||||
}
|
||||
|
||||
if (start_x > end_x) {
|
||||
xinc1 = -xinc1;
|
||||
xinc2 = -xinc2;
|
||||
}
|
||||
if (start_y > end_y) {
|
||||
yinc1 = -yinc1;
|
||||
yinc2 = -yinc2;
|
||||
}
|
||||
|
||||
for (uint32 i = 0; i < count; i++) {
|
||||
// only test for collision if tile coordinates have changed
|
||||
if ((scale_factor_log2 == 0 || x >> scale_factor_log2 != xtile || y >> scale_factor_log2 != ytile)) {
|
||||
xtile_prev = xtile;
|
||||
ytile_prev = ytile;
|
||||
xtile = x >> scale_factor_log2; // scale back down to tile
|
||||
ytile = y >> scale_factor_log2; // space if necessary
|
||||
// test the current location
|
||||
if ((i >= skip) && (testIntersection(xtile, ytile, level, flags, Result, excluded_obj) == true)) {
|
||||
Result.pre_hit_x = xtile_prev;
|
||||
Result.pre_hit_y = ytile_prev;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (d < 0) {
|
||||
d += dinc1;
|
||||
x += xinc1;
|
||||
y += yinc1;
|
||||
} else {
|
||||
d += dinc2;
|
||||
x += xinc2;
|
||||
y += yinc2;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
Reference in New Issue
Block a user