/* 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 . */ #include "collider.hpp" #include "grid_raytrace.hpp" #include "worldgrid.hpp" #include "vectorwrapper/sequence_range.hpp" #include 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(parObjSize.x() - 1) : 0); const uint16_t vert_offs = (parDirection.y() > 0.0f ? static_cast(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(parTileSize); vec2f tile_start = tile_size * vector_cast(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(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(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