shitman/lib/game/components/vision_cone.dart
zeyus 67aaa9589f
All checks were successful
/ build-web (push) Successful in 4m5s
updated level...player can now "complete" (no ui)
2025-07-27 17:41:51 +02:00

174 lines
4.8 KiB
Dart

import 'package:flame/components.dart';
import 'package:flutter/material.dart';
import 'dart:math';
import 'package:shitman/game/components/base.dart';
class VisionCone extends ShitComponent {
double direction; // Angle in radians
final double range;
final double fov; // Field of view angle in radians
final Color color;
double opacity;
List<Vector2> visionVertices = [];
late Paint fillPaint;
late Paint strokePaint;
VisionCone({
required Vector2 origin,
required this.direction,
this.range = 150.0,
this.fov = pi / 3, // 60 degrees
this.color = Colors.red,
this.opacity = 0.3,
}) : super(position: origin);
@override
Future<void> onLoad() async {
await super.onLoad();
fillPaint =
Paint()
..color = color.withValues(alpha: opacity)
..style = PaintingStyle.fill;
strokePaint =
Paint()
..color = color.withValues(alpha: opacity + 0.2)
..style = PaintingStyle.stroke
..strokeWidth = 2.0;
_calculateVisionCone();
}
void updatePosition(Vector2 newOrigin, double newDirection) {
position.setFrom(newOrigin);
direction = newDirection;
_calculateVisionCone();
}
void _calculateVisionCone() {
visionVertices.clear();
// Start from the origin (0,0 relative to this component)
visionVertices.add(Vector2.zero());
// Calculate the two edge rays of the vision cone
final leftAngle = direction - fov / 2;
final rightAngle = direction + fov / 2;
// Create points along the vision cone arc
const int arcPoints = 10;
for (int i = 0; i <= arcPoints; i++) {
final angle = leftAngle + (rightAngle - leftAngle) * (i / arcPoints);
final point = Vector2(cos(angle), sin(angle)) * range;
visionVertices.add(point);
}
// Close the cone back to origin
visionVertices.add(Vector2.zero());
}
/// Check if a point is within the vision cone
bool canSee(Vector2 point) {
// Get the world position of this vision cone by adding parent position
final parentPosition =
(parent as PositionComponent?)?.position ?? Vector2.zero();
final worldPosition = parentPosition + position;
final toPoint = point - worldPosition;
final distance = toPoint.length;
// Check if within range
if (distance > range) return false;
// Check if within angle
final angleToPoint = atan2(toPoint.y, toPoint.x);
final angleDiff = _normalizeAngle(angleToPoint - direction);
return angleDiff.abs() <= fov / 2;
}
/// Normalize angle to [-pi, pi]
double _normalizeAngle(double angle) {
while (angle > pi) {
angle -= 2 * pi;
}
while (angle < -pi) {
angle += 2 * pi;
}
return angle;
}
/// Raycast from origin to target, checking for obstructions
bool hasLineOfSight(Vector2 target, List<Component> obstacles) {
// Simple line-of-sight check - can be enhanced with proper raycasting
final parentPosition =
(parent as PositionComponent?)?.position ?? Vector2.zero();
final worldPosition = parentPosition + position;
final rayDirection = target - worldPosition;
final distance = rayDirection.length;
final normalizedDir = rayDirection.normalized();
// Check points along the ray for obstacles
const double stepSize = 5.0;
for (double step = stepSize; step < distance; step += stepSize) {
final checkPoint = worldPosition + normalizedDir * step;
// Check if this point intersects with any obstacles
for (final obstacle in obstacles) {
if (obstacle is RectangleComponent) {
if (obstacle.containsPoint(checkPoint)) {
return false; // Line of sight blocked
}
}
}
}
return true; // Clear line of sight
}
void updateOpacity(double newOpacity) {
opacity = newOpacity;
fillPaint =
Paint()
..color = color.withValues(alpha: opacity)
..style = PaintingStyle.fill;
strokePaint =
Paint()
..color = color.withValues(alpha: opacity + 0.2)
..style = PaintingStyle.stroke
..strokeWidth = 2.0;
}
@override
void render(Canvas canvas) {
if (visionVertices.length < 3 || opacity <= 0.0) return;
// Create path for vision cone
final path = Path();
path.moveTo(visionVertices[0].x, visionVertices[0].y);
for (int i = 1; i < visionVertices.length; i++) {
path.lineTo(visionVertices[i].x, visionVertices[i].y);
}
path.close();
// Draw filled vision cone
canvas.drawPath(path, fillPaint);
// Draw vision cone outline
canvas.drawPath(path, strokePaint);
}
@override
Future<void> reset() async {
// Reset vision cone state
direction = 0.0;
opacity = 0.3;
visionVertices.clear();
_calculateVisionCone();
appLog.finest('Vision cone reset');
}
}