Files
tactical-shooter/gdextension/simulation/src/hit_detection.h
T
shawn e9dc05983c Save workspace artifacts: character-controller, gdextension scaffold, network scripts, map-pipeline, server config
Includes code from task workspaces that was never pushed:
- client/characters/ — fps_character_controller.gd (400 lines), fps_camera.gd, input_handler.gd
- gdextension/simulation/ — C++ GDExtension scaffold: entity, movement, hit detection, simulation server, state serializer, bitstream (14 files)
- scripts/network/ — ENet-based network_manager.gd, server/client_main.gd, player.gd
- scripts/map_packaging/ — PCK pipeline: pack_map.gd, map_downloader.gd, map_registry_server.py, README
- scripts/config/ — server_config.gd (480 lines)
- scenes/ — client_main, server_main, player, test_range
- project.godot, export_presets.cfg
2026-07-01 18:30:44 -04:00

139 lines
4.2 KiB
C++

#ifndef TACTICAL_SHOOTER_HIT_DETECTION_H
#define TACTICAL_SHOOTER_HIT_DETECTION_H
#include "entity.h"
#include <cstdint>
#include <vector>
namespace tactical_shooter {
/**
* Hit detection system that operates on the simulation entities directly.
*
* Because the simulation core runs in GDExtension (not GDScript), we avoid
* crossing the script→engine boundary for every raycast during the hot loop.
* Instead, this component provides simplified geometric checks that can be
* resolved locally, or queues world-space queries for the Godot PhysicsServer3D
* if precise scene geometry is needed.
*
* Phase 1 uses sphere/box overlap against entity positions — fast, no physics
* dependency. Phase 2+ may add proper PhysicsServer3D raycasts against the
* world geometry.
*/
/**
* Result of a single hit query.
*/
struct HitResult {
bool hit = false;
uint16_t entity_id = 0xFFFF; // 0xFFFF = world geometry / no entity
float damage = 0.0f;
float distance = 0.0f;
godot::Vector3 point;
godot::Vector3 normal;
uint8_t hitbox_id = 0; // 0=body, 1=head, 2=arms, 3=legs
};
/**
* Weapon damage profile.
*/
struct WeaponDamage {
float base_damage = 30.0f;
float head_multiplier = 4.0f;
float body_multiplier = 1.0f;
float arm_multiplier = 0.75f;
float leg_multiplier = 0.6f;
float max_range = 500.0f; // units
float spread_degrees = 0.5f; // random spread per shot
};
class HitDetection {
public:
HitDetection();
/**
* Set which entities are currently in play (reference list).
* Must be called before any hit queries each frame.
*/
void set_entities(const std::vector<Entity *> &entities);
/**
* Raycast against entity bounding spheres.
* No PhysicsServer3D dependency — pure geometric check.
*
* @param origin Ray origin
* @param direction Normalized ray direction
* @param max_distance Maximum trace distance
* @param exclude_id Entity ID to exclude (shooter)
* @return First entity hit (if any)
*/
HitResult raycast_entity(
const godot::Vector3 &origin,
const godot::Vector3 &direction,
float max_distance,
uint16_t exclude_id = 0xFFFF
) const;
/**
* Process a weapon fire: apply damage to hit entity.
* Combines raycast + damage application + amortization.
*
* @param origin Fire origin
* @param direction Fire direction (with spread already applied)
* @param shooter_id Entity that fired
* @param weapon Weapon damage profile
* @return Hit result with damage
*/
HitResult process_shot(
const godot::Vector3 &origin,
const godot::Vector3 &direction,
uint16_t shooter_id,
const WeaponDamage &weapon
);
/**
* Apply damage to an entity and update its state.
* Respects armor reduction.
*
* @param entity Target entity
* @param damage Raw damage before armor
* @param mult Hitbox multiplier
* @return Actual health removed
*/
static float apply_damage(Entity &entity, float damage, float mult);
/**
* Sphere overlap detection — find all entities within radius.
* Useful for explosive damage.
*/
std::vector<HitResult> sphere_overlap(
const godot::Vector3 &center,
float radius,
uint16_t exclude_id = 0xFFFF
) const;
private:
/**
* Get the hitbox multiplier for a hit location.
*/
float get_hitbox_multiplier(uint8_t hitbox_id, const WeaponDamage &weapon) const;
/**
* Determine which hitbox a ray hit based on local-space intersection.
* Simple head/body/leg classification by vertical offset.
*/
uint8_t classify_hitbox(const Entity &entity, const godot::Vector3 &hit_point) const;
const std::vector<Entity *> *entities_ = nullptr;
// Bounding sphere radius for entity hit detection
static constexpr float kEntityRadius = 0.4f; // ~arm span / 2
static constexpr float kHeadRadius = 0.2f;
static constexpr float kEntityHeight = 1.8f; // standing height
};
} // namespace tactical_shooter
#endif // TACTICAL_SHOOTER_HIT_DETECTION_H