183 lines
5.8 KiB
Dart
183 lines
5.8 KiB
Dart
import 'dart:math' as math;
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/scheduler.dart';
|
|
import 'package:wolf_3d_data_types/wolf_3d_data_types.dart';
|
|
import 'package:wolf_3d_engine/wolf_3d_engine.dart';
|
|
import 'package:wolf_3d_input/wolf_3d_input.dart';
|
|
import 'package:wolf_3d_renderer/hud.dart';
|
|
import 'package:wolf_3d_renderer/raycast_painter.dart';
|
|
import 'package:wolf_3d_renderer/weapon_painter.dart';
|
|
|
|
class WolfRenderer extends StatefulWidget {
|
|
const WolfRenderer(
|
|
this.data, {
|
|
required this.difficulty,
|
|
required this.startingEpisode,
|
|
required this.audio,
|
|
super.key,
|
|
});
|
|
|
|
final WolfensteinData data;
|
|
final Difficulty difficulty;
|
|
final int startingEpisode;
|
|
final EngineAudio audio;
|
|
|
|
@override
|
|
State<WolfRenderer> createState() => _WolfRendererState();
|
|
}
|
|
|
|
class _WolfRendererState extends State<WolfRenderer>
|
|
with SingleTickerProviderStateMixin {
|
|
// 1. The input reader
|
|
final WolfInput inputManager = WolfInput();
|
|
|
|
// 2. The central brain of the game
|
|
late final WolfEngine engine;
|
|
|
|
late Ticker _gameLoop;
|
|
final FocusNode _focusNode = FocusNode();
|
|
|
|
final double fov = math.pi / 3;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
|
|
// Initialize the engine and hand over all the data and dependencies
|
|
engine = WolfEngine(
|
|
data: widget.data,
|
|
difficulty: widget.difficulty,
|
|
startingEpisode: widget.startingEpisode,
|
|
audio: widget.audio,
|
|
onGameWon: () {
|
|
Navigator.of(context).pop();
|
|
},
|
|
);
|
|
|
|
engine.init();
|
|
|
|
// Start the loop
|
|
_gameLoop = createTicker(_tick)..start();
|
|
_focusNode.requestFocus();
|
|
}
|
|
|
|
// --- ORCHESTRATOR ---
|
|
void _tick(Duration elapsed) {
|
|
// 1. Read the keyboard state
|
|
inputManager.update();
|
|
|
|
// 2. Let the engine do all the math, physics, collision, and logic!
|
|
engine.tick(elapsed, inputManager.currentInput);
|
|
|
|
// 3. Force a UI repaint using the newly updated engine state
|
|
setState(() {});
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_gameLoop.dispose();
|
|
_focusNode.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
// Wait for the engine to finish parsing the level
|
|
if (!engine.isInitialized) {
|
|
return const Center(child: CircularProgressIndicator(color: Colors.teal));
|
|
}
|
|
|
|
return Scaffold(
|
|
backgroundColor: Colors.black,
|
|
body: KeyboardListener(
|
|
focusNode: _focusNode,
|
|
autofocus: true,
|
|
onKeyEvent: (_) {},
|
|
child: Column(
|
|
children: [
|
|
Expanded(
|
|
child: LayoutBuilder(
|
|
builder: (context, constraints) {
|
|
return Center(
|
|
child: AspectRatio(
|
|
aspectRatio: 16 / 10,
|
|
child: Stack(
|
|
children: [
|
|
// --- 3D WORLD ---
|
|
CustomPaint(
|
|
size: Size(
|
|
constraints.maxWidth,
|
|
constraints.maxHeight,
|
|
),
|
|
painter: RaycasterPainter(
|
|
// Read state directly from the engine
|
|
map: engine.currentLevel,
|
|
textures: widget.data.walls,
|
|
player: engine.player,
|
|
fov: fov,
|
|
doorOffsets: engine.doorManager
|
|
.getOffsetsForRenderer(),
|
|
entities: engine.entities,
|
|
sprites: widget.data.sprites,
|
|
activePushwall:
|
|
engine.pushwallManager.activePushwall,
|
|
),
|
|
),
|
|
|
|
// --- FIRST PERSON WEAPON ---
|
|
Positioned(
|
|
bottom: -20,
|
|
left: 0,
|
|
right: 0,
|
|
child: Center(
|
|
child: Transform.translate(
|
|
offset: Offset(
|
|
0,
|
|
engine.player.weaponAnimOffset,
|
|
),
|
|
child: SizedBox(
|
|
width: 500,
|
|
height: 500,
|
|
child: CustomPaint(
|
|
painter: WeaponPainter(
|
|
sprite:
|
|
widget.data.sprites[engine
|
|
.player
|
|
.currentWeapon
|
|
.getCurrentSpriteIndex(
|
|
widget.data.sprites.length,
|
|
)],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
|
|
// --- DAMAGE FLASH ---
|
|
if (engine.damageFlashOpacity > 0)
|
|
Positioned.fill(
|
|
child: Container(
|
|
color: Colors.red.withValues(
|
|
alpha: engine.damageFlashOpacity,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
|
|
// --- UI ---
|
|
Hud(player: engine.player),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|