e9dc05983c
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
139 lines
4.2 KiB
C++
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 ¢er,
|
|
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
|