110 lines
3.7 KiB
C++
110 lines
3.7 KiB
C++
/*
|
|
Copyright 2016-2018 Michele "King_DuckZ" Santullo
|
|
|
|
This file is part of MyCurry.
|
|
|
|
MyCurry 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.
|
|
|
|
MyCurry 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 MyCurry. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "collider.hpp"
|
|
#include "grid_raytrace.hpp"
|
|
#include "worldgrid.hpp"
|
|
#include "vectorwrapper/sequence_range.hpp"
|
|
#include <cstdint>
|
|
|
|
namespace curry {
|
|
namespace {
|
|
vec2us collision_borders (const vec2us& parObjSize, const vec2f& parDirection) {
|
|
assert(parObjSize > 0);
|
|
const uint16_t horz_offs = (parDirection.x() > 0.0f ? static_cast<uint16_t>(parObjSize.x() - 1) : 0);
|
|
const uint16_t vert_offs = (parDirection.y() > 0.0f ? static_cast<uint16_t>(parObjSize.y() - 1) : 0);
|
|
return vec2us(horz_offs, vert_offs);
|
|
}
|
|
|
|
vec2us check_tiles_count (const vec2us& parObjSize, const vec2f& parDirection) {
|
|
assert(parObjSize > 0);
|
|
const uint16_t horz = (parDirection.y() != 0.0f ? parObjSize.x() : 1);
|
|
const uint16_t vert = (parDirection.x() != 0.0f ? parObjSize.y() : 1);
|
|
return vec2us(horz, vert);
|
|
}
|
|
|
|
vec2f direction_mask (const vec2f& parDirection) {
|
|
const auto horz = (parDirection.x() == 0.0f ? 0.0f : 1.0f);
|
|
const auto vert = (parDirection.y() == 0.0f ? 0.0f : 1.0f);
|
|
return vec2f(horz, vert);
|
|
}
|
|
|
|
vec2f make_pixel_side_offset (
|
|
const vec2us& parTileSize,
|
|
const vec2us& parSideOffset,
|
|
const vec2f& parDirection
|
|
) {
|
|
const vec2f tile_size = vector_cast<vec2f>(parTileSize);
|
|
vec2f tile_start = tile_size * vector_cast<vec2f>(parSideOffset);
|
|
if (parDirection.x() > 0.0f)
|
|
tile_start.x() += tile_size.x() - 1.0f;
|
|
if (parDirection.y() > 0.0f)
|
|
tile_start.y() += tile_size.y() - 1.0f;
|
|
return tile_start;
|
|
}
|
|
} //unnamed namespace
|
|
|
|
vec2f Collider::try_reach_tile (
|
|
const vec2us& parObjectSize,
|
|
const vec2f& parFrom,
|
|
const vec2f& parTo,
|
|
const WorldGrid& parWorld
|
|
) {
|
|
const vec2f direction = parTo - parFrom;
|
|
if (direction == 0.0f)
|
|
return parTo;
|
|
|
|
const vec2us side_offset = collision_borders(parObjectSize, direction);
|
|
const vec2us length = check_tiles_count(parObjectSize, direction);
|
|
const vec2f pix_side_offset =
|
|
make_pixel_side_offset(parWorld.tile_size(), side_offset, direction);
|
|
const vec2f to = parTo + pix_side_offset;
|
|
const vec2f from = parFrom + pix_side_offset;
|
|
vec2us stop_at = pixel_to_world_tile(parWorld, from);
|
|
const vec2us dest_tile = pixel_to_world_tile(parWorld, to);
|
|
vec2f tile_relative_offs = to - world_tile_to_pixel(parWorld, dest_tile);
|
|
assert(tile_relative_offs < vector_cast<vec2f>(parWorld.tile_size()));
|
|
|
|
vec2f collision_mask(0.0f);
|
|
auto forward_callback = [=,&parWorld,&stop_at,&tile_relative_offs,&collision_mask](vec2us tile) {
|
|
for (auto curr_pos : vwr::increasing_sequence_range<vec2us>(tile, tile + length)) {
|
|
const bool walkable = parWorld.tile_property(parWorld.tile(curr_pos)).walkable;
|
|
if (not walkable) {
|
|
collision_mask = direction_mask(direction);
|
|
tile_relative_offs *= (1.0f - collision_mask);
|
|
return false;
|
|
}
|
|
}
|
|
stop_at = tile;
|
|
return true;
|
|
};
|
|
|
|
for_each_cell_under_segment(
|
|
from,
|
|
to,
|
|
parObjectSize,
|
|
parWorld,
|
|
forward_callback,
|
|
false
|
|
);
|
|
|
|
return world_tile_to_pixel(parWorld, stop_at) + tile_relative_offs - pix_side_offset + collision_mask * direction_mask(pix_side_offset) * 31.0f;
|
|
}
|
|
} //namespace curry
|