<commit_before>/*

    Copyright <NAME> and <NAME>, 2011, 2012
    
    This file is part of Lasercake.

    Lasercake 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.

    Lasercake 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 Lasercake.  If not, see <http://www.gnu.org/licenses/>.

*/

#include "specific_object_types.hpp"

#include "data_structures/bbox_collision_detector_iteration.hpp"
#include "tile_physics.hpp"

namespace /* anonymous */ {

typedef polygon_rational_type rational;

struct beam_first_contact_finder {
  beam_first_contact_finder(world const& w, line_segment beam, object_or_tile_identifier ignore):w_(w),beam_(beam) {ignores_.insert(ignore);}
  typedef rational cost_type;
  typedef optional_rational result_type;
  static const uint64_t too_large = (1ULL << 32) - 1;
  bool bbox_is_too_large(world_collision_detector::bounding_box const& bbox) const {
    return (bbox.size_minus_one(X) >= too_large || bbox.size_minus_one(Y) >= too_large || bbox.size_minus_one(Z) >= too_large);
  }
  bool bbox_is_too_large(tiles_collision_detector::bounding_box const& bbox) const {
    return (bbox.size_minus_one(X) >= (too_large/get_primitive_int(tile_width))
         || bbox.size_minus_one(Y) >= (too_large/get_primitive_int(tile_width))
         || bbox.size_minus_one(Z) >= (too_large/get_primitive_int(tile_height)));
  }
  result_type min_cost(tiles_collision_detector::bounding_box const& bbox) const {
    // hack - avoid overflow - don't try to filter out too-large regions
    if(bbox_is_too_large(bbox)) return rational(0);
    return get_first_intersection(beam_, convert_to_fine_units(tiles_collision_detector_bbox_to_tile_bounding_box(bbox)));
  }
  result_type min_cost(world_collision_detector::bounding_box const& bbox) const {
    // hack - avoid overflow - don't try to filter out too-large regions
    if(bbox_is_too_large(bbox)) return rational(0);
    return get_first_intersection(beam_, bbox);
  }
  result_type cost(object_identifier id, world_collision_detector::bounding_box const& bbox) const {
    // hack - avoid overflow - effect: incredibly large objects can't be hit by lasers
    if(bbox_is_too_large(bbox)) return result_type();
    if(ignores_.find(id) != ignores_.end()) return result_type();
    return w_.get_detail_shape_of_object_or_tile(id).first_intersection(beam_);
  }
  result_type cost(tile_location id, tiles_collision_detector::bounding_box const& bbox) const {
    // hack - avoid overflow - effect: incredibly large objects can't be hit by lasers
    if(bbox_is_too_large(bbox)) return result_type();
    if(ignores_.find(id) != ignores_.end()) return result_type();
    return get_first_intersection(beam_, fine_bounding_box_of_tile(id.coords()));
  }
private:
  world const& w_;
  line_segment beam_;
  unordered_set<object_or_tile_identifier> ignores_;
};

void fire_standard_laser(world& w, object_identifier my_id, vector3<fine_scalar> location, vector3<fine_scalar> facing) {
  facing = facing * tile_width * 2 / facing.magnitude_within_32_bits();
  const line_segment beam(location, location + facing * 50);
  const beam_first_contact_finder finder(w, beam, my_id);
  auto hit_tile =  w.tiles_exposed_to_collision().find_least(finder);
  auto hit_object =  w.objects_exposed_to_collision().find_least(finder);
  const rational farther_away_than_possible_intercept_point = rational(2); //the valid max is 1
  rational best_intercept_point = farther_away_than_possible_intercept_point;
  object_or_tile_identifier hit_thing;
  if(hit_tile) {
    hit_thing = hit_tile->object;
    best_intercept_point = hit_tile->cost;
  }
  if(hit_object && hit_object->cost <= best_intercept_point) {
    hit_thing = hit_object->object;
    best_intercept_point = hit_object->cost;
  }
  if(hit_thing != object_or_tile_identifier()) {
    // TODO do I have to worry about overflow?
    w.add_laser_sfx(location, multiply_rational_into(facing * 50, best_intercept_point));
    if(tile_location const* locp = hit_thing.get_tile_location()) {
      if (locp->stuff_at().contents() == ROCK) {
        w.replace_substance(*locp, ROCK, RUBBLE);
      }
    }
  }
  else {
    w.add_laser_sfx(location, facing * 50);
  }
}

const int robot_max_carrying_capacity = 4;

// Our robot "location" gets out of date relative to the personal_space_shape
// that the world defines us with (and changes due to velocity),
// so update it.
void update_location(vector3<fine_scalar>& location_, world& w, object_identifier id) {
  const bounding_box shape_bounds = w.get_object_personal_space_shapes().find(id)->second.bounds();
  const vector3<fine_scalar> middle = (shape_bounds.min() + shape_bounds.max()) / 2; //hmm, rounding.
  location_ = middle;
}

void float_above_ground(vector3<fine_scalar>& velocity_, world& w, object_identifier id) {
  const bounding_box shape_bounds = w.get_object_personal_space_shapes().find(id)->second.bounds();<commit_msg>Refactor laser code to be more reusable.<commit_after>/*

    Copyright <NAME> and <NAME>, 2011, 2012
    
    This file is part of Lasercake.

    Lasercake 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.

    Lasercake 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 Lasercake.  If not, see <http://www.gnu.org/licenses/>.

*/

#include "specific_object_types.hpp"

#include "data_structures/bbox_collision_detector_iteration.hpp"
#include "tile_physics.hpp"

namespace /* anonymous */ {

typedef polygon_rational_type rational;

struct beam_first_contact_finder {
  beam_first_contact_finder(world const& w, line_segment beam, object_or_tile_identifier ignore):w_(w),beam_(beam) {ignores_.insert(ignore);}
  typedef rational cost_type;
  typedef optional_rational result_type;
  static const uint64_t too_large = (1ULL << 32) - 1;
  bool bbox_is_too_large(world_collision_detector::bounding_box const& bbox) const {
    return (bbox.size_minus_one(X) >= too_large || bbox.size_minus_one(Y) >= too_large || bbox.size_minus_one(Z) >= too_large);
  }
  bool bbox_is_too_large(tiles_collision_detector::bounding_box const& bbox) const {
    return (bbox.size_minus_one(X) >= (too_large/get_primitive_int(tile_width))
         || bbox.size_minus_one(Y) >= (too_large/get_primitive_int(tile_width))
         || bbox.size_minus_one(Z) >= (too_large/get_primitive_int(tile_height)));
  }
  result_type min_cost(tiles_collision_detector::bounding_box const& bbox) const {
    // hack - avoid overflow - don't try to filter out too-large regions
    if(bbox_is_too_large(bbox)) return rational(0);
    return get_first_intersection(beam_, convert_to_fine_units(tiles_collision_detector_bbox_to_tile_bounding_box(bbox)));
  }
  result_type min_cost(world_collision_detector::bounding_box const& bbox) const {
    // hack - avoid overflow - don't try to filter out too-large regions
    if(bbox_is_too_large(bbox)) return rational(0);
    return get_first_intersection(beam_, bbox);
  }
  result_type cost(object_identifier id, world_collision_detector::bounding_box const& bbox) const {
    // hack - avoid overflow - effect: incredibly large objects can't be hit by lasers
    if(bbox_is_too_large(bbox)) return result_type();
    if(ignores_.find(id) != ignores_.end()) return result_type();
    return w_.get_detail_shape_of_object_or_tile(id).first_intersection(beam_);
  }
  result_type cost(tile_location id, tiles_collision_detector::bounding_box const& bbox) const {
    // hack - avoid overflow - effect: incredibly large objects can't be hit by lasers
    if(bbox_is_too_large(bbox)) return result_type();
    if(ignores_.find(id) != ignores_.end()) return result_type();
    return get_first_intersection(beam_, fine_bounding_box_of_tile(id.coords()));
  }
private:
  world const& w_;
  line_segment beam_;
  unordered_set<object_or_tile_identifier> ignores_;
};

// Returns object_or_tile_identifier() if nothing was hit.
// Has no side_effects unless add_laser_sfx==true.
object_or_tile_identifier laser_find(
    world& w,
    object_identifier dont_hit_this,
    vector3<fine_scalar> source,
    vector3<fine_scalar> beam_vector,
    bool add_laser_sfx
) {
  const line_segment beam(source, source + beam_vector);
  const beam_first_contact_finder finder(w, beam, dont_hit_this);
  auto hit_tile =  w.tiles_exposed_to_collision().find_least(finder);
  auto hit_object =  w.objects_exposed_to_collision().find_least(finder);
  const rational farther_away_than_possible_intercept_point = rational(2); //the valid max is 1
  rational best_intercept_point = farther_away_than_possible_intercept_point;
  object_or_tile_identifier hit_thing;
  if(hit_tile) {
    hit_thing = hit_tile->object;
    best_intercept_point = hit_tile->cost;
  }
  if(hit_object && hit_object->cost <= best_intercept_point) {
    hit_thing = hit_object->object;
    best_intercept_point = hit_object->cost;
  }
  if(add_laser_sfx) {
    if(hit_thing != object_or_tile_identifier()) {
      // TODO do I have to worry about overflow?
      w.add_laser_sfx(source, multiply_rational_into(beam_vector, best_intercept_point));
    }
    else {
      w.add_laser_sfx(source, beam_vector);
    }
  }
  return hit_thing;
}

void fire_standard_laser(world& w, object_identifier my_id, vector3<fine_scalar> location, vector3<fine_scalar> facing) {
  const vector3<fine_scalar> beam_vector = facing * tile_width * 2 / facing.magnitude_within_32_bits() * 50;
  const object_or_tile_identifier hit_thing = laser_find(w, my_id, location, beam_vector, true);
  if(tile_location const* locp = hit_thing.get_tile_location()) {
    if (locp->stuff_at().contents() == ROCK) {
      w.replace_substance(*locp, ROCK, RUBBLE);
    }
  }
}

const int robot_max_carrying_capacity = 4;

// Our robot "location" gets out of date relative to the personal_space_shape
// that the world defines us with (and changes due to velocity),
// so update it.
void update_location(vector3<fine_scalar>& location_, world& w, object_identifier id) {
  const bounding_box shape_bounds = w.get_object_personal_space_shapes().find(id)->second.bounds();
  const vector3<fine_scalar> middle = (shape_bounds.min() + shape_bounds.max()) / 2; //hmm, rounding.
  location_ = middle;
}

void float_above_ground(vector3<fine_scalar>& velocity_, world& w, object_identifier id) {
  const bounding_box shape_bounds = w.get_object_personal_space_shapes().find(id)->second.bounds();
{'fraction_non_alphanumeric': 0.09646388171372584, 'fraction_numerical': 0.006145898280929901, 'mean_word_length': 4.956498673740053, 'pattern_counts': {'":': 0, '<': 30, '<?xml version=': 0, '>': 52, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3211968', 'n_tokens_mistral': 3763, 'n_tokens_neox': 3617, 'n_words': 879}