[code] Add ts instruments & refactor all code
parent
24e515e80d
commit
f572fdebca
|
|
@ -0,0 +1,40 @@
|
||||||
|
# Dependencies
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Build outputs
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
out/
|
||||||
|
|
||||||
|
# TypeScript declaration files
|
||||||
|
**/*.d.ts
|
||||||
|
|
||||||
|
# ESLint config files
|
||||||
|
.eslintrc.js
|
||||||
|
.eslintrc.cjs
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
# IDE files
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# OS files
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Temporary files
|
||||||
|
*.tmp
|
||||||
|
*.temp
|
||||||
|
|
||||||
|
# Test coverage
|
||||||
|
coverage/
|
||||||
|
|
||||||
|
# Package manager files
|
||||||
|
package-lock.json
|
||||||
|
yarn.lock
|
||||||
|
|
@ -0,0 +1,181 @@
|
||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
parser: '@typescript-eslint/parser',
|
||||||
|
parserOptions: {
|
||||||
|
ecmaVersion: 2022,
|
||||||
|
sourceType: 'module',
|
||||||
|
project: './tsconfig.json',
|
||||||
|
tsconfigRootDir: __dirname,
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
'@typescript-eslint',
|
||||||
|
'import',
|
||||||
|
'prefer-arrow',
|
||||||
|
],
|
||||||
|
extends: [
|
||||||
|
'eslint:recommended',
|
||||||
|
'plugin:@typescript-eslint/recommended',
|
||||||
|
'plugin:@typescript-eslint/recommended-requiring-type-checking',
|
||||||
|
'prettier', // Must be last to override other configs
|
||||||
|
],
|
||||||
|
rules: {
|
||||||
|
// TypeScript specific
|
||||||
|
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
|
||||||
|
'@typescript-eslint/explicit-function-return-type': 'warn',
|
||||||
|
'@typescript-eslint/explicit-module-boundary-types': 'warn',
|
||||||
|
'@typescript-eslint/no-explicit-any': 'error',
|
||||||
|
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||||
|
'@typescript-eslint/prefer-nullish-coalescing': 'error',
|
||||||
|
'@typescript-eslint/prefer-optional-chain': 'error',
|
||||||
|
'@typescript-eslint/no-unnecessary-type-assertion': 'error',
|
||||||
|
'@typescript-eslint/no-floating-promises': 'error',
|
||||||
|
'@typescript-eslint/await-thenable': 'error',
|
||||||
|
'@typescript-eslint/require-await': 'error',
|
||||||
|
'@typescript-eslint/no-misused-promises': 'error',
|
||||||
|
|
||||||
|
// Import rules
|
||||||
|
'import/order': ['error', {
|
||||||
|
'groups': [
|
||||||
|
'builtin',
|
||||||
|
'external',
|
||||||
|
'internal',
|
||||||
|
'parent',
|
||||||
|
'sibling',
|
||||||
|
'index',
|
||||||
|
],
|
||||||
|
'pathGroups': [
|
||||||
|
{
|
||||||
|
'pattern': '#root/**',
|
||||||
|
'group': 'internal',
|
||||||
|
'position': 'before'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'pathGroupsExcludedImportTypes': ['builtin'],
|
||||||
|
'newlines-between': 'never',
|
||||||
|
'alphabetize': {
|
||||||
|
'order': 'asc',
|
||||||
|
'caseInsensitive': true
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
'import/no-unresolved': 'error',
|
||||||
|
'import/extensions': ['error', 'always', {
|
||||||
|
'ts': 'always',
|
||||||
|
'js': 'always'
|
||||||
|
}],
|
||||||
|
|
||||||
|
// General code quality
|
||||||
|
'no-console': 'off', // Allowed for game development
|
||||||
|
'no-debugger': 'warn',
|
||||||
|
'no-unused-vars': 'off', // Use TypeScript version instead
|
||||||
|
'prefer-const': 'error',
|
||||||
|
'no-var': 'error',
|
||||||
|
'object-shorthand': 'error',
|
||||||
|
'prefer-template': 'error',
|
||||||
|
'prefer-arrow-callback': 'error',
|
||||||
|
'arrow-body-style': ['error', 'as-needed'],
|
||||||
|
|
||||||
|
// Stylistic (handled by Prettier, but some logical ones)
|
||||||
|
'curly': ['error', 'all'],
|
||||||
|
'brace-style': 'off', // Prettier handles this
|
||||||
|
'comma-dangle': 'off', // Prettier handles this
|
||||||
|
'quotes': 'off', // Prettier handles this
|
||||||
|
'semi': 'off', // Prettier handles this
|
||||||
|
|
||||||
|
// Best practices
|
||||||
|
'eqeqeq': ['error', 'always'],
|
||||||
|
'no-eval': 'error',
|
||||||
|
'no-implied-eval': 'error',
|
||||||
|
'no-new-wrappers': 'error',
|
||||||
|
'no-throw-literal': 'error',
|
||||||
|
'no-return-await': 'error',
|
||||||
|
|
||||||
|
// Complexity
|
||||||
|
'complexity': ['warn', 10],
|
||||||
|
'max-lines': ['warn', { max: 500, skipBlankLines: true, skipComments: true }],
|
||||||
|
'max-lines-per-function': ['warn', { max: 100, skipBlankLines: true, skipComments: true }],
|
||||||
|
|
||||||
|
// Naming conventions
|
||||||
|
'@typescript-eslint/naming-convention': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
'selector': 'default',
|
||||||
|
'format': ['PascalCase', 'camelCase']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'selector': 'function',
|
||||||
|
'format': ['PascalCase', 'camelCase']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'selector': 'variable',
|
||||||
|
'format': ['camelCase', 'UPPER_CASE', 'PascalCase'],
|
||||||
|
'prefix': ['_', 'BP_', 'AC_', 'WBP_', 'UBP_', 'ABP_', 'IMC_', 'IA_', 'DT_', 'BFL_', 'U', 'A', 'T', 'F']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'selector': 'variable',
|
||||||
|
'format': ['camelCase', 'UPPER_CASE', 'PascalCase'],
|
||||||
|
'filter': {
|
||||||
|
'regex': '^(?!_|BP_|AC_|WBP_|UBP_|ABP_|IA_|DT_|IMC_|BFL_|U[A-Z]|A[A-Z]|T[A-Z]|F[A-Z])',
|
||||||
|
'match': true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Unreal Engine classes with prefixes
|
||||||
|
{
|
||||||
|
'selector': 'class',
|
||||||
|
'format': ['PascalCase'],
|
||||||
|
'prefix': ['_', 'BP_', 'AC_', 'WBP_', 'UBP_', 'ABP_', 'IMC_', 'IA_', 'DT_', 'FT_', 'BFL_', 'U', 'A', 'T', 'F']
|
||||||
|
},
|
||||||
|
// Regular classes without prefix
|
||||||
|
{
|
||||||
|
'selector': 'class',
|
||||||
|
'format': ['PascalCase'],
|
||||||
|
'filter': {
|
||||||
|
'regex': '^(?!_|BP_|AC_|WBP_|UBP_|ABP_|IA_|IMC_|BFL_|U[A-Z]|A[A-Z]|T[A-Z]|F[A-Z])',
|
||||||
|
'match': true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Interfaces and Structs
|
||||||
|
{
|
||||||
|
'selector': 'interface',
|
||||||
|
'format': ['PascalCase'],
|
||||||
|
'prefix': ['S_', 'I_', 'I']
|
||||||
|
},
|
||||||
|
// Enums
|
||||||
|
{
|
||||||
|
'selector': 'enum',
|
||||||
|
'format': ['PascalCase'],
|
||||||
|
'prefix': ['E_', 'E']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'selector': 'enumMember',
|
||||||
|
'format': ['PascalCase']
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
'@typescript-eslint/no-unsafe-member-access': 'off'
|
||||||
|
},
|
||||||
|
settings: {
|
||||||
|
'import/resolver': {
|
||||||
|
'typescript': {
|
||||||
|
'alwaysTryTypes': true,
|
||||||
|
'project': './tsconfig.json'
|
||||||
|
},
|
||||||
|
'node': {
|
||||||
|
'extensions': ['.js', '.ts', '.json']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'import/parsers': {
|
||||||
|
'@typescript-eslint/parser': ['.ts', '.tsx']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
env: {
|
||||||
|
browser: true,
|
||||||
|
es2022: true,
|
||||||
|
node: true,
|
||||||
|
},
|
||||||
|
ignorePatterns: [
|
||||||
|
'node_modules/',
|
||||||
|
'dist/',
|
||||||
|
'build/',
|
||||||
|
'**/*.d.ts'
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
@ -13,6 +13,13 @@
|
||||||
!/Config/**
|
!/Config/**
|
||||||
!/Plugins/**
|
!/Plugins/**
|
||||||
!/Documentation/**
|
!/Documentation/**
|
||||||
|
!/.eslintignore
|
||||||
|
!/.eslintrc.js
|
||||||
|
!/.prettierignore
|
||||||
|
!/.prettierrc.js
|
||||||
|
!/package.json
|
||||||
|
!/package-lock.json
|
||||||
|
!/tsconfig.json
|
||||||
|
|
||||||
# Only allow .uasset, .umap and .ts files from /Content dir.
|
# Only allow .uasset, .umap and .ts files from /Content dir.
|
||||||
# .uasset and .umap tracked by git-lfs, don't forget to track
|
# .uasset and .umap tracked by git-lfs, don't forget to track
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
# Dependencies
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Build outputs
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
out/
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# IDE files
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# OS files
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Package manager files
|
||||||
|
package-lock.json
|
||||||
|
yarn.lock
|
||||||
|
|
||||||
|
# Generated files
|
||||||
|
**/*.d.ts
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
module.exports = {
|
||||||
|
// Basic formatting
|
||||||
|
printWidth: 80,
|
||||||
|
tabWidth: 2,
|
||||||
|
useTabs: false,
|
||||||
|
semi: true,
|
||||||
|
singleQuote: true,
|
||||||
|
quoteProps: 'as-needed',
|
||||||
|
trailingComma: 'es5',
|
||||||
|
bracketSpacing: true,
|
||||||
|
bracketSameLine: false,
|
||||||
|
arrowParens: 'avoid',
|
||||||
|
|
||||||
|
// Line endings
|
||||||
|
endOfLine: 'lf',
|
||||||
|
|
||||||
|
// TypeScript specific
|
||||||
|
parser: 'typescript',
|
||||||
|
|
||||||
|
// File-specific overrides
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
files: '*.json',
|
||||||
|
options: {
|
||||||
|
parser: 'json',
|
||||||
|
printWidth: 100,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: '*.md',
|
||||||
|
options: {
|
||||||
|
parser: 'markdown',
|
||||||
|
printWidth: 100,
|
||||||
|
proseWrap: 'always',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: '*.{js,ts}',
|
||||||
|
options: {
|
||||||
|
parser: 'typescript',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
@ -1,76 +1,129 @@
|
||||||
// Content/Blueprints/BP_MainCharacter.ts
|
// Blueprints/BP_MainCharacter.ts
|
||||||
|
|
||||||
import {AC_Movement} from "../Movement/Components/AC_Movement.js";
|
import { AC_DebugHUD } from '#root/Debug/Components/AC_DebugHUD.ts';
|
||||||
import type {Controller, Float} from "../types.js";
|
import { IMC_Default } from '#root/Input/IMC_Default.ts';
|
||||||
import {
|
import { AC_Movement } from '#root/Movement/Components/AC_Movement.ts';
|
||||||
AddMappingContext,
|
import { AC_ToastSystem } from '#root/Toasts/Components/AC_ToastSystem.ts';
|
||||||
CastToPlayController,
|
import { Cast } from '#root/UE/Cast.ts';
|
||||||
EnhancedInputLocalPlayerSubsystem,
|
import type { Controller } from '#root/UE/Controller.ts';
|
||||||
GetGameTimeInSeconds,
|
import { EnhancedInputLocalPlayerSubsystem } from '#root/UE/EnhancedInputLocalPlayerSubsystem.ts';
|
||||||
} from "../functions.js";
|
import type { Float } from '#root/UE/Float.ts';
|
||||||
import {IMC_Default} from "../Input/IMC_Default.js";
|
import { Pawn } from '#root/UE/Pawn.ts';
|
||||||
import {AC_DebugHUD} from "../Debug/Components/AC_DebugHUD.js";
|
import type { PlayerController } from '#root/UE/PlayerController.ts';
|
||||||
import {AC_ToastSystem} from "../Toasts/Compontents/AC_ToastSystem.js";
|
import { SystemLibrary } from '#root/UE/SystemLibrary.ts';
|
||||||
|
|
||||||
export class BP_MainCharacter {
|
/**
|
||||||
// Category: "Components"
|
* Main Character Blueprint
|
||||||
MovementComponent = new AC_Movement();
|
* Core player character with deterministic movement system
|
||||||
|
* Integrates debug HUD and toast notification systems
|
||||||
// Category: "Components"
|
*/
|
||||||
DebugHUDComponent = new AC_DebugHUD();
|
export class BP_MainCharacter extends Pawn {
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
// Category: "Components"
|
// GRAPHS
|
||||||
ToastSystemComponent = new AC_ToastSystem();
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
// Category: "Debug"
|
|
||||||
// Instance Editable: true
|
|
||||||
ShowDebugInfo: boolean = true;
|
|
||||||
|
|
||||||
|
// ────────────────────────────────────────────────────────────────────────────────────────
|
||||||
// EventGraph
|
// EventGraph
|
||||||
EventReceiveControllerChanged(NewController: Controller): void {
|
// ────────────────────────────────────────────────────────────────────────────────────────
|
||||||
const AsPlayerController = CastToPlayController(NewController);
|
|
||||||
const Target = EnhancedInputLocalPlayerSubsystem(AsPlayerController);
|
|
||||||
|
|
||||||
AddMappingContext(Target, IMC_Default);
|
/**
|
||||||
|
* Handle controller change events - sets up Enhanced Input mapping context
|
||||||
|
*/
|
||||||
|
EventReceiveControllerChanged(NewController: Controller): void {
|
||||||
|
const controller = Cast<PlayerController>(NewController);
|
||||||
|
const system = new EnhancedInputLocalPlayerSubsystem();
|
||||||
|
|
||||||
|
if (controller) {
|
||||||
|
system.AddMappingContext(IMC_Default);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EnhancedInputActionIA_PrevDebugMode() {
|
/** Navigate to previous debug page */
|
||||||
|
EnhancedInputActionPrevDebugMode(): void {
|
||||||
if (this.ShowDebugInfo) {
|
if (this.ShowDebugInfo) {
|
||||||
this.DebugHUDComponent.PreviousPage();
|
this.DebugHUDComponent.PreviousPage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EnhancedInputActionIA_NextDebugMode() {
|
/** Navigate to next debug page */
|
||||||
|
EnhancedInputActionINextDebugMode(): void {
|
||||||
if (this.ShowDebugInfo) {
|
if (this.ShowDebugInfo) {
|
||||||
this.DebugHUDComponent.NextPage();
|
this.DebugHUDComponent.NextPage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EnhancedInputActionToggleHUD() {
|
/** Toggle debug HUD visibility */
|
||||||
|
EnhancedInputActionToggleHUD(): void {
|
||||||
if (this.ShowDebugInfo) {
|
if (this.ShowDebugInfo) {
|
||||||
this.DebugHUDComponent.ToggleDebugHUD();
|
this.DebugHUDComponent.ToggleDebugHUD();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EnhancedInputActionIA_ToggleVisualDebug() {
|
/** Toggle visual debug rendering */
|
||||||
|
EnhancedInputActionToggleVisualDebug(): void {
|
||||||
if (this.ShowDebugInfo) {
|
if (this.ShowDebugInfo) {
|
||||||
this.DebugHUDComponent.ToggleVisualDebug();
|
this.DebugHUDComponent.ToggleVisualDebug();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EventBeginPlay() {
|
/**
|
||||||
this.MovementComponent.InitializeMovementSystem();
|
* Initialize all systems when character spawns
|
||||||
|
* Order: Toast → Debug → Movement (movement last as it may generate debug output)
|
||||||
|
*/
|
||||||
|
EventBeginPlay(): void {
|
||||||
|
// Initialize debug systems first if enabled
|
||||||
if (this.ShowDebugInfo) {
|
if (this.ShowDebugInfo) {
|
||||||
this.DebugHUDComponent.InitializeDebugHUD(this.MovementComponent);
|
|
||||||
this.ToastSystemComponent.InitializeToastSystem();
|
this.ToastSystemComponent.InitializeToastSystem();
|
||||||
}
|
this.DebugHUDComponent.InitializeDebugHUD(
|
||||||
|
this.MovementComponent,
|
||||||
|
this.ToastSystemComponent
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize movement system last (may generate debug output)
|
||||||
|
this.MovementComponent.InitializeMovementSystem();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update all systems each frame
|
||||||
|
* Called by Unreal Engine game loop
|
||||||
|
*/
|
||||||
EventTick(DeltaTime: Float): void {
|
EventTick(DeltaTime: Float): void {
|
||||||
if (this.ShowDebugInfo) {
|
if (this.ShowDebugInfo) {
|
||||||
this.DebugHUDComponent.UpdateHUD(GetGameTimeInSeconds(), DeltaTime);
|
this.DebugHUDComponent.UpdateHUD(
|
||||||
|
SystemLibrary.GetGameTimeInSeconds(),
|
||||||
|
DeltaTime
|
||||||
|
);
|
||||||
this.ToastSystemComponent.UpdateToastSystem();
|
this.ToastSystemComponent.UpdateToastSystem();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
// VARIABLES
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Core movement system component - handles deterministic 3D platformer movement
|
||||||
|
* @category Components
|
||||||
|
*/
|
||||||
|
MovementComponent = new AC_Movement();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Debug HUD system - displays movement parameters and performance metrics
|
||||||
|
* @category Components
|
||||||
|
*/
|
||||||
|
DebugHUDComponent = new AC_DebugHUD();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toast notification system - displays temporary status messages
|
||||||
|
* @category Components
|
||||||
|
*/
|
||||||
|
ToastSystemComponent = new AC_ToastSystem();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Master debug toggle - controls all debug systems (HUD, toasts, visual debug)
|
||||||
|
* @category Debug
|
||||||
|
* @instanceEditable true
|
||||||
|
*/
|
||||||
|
private ShowDebugInfo: boolean = true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
BIN
Content/Blueprints/BP_MainCharacter.uasset (Stored with Git LFS)
BIN
Content/Blueprints/BP_MainCharacter.uasset (Stored with Git LFS)
Binary file not shown.
|
|
@ -0,0 +1,8 @@
|
||||||
|
// Blueprints/BP_TengriGameMode.ts
|
||||||
|
|
||||||
|
import { BP_MainCharacter } from '#root/Blueprints/BP_MainCharacter.ts';
|
||||||
|
import { GameModeBase } from '#root/UE/GameModeBase.ts';
|
||||||
|
|
||||||
|
export class BP_TengriGameMode extends GameModeBase {
|
||||||
|
DefaultPawnClass = BP_MainCharacter;
|
||||||
|
}
|
||||||
|
|
@ -1,123 +1,81 @@
|
||||||
// Content/Debug/Components/AC_DebugHUD.ts
|
// Debug/Components/AC_DebugHUD.ts
|
||||||
|
|
||||||
import type {AC_Movement} from "../../Movement/Components/AC_Movement.js";
|
import { E_DebugUpdateFunction } from '#root/Debug/Enums/E_DebugUpdateFunction.ts';
|
||||||
import type {Float, Integer} from "../../types.js";
|
import type { S_DebugPage } from '#root/Debug/Structs/S_DebugPage.ts';
|
||||||
import type {S_DebugPage} from "../Structs/S_DebugPage.js";
|
import type { S_DebugSettings } from '#root/Debug/Structs/S_DebugSettings.ts';
|
||||||
import {AddToArray, CreateWidget, GetFromArray, IsValid, Print, SetArrayElem} from "../../functions.js";
|
import { DT_DebugPages } from '#root/Debug/Tables/DT_DebugPages.ts';
|
||||||
import {E_DebugPageID} from "../Enums/E_DebugPageID.js";
|
import { WBP_DebugHUD } from '#root/Debug/UI/WBP_DebugHUD.ts';
|
||||||
import {E_DebugUpdateFunction} from "../Enums/E_DebugUpdateFunction.js";
|
import type { AC_Movement } from '#root/Movement/Components/AC_Movement.ts';
|
||||||
import {WBP_DebugHUD} from "../UI/WBP_DebugHUD.js";
|
import type { AC_ToastSystem } from '#root/Toasts/Components/AC_ToastSystem.ts';
|
||||||
import type {S_DebugSettings} from "../Structs/S_DebugSettings.js";
|
import { ActorComponent } from '#root/UE/ActorComponent.ts';
|
||||||
import {E_DebugMode} from "../Enums/E_DebugMode.js";
|
import { CreateWidget } from '#root/UE/CteateWidget.ts';
|
||||||
import {ESlateVisibility} from "../../enums.js";
|
import type { DataTable } from '#root/UE/DataTable.ts';
|
||||||
|
import { DataTableFunctionLibrary } from '#root/UE/DataTableFunctionLibrary.ts';
|
||||||
|
import { ESlateVisibility } from '#root/UE/ESlateVisibility.ts';
|
||||||
|
import type { Float } from '#root/UE/Float.ts';
|
||||||
|
import type { Integer } from '#root/UE/Integer.ts';
|
||||||
|
import { SystemLibrary } from '#root/UE/SystemLibrary.ts';
|
||||||
|
import { UEArray } from '#root/UE/UEArray.ts';
|
||||||
|
import { E_MessageType } from '#root/UI/Enums/E_MessageType.ts';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Debug HUD Controller Component
|
* Debug HUD Controller Component
|
||||||
* Manages debug information display system for deterministic movement
|
* Manages debug information display system for deterministic movement
|
||||||
* Provides real-time performance monitoring and parameter visualization
|
* Provides real-time performance monitoring and parameter visualization
|
||||||
* Part of Stage 2: Debug HUD system implementation
|
|
||||||
*/
|
*/
|
||||||
export class AC_DebugHUD {
|
export class AC_DebugHUD extends ActorComponent {
|
||||||
// Category: "DebugConfig"
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
// Instance Editable: true
|
// FUNCTIONS
|
||||||
/**
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
* Debug system configuration settings
|
|
||||||
* Controls visibility, update frequency, and current page
|
|
||||||
* Instance editable for designer customization
|
|
||||||
*/
|
|
||||||
public DebugSettings: S_DebugSettings = {
|
|
||||||
CurrentMode: E_DebugMode.Visible,
|
|
||||||
CurrentPageIndex: 0,
|
|
||||||
ShowVisualDebug: false,
|
|
||||||
UpdateFrequency: 0.0,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Category: "DebugState"
|
|
||||||
/**
|
|
||||||
* System initialization state flag
|
|
||||||
* Set to true after successful InitializeDebugHUD call
|
|
||||||
*/
|
|
||||||
private IsInitialized: boolean = false;
|
|
||||||
|
|
||||||
// Category: "DebugState"
|
|
||||||
/**
|
|
||||||
* Timestamp of last HUD update (seconds)
|
|
||||||
* Used for update frequency control
|
|
||||||
*/
|
|
||||||
private LastUpdateTime: Float = 0;
|
|
||||||
|
|
||||||
// Category: "DebugState"
|
|
||||||
/**
|
|
||||||
* Current frame counter for performance tracking
|
|
||||||
* Incremented each update cycle
|
|
||||||
*/
|
|
||||||
private FrameCounter: Float = 0;
|
|
||||||
|
|
||||||
// Category: "DebugState"
|
|
||||||
/**
|
|
||||||
* Current frames per second calculation
|
|
||||||
* Calculated as 1.0 / DeltaTime
|
|
||||||
*/
|
|
||||||
private FPS: Float = 0;
|
|
||||||
|
|
||||||
// Category: "DebugState"
|
|
||||||
/**
|
|
||||||
* Reference to movement component being debugged
|
|
||||||
* Set during initialization, used for accessing movement data
|
|
||||||
*/
|
|
||||||
private MovementComponent: AC_Movement | null = null;
|
|
||||||
|
|
||||||
// Category: "Widget Control"
|
|
||||||
/**
|
|
||||||
* Debug HUD widget instance
|
|
||||||
* Created during initialization, manages UI display
|
|
||||||
*/
|
|
||||||
private DebugWidget: WBP_DebugHUD | null = null;
|
|
||||||
|
|
||||||
// Category: "Page System"
|
|
||||||
/**
|
|
||||||
* Array of registered debug pages
|
|
||||||
* Contains all available pages with their data and update functions
|
|
||||||
*/
|
|
||||||
private DebugPages: S_DebugPage[] = [];
|
|
||||||
|
|
||||||
// Category: "HUD Control"
|
|
||||||
// Pure: true
|
|
||||||
/**
|
/**
|
||||||
* Check if debug HUD should be visible
|
* Check if debug HUD should be visible
|
||||||
* @returns True if system is initialized and mode is visible
|
* @returns True if system is initialized and mode is visible
|
||||||
* @pure Function has no side effects
|
* @category HUD Control
|
||||||
|
* @pure true
|
||||||
*/
|
*/
|
||||||
private ShouldShowDebugHUD() {
|
private ShouldShowDebugHUD(): boolean {
|
||||||
return this.IsInitialized && this.DebugSettings.CurrentMode === E_DebugMode.Visible;
|
const IsSystemReadyAndVisible = (modeIsVisible: boolean): boolean =>
|
||||||
|
this.IsInitialized && modeIsVisible;
|
||||||
|
|
||||||
|
return IsSystemReadyAndVisible(
|
||||||
|
this.DebugSettings.CurrentMode === ESlateVisibility.Visible
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Category: "HUD Control"
|
|
||||||
// Pure: true
|
|
||||||
/**
|
/**
|
||||||
* Check if debug HUD should update this frame
|
* Check if debug HUD should update this frame
|
||||||
* @param CurrentTime - Current game time in seconds
|
|
||||||
* @returns True if enough time has passed since last update
|
|
||||||
* @pure Function has no side effects
|
|
||||||
* @example
|
* @example
|
||||||
* // Update every frame (UpdateFrequency = 0)
|
* // Update every frame (UpdateFrequency = 0)
|
||||||
* ShouldUpdateDebugHUD(gameTime) // returns true
|
* ShouldUpdateDebugHUD(gameTime) // returns true
|
||||||
* // Update at 30Hz (UpdateFrequency = 30)
|
* // Update at 30Hz (UpdateFrequency = 30)
|
||||||
* ShouldUpdateDebugHUD(gameTime) // returns true every 1/30 seconds
|
* ShouldUpdateDebugHUD(gameTime) // returns true every 1/30 seconds
|
||||||
|
* @param CurrentTime - Current game time in seconds
|
||||||
|
* @returns True if enough time has passed since last update
|
||||||
|
* @pure true
|
||||||
|
* @category HUD Control
|
||||||
*/
|
*/
|
||||||
private ShouldUpdateDebugHUD(CurrentTime: Float): boolean {
|
private ShouldUpdateDebugHUD(CurrentTime: Float = 0): boolean {
|
||||||
if (this.DebugSettings.UpdateFrequency <= 0) {
|
const IsFrequencyLimited = (updateFrequency: Float): boolean =>
|
||||||
return true; // Update every frame
|
updateFrequency > 0;
|
||||||
|
|
||||||
|
const IsUpdateIntervalReached = (
|
||||||
|
currentTime: Float,
|
||||||
|
updateFrequency: Float
|
||||||
|
): boolean => currentTime - this.LastUpdateTime >= 1 / updateFrequency;
|
||||||
|
|
||||||
|
if (IsFrequencyLimited(this.DebugSettings.UpdateFrequency)) {
|
||||||
|
return IsUpdateIntervalReached(
|
||||||
|
CurrentTime,
|
||||||
|
this.DebugSettings.UpdateFrequency
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
return (CurrentTime - this.LastUpdateTime) >= (1.0 / this.DebugSettings.UpdateFrequency);
|
return true; // Update every frame
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Category: "Page Management"
|
|
||||||
/**
|
/**
|
||||||
* Register or update a debug page in the system
|
* Register or update a debug page in the system
|
||||||
* @param PageData - Page configuration and content data
|
|
||||||
* @private Internal page management method
|
|
||||||
* @example
|
* @example
|
||||||
* // Register movement constants page
|
* // Register movement constants page
|
||||||
* RegisterDebugPage({
|
* RegisterDebugPage({
|
||||||
|
|
@ -127,279 +85,333 @@ export class AC_DebugHUD {
|
||||||
* IsVisible: true,
|
* IsVisible: true,
|
||||||
* UpdateFunction: E_DebugUpdateFunction.UpdateMovementPage
|
* UpdateFunction: E_DebugUpdateFunction.UpdateMovementPage
|
||||||
* })
|
* })
|
||||||
|
* @param PageData - Page configuration and content data
|
||||||
|
* @category Page Management
|
||||||
*/
|
*/
|
||||||
private RegisterDebugPage(PageData: S_DebugPage): void {
|
private RegisterDebugPage(PageData: S_DebugPage): void {
|
||||||
let existingIndex: Integer = -1;
|
let existingIndex: Integer = -1;
|
||||||
|
|
||||||
this.DebugPages.forEach((page, index) => {
|
this.DebugPages.forEach((page, index) => {
|
||||||
if (page.PageID === PageData.PageID) {
|
if (page.PageID === PageData.PageID) {
|
||||||
this.DebugPages[index] = PageData;
|
existingIndex = index;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
if (existingIndex >= 0) {
|
const IsPageExists = (): boolean => existingIndex >= 0;
|
||||||
SetArrayElem(this.DebugPages, existingIndex, PageData);
|
|
||||||
|
if (IsPageExists()) {
|
||||||
|
this.DebugPages.SetArrayElem(existingIndex, PageData);
|
||||||
} else {
|
} else {
|
||||||
AddToArray(this.DebugPages, PageData);
|
this.DebugPages.Add(PageData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Category: "Page Management"
|
|
||||||
// Pure: true
|
|
||||||
/**
|
/**
|
||||||
* Get all currently visible debug pages
|
* Get all currently visible debug pages
|
||||||
* @returns Array of visible pages only
|
* @returns Array of visible pages only
|
||||||
* @pure Function has no side effects
|
* @category Page Management
|
||||||
|
* @pure true
|
||||||
*/
|
*/
|
||||||
private GetVisiblePages(): S_DebugPage[] {
|
public GetVisiblePages(): UEArray<S_DebugPage> {
|
||||||
const filteredPages: S_DebugPage[] = [];
|
const filteredPages: UEArray<S_DebugPage> = new UEArray([]);
|
||||||
|
|
||||||
this.DebugPages.forEach((page) => {
|
this.DebugPages.forEach(page => {
|
||||||
if (page.IsVisible) {
|
if (page.IsVisible) {
|
||||||
AddToArray(filteredPages, page);
|
filteredPages.Add(page);
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
return filteredPages;
|
return filteredPages;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Category: "Page Management"
|
|
||||||
/**
|
/**
|
||||||
* Get currently selected debug page
|
* Get currently selected debug page
|
||||||
* @returns Object with page data and validity flag
|
* @returns Object with page data and validity flag
|
||||||
* @pure Function has no side effects
|
* @category Page Management
|
||||||
|
* @pure true
|
||||||
*/
|
*/
|
||||||
private GetCurrentPage(): {Page: S_DebugPage | null, IsFound: boolean} {
|
private GetCurrentPage():
|
||||||
const length = this.GetVisiblePages().length;
|
| { Page: S_DebugPage | null; IsFound: true }
|
||||||
|
| { Page: null; IsFound: false } {
|
||||||
|
const IsPageIndexInvalid = (
|
||||||
|
length: Integer,
|
||||||
|
currentPageIndex: Integer
|
||||||
|
): boolean => length === 0 || currentPageIndex >= length;
|
||||||
|
|
||||||
if (!((length === 0) || (this.DebugSettings.CurrentPageIndex >= length))) {
|
return IsPageIndexInvalid(
|
||||||
return {Page: GetFromArray(this.DebugPages, this.DebugSettings.CurrentPageIndex), IsFound: true};
|
this.GetVisiblePages().length,
|
||||||
} else {
|
this.DebugSettings.CurrentPageIndex
|
||||||
return {Page: null, IsFound: false};
|
)
|
||||||
}
|
? { Page: null, IsFound: false }
|
||||||
|
: {
|
||||||
|
Page: this.GetVisiblePages().Get(this.DebugSettings.CurrentPageIndex),
|
||||||
|
IsFound: true,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Category: "Navigation"
|
|
||||||
/**
|
/**
|
||||||
* Toggle debug HUD visibility between visible and hidden
|
* Toggle debug HUD visibility between visible and hidden
|
||||||
* @public User input handler for F1 key or similar
|
* @category Navigation
|
||||||
*/
|
*/
|
||||||
public ToggleDebugHUD() {
|
public ToggleDebugHUD(): void {
|
||||||
this.DebugSettings.CurrentMode = this.DebugSettings.CurrentMode === E_DebugMode.Visible ? E_DebugMode.Hidden : E_DebugMode.Visible;
|
this.DebugSettings.CurrentMode =
|
||||||
|
this.DebugSettings.CurrentMode === ESlateVisibility.Visible
|
||||||
|
? ESlateVisibility.Hidden
|
||||||
|
: ESlateVisibility.Visible;
|
||||||
|
this.UpdateWidgetVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Category: "Navigation"
|
|
||||||
/**
|
/**
|
||||||
* Navigate to previous debug page
|
* Navigate to previous debug page
|
||||||
* Wraps around to last page if at beginning
|
* Wraps around to last page if at beginning
|
||||||
* @public User input handler for PageUp key
|
* @category Navigation
|
||||||
*/
|
*/
|
||||||
public PreviousPage() {
|
public PreviousPage(): void {
|
||||||
const length = this.GetVisiblePages().length;
|
const length = this.GetVisiblePages().length;
|
||||||
|
|
||||||
if (length > 1) {
|
if (length > 1) {
|
||||||
const currentPage = this.DebugSettings.CurrentPageIndex;
|
const currentPage = this.DebugSettings.CurrentPageIndex;
|
||||||
|
|
||||||
this.DebugSettings.CurrentPageIndex = currentPage - 1 < 0 ? length - 1 : currentPage - 1;
|
const isAtFirstPage = (): boolean => currentPage - 1 < 0;
|
||||||
|
const getLastPageIndex = (): Integer => length - 1;
|
||||||
|
const getPreviousPageIndex = (): Integer => currentPage - 1;
|
||||||
|
|
||||||
|
this.DebugSettings.CurrentPageIndex = isAtFirstPage()
|
||||||
|
? getLastPageIndex()
|
||||||
|
: getPreviousPageIndex();
|
||||||
|
|
||||||
|
this.UpdateCurrentPage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Category: "Navigation"
|
|
||||||
/**
|
/**
|
||||||
* Navigate to next debug page
|
* Navigate to next debug page
|
||||||
* Wraps around to first page if at end
|
* Wraps around to first page if at end
|
||||||
* @public User input handler for PageDown key
|
* @category Navigation
|
||||||
*/
|
*/
|
||||||
public NextPage() {
|
public NextPage(): void {
|
||||||
const length = this.GetVisiblePages().length;
|
const length = this.GetVisiblePages().length;
|
||||||
|
|
||||||
if (length > 1) {
|
const HasMultiplePages = (): boolean => length > 1;
|
||||||
this.DebugSettings.CurrentPageIndex = (this.DebugSettings.CurrentPageIndex + 1) % length;
|
const GetNextPageIndex = (currentPageIndex: Integer): Integer =>
|
||||||
|
(currentPageIndex + 1) % length;
|
||||||
|
|
||||||
|
if (HasMultiplePages()) {
|
||||||
|
this.DebugSettings.CurrentPageIndex = GetNextPageIndex(
|
||||||
|
this.DebugSettings.CurrentPageIndex
|
||||||
|
);
|
||||||
|
|
||||||
|
this.UpdateCurrentPage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Category: "Visual Debug"
|
|
||||||
/**
|
/**
|
||||||
* Toggle visual debug rendering (collision shapes, rays, etc.)
|
* Toggle visual debug rendering (collision shapes, rays, etc.)
|
||||||
* @public User input handler for visual debug toggle
|
* @category Visual Debug
|
||||||
*/
|
*/
|
||||||
public ToggleVisualDebug() {
|
public ToggleVisualDebug(): void {
|
||||||
this.DebugSettings.ShowVisualDebug = !this.DebugSettings.ShowVisualDebug;
|
this.DebugSettings.ShowVisualDebug = !this.DebugSettings.ShowVisualDebug;
|
||||||
|
|
||||||
|
if (SystemLibrary.IsValid(this.ToastComponent)) {
|
||||||
|
this.ToastComponent.ShowToast(
|
||||||
|
`Visual Debug ${this.DebugSettings.ShowVisualDebug ? 'Enabled' : 'Disabled'}`
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Category: "Page Updates"
|
|
||||||
/**
|
/**
|
||||||
* Update content of currently selected page
|
* Update content of currently selected page
|
||||||
* Calls appropriate update function based on page type
|
* Calls appropriate update function based on page type
|
||||||
* @private Internal update system method
|
* @category Page Updates
|
||||||
*/
|
*/
|
||||||
private UpdateCurrentPage() {
|
private UpdateCurrentPage(): void {
|
||||||
const {Page, IsFound}: {Page: S_DebugPage, IsFound: boolean} = this.GetCurrentPage();
|
let CurrentPage = this.GetCurrentPage().Page;
|
||||||
let CurrentPage = Page;
|
|
||||||
|
|
||||||
if (IsFound) {
|
if (this.GetCurrentPage().IsFound && CurrentPage !== null) {
|
||||||
switch (CurrentPage.UpdateFunction) {
|
switch (CurrentPage.UpdateFunction) {
|
||||||
case E_DebugUpdateFunction.UpdateMovementPage:
|
case E_DebugUpdateFunction.UpdateMovementPage: {
|
||||||
CurrentPage = this.UpdateMovementPage(CurrentPage);
|
CurrentPage = this.UpdateMovementPage(CurrentPage);
|
||||||
break;
|
break;
|
||||||
case E_DebugUpdateFunction.UpdateSurfacePage:
|
}
|
||||||
|
case E_DebugUpdateFunction.UpdateSurfacePage: {
|
||||||
CurrentPage = this.UpdateSurfacePage(CurrentPage);
|
CurrentPage = this.UpdateSurfacePage(CurrentPage);
|
||||||
break;
|
break;
|
||||||
case E_DebugUpdateFunction.UpdatePerformancePage:
|
}
|
||||||
|
case E_DebugUpdateFunction.UpdatePerformancePage: {
|
||||||
CurrentPage = this.UpdatePerformancePage(CurrentPage);
|
CurrentPage = this.UpdatePerformancePage(CurrentPage);
|
||||||
break;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SetArrayElem(this.DebugPages, this.DebugSettings.CurrentPageIndex, CurrentPage);
|
this.DebugPages.SetArrayElem(
|
||||||
|
this.DebugSettings.CurrentPageIndex,
|
||||||
|
CurrentPage
|
||||||
|
);
|
||||||
|
|
||||||
this.UpdateWidgetPage();
|
this.UpdateWidgetPage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Category: "Page Updates"
|
|
||||||
/**
|
/**
|
||||||
* Update movement constants page content
|
* Update movement constants page content
|
||||||
* @param Page - Page structure to update
|
* @param Page - Page structure to update
|
||||||
* @returns Updated page with current movement data
|
* @returns Updated page with current movement data
|
||||||
* @private Page-specific update method
|
* @category Page Updates
|
||||||
*/
|
*/
|
||||||
private UpdateMovementPage(Page: S_DebugPage): S_DebugPage {
|
public UpdateMovementPage(Page: S_DebugPage): S_DebugPage {
|
||||||
if (IsValid(this.MovementComponent)) {
|
if (SystemLibrary.IsValid(this.MovementComponent)) {
|
||||||
return {
|
return {
|
||||||
PageID: Page.PageID,
|
PageID: Page.PageID,
|
||||||
Title: Page.Title,
|
Title: Page.Title,
|
||||||
Content: `Max Speed: ${this.MovementComponent.MovementConstants.MaxSpeed}\n` +
|
Content:
|
||||||
|
`Max Speed: ${this.MovementComponent.MovementConstants.MaxSpeed}\n` +
|
||||||
`Acceleration: ${this.MovementComponent.MovementConstants.Acceleration}\n` +
|
`Acceleration: ${this.MovementComponent.MovementConstants.Acceleration}\n` +
|
||||||
`Friction: ${this.MovementComponent.MovementConstants.Friction}\n` +
|
`Friction: ${this.MovementComponent.MovementConstants.Friction}\n` +
|
||||||
`Gravity: ${this.MovementComponent.MovementConstants.Gravity}`,
|
`Gravity: ${this.MovementComponent.MovementConstants.Gravity}`,
|
||||||
IsVisible: Page.IsVisible,
|
IsVisible: Page.IsVisible,
|
||||||
UpdateFunction: Page.UpdateFunction
|
UpdateFunction: Page.UpdateFunction,
|
||||||
}
|
};
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
PageID: Page.PageID,
|
PageID: Page.PageID,
|
||||||
Title: Page.Title,
|
Title: Page.Title,
|
||||||
Content: 'Movement Component Not Found',
|
Content: 'Movement Component Not Found',
|
||||||
IsVisible: Page.IsVisible,
|
IsVisible: Page.IsVisible,
|
||||||
UpdateFunction: Page.UpdateFunction
|
UpdateFunction: Page.UpdateFunction,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Category: "Page Updates"
|
|
||||||
/**
|
/**
|
||||||
* Update surface classification page content
|
* Update surface classification page content
|
||||||
* @param Page - Page structure to update
|
* @param Page - Page structure to update
|
||||||
* @returns Updated page with current surface angle thresholds
|
* @returns Updated page with current surface angle thresholds
|
||||||
* @private Page-specific update method
|
* @category Page Updates
|
||||||
*/
|
*/
|
||||||
private UpdateSurfacePage(Page: S_DebugPage): S_DebugPage {
|
public UpdateSurfacePage(Page: S_DebugPage): S_DebugPage {
|
||||||
if (IsValid(this.MovementComponent)) {
|
if (SystemLibrary.IsValid(this.MovementComponent)) {
|
||||||
return {
|
return {
|
||||||
PageID: Page.PageID,
|
PageID: Page.PageID,
|
||||||
Title: Page.Title,
|
Title: Page.Title,
|
||||||
Content: `Walkable: ≤${this.MovementComponent.AngleThresholdsDegrees.Walkable}°\n` +
|
Content:
|
||||||
|
`Walkable: ≤${this.MovementComponent.AngleThresholdsDegrees.Walkable}°\n` +
|
||||||
`Steep Slope: ${this.MovementComponent.AngleThresholdsDegrees.Walkable}°-${this.MovementComponent.AngleThresholdsDegrees.SteepSlope}°\n` +
|
`Steep Slope: ${this.MovementComponent.AngleThresholdsDegrees.Walkable}°-${this.MovementComponent.AngleThresholdsDegrees.SteepSlope}°\n` +
|
||||||
`Wall: ${this.MovementComponent.AngleThresholdsDegrees.SteepSlope}°-${this.MovementComponent.AngleThresholdsDegrees.Wall}°\n` +
|
`Wall: ${this.MovementComponent.AngleThresholdsDegrees.SteepSlope}°-${this.MovementComponent.AngleThresholdsDegrees.Wall}°\n` +
|
||||||
`Ceiling: >${this.MovementComponent.AngleThresholdsDegrees.Wall}°`,
|
`Ceiling: >${this.MovementComponent.AngleThresholdsDegrees.Wall}°`,
|
||||||
IsVisible: Page.IsVisible,
|
IsVisible: Page.IsVisible,
|
||||||
UpdateFunction: Page.UpdateFunction
|
UpdateFunction: Page.UpdateFunction,
|
||||||
}
|
};
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
PageID: Page.PageID,
|
PageID: Page.PageID,
|
||||||
Title: Page.Title,
|
Title: Page.Title,
|
||||||
Content: 'Movement Component Not Found',
|
Content: 'Movement Component Not Found',
|
||||||
IsVisible: Page.IsVisible,
|
IsVisible: Page.IsVisible,
|
||||||
UpdateFunction: Page.UpdateFunction
|
UpdateFunction: Page.UpdateFunction,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Category: "Page Updates"
|
|
||||||
/**
|
/**
|
||||||
* Update performance metrics page content
|
* Update performance metrics page content
|
||||||
* @param Page - Page structure to update
|
* @param Page - Page structure to update
|
||||||
* @returns Updated page with current performance data
|
* @returns Updated page with current performance data
|
||||||
* @private Page-specific update method
|
* @category Page Updates
|
||||||
*/
|
*/
|
||||||
private UpdatePerformancePage(Page: S_DebugPage): S_DebugPage {
|
public UpdatePerformancePage(Page: S_DebugPage): S_DebugPage {
|
||||||
if (IsValid(this.MovementComponent)) {
|
if (SystemLibrary.IsValid(this.MovementComponent)) {
|
||||||
|
const IsUpdatingEveryFrame = (updateFrequency: Float): boolean =>
|
||||||
|
updateFrequency <= 0;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
PageID: Page.PageID,
|
PageID: Page.PageID,
|
||||||
Title: Page.Title,
|
Title: Page.Title,
|
||||||
Content: `Frame: ${this.FrameCounter}\n` +
|
Content:
|
||||||
|
`Frame: ${this.FrameCounter}\n` +
|
||||||
`FPS: ${this.FPS}\n` +
|
`FPS: ${this.FPS}\n` +
|
||||||
`Update Rate: ${this.DebugSettings.UpdateFrequency <= 0 ? 'Every Frame' : (this.DebugSettings.UpdateFrequency + ' Hz')}\n` +
|
`Update Rate: ${IsUpdatingEveryFrame(this.DebugSettings.UpdateFrequency) ? 'Every Frame' : `${this.DebugSettings.UpdateFrequency} Hz`}\n` +
|
||||||
`ActivePages: ${this.GetVisiblePages().length}`,
|
`ActivePages: ${this.GetVisiblePages().length}`,
|
||||||
IsVisible: Page.IsVisible,
|
IsVisible: Page.IsVisible,
|
||||||
UpdateFunction: Page.UpdateFunction
|
UpdateFunction: Page.UpdateFunction,
|
||||||
}
|
};
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
PageID: Page.PageID,
|
PageID: Page.PageID,
|
||||||
Title: Page.Title,
|
Title: Page.Title,
|
||||||
Content: 'Movement Component Not Found',
|
Content: 'Movement Component Not Found',
|
||||||
IsVisible: Page.IsVisible,
|
IsVisible: Page.IsVisible,
|
||||||
UpdateFunction: Page.UpdateFunction
|
UpdateFunction: Page.UpdateFunction,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Category: "Widget Control"
|
|
||||||
/**
|
/**
|
||||||
* Create debug widget instance and add to viewport
|
* Create debug widget instance and add to viewport
|
||||||
* @private Internal widget management method
|
* @category Widget Control
|
||||||
*/
|
*/
|
||||||
private CreateDebugWidget() {
|
private CreateDebugWidget(): void {
|
||||||
this.DebugWidget = CreateWidget<WBP_DebugHUD>(new WBP_DebugHUD);
|
this.DebugWidget = CreateWidget(WBP_DebugHUD);
|
||||||
this.DebugWidget.MovementComponent = this.MovementComponent;
|
this.DebugWidget.MovementComponent = this.MovementComponent;
|
||||||
|
|
||||||
if (IsValid(this.DebugWidget)) {
|
if (SystemLibrary.IsValid(this.DebugWidget)) {
|
||||||
this.DebugWidget.AddToViewport();
|
this.DebugWidget.AddToViewport();
|
||||||
} else {
|
} else {
|
||||||
Print('Failed to create debug widget');
|
if (SystemLibrary.IsValid(this.ToastComponent)) {
|
||||||
|
this.ToastComponent.ShowToast('Failed to create debug widget');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Category: "Widget Control"
|
|
||||||
/**
|
/**
|
||||||
* Update widget visibility based on current debug mode
|
* Update widget visibility based on current debug mode
|
||||||
* @private Internal widget management method
|
* @category Widget Control
|
||||||
*/
|
*/
|
||||||
private UpdateWidgetVisibility() {
|
private UpdateWidgetVisibility(): void {
|
||||||
if (IsValid(this.DebugWidget)) {
|
if (SystemLibrary.IsValid(this.DebugWidget)) {
|
||||||
this.DebugWidget.SetVisibility(this.ShouldShowDebugHUD() ? ESlateVisibility.Visible : ESlateVisibility.Hidden);
|
this.DebugWidget.SetVisibility(
|
||||||
|
this.ShouldShowDebugHUD()
|
||||||
|
? ESlateVisibility.Visible
|
||||||
|
: ESlateVisibility.Hidden
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Category: "Widget Communication"
|
|
||||||
/**
|
/**
|
||||||
* Send current page data to debug widget for display
|
* Send current page data to debug widget for display
|
||||||
* @private Internal widget communication method
|
* @category Widget Communication
|
||||||
*/
|
*/
|
||||||
private UpdateWidgetPage() {
|
private UpdateWidgetPage(): void {
|
||||||
const {Page, IsFound} = this.GetCurrentPage();
|
if (
|
||||||
|
this.GetCurrentPage().IsFound &&
|
||||||
|
SystemLibrary.IsValid(this.DebugWidget)
|
||||||
|
) {
|
||||||
|
const currentPage = this.GetCurrentPage().Page;
|
||||||
|
const visiblePages = this.GetVisiblePages();
|
||||||
|
|
||||||
if (IsFound) {
|
this.DebugWidget.SetHeaderText(currentPage!.Title);
|
||||||
this.DebugWidget.SetHeaderText(Page.Title);
|
this.DebugWidget.SetContentText(currentPage!.Content);
|
||||||
this.DebugWidget.SetContentText(Page.Content);
|
this.DebugWidget.SetNavigationText(
|
||||||
this.DebugWidget.SetNavigationText(`Page ${this.DebugSettings.CurrentPageIndex + 1}/${this.GetVisiblePages().length} | PageUp/PageDown - Navigate`);
|
`Page ${this.DebugSettings.CurrentPageIndex + 1}/${visiblePages.length} | PageUp/PageDown - Navigate`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Category: "HUD Rendering"
|
|
||||||
/**
|
/**
|
||||||
* Main update loop for debug HUD system
|
* Main update loop for debug HUD system
|
||||||
* Called every frame from game loop
|
* Called every frame from game loop
|
||||||
* @param CurrentTime - Current game time in seconds
|
* @param CurrentTime - Current game time in seconds
|
||||||
* @param DeltaTime - Time since last frame in seconds
|
* @param DeltaTime - Time since last frame in seconds
|
||||||
* @public Called by BP_MainCharacter or game framework
|
* @category HUD Rendering
|
||||||
*/
|
*/
|
||||||
public UpdateHUD(CurrentTime: Float, DeltaTime: Float) {
|
public UpdateHUD(CurrentTime: Float, DeltaTime: Float): void {
|
||||||
if (this.IsInitialized && this.ShouldUpdateDebugHUD(CurrentTime)) {
|
const IsReadyForUpdate = (shouldUpdateHUD: boolean): boolean =>
|
||||||
|
this.IsInitialized && shouldUpdateHUD;
|
||||||
|
const IsValidDeltaTime = (deltaTime: Float): boolean => deltaTime > 0;
|
||||||
|
const CalculateFPS = (deltaTime: Float): Float => 1 / deltaTime;
|
||||||
|
|
||||||
|
if (IsReadyForUpdate(this.ShouldUpdateDebugHUD(CurrentTime))) {
|
||||||
this.FrameCounter++;
|
this.FrameCounter++;
|
||||||
this.FPS = DeltaTime > 0.0 ? 1.0 / DeltaTime : 0.0;
|
this.FPS = IsValidDeltaTime(DeltaTime) ? CalculateFPS(DeltaTime) : 0;
|
||||||
this.LastUpdateTime = CurrentTime;
|
this.LastUpdateTime = CurrentTime;
|
||||||
|
|
||||||
if (this.ShouldShowDebugHUD()) {
|
if (this.ShouldShowDebugHUD()) {
|
||||||
|
|
@ -409,223 +421,146 @@ export class AC_DebugHUD {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Category: "System Setup"
|
|
||||||
/**
|
/**
|
||||||
* Register default debug pages (Movement, Surface, Performance)
|
* Register default debug pages (Movement, Surface, Performance)
|
||||||
* @private Internal system setup method
|
* @category System Setup
|
||||||
*/
|
*/
|
||||||
private RegisterDefaultPages() {
|
private RegisterDefaultPages(): void {
|
||||||
this.RegisterDebugPage({
|
DataTableFunctionLibrary.GetDataTableRowNames(this.DebugDataTable).forEach(
|
||||||
PageID: E_DebugPageID.MovementInfo,
|
arrayElement => {
|
||||||
Title: "Movement Constants",
|
this.DebugDataTable.GetDataTableRow(arrayElement, row => {
|
||||||
Content: '',
|
this.RegisterDebugPage(row);
|
||||||
IsVisible: true,
|
|
||||||
UpdateFunction: E_DebugUpdateFunction.UpdateMovementPage
|
|
||||||
});
|
|
||||||
|
|
||||||
this.RegisterDebugPage({
|
|
||||||
PageID: E_DebugPageID.SurfaceInfo,
|
|
||||||
Title: "Surface Classification",
|
|
||||||
Content: '',
|
|
||||||
IsVisible: true,
|
|
||||||
UpdateFunction: E_DebugUpdateFunction.UpdateSurfacePage
|
|
||||||
});
|
|
||||||
|
|
||||||
this.RegisterDebugPage({
|
|
||||||
PageID: E_DebugPageID.PerformanceInfo,
|
|
||||||
Title: "Performance Metrics",
|
|
||||||
Content: '',
|
|
||||||
IsVisible: true,
|
|
||||||
UpdateFunction: E_DebugUpdateFunction.UpdatePerformancePage
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Category: "Testing"
|
|
||||||
/**
|
/**
|
||||||
* Test basic debug system functionality
|
* Get comprehensive test data for debug system validation
|
||||||
* @returns True if all basic tests pass
|
* @returns Object containing initialization status, DataTable reference, and pages array
|
||||||
* @private Internal testing method
|
* @category Testing
|
||||||
*/
|
*/
|
||||||
private TestDebugSystem() {
|
public GetTestData(): {
|
||||||
Print('=== DEBUG SYSTEM TESTS ===');
|
IsInitialized: boolean;
|
||||||
|
DebugDataTable: DataTable<S_DebugPage>;
|
||||||
if (this.IsInitialized) {
|
DebugPages: UEArray<S_DebugPage>;
|
||||||
Print('✅ System initialized');
|
} {
|
||||||
|
return {
|
||||||
if (this.DebugPages.length === 3) {
|
IsInitialized: this.IsInitialized,
|
||||||
Print('✅ All pages registered');
|
DebugDataTable: this.DebugDataTable,
|
||||||
|
DebugPages: this.DebugPages,
|
||||||
if (IsValid(this.MovementComponent)) {
|
};
|
||||||
Print('✅ Movement component valid');
|
|
||||||
|
|
||||||
if (IsValid(this.DebugWidget)) {
|
|
||||||
Print('✅ Debug widget created');
|
|
||||||
Print('✅ ALL TESTS PASSED');
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
Print('❌ Debug widget not created');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Print('❌ Movement component not valid');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Print(`❌ Expected 3 pages, got ${this.DebugPages.length}`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Print('❌ System not initialized');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Category: "Testing"
|
|
||||||
/**
|
|
||||||
* Test page content generation for all registered pages
|
|
||||||
* @returns True if all pages generate non-empty content
|
|
||||||
* @private Internal testing method
|
|
||||||
*/
|
|
||||||
private TestPageContentGeneration() {
|
|
||||||
let allTestsPassed = true;
|
|
||||||
|
|
||||||
this.DebugPages.forEach((page, index) => {
|
|
||||||
let updatedPage: S_DebugPage = page;
|
|
||||||
|
|
||||||
switch (updatedPage.UpdateFunction) {
|
|
||||||
case E_DebugUpdateFunction.UpdateMovementPage:
|
|
||||||
updatedPage = this.UpdateMovementPage(updatedPage);
|
|
||||||
break;
|
|
||||||
case E_DebugUpdateFunction.UpdateSurfacePage:
|
|
||||||
updatedPage = this.UpdateSurfacePage(updatedPage);
|
|
||||||
break;
|
|
||||||
case E_DebugUpdateFunction.UpdatePerformancePage:
|
|
||||||
updatedPage = this.UpdatePerformancePage(updatedPage);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (updatedPage.Content.length > 0) {
|
|
||||||
Print(`✅ Page ${index + 1} content generated`);
|
|
||||||
} else {
|
|
||||||
Print(`❌ Page ${index + 1} content empty`);
|
|
||||||
allTestsPassed = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return allTestsPassed;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Category: "Testing"
|
|
||||||
// Pure: true
|
|
||||||
/**
|
|
||||||
* Validate current navigation state
|
|
||||||
* @returns True if current page index is valid
|
|
||||||
* @pure Function has no side effects
|
|
||||||
*/
|
|
||||||
private IsValidNavigationState() {
|
|
||||||
if (this.DebugSettings.CurrentPageIndex >= 0) {
|
|
||||||
const visiblePagesLength = this.GetVisiblePages().length;
|
|
||||||
|
|
||||||
return !((visiblePagesLength > 0) && (this.DebugSettings.CurrentPageIndex >= visiblePagesLength));
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Category: "Testing"
|
|
||||||
/**
|
|
||||||
* Test page navigation functionality
|
|
||||||
* @param StartCurrentPage - Initial page index to restore after test
|
|
||||||
* @returns True if navigation works correctly
|
|
||||||
* @private Internal testing method
|
|
||||||
*/
|
|
||||||
private TestNavigation(StartCurrentPage: Integer) {
|
|
||||||
if (this.IsValidNavigationState()) {
|
|
||||||
if (this.IsValidNavigationState()) {
|
|
||||||
this.NextPage();
|
|
||||||
|
|
||||||
if (this.IsValidNavigationState()) {
|
|
||||||
if (this.IsValidNavigationState()) {
|
|
||||||
this.PreviousPage();
|
|
||||||
|
|
||||||
if (this.IsValidNavigationState()) {
|
|
||||||
this.DebugSettings.CurrentPageIndex = StartCurrentPage;
|
|
||||||
|
|
||||||
if (this.IsValidNavigationState()) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
Print('❌ Navigation: Failed to restore valid state');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Print('❌ Navigation: PrevPage failed — State became invalid after PreviousPage');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Print('❌ Navigation: PreviousPage failed — Invalid state before PreviousPage');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Print('❌ Navigation: NextPage failed — State became invalid after NextPage');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Print('❌ Navigation: NextPage failed — Invalid state before NextPage');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Print('❌ Navigation: Invalid initial state');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Category: "Testing"
|
|
||||||
/**
|
|
||||||
* Run comprehensive test suite for debug system
|
|
||||||
* Tests initialization, page content generation, and navigation
|
|
||||||
* @private Called during initialization for validation
|
|
||||||
*/
|
|
||||||
private RunAllTests() {
|
|
||||||
Print('=== COMPREHENSIVE DEBUG SYSTEM TESTING ===');
|
|
||||||
|
|
||||||
if (this.TestDebugSystem()) {
|
|
||||||
if (this.TestPageContentGeneration()) {
|
|
||||||
if (this.TestNavigation(this.DebugSettings.CurrentPageIndex)) {
|
|
||||||
Print('✅ ALL TESTS PASSED');
|
|
||||||
} else {
|
|
||||||
Print('❌ Navigation Tests: Failed');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Print('❌ Page Content Generation Tests: Failed');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Print('❌ System Tests: Failed');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Category: "System Setup"
|
|
||||||
/**
|
/**
|
||||||
* Initialize debug HUD system with movement component reference
|
* Initialize debug HUD system with movement component reference
|
||||||
* Sets up pages, creates widget, runs tests, and starts display
|
* Sets up pages, creates widget, runs tests, and starts display
|
||||||
* @param MovementComponentRef - Reference to movement component to debug
|
* @param MovementComponentRef - Reference to movement component to debug
|
||||||
* @public Called by BP_MainCharacter during initialization
|
* @param ToastComponentRef - Reference to toast system for notifications
|
||||||
* @example
|
* @example
|
||||||
* // Initialize debug HUD in main character
|
* // Initialize debug HUD in main character
|
||||||
* this.DebugHUDComponent.InitializeDebugHUD(this.MovementComponent);
|
* this.DebugHUDComponent.InitializeDebugHUD(this.MovementComponent);
|
||||||
|
* @category System Setup
|
||||||
*/
|
*/
|
||||||
public InitializeDebugHUD(MovementComponentRef: AC_Movement) {
|
public InitializeDebugHUD(
|
||||||
|
MovementComponentRef: AC_Movement,
|
||||||
|
ToastComponentRef: AC_ToastSystem
|
||||||
|
): void {
|
||||||
this.MovementComponent = MovementComponentRef;
|
this.MovementComponent = MovementComponentRef;
|
||||||
|
this.ToastComponent = ToastComponentRef;
|
||||||
this.IsInitialized = true;
|
this.IsInitialized = true;
|
||||||
this.FrameCounter = 0;
|
this.FrameCounter = 0;
|
||||||
this.LastUpdateTime = 0;
|
this.LastUpdateTime = 0;
|
||||||
this.FPS = 0;
|
this.FPS = 0;
|
||||||
this.RegisterDefaultPages();
|
this.RegisterDefaultPages();
|
||||||
this.CreateDebugWidget();
|
this.CreateDebugWidget();
|
||||||
this.RunAllTests();
|
|
||||||
this.DebugSettings.CurrentPageIndex = 0;
|
this.DebugSettings.CurrentPageIndex = 0;
|
||||||
this.UpdateWidgetVisibility();
|
this.UpdateWidgetVisibility();
|
||||||
Print('=== DEBUG HUD INITIALIZED ===');
|
this.ToastComponent.ShowToast(
|
||||||
|
'Debug HUD Initialized',
|
||||||
|
E_MessageType.Success
|
||||||
|
);
|
||||||
this.UpdateCurrentPage();
|
this.UpdateCurrentPage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
// VARIABLES
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference to movement component being debugged
|
||||||
|
* Set during initialization, used for accessing movement data
|
||||||
|
* @category Components
|
||||||
|
*/
|
||||||
|
private MovementComponent: AC_Movement | null = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference to toast system component for debug messaging
|
||||||
|
* Set during initialization, used for displaying debug notifications
|
||||||
|
* @category Components
|
||||||
|
*/
|
||||||
|
public ToastComponent: AC_ToastSystem | null = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Debug system configuration settings
|
||||||
|
* Controls visibility, update frequency, and current page
|
||||||
|
* Instance editable for designer customization
|
||||||
|
* @category DebugConfig
|
||||||
|
* @instanceEditable true
|
||||||
|
*/
|
||||||
|
public DebugSettings: S_DebugSettings = {
|
||||||
|
CurrentMode: ESlateVisibility.Visible,
|
||||||
|
CurrentPageIndex: 0,
|
||||||
|
ShowVisualDebug: false,
|
||||||
|
UpdateFrequency: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* System initialization state flag
|
||||||
|
* Set to true after successful InitializeDebugHUD call
|
||||||
|
* @category DebugState
|
||||||
|
*/
|
||||||
|
private IsInitialized: boolean = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timestamp of last HUD update (seconds)
|
||||||
|
* Used for update frequency control
|
||||||
|
* @category DebugState
|
||||||
|
*/
|
||||||
|
private LastUpdateTime: Float = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current frame counter for performance tracking
|
||||||
|
* Incremented each update cycle
|
||||||
|
* @category DebugState
|
||||||
|
*/
|
||||||
|
private FrameCounter: Float = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current frames per second calculation
|
||||||
|
* Calculated as 1.0 / DeltaTime
|
||||||
|
* @category DebugState
|
||||||
|
*/
|
||||||
|
private FPS: Float = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Debug HUD widget instance
|
||||||
|
* Created during initialization, manages UI display
|
||||||
|
* @category Widget Control
|
||||||
|
*/
|
||||||
|
private DebugWidget: WBP_DebugHUD | null = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array of registered debug pages
|
||||||
|
* Contains all available pages with their data and update functions
|
||||||
|
* @category Page System
|
||||||
|
*/
|
||||||
|
private DebugPages: UEArray<S_DebugPage> = new UEArray([]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DataTable reference for debug pages storage
|
||||||
|
* Contains structured page data with Name-based indexing for efficient lookup
|
||||||
|
* @category Page System
|
||||||
|
*/
|
||||||
|
private DebugDataTable: DataTable<S_DebugPage> = DT_DebugPages;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
BIN
Content/Debug/Components/AC_DebugHUD.uasset (Stored with Git LFS)
BIN
Content/Debug/Components/AC_DebugHUD.uasset (Stored with Git LFS)
Binary file not shown.
|
|
@ -1,6 +0,0 @@
|
||||||
// Content/Debug/Enums/E_DebugMode.ts
|
|
||||||
|
|
||||||
export enum E_DebugMode {
|
|
||||||
Hidden = "Hidden",
|
|
||||||
Visible = "Visible",
|
|
||||||
}
|
|
||||||
BIN
Content/Debug/Enums/E_DebugMode.uasset (Stored with Git LFS)
BIN
Content/Debug/Enums/E_DebugMode.uasset (Stored with Git LFS)
Binary file not shown.
|
|
@ -1,7 +1,7 @@
|
||||||
// Content/Debug/Enums/E_DebugPageID.ts
|
// Debug/Enums/E_DebugPageID.ts
|
||||||
|
|
||||||
export enum E_DebugPageID {
|
export enum E_DebugPageID {
|
||||||
MovementInfo = "MovementInfo",
|
MovementInfo = 'MovementInfo',
|
||||||
SurfaceInfo = "SurfaceInfo",
|
SurfaceInfo = 'SurfaceInfo',
|
||||||
PerformanceInfo = "PerformanceInfo"
|
PerformanceInfo = 'PerformanceInfo',
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// Content/Enums/E_DebugUpdateFunction.ts
|
// Debug/Enums/E_DebugUpdateFunction.ts
|
||||||
|
|
||||||
export enum E_DebugUpdateFunction {
|
export enum E_DebugUpdateFunction {
|
||||||
UpdateMovementPage = "UpdateMovementPage",
|
UpdateMovementPage = 'UpdateMovementPage',
|
||||||
UpdateSurfacePage = "UpdateSurfacePage",
|
UpdateSurfacePage = 'UpdateSurfacePage',
|
||||||
UpdatePerformancePage = "UpdatePerformancePage"
|
UpdatePerformancePage = 'UpdatePerformancePage',
|
||||||
}
|
}
|
||||||
|
|
|
||||||
BIN
Content/Debug/Structs/S_DebugColors.uasset (Stored with Git LFS)
BIN
Content/Debug/Structs/S_DebugColors.uasset (Stored with Git LFS)
Binary file not shown.
|
|
@ -1,8 +1,8 @@
|
||||||
// Content/Debug/Structs/S_DebugPage.ts
|
// Debug/Structs/S_DebugPage.ts
|
||||||
|
|
||||||
import type {E_DebugPageID} from "../Enums/E_DebugPageID.js";
|
import type { E_DebugPageID } from '#root/Debug/Enums/E_DebugPageID.js';
|
||||||
import type {Text} from "../../types.js";
|
import type { E_DebugUpdateFunction } from '#root/Debug/Enums/E_DebugUpdateFunction.js';
|
||||||
import type {E_DebugUpdateFunction} from "../Enums/E_DebugUpdateFunction.js";
|
import type { Text } from '#root/UE/Text.ts';
|
||||||
|
|
||||||
export interface S_DebugPage {
|
export interface S_DebugPage {
|
||||||
PageID: E_DebugPageID;
|
PageID: E_DebugPageID;
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
// Content/Debug/Enums/S_DebugSettings.ts
|
// Debug/Enums/S_DebugSettings.ts
|
||||||
|
|
||||||
import type {E_DebugMode} from "../Enums/E_DebugMode.js";
|
import type { ESlateVisibility } from '#root/UE/ESlateVisibility.ts';
|
||||||
import type {Float, Integer} from "../../types.js";
|
import type { Float } from '#root/UE/Float.ts';
|
||||||
|
import type { Integer } from '#root/UE/Integer.ts';
|
||||||
|
|
||||||
export interface S_DebugSettings {
|
export interface S_DebugSettings {
|
||||||
CurrentMode: E_DebugMode;
|
CurrentMode: ESlateVisibility;
|
||||||
CurrentPageIndex: Integer;
|
CurrentPageIndex: Integer;
|
||||||
ShowVisualDebug: boolean;
|
ShowVisualDebug: boolean;
|
||||||
UpdateFrequency: Float;
|
UpdateFrequency: Float;
|
||||||
|
|
|
||||||
BIN
Content/Debug/Structs/S_DebugSettings.uasset (Stored with Git LFS)
BIN
Content/Debug/Structs/S_DebugSettings.uasset (Stored with Git LFS)
Binary file not shown.
|
|
@ -0,0 +1,39 @@
|
||||||
|
// Debug/Tables/DT_DebugPages.ts
|
||||||
|
|
||||||
|
import { E_DebugPageID } from '#root/Debug/Enums/E_DebugPageID.ts';
|
||||||
|
import { E_DebugUpdateFunction } from '#root/Debug/Enums/E_DebugUpdateFunction.ts';
|
||||||
|
import type { S_DebugPage } from '#root/Debug/Structs/S_DebugPage.ts';
|
||||||
|
import { DataTable } from '#root/UE/DataTable.ts';
|
||||||
|
import { Name } from '#root/UE/Name.ts';
|
||||||
|
import { UEArray } from '#root/UE/UEArray.ts';
|
||||||
|
|
||||||
|
export const DT_DebugPages = new DataTable<S_DebugPage>(
|
||||||
|
null,
|
||||||
|
new Name('DT_DebugPages'),
|
||||||
|
new UEArray<S_DebugPage & { Name: Name }>(
|
||||||
|
{
|
||||||
|
Name: new Name('MovementInfo'),
|
||||||
|
PageID: E_DebugPageID.MovementInfo,
|
||||||
|
Title: 'Movement Constants',
|
||||||
|
Content: '',
|
||||||
|
IsVisible: true,
|
||||||
|
UpdateFunction: E_DebugUpdateFunction.UpdateMovementPage,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: new Name('SurfaceInfo'),
|
||||||
|
PageID: E_DebugPageID.SurfaceInfo,
|
||||||
|
Title: 'Surface Classification',
|
||||||
|
Content: '',
|
||||||
|
IsVisible: true,
|
||||||
|
UpdateFunction: E_DebugUpdateFunction.UpdateSurfacePage,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: new Name('PerformanceInfo'),
|
||||||
|
PageID: E_DebugPageID.PerformanceInfo,
|
||||||
|
Title: 'Performance Metrics',
|
||||||
|
Content: '',
|
||||||
|
IsVisible: true,
|
||||||
|
UpdateFunction: E_DebugUpdateFunction.UpdatePerformancePage,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
Binary file not shown.
|
|
@ -0,0 +1,112 @@
|
||||||
|
// Debug/Tests/FT_DebugNavigation.ts
|
||||||
|
|
||||||
|
import { AC_DebugHUD } from '#root/Debug/Components/AC_DebugHUD.ts';
|
||||||
|
import { AC_Movement } from '#root/Movement/Components/AC_Movement.ts';
|
||||||
|
import { AC_ToastSystem } from '#root/Toasts/Components/AC_ToastSystem.ts';
|
||||||
|
import { EFunctionalTestResult } from '#root/UE/EFunctionalTestResult.ts';
|
||||||
|
import { FunctionalTest } from '#root/UE/FunctionalTest.ts';
|
||||||
|
import type { Integer } from '#root/UE/Integer.ts';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Functional Test: Debug HUD Navigation System
|
||||||
|
* Tests page navigation state management during NextPage/PreviousPage operations
|
||||||
|
*/
|
||||||
|
export class FT_DebugNavigation extends FunctionalTest {
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
// GRAPHS
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
// ────────────────────────────────────────────────────────────────────────────────────────
|
||||||
|
// EventGraph
|
||||||
|
// ────────────────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test entry point - validates navigation state during page operations
|
||||||
|
* Uses nested validation to ensure CurrentPageIndex stays valid
|
||||||
|
*/
|
||||||
|
EventStartTest(): void {
|
||||||
|
this.DebugHUDComponent.InitializeDebugHUD(
|
||||||
|
this.MovementComponent,
|
||||||
|
this.ToastSystemComponent
|
||||||
|
);
|
||||||
|
|
||||||
|
this.IfValid('Debug HUD: Navigation invalid initial state', () => {
|
||||||
|
this.IfValid(
|
||||||
|
'Debug HUD: NextPage failed — Invalid state before NextPage',
|
||||||
|
() => {
|
||||||
|
this.DebugHUDComponent.NextPage();
|
||||||
|
|
||||||
|
this.IfValid(
|
||||||
|
'Debug HUD: NextPage failed — State became invalid after NextPage',
|
||||||
|
() => {
|
||||||
|
this.DebugHUDComponent.PreviousPage();
|
||||||
|
|
||||||
|
this.IfValid(
|
||||||
|
'Debug HUD: PrevPage failed — State became invalid after PreviousPage',
|
||||||
|
() => {
|
||||||
|
this.FinishTest(EFunctionalTestResult.Succeeded);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
// MACROS
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates current page index and executes callback if state is valid
|
||||||
|
* @param Message - Error message if validation fails
|
||||||
|
* @param Out - Callback to execute if state is valid
|
||||||
|
*/
|
||||||
|
private IfValid(Message: string, Out: () => void): void {
|
||||||
|
const IsPageIndexOutOfBounds = (
|
||||||
|
visiblePagesLength: Integer,
|
||||||
|
currentPage: Integer
|
||||||
|
): boolean => visiblePagesLength > 0 && currentPage >= visiblePagesLength;
|
||||||
|
|
||||||
|
const IsPageIndexNonNegative = (currentPage: Integer): boolean =>
|
||||||
|
currentPage >= 0;
|
||||||
|
|
||||||
|
if (
|
||||||
|
!IsPageIndexOutOfBounds(
|
||||||
|
this.DebugHUDComponent.GetVisiblePages().length,
|
||||||
|
this.DebugHUDComponent.DebugSettings.CurrentPageIndex
|
||||||
|
) &&
|
||||||
|
IsPageIndexNonNegative(
|
||||||
|
this.DebugHUDComponent.DebugSettings.CurrentPageIndex
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Out();
|
||||||
|
} else {
|
||||||
|
this.FinishTest(EFunctionalTestResult.Failed, Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
// VARIABLES
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Movement system component - required for debug HUD initialization
|
||||||
|
* @category Components
|
||||||
|
*/
|
||||||
|
MovementComponent = new AC_Movement();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Debug HUD system - primary component under test
|
||||||
|
* Tests page navigation state management
|
||||||
|
* @category Components
|
||||||
|
*/
|
||||||
|
DebugHUDComponent = new AC_DebugHUD();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toast notification system - required for debug HUD initialization
|
||||||
|
* @category Components
|
||||||
|
*/
|
||||||
|
ToastSystemComponent = new AC_ToastSystem();
|
||||||
|
}
|
||||||
Binary file not shown.
|
|
@ -0,0 +1,109 @@
|
||||||
|
// Debug/Tests/FT_DebugPageContentGenerator.ts
|
||||||
|
|
||||||
|
import { AC_DebugHUD } from '#root/Debug/Components/AC_DebugHUD.ts';
|
||||||
|
import { E_DebugPageID } from '#root/Debug/Enums/E_DebugPageID.ts';
|
||||||
|
import { E_DebugUpdateFunction } from '#root/Debug/Enums/E_DebugUpdateFunction.ts';
|
||||||
|
import type { S_DebugPage } from '#root/Debug/Structs/S_DebugPage.ts';
|
||||||
|
import { AC_Movement } from '#root/Movement/Components/AC_Movement.ts';
|
||||||
|
import { AC_ToastSystem } from '#root/Toasts/Components/AC_ToastSystem.ts';
|
||||||
|
import { EFunctionalTestResult } from '#root/UE/EFunctionalTestResult.ts';
|
||||||
|
import { FunctionalTest } from '#root/UE/FunctionalTest.ts';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Functional Test: Debug HUD Page Content Generation
|
||||||
|
* Validates that all registered debug pages generate non-empty content
|
||||||
|
*/
|
||||||
|
export class FT_DebugPageContentGenerator extends FunctionalTest {
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
// GRAPHS
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
// ────────────────────────────────────────────────────────────────────────────────────────
|
||||||
|
// EventGraph
|
||||||
|
// ────────────────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test entry point - validates content generation for all debug pages
|
||||||
|
* Iterates through all pages and tests their update functions
|
||||||
|
*/
|
||||||
|
EventStartTest(): void {
|
||||||
|
this.DebugHUDComponent.InitializeDebugHUD(
|
||||||
|
this.MovementComponent,
|
||||||
|
this.ToastSystemComponent
|
||||||
|
);
|
||||||
|
|
||||||
|
this.DebugHUDComponent.GetTestData().DebugPages.forEach(
|
||||||
|
(arrayElement, arrayIndex) => {
|
||||||
|
this.UpdatedPage = arrayElement;
|
||||||
|
|
||||||
|
switch (this.UpdatedPage.UpdateFunction) {
|
||||||
|
case E_DebugUpdateFunction.UpdateMovementPage: {
|
||||||
|
this.UpdatedPage = this.DebugHUDComponent.UpdateMovementPage(
|
||||||
|
this.UpdatedPage
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case E_DebugUpdateFunction.UpdateSurfacePage: {
|
||||||
|
this.UpdatedPage = this.DebugHUDComponent.UpdateSurfacePage(
|
||||||
|
this.UpdatedPage
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case E_DebugUpdateFunction.UpdatePerformancePage: {
|
||||||
|
this.UpdatedPage = this.DebugHUDComponent.UpdatePerformancePage(
|
||||||
|
this.UpdatedPage
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.UpdatedPage.Content.length > 0) {
|
||||||
|
this.FinishTest(EFunctionalTestResult.Succeeded);
|
||||||
|
} else {
|
||||||
|
this.FinishTest(
|
||||||
|
EFunctionalTestResult.Failed,
|
||||||
|
`DebugHUD: Page ${arrayIndex + 1} content empty`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
// VARIABLES
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Movement system component - required for debug HUD initialization
|
||||||
|
* @category Components
|
||||||
|
*/
|
||||||
|
MovementComponent = new AC_Movement();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Debug HUD system - primary component under test
|
||||||
|
* Tests page content generation functionality
|
||||||
|
* @category Components
|
||||||
|
*/
|
||||||
|
DebugHUDComponent = new AC_DebugHUD();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toast notification system - required for debug HUD initialization
|
||||||
|
* @category Components
|
||||||
|
*/
|
||||||
|
ToastSystemComponent = new AC_ToastSystem();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Working copy of debug page for content generation testing
|
||||||
|
* Updated during test execution for each page
|
||||||
|
* @category Test State
|
||||||
|
*/
|
||||||
|
UpdatedPage: S_DebugPage = {
|
||||||
|
PageID: E_DebugPageID.MovementInfo,
|
||||||
|
Title: '',
|
||||||
|
Content: '',
|
||||||
|
IsVisible: false,
|
||||||
|
UpdateFunction: E_DebugUpdateFunction.UpdateMovementPage,
|
||||||
|
};
|
||||||
|
}
|
||||||
Binary file not shown.
|
|
@ -0,0 +1,95 @@
|
||||||
|
// Debug/Tests/FT_DebugSystem.ts
|
||||||
|
|
||||||
|
import { AC_DebugHUD } from '#root/Debug/Components/AC_DebugHUD.ts';
|
||||||
|
import { AC_Movement } from '#root/Movement/Components/AC_Movement.ts';
|
||||||
|
import { AC_ToastSystem } from '#root/Toasts/Components/AC_ToastSystem.ts';
|
||||||
|
import { DataTableFunctionLibrary } from '#root/UE/DataTableFunctionLibrary.ts';
|
||||||
|
import { EFunctionalTestResult } from '#root/UE/EFunctionalTestResult.ts';
|
||||||
|
import { FunctionalTest } from '#root/UE/FunctionalTest.ts';
|
||||||
|
import { SystemLibrary } from '#root/UE/SystemLibrary.ts';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Functional Test: Debug System Basic Functionality
|
||||||
|
* Validates initialization, component validity, and data table consistency
|
||||||
|
*/
|
||||||
|
export class FT_DebugSystem extends FunctionalTest {
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
// GRAPHS
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
// ────────────────────────────────────────────────────────────────────────────────────────
|
||||||
|
// EventGraph
|
||||||
|
// ────────────────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test entry point - validates basic debug system functionality
|
||||||
|
* Uses nested validation to check initialization, page count, and component validity
|
||||||
|
*/
|
||||||
|
EventStartTest(): void {
|
||||||
|
this.DebugHUDComponent.InitializeDebugHUD(
|
||||||
|
this.MovementComponent,
|
||||||
|
this.ToastSystemComponent
|
||||||
|
);
|
||||||
|
|
||||||
|
if (this.DebugHUDComponent.GetTestData().IsInitialized) {
|
||||||
|
if (
|
||||||
|
DataTableFunctionLibrary.GetDataTableRowNames(
|
||||||
|
this.DebugHUDComponent.GetTestData().DebugDataTable
|
||||||
|
).length === this.DebugHUDComponent.GetTestData().DebugPages.length
|
||||||
|
) {
|
||||||
|
if (SystemLibrary.IsValid(this.MovementComponent)) {
|
||||||
|
if (SystemLibrary.IsValid(this.DebugHUDComponent)) {
|
||||||
|
this.FinishTest(EFunctionalTestResult.Succeeded);
|
||||||
|
} else {
|
||||||
|
this.FinishTest(
|
||||||
|
EFunctionalTestResult.Failed,
|
||||||
|
'DebugHUD component not valid'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.FinishTest(
|
||||||
|
EFunctionalTestResult.Failed,
|
||||||
|
'Debug HUD: Movement component not valid'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.FinishTest(
|
||||||
|
EFunctionalTestResult.Failed,
|
||||||
|
`Debug HUD: Expected ${this.DebugHUDComponent.GetTestData().DebugPages.length} pages, got ${
|
||||||
|
DataTableFunctionLibrary.GetDataTableRowNames(
|
||||||
|
this.DebugHUDComponent.GetTestData().DebugDataTable
|
||||||
|
).length
|
||||||
|
}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.FinishTest(
|
||||||
|
EFunctionalTestResult.Failed,
|
||||||
|
'Debug HUD failed to initialize'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
// VARIABLES
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Movement system component - required for debug HUD initialization
|
||||||
|
* @category Components
|
||||||
|
*/
|
||||||
|
MovementComponent = new AC_Movement();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Debug HUD system - primary component under test
|
||||||
|
* Tests basic system initialization and component validity
|
||||||
|
* @category Components
|
||||||
|
*/
|
||||||
|
DebugHUDComponent = new AC_DebugHUD();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toast notification system - required for debug HUD initialization
|
||||||
|
* @category Components
|
||||||
|
*/
|
||||||
|
ToastSystemComponent = new AC_ToastSystem();
|
||||||
|
}
|
||||||
Binary file not shown.
|
|
@ -1,132 +1,158 @@
|
||||||
// Content/Debug/UI/WBP_DebugHUD.ts
|
// Debug/UI/WBP_DebugHUD.ts
|
||||||
|
|
||||||
import type {AC_Movement} from "../../Movement/Components/AC_Movement.js";
|
import type { AC_Movement } from '#root/Movement/Components/AC_Movement.js';
|
||||||
import {TextBox, Widget} from "../../classes.js";
|
import { SystemLibrary } from '#root/UE/SystemLibrary.ts';
|
||||||
import type {Text} from "../../types.js";
|
import type { Text } from '#root/UE/Text.ts';
|
||||||
import {IsValid} from "../../functions.js";
|
import { TextBlock } from '#root/UE/TextBlock.ts';
|
||||||
|
import { UserWidget } from '#root/UE/UserWidget.ts';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Debug HUD Widget for displaying system information
|
* Debug HUD Widget for displaying system information
|
||||||
* Provides real-time debug information display with multiple pages
|
* Provides real-time debug information display with multiple pages
|
||||||
* Part of the deterministic movement system debug infrastructure
|
* Part of the deterministic movement system debug infrastructure
|
||||||
*/
|
*/
|
||||||
export class WBP_DebugHUD extends Widget{
|
export class WBP_DebugHUD extends UserWidget {
|
||||||
// Category: "Components"
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
/**
|
// GRAPHS
|
||||||
* Reference to movement component for accessing debug data
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
* Set by AC_DebugHUD during initialization
|
|
||||||
*/
|
|
||||||
public MovementComponent: AC_Movement | null = null;
|
|
||||||
|
|
||||||
// Category: "PageData"
|
// ────────────────────────────────────────────────────────────────────────────────────────
|
||||||
/**
|
// EventGraph
|
||||||
* Current page title text
|
// ────────────────────────────────────────────────────────────────────────────────────────
|
||||||
* Updated by AC_DebugHUD when switching pages
|
|
||||||
*/
|
|
||||||
private HeaderText: Text = "Debug HUD";
|
|
||||||
|
|
||||||
// Category: "PageData"
|
|
||||||
/**
|
/**
|
||||||
* Current page content text
|
* Widget construction event - initializes all text displays
|
||||||
* Contains the main debug information for current page
|
* Called automatically when widget is created
|
||||||
*/
|
*/
|
||||||
private ContentText: Text = "Text";
|
EventConstruct(): void {
|
||||||
|
this.UpdateHeaderDisplay();
|
||||||
|
this.UpdateContentDisplay();
|
||||||
|
this.UpdateNavigationDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
// FUNCTIONS
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
// Category: "PageData"
|
|
||||||
/**
|
/**
|
||||||
* Navigation instructions text
|
* Update header text box with current header text
|
||||||
* Shows current page index and navigation controls
|
* @category Display Updates
|
||||||
*/
|
*/
|
||||||
private NavigationText: Text = "Navigation";
|
private UpdateHeaderDisplay(): void {
|
||||||
|
if (SystemLibrary.IsValid(this.HeaderTextBox)) {
|
||||||
// Category: "UI Components"
|
|
||||||
/**
|
|
||||||
* Text box widget for displaying page header/title
|
|
||||||
* Positioned at top of debug HUD
|
|
||||||
*/
|
|
||||||
private HeaderTextBox = new TextBox();
|
|
||||||
|
|
||||||
// Category: "UI Components"
|
|
||||||
/**
|
|
||||||
* Text box widget for displaying main debug content
|
|
||||||
* Contains multi-line debug information for current page
|
|
||||||
*/
|
|
||||||
private ContentTextBox = new TextBox();
|
|
||||||
|
|
||||||
// Category: "UI Components"
|
|
||||||
/**
|
|
||||||
* Text box widget for displaying navigation help
|
|
||||||
* Shows page counter and keyboard shortcuts
|
|
||||||
*/
|
|
||||||
private NavigationTextBox = new TextBox();
|
|
||||||
|
|
||||||
// Category: "Display Updates"
|
|
||||||
private UpdateHeaderDisplay() {
|
|
||||||
if (IsValid(this.HeaderTextBox)) {
|
|
||||||
this.HeaderTextBox.SetText(this.HeaderText);
|
this.HeaderTextBox.SetText(this.HeaderText);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Category: "Display Updates"
|
|
||||||
/**
|
/**
|
||||||
* Update header text box with current header text
|
* Update content text box with current content text
|
||||||
* Called automatically when header text changes
|
* @category Display Updates
|
||||||
* @private Internal display update method
|
|
||||||
*/
|
*/
|
||||||
private UpdateContentDisplay() {
|
private UpdateContentDisplay(): void {
|
||||||
if (IsValid(this.ContentTextBox)) {
|
if (SystemLibrary.IsValid(this.ContentTextBox)) {
|
||||||
this.ContentTextBox.SetText(this.ContentText);
|
this.ContentTextBox.SetText(this.ContentText);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Category: "Display Updates"
|
|
||||||
/**
|
/**
|
||||||
* Update navigation text box with current navigation text
|
* Update navigation text box with current navigation text
|
||||||
* Called automatically when navigation text changes
|
* @category Display Updates
|
||||||
* @private Internal display update method
|
|
||||||
*/
|
*/
|
||||||
private UpdateNavigationDisplay() {
|
private UpdateNavigationDisplay(): void {
|
||||||
if (IsValid(this.NavigationTextBox)) {
|
if (SystemLibrary.IsValid(this.NavigationTextBox)) {
|
||||||
this.NavigationTextBox.SetText(this.NavigationText);
|
this.NavigationTextBox.SetText(this.NavigationText);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Category: "Public Interface"
|
|
||||||
/**
|
/**
|
||||||
* Set new header text and update display
|
* Set new header text and update display
|
||||||
* @param NewHeaderText - New title text for current debug page
|
* @param NewHeaderText - New title text for current debug page
|
||||||
* @example
|
* @example
|
||||||
* // Set movement page header
|
* // Set movement page header
|
||||||
* SetHeaderText("Movement Constants")
|
* SetHeaderText("Movement Constants")
|
||||||
|
* @category Public Interface
|
||||||
*/
|
*/
|
||||||
public SetHeaderText(NewHeaderText: string): void {
|
public SetHeaderText(NewHeaderText: Text): void {
|
||||||
this.HeaderText = NewHeaderText;
|
this.HeaderText = NewHeaderText;
|
||||||
this.UpdateHeaderDisplay();
|
this.UpdateHeaderDisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Category: "Public Interface"
|
|
||||||
/**
|
/**
|
||||||
* Set new content text and update display
|
* Set new content text and update display
|
||||||
* @param NewContentText - New debug content (supports multi-line text)
|
* @param NewContentText - New debug content (supports multi-line text)
|
||||||
* @example
|
* @example
|
||||||
* // Set movement constants content
|
* // Set movement constants content
|
||||||
* SetContentText("Max Speed: 600\nAcceleration: 2000\nFriction: 8.0")
|
* SetContentText("Max Speed: 600\nAcceleration: 2000\nFriction: 8.0")
|
||||||
|
* @category Public Interface
|
||||||
*/
|
*/
|
||||||
public SetContentText(NewContentText: string): void {
|
public SetContentText(NewContentText: Text): void {
|
||||||
this.ContentText = NewContentText;
|
this.ContentText = NewContentText;
|
||||||
this.UpdateContentDisplay();
|
this.UpdateContentDisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Category: "Public Interface"
|
|
||||||
/**
|
/**
|
||||||
* Set new navigation text and update display
|
* Set new navigation text and update display
|
||||||
* @param NewNavigationText - Navigation instructions and page info
|
* @param NewNavigationText - Navigation instructions and page info
|
||||||
* @example
|
* @example
|
||||||
* // Set navigation with page counter
|
* // Set navigation with page counter
|
||||||
* SetNavigationText("Page 1/3 | PageUp/PageDown - Navigate")
|
* SetNavigationText("Page 1/3 | PageUp/PageDown - Navigate")
|
||||||
|
* @category Public Interface
|
||||||
*/
|
*/
|
||||||
public SetNavigationText(NewNavigationText: string): void {
|
public SetNavigationText(NewNavigationText: Text): void {
|
||||||
this.NavigationText = NewNavigationText;
|
this.NavigationText = NewNavigationText;
|
||||||
this.UpdateNavigationDisplay();
|
this.UpdateNavigationDisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
// VARIABLES
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference to movement component for accessing debug data
|
||||||
|
* Set by AC_DebugHUD during initialization
|
||||||
|
* @category Components
|
||||||
|
*/
|
||||||
|
public MovementComponent: AC_Movement | null = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current page title text
|
||||||
|
* Updated by AC_DebugHUD when switching pages
|
||||||
|
* @category PageData
|
||||||
|
*/
|
||||||
|
private HeaderText: Text = 'Debug HUD';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current page content text
|
||||||
|
* Contains the main debug information for current page
|
||||||
|
* @category PageData
|
||||||
|
*/
|
||||||
|
private ContentText: Text = 'Text';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigation instructions text
|
||||||
|
* Shows current page index and navigation controls
|
||||||
|
* @category PageData
|
||||||
|
*/
|
||||||
|
private NavigationText: Text = 'Navigation';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Text box widget for displaying page header/title
|
||||||
|
* Positioned at top of debug HUD
|
||||||
|
* @category UI Components
|
||||||
|
*/
|
||||||
|
private HeaderTextBox = new TextBlock();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Text box widget for displaying main debug content
|
||||||
|
* Contains multi-line debug information for current page
|
||||||
|
* @category UI Components
|
||||||
|
*/
|
||||||
|
private ContentTextBox = new TextBlock();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Text box widget for displaying navigation help
|
||||||
|
* Shows page counter and keyboard shortcuts
|
||||||
|
* @category UI Components
|
||||||
|
*/
|
||||||
|
private NavigationTextBox = new TextBlock();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
BIN
Content/Debug/UI/WBP_DebugHUD.uasset (Stored with Git LFS)
BIN
Content/Debug/UI/WBP_DebugHUD.uasset (Stored with Git LFS)
Binary file not shown.
BIN
Content/Debug/UI/WBP_TestResult.uasset (Stored with Git LFS)
BIN
Content/Debug/UI/WBP_TestResult.uasset (Stored with Git LFS)
Binary file not shown.
|
|
@ -1,5 +1,6 @@
|
||||||
// Content/Input/Actions/IA_LeftTrigger.ts
|
// Input/Actions/IA_LeftTrigger.ts
|
||||||
|
|
||||||
import type {InputMapping} from "../../types.js";
|
import { InputAction } from '#root/UE/InputAction.ts';
|
||||||
|
import { Name } from '#root/UE/Name.ts';
|
||||||
|
|
||||||
export const IA_LeftTrigger: InputMapping = {}
|
export const IA_LeftTrigger = new InputAction(null, new Name('IA_LeftTrigger'));
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
// Input/Actions/IA_Look.ts
|
||||||
|
|
||||||
|
import { InputAction } from '#root/UE/InputAction.ts';
|
||||||
|
import { Name } from '#root/UE/Name.ts';
|
||||||
|
|
||||||
|
export const IA_Look = new InputAction(null, new Name('IA_Look'));
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
// Input/Actions/IA_Move.ts
|
||||||
|
|
||||||
|
import { InputAction } from '#root/UE/InputAction.ts';
|
||||||
|
import { Name } from '#root/UE/Name.ts';
|
||||||
|
|
||||||
|
export const IA_Move = new InputAction(null, new Name('IA_Move'));
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
// Content/Input/Actions/IA_NextDebugMode.ts
|
// Input/Actions/IA_NextDebugMode.ts
|
||||||
|
|
||||||
import type {InputMapping} from "../../types.js";
|
import { InputAction } from '#root/UE/InputAction.ts';
|
||||||
|
|
||||||
export const IA_NextDebugMode: InputMapping = {}
|
export const IA_NextDebugMode = new InputAction(null, 'IA_NextDebugMode');
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
// Content/Input/Actions/IA_PrevDebugMode.ts
|
// Input/Actions/IA_PrevDebugMode.ts
|
||||||
|
|
||||||
import type {InputMapping} from "../../types.js";
|
import { InputAction } from '#root/UE/InputAction.ts';
|
||||||
|
|
||||||
export const IA_PrevDebugMode: InputMapping = {}
|
export const IA_PrevDebugMode = new InputAction(null, 'IA_PrevDebugMode');
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
// Content/Input/Actions/IA_RightTrigger.ts
|
// Input/Actions/IA_RightTrigger.ts
|
||||||
|
|
||||||
import type {InputMapping} from "../../types.js";
|
import { InputAction } from '#root/UE/InputAction.ts';
|
||||||
|
|
||||||
export const IA_RightTrigger: InputMapping = {}
|
export const IA_RightTrigger = new InputAction(null, 'IA_RightTrigger');
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
// Content/Input/Actions/IA_ToggleHUD.ts
|
// Input/Actions/IA_ToggleHUD.ts
|
||||||
|
|
||||||
import type {InputMapping} from "../../types.js";
|
import { InputAction } from '#root/UE/InputAction.ts';
|
||||||
|
|
||||||
export const IA_ToggleHUD: InputMapping = {}
|
export const IA_ToggleHUD = new InputAction(null, 'IA_ToggleHUD');
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
// Content/Input/Actions/IA_ToggleVisualDebug.ts
|
// Input/Actions/IA_ToggleVisualDebug.ts
|
||||||
|
|
||||||
import type {InputMapping} from "../../types.js";
|
import { InputAction } from '#root/UE/InputAction.ts';
|
||||||
|
|
||||||
export const IA_ToggleVisualDebug: InputMapping = {}
|
export const IA_ToggleVisualDebug = new InputAction(
|
||||||
|
null,
|
||||||
|
'IA_ToggleVisualDebug'
|
||||||
|
);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
// Input/Enums/E_InputDeviceType.ts
|
||||||
|
|
||||||
|
export enum E_InputDeviceType {
|
||||||
|
Unknown = 'Unknown',
|
||||||
|
Keyboard = 'Keyboard',
|
||||||
|
Gamepad = 'Gamepad',
|
||||||
|
}
|
||||||
|
|
@ -1,18 +1,24 @@
|
||||||
// Content/Input/IMC_Default.ts
|
// Input/IMC_Default.ts
|
||||||
|
|
||||||
import type {InputMappingContext} from "../types.js";
|
import { IA_LeftTrigger } from '#root/Input/Actions/IA_LeftTrigger.ts';
|
||||||
import {IA_LeftTrigger} from "./Actions/IA_LeftTrigger.js";
|
import { IA_Move } from '#root/Input/Actions/IA_Move.ts';
|
||||||
import {IA_RightTrigger} from "./Actions/IA_RightTrigger.js";
|
import { IA_NextDebugMode } from '#root/Input/Actions/IA_NextDebugMode.ts';
|
||||||
import {IA_NextDebugMode} from "./Actions/IA_NextDebugMode.js";
|
import { IA_PrevDebugMode } from '#root/Input/Actions/IA_PrevDebugMode.ts';
|
||||||
import {IA_PrevDebugMode} from "./Actions/IA_PrevDebugMode.js";
|
import { IA_RightTrigger } from '#root/Input/Actions/IA_RightTrigger.ts';
|
||||||
import {IA_ToggleHUD} from "./Actions/IA_ToggleHUD.js";
|
import { IA_ToggleHUD } from '#root/Input/Actions/IA_ToggleHUD.ts';
|
||||||
import {IA_ToggleVisualDebug} from "./Actions/IA_ToggleVisualDebug.js";
|
import { IA_ToggleVisualDebug } from '#root/Input/Actions/IA_ToggleVisualDebug.ts';
|
||||||
|
import { InputMappingContext } from '#root/UE/InputMappingContext.ts';
|
||||||
|
import type { Key } from '#root/UE/Key.ts';
|
||||||
|
|
||||||
export const IMC_Default: InputMappingContext = [
|
export const IMC_Default = new InputMappingContext();
|
||||||
IA_LeftTrigger,
|
|
||||||
IA_RightTrigger,
|
IMC_Default.mapKey(IA_LeftTrigger, 'IA_LeftTrigger' as unknown as Key);
|
||||||
IA_NextDebugMode,
|
IMC_Default.mapKey(IA_RightTrigger, 'IA_RightTrigger' as unknown as Key);
|
||||||
IA_PrevDebugMode,
|
IMC_Default.mapKey(IA_NextDebugMode, 'IA_NextDebugMode' as unknown as Key);
|
||||||
IA_ToggleHUD,
|
IMC_Default.mapKey(IA_PrevDebugMode, 'IA_PrevDebugMode' as unknown as Key);
|
||||||
|
IMC_Default.mapKey(IA_ToggleHUD, 'IA_ToggleHUD' as unknown as Key);
|
||||||
|
IMC_Default.mapKey(
|
||||||
IA_ToggleVisualDebug,
|
IA_ToggleVisualDebug,
|
||||||
]
|
'IA_ToggleVisualDebug' as unknown as Key
|
||||||
|
);
|
||||||
|
IMC_Default.mapKey(IA_Move, 'IA_Move' as unknown as Key);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
// Content/Levels/TestLevel.ts
|
// Levels/TestLevel.ts
|
||||||
|
|
||||||
import {BP_MainCharacter} from "../Blueprints/BP_MainCharacter.js";
|
import { BP_MainCharacter } from '#root/Blueprints/BP_MainCharacter.ts';
|
||||||
|
|
||||||
const MainCharacter = new BP_MainCharacter();
|
new BP_MainCharacter();
|
||||||
|
|
|
||||||
BIN
Content/Levels/TestLevel.umap (Stored with Git LFS)
BIN
Content/Levels/TestLevel.umap (Stored with Git LFS)
Binary file not shown.
|
|
@ -0,0 +1,85 @@
|
||||||
|
// Math/Libraries/BFL_Vectors.ts
|
||||||
|
|
||||||
|
import { BlueprintFunctionLibrary } from '#root/UE/BlueprintFunctionLibrary.ts';
|
||||||
|
import type { Float } from '#root/UE/Float.ts';
|
||||||
|
import { MathLibrary } from '#root/UE/MathLibrary.ts';
|
||||||
|
import { Vector } from '#root/UE/Vector.ts';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blueprint Function Library: Vector Mathematics
|
||||||
|
* Pure mathematical functions for vector operations and surface angle calculations
|
||||||
|
* Used by movement system for deterministic surface classification
|
||||||
|
*/
|
||||||
|
export class BFL_VectorsClass extends BlueprintFunctionLibrary {
|
||||||
|
constructor(
|
||||||
|
outer: null | BlueprintFunctionLibrary = null,
|
||||||
|
name: string = 'BFL_Vectors'
|
||||||
|
) {
|
||||||
|
super(outer, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
// FUNCTIONS
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate angle between two normalized vectors
|
||||||
|
* @param Vector1 - First normalized vector
|
||||||
|
* @param Vector2 - Second normalized vector
|
||||||
|
* @returns Angle between vectors in radians (0 to π)
|
||||||
|
* @example
|
||||||
|
* // 90° angle between X and Z axes
|
||||||
|
* GetAngleBetweenVectors(new Vector(1,0,0), new Vector(0,0,1)) // returns π/2
|
||||||
|
*/
|
||||||
|
public GetAngleBetweenVectors(Vector1: Vector, Vector2: Vector): Float {
|
||||||
|
/**
|
||||||
|
* Internal calculation using dot product and arccosine
|
||||||
|
*/
|
||||||
|
const CalculateAngleBetweenVectors = (v1: Vector, v2: Vector): Float =>
|
||||||
|
MathLibrary.Acos(MathLibrary.Dot(v1, v2));
|
||||||
|
|
||||||
|
return CalculateAngleBetweenVectors(Vector1, Vector2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate surface normal vector from angle in degrees
|
||||||
|
* @param AngleDegrees - Angle from horizontal in degrees (0-180)
|
||||||
|
* @returns Normalized surface normal vector
|
||||||
|
* @example
|
||||||
|
* // Flat surface (0°)
|
||||||
|
* GetNormalFromAngle(0) // returns Vector(0,0,1)
|
||||||
|
* // Vertical wall (90°)
|
||||||
|
* GetNormalFromAngle(90) // returns Vector(1,0,0)
|
||||||
|
*/
|
||||||
|
public GetNormalFromAngle(AngleDegrees: Float): Vector {
|
||||||
|
/**
|
||||||
|
* Calculate X component using sine of angle
|
||||||
|
*/
|
||||||
|
const CalculateX = (angle: Float): Float =>
|
||||||
|
MathLibrary.Sin(MathLibrary.DegreesToRadians(angle));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate Z component using cosine of angle
|
||||||
|
*/
|
||||||
|
const CalculateZ = (angle: Float): Float =>
|
||||||
|
MathLibrary.Cos(MathLibrary.DegreesToRadians(angle));
|
||||||
|
|
||||||
|
return new Vector(CalculateX(AngleDegrees), 0, CalculateZ(AngleDegrees));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate angle between surface normal and up vector
|
||||||
|
* @param SurfaceNormal - Normalized surface normal vector
|
||||||
|
* @returns Angle from horizontal plane in radians (0 = flat, π/2 = vertical)
|
||||||
|
* @example
|
||||||
|
* // Flat surface
|
||||||
|
* GetSurfaceAngle(new Vector(0,0,1)) // returns 0
|
||||||
|
* // Vertical wall
|
||||||
|
* GetSurfaceAngle(new Vector(1,0,0)) // returns π/2
|
||||||
|
*/
|
||||||
|
public GetSurfaceAngle(SurfaceNormal: Vector): Float {
|
||||||
|
return this.GetAngleBetweenVectors(SurfaceNormal, new Vector(0, 0, 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const BFL_Vectors = new BFL_VectorsClass();
|
||||||
Binary file not shown.
|
|
@ -1,94 +1,24 @@
|
||||||
// Content/Movement/Components/AC_Movement.ts
|
// Movement/Components/AC_Movement.ts
|
||||||
|
|
||||||
import type {S_MovementConstants} from "../Structs/S_MovementConstants.js";
|
import { BFL_Vectors } from '#root/Math/Libraries/BFL_Vectors.ts';
|
||||||
import type {S_AngleThresholds} from "../Structs/S_AngleThresholds.js";
|
import { E_SurfaceType } from '#root/Movement/Enums/E_SurfaceType.ts';
|
||||||
import type {Float, Vector} from "../../types.js";
|
import { S_AngleThresholds } from '#root/Movement/Structs/S_AngleThresholds.ts';
|
||||||
import {acos, cos, D2R, Dot, Print, sin} from "../../functions.js";
|
import type { S_MovementConstants } from '#root/Movement/Structs/S_MovementConstants.ts';
|
||||||
import {E_SurfaceType} from "../Enums/E_SurfaceType.js";
|
import { ActorComponent } from '#root/UE/ActorComponent.ts';
|
||||||
import type {S_SurfaceTestCase} from "../Structs/S_SurfaceTestCase.js";
|
import type { Float } from '#root/UE/Float.ts';
|
||||||
|
import { MathLibrary } from '#root/UE/MathLibrary.ts';
|
||||||
|
import type { Vector } from '#root/UE/Vector.ts';
|
||||||
|
|
||||||
export class AC_Movement {
|
|
||||||
// === Movement configuration exposed to designers ===
|
|
||||||
// Category: "Movement Config"
|
|
||||||
// Instance editable: true
|
|
||||||
public readonly MovementConstants: S_MovementConstants = {
|
|
||||||
MaxSpeed: 600.0,
|
|
||||||
Acceleration: 2000.0,
|
|
||||||
Friction: 8.0,
|
|
||||||
Gravity: 980.0,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Category: "Movement Config"
|
|
||||||
// Instance editable: true
|
|
||||||
readonly AngleThresholdsDegrees: S_AngleThresholds = {
|
|
||||||
Walkable: 50.0, // degrees
|
|
||||||
SteepSlope: 85.0, // degrees
|
|
||||||
Wall: 95.0, // degrees
|
|
||||||
};
|
|
||||||
// =====================================================
|
|
||||||
|
|
||||||
// Category: "Debug"
|
|
||||||
IsInitialized: boolean = false;
|
|
||||||
|
|
||||||
// Category: "Debug"
|
|
||||||
// Instance editable: true
|
|
||||||
ShowDebugInfo: boolean = true;
|
|
||||||
|
|
||||||
// === Runtime cached values for performance (radians) ===
|
|
||||||
// Category: "InternalCache"
|
|
||||||
AngleThresholdsRads: S_AngleThresholds;
|
|
||||||
// =====================================================
|
|
||||||
|
|
||||||
// Category: "Math"
|
|
||||||
// Pure: true
|
|
||||||
/**
|
/**
|
||||||
* Calculate angle between two normalized vectors
|
* Movement System Component
|
||||||
* @param Vector1 - First normalized vector
|
* Core deterministic movement system for 3D platformer
|
||||||
* @param Vector2 - Second normalized vector
|
* Handles surface classification and movement physics calculations
|
||||||
* @returns Angle between vectors in radians (0 to π)
|
|
||||||
* @example
|
|
||||||
* // 90° angle between X and Z axes
|
|
||||||
* GetAngleBetweenVectors([1,0,0], [0,0,1]) // returns π/2
|
|
||||||
*/
|
*/
|
||||||
GetAngleBetweenVectors(Vector1: Vector, Vector2: Vector) {
|
export class AC_Movement extends ActorComponent {
|
||||||
return acos(Dot(Vector1, Vector2));
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
}
|
// FUNCTIONS
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
// Category: "Math"
|
|
||||||
// Pure: true
|
|
||||||
/**
|
|
||||||
* Calculate angle between surface normal and up vector
|
|
||||||
* @param SurfaceNormal - Normalized surface normal vector
|
|
||||||
* @returns Angle from horizontal plane in radians (0 = flat, π/2 = vertical)
|
|
||||||
* @example
|
|
||||||
* // Flat surface
|
|
||||||
* GetSurfaceAngle([0,0,1]) // returns 0
|
|
||||||
* // Vertical wall
|
|
||||||
* GetSurfaceAngle([1,0,0]) // returns π/2
|
|
||||||
*/
|
|
||||||
GetSurfaceAngle(SurfaceNormal: Vector): Float {
|
|
||||||
return this.GetAngleBetweenVectors(SurfaceNormal, [0.0, 0.0, 1.0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Category: "Math"
|
|
||||||
// Pure: true
|
|
||||||
/**
|
|
||||||
* Generate surface normal vector from angle in degrees
|
|
||||||
* @param AngleDegrees - Angle from horizontal in degrees (0-180)
|
|
||||||
* @returns Normalized surface normal vector
|
|
||||||
* @example
|
|
||||||
* // Flat surface (0°)
|
|
||||||
* GenerateNormalFromAngle(0) // returns [0,0,1]
|
|
||||||
* // Vertical wall (90°)
|
|
||||||
* GenerateNormalFromAngle(90) // returns [1,0,0]
|
|
||||||
*/
|
|
||||||
GenerateNormalFromAngle(AngleDegrees: Float): Vector {
|
|
||||||
const AngleRads = D2R(AngleDegrees);
|
|
||||||
return [sin(AngleRads), 0.0, cos(AngleRads)];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Category: "Surface Detection"
|
|
||||||
// Pure: true
|
|
||||||
/**
|
/**
|
||||||
* Classify surface type based on normal vector
|
* Classify surface type based on normal vector
|
||||||
* @param SurfaceNormal - Normalized surface normal vector
|
* @param SurfaceNormal - Normalized surface normal vector
|
||||||
|
|
@ -96,140 +26,163 @@ export class AC_Movement {
|
||||||
* @returns Surface type classification
|
* @returns Surface type classification
|
||||||
* @example
|
* @example
|
||||||
* // Classify flat ground
|
* // Classify flat ground
|
||||||
* ClassifySurface([0,0,1], thresholds) // returns E_SurfaceType.Walkable
|
* ClassifySurface(new Vector(0,0,1), thresholds) // returns E_SurfaceType.Walkable
|
||||||
|
* @pure true
|
||||||
|
* @category Surface Detection
|
||||||
*/
|
*/
|
||||||
ClassifySurface(SurfaceNormal: Vector, AngleThresholds: S_AngleThresholds): E_SurfaceType {
|
public ClassifySurface(
|
||||||
const SurfaceAngle = this.GetSurfaceAngle(SurfaceNormal);
|
SurfaceNormal: Vector,
|
||||||
|
AngleThresholds: S_AngleThresholds
|
||||||
|
): E_SurfaceType {
|
||||||
|
const SurfaceAngle = BFL_Vectors.GetSurfaceAngle(SurfaceNormal);
|
||||||
|
|
||||||
if (SurfaceAngle <= AngleThresholds.Walkable) {
|
/**
|
||||||
|
* Check if angle is within walkable range
|
||||||
|
*/
|
||||||
|
const IsWalkableAngle = (walkableAngle: Float): boolean =>
|
||||||
|
SurfaceAngle <= walkableAngle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if angle is within steep slope range
|
||||||
|
*/
|
||||||
|
const IsSteepSlopeAngle = (steepSlopeAngle: Float): boolean =>
|
||||||
|
SurfaceAngle <= steepSlopeAngle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if angle is within wall range
|
||||||
|
*/
|
||||||
|
const IsWallAngle = (wallAngle: Float): boolean =>
|
||||||
|
SurfaceAngle <= wallAngle;
|
||||||
|
|
||||||
|
if (IsWalkableAngle(AngleThresholds.Walkable)) {
|
||||||
return E_SurfaceType.Walkable;
|
return E_SurfaceType.Walkable;
|
||||||
} else if (SurfaceAngle <= AngleThresholds.SteepSlope) {
|
} else if (IsSteepSlopeAngle(AngleThresholds.SteepSlope)) {
|
||||||
return E_SurfaceType.SteepSlope;
|
return E_SurfaceType.SteepSlope;
|
||||||
} else if (SurfaceAngle <= AngleThresholds.Wall) {
|
} else if (IsWallAngle(AngleThresholds.Wall)) {
|
||||||
return E_SurfaceType.Wall;
|
return E_SurfaceType.Wall;
|
||||||
} else {
|
} else {
|
||||||
return E_SurfaceType.Ceiling;
|
return E_SurfaceType.Ceiling;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Category: "Surface Detection"
|
|
||||||
// Pure: true
|
|
||||||
/**
|
/**
|
||||||
* Check if surface allows normal walking movement
|
* Check if surface allows normal walking movement
|
||||||
* @param SurfaceType - Surface type to check
|
* @param SurfaceType - Surface type to check
|
||||||
* @returns True if surface is walkable
|
* @returns True if surface is walkable
|
||||||
|
* @pure true
|
||||||
|
* @category Surface Detection
|
||||||
*/
|
*/
|
||||||
IsSurfaceWalkable(SurfaceType: E_SurfaceType): boolean {
|
private IsSurfaceWalkable(SurfaceType: E_SurfaceType): boolean {
|
||||||
return SurfaceType === E_SurfaceType.Walkable;
|
return SurfaceType === E_SurfaceType.Walkable;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Category: "Surface Detection"
|
|
||||||
// Pure: true
|
|
||||||
/**
|
/**
|
||||||
* Check if surface causes sliding behavior
|
* Check if surface causes sliding behavior
|
||||||
* @param SurfaceType - Surface type to check
|
* @param SurfaceType - Surface type to check
|
||||||
* @returns True if surface is steep slope
|
* @returns True if surface is steep slope
|
||||||
|
* @pure true
|
||||||
|
* @category Surface Detection
|
||||||
*/
|
*/
|
||||||
IsSurfaceSteep(SurfaceType: E_SurfaceType): boolean {
|
private IsSurfaceSteep(SurfaceType: E_SurfaceType): boolean {
|
||||||
return SurfaceType === E_SurfaceType.SteepSlope;
|
return SurfaceType === E_SurfaceType.SteepSlope;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Category: "Surface Detection"
|
|
||||||
// Pure: true
|
|
||||||
/**
|
/**
|
||||||
* Check if surface blocks movement (collision)
|
* Check if surface blocks movement (collision)
|
||||||
* @param SurfaceType - Surface type to check
|
* @param SurfaceType - Surface type to check
|
||||||
* @returns True if surface is a wall
|
* @returns True if surface is a wall
|
||||||
|
* @pure true
|
||||||
|
* @category Surface Detection
|
||||||
*/
|
*/
|
||||||
IsSurfaceWall(SurfaceType: E_SurfaceType): boolean {
|
private IsSurfaceWall(SurfaceType: E_SurfaceType): boolean {
|
||||||
return SurfaceType === E_SurfaceType.Wall;
|
return SurfaceType === E_SurfaceType.Wall;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Category: "Surface Detection"
|
|
||||||
// Pure: true
|
|
||||||
/**
|
/**
|
||||||
* Check if surface is overhead (ceiling)
|
* Check if surface is overhead (ceiling)
|
||||||
* @param SurfaceType - Surface type to check
|
* @param SurfaceType - Surface type to check
|
||||||
* @returns True if surface is ceiling
|
* @returns True if surface is ceiling
|
||||||
|
* @pure true
|
||||||
|
* @category Surface Detection
|
||||||
*/
|
*/
|
||||||
IsSurfaceCeiling(SurfaceType: E_SurfaceType): boolean {
|
private IsSurfaceCeiling(SurfaceType: E_SurfaceType): boolean {
|
||||||
return SurfaceType === E_SurfaceType.Ceiling;
|
return SurfaceType === E_SurfaceType.Ceiling;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Category: "Surface Detection"
|
|
||||||
// Pure: true
|
|
||||||
/**
|
/**
|
||||||
* Check if no surface detected (airborne state)
|
* Check if no surface detected (airborne state)
|
||||||
* @param SurfaceType - Surface type to check
|
* @param SurfaceType - Surface type to check
|
||||||
* @returns True if no surface contact
|
* @returns True if no surface contact
|
||||||
|
* @pure true
|
||||||
|
* @category Surface Detection
|
||||||
*/
|
*/
|
||||||
IsSurfaceNone(SurfaceType: E_SurfaceType): boolean {
|
private IsSurfaceNone(SurfaceType: E_SurfaceType): boolean {
|
||||||
return SurfaceType === E_SurfaceType.None;
|
return SurfaceType === E_SurfaceType.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Category: "Debug"
|
|
||||||
PrintDebugInfo() {
|
|
||||||
Print('=== Movement System Initialized ===');
|
|
||||||
Print(`Walkable: ≤ ${this.AngleThresholdsDegrees.Walkable}°, Steep: ${this.AngleThresholdsDegrees.Walkable}°-${this.AngleThresholdsDegrees.SteepSlope}°, Wall: ${this.AngleThresholdsDegrees.SteepSlope}°-${this.AngleThresholdsDegrees.Wall}°, Ceiling: >${this.AngleThresholdsDegrees.Wall}°`);
|
|
||||||
Print(`Max Speed: ${this.MovementConstants.MaxSpeed}, Acceleration: ${this.MovementConstants.Acceleration}, Friction: ${this.MovementConstants.Friction}, Gravity: ${this.MovementConstants.Gravity}`);
|
|
||||||
Print('==================================');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Category: "Debug"
|
|
||||||
/**
|
/**
|
||||||
* Run comprehensive surface classification test suite
|
* Initialize movement system with angle conversion
|
||||||
* @returns True if all tests pass
|
|
||||||
*/
|
|
||||||
TestSurfaceClassification() {
|
|
||||||
let AllTestsPassed: boolean = true;
|
|
||||||
|
|
||||||
const TestCases: S_SurfaceTestCase[] = [
|
|
||||||
{ AngleDegrees: 0.0, ExpectedType: E_SurfaceType.Walkable, Description: "Flat surface" },
|
|
||||||
{ AngleDegrees: 25.0, ExpectedType: E_SurfaceType.Walkable, Description: "Gentle slope" },
|
|
||||||
{ AngleDegrees: 49.0, ExpectedType: E_SurfaceType.Walkable, Description: "Max walkable" },
|
|
||||||
{ AngleDegrees: 51.0, ExpectedType: E_SurfaceType.SteepSlope, Description: "Steep slope" },
|
|
||||||
{ AngleDegrees: 70.0, ExpectedType: E_SurfaceType.SteepSlope, Description: "Very steep" },
|
|
||||||
{ AngleDegrees: 84.0, ExpectedType: E_SurfaceType.SteepSlope, Description: "Max steep" },
|
|
||||||
{ AngleDegrees: 90.0, ExpectedType: E_SurfaceType.Wall, Description: "Vertical wall" },
|
|
||||||
{ AngleDegrees: 94.0, ExpectedType: E_SurfaceType.Wall, Description: "Max wall" },
|
|
||||||
{ AngleDegrees: 120.0, ExpectedType: E_SurfaceType.Ceiling, Description: "Overhang" },
|
|
||||||
{ AngleDegrees: 180.0, ExpectedType: E_SurfaceType.Ceiling, Description: "Ceiling" }
|
|
||||||
];
|
|
||||||
|
|
||||||
for (let i = 0; i < TestCases.length; i++) {
|
|
||||||
const testCase = TestCases[i];
|
|
||||||
const normal = this.GenerateNormalFromAngle(testCase.AngleDegrees);
|
|
||||||
const result = this.ClassifySurface(normal, this.AngleThresholdsRads);
|
|
||||||
|
|
||||||
if (result === testCase.ExpectedType) {
|
|
||||||
Print(`✅ Test ${i+1} PASS: ${testCase.Description} (${testCase.AngleDegrees}°) = ${testCase.ExpectedType}`);
|
|
||||||
} else {
|
|
||||||
Print(`❌ Test ${i+1} FAIL: ${testCase.Description} (${testCase.AngleDegrees}°) expected ${testCase.ExpectedType}, got ${result}`);
|
|
||||||
AllTestsPassed = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return AllTestsPassed;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Category: "System"
|
|
||||||
/**
|
|
||||||
* Initialize movement system with angle conversion and testing
|
|
||||||
* Converts degree thresholds to radians for runtime performance
|
* Converts degree thresholds to radians for runtime performance
|
||||||
* Runs automated tests if debug mode enabled
|
* @category System
|
||||||
*/
|
*/
|
||||||
InitializeMovementSystem() {
|
public InitializeMovementSystem(): void {
|
||||||
this.IsInitialized = true;
|
this.IsInitialized = true;
|
||||||
|
|
||||||
this.AngleThresholdsRads = {
|
this.AngleThresholdsRads = {
|
||||||
Walkable: D2R(this.AngleThresholdsDegrees.Walkable),
|
Walkable: MathLibrary.DegreesToRadians(
|
||||||
SteepSlope: D2R(this.AngleThresholdsDegrees.SteepSlope),
|
this.AngleThresholdsDegrees.Walkable
|
||||||
Wall: D2R(this.AngleThresholdsDegrees.Wall),
|
),
|
||||||
|
SteepSlope: MathLibrary.DegreesToRadians(
|
||||||
|
this.AngleThresholdsDegrees.SteepSlope
|
||||||
|
),
|
||||||
|
Wall: MathLibrary.DegreesToRadians(this.AngleThresholdsDegrees.Wall),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
// VARIABLES
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Movement physics constants
|
||||||
|
* Controls speed, acceleration, friction, and gravity values
|
||||||
|
* @category Movement Config
|
||||||
|
* @instanceEditable true
|
||||||
|
*/
|
||||||
|
public readonly MovementConstants: S_MovementConstants = {
|
||||||
|
MaxSpeed: 600.0,
|
||||||
|
Acceleration: 2000.0,
|
||||||
|
Friction: 8.0,
|
||||||
|
Gravity: 980.0,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.ShowDebugInfo) {
|
/**
|
||||||
this.PrintDebugInfo();
|
* Surface classification angle thresholds in degrees
|
||||||
this.TestSurfaceClassification();
|
* Walkable ≤50°, SteepSlope ≤85°, Wall ≤95°, Ceiling >95°
|
||||||
}
|
* @category Movement Config
|
||||||
}
|
* @instanceEditable true
|
||||||
|
*/
|
||||||
|
public readonly AngleThresholdsDegrees: S_AngleThresholds = {
|
||||||
|
Walkable: 50.0,
|
||||||
|
SteepSlope: 85.0,
|
||||||
|
Wall: 95.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runtime cached angle thresholds in radians
|
||||||
|
* Converted from degrees during initialization for performance
|
||||||
|
* @category Internal Cache
|
||||||
|
*/
|
||||||
|
private AngleThresholdsRads: S_AngleThresholds = {
|
||||||
|
Walkable: 0.0,
|
||||||
|
SteepSlope: 0.0,
|
||||||
|
Wall: 0.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flag indicating if movement system has been initialized
|
||||||
|
* Ensures angle thresholds are converted before use
|
||||||
|
* @category Debug
|
||||||
|
*/
|
||||||
|
public IsInitialized = false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
BIN
Content/Movement/Components/AC_Movement.uasset (Stored with Git LFS)
BIN
Content/Movement/Components/AC_Movement.uasset (Stored with Git LFS)
Binary file not shown.
|
|
@ -1,9 +1,9 @@
|
||||||
// Content/Movement/Enums/E_SurfaceType.ts
|
// Movement/Enums/E_SurfaceType.ts
|
||||||
|
|
||||||
export enum E_SurfaceType {
|
export enum E_SurfaceType {
|
||||||
None = "None",
|
None = 'None',
|
||||||
Walkable = "Walkable",
|
Walkable = 'Walkable',
|
||||||
SteepSlope = "SteepSlope",
|
SteepSlope = 'SteepSlope',
|
||||||
Wall = "Wall",
|
Wall = 'Wall',
|
||||||
Ceiling = "Ceiling"
|
Ceiling = 'Ceiling',
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
// Content/Movement/Structs/S_AngleThresholds.ts
|
// Movement/Structs/S_AngleThresholds.ts
|
||||||
|
|
||||||
import type {Float} from "../../types.js";
|
import type { Float } from '#root/UE/Float.ts';
|
||||||
|
|
||||||
export interface S_AngleThresholds {
|
export interface S_AngleThresholds {
|
||||||
Walkable: Float,
|
Walkable: Float;
|
||||||
SteepSlope: Float,
|
SteepSlope: Float;
|
||||||
Wall: Float,
|
Wall: Float;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// Content/Movement/Structs/S_MovementConstants.ts
|
// Movement/Structs/S_MovementConstants.ts
|
||||||
|
|
||||||
import type {Float} from "../../types.js";
|
import type { Float } from '#root/UE/Float.ts';
|
||||||
|
|
||||||
export interface S_MovementConstants {
|
export interface S_MovementConstants {
|
||||||
MaxSpeed: Float;
|
MaxSpeed: Float;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// Content/Movement/Structs/S_SurfaceTestCase.ts
|
// Movement/Structs/S_SurfaceTestCase.ts
|
||||||
|
|
||||||
import type {Float} from "../../types.js";
|
import type { E_SurfaceType } from '#root/Movement/Enums/E_SurfaceType.ts';
|
||||||
import type {E_SurfaceType} from "../Enums/E_SurfaceType.js";
|
import type { Float } from '#root/UE/Float.ts';
|
||||||
|
|
||||||
export interface S_SurfaceTestCase {
|
export interface S_SurfaceTestCase {
|
||||||
AngleDegrees: Float;
|
AngleDegrees: Float;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,146 @@
|
||||||
|
// Movement/Tests/FT_SurfaceClassification.ts
|
||||||
|
|
||||||
|
import { BFL_Vectors } from '#root/Math/Libraries/BFL_Vectors.ts';
|
||||||
|
import { AC_Movement } from '#root/Movement/Components/AC_Movement.ts';
|
||||||
|
import { E_SurfaceType } from '#root/Movement/Enums/E_SurfaceType.ts';
|
||||||
|
import { S_AngleThresholds } from '#root/Movement/Structs/S_AngleThresholds.ts';
|
||||||
|
import type { S_SurfaceTestCase } from '#root/Movement/Structs/S_SurfaceTestCase.ts';
|
||||||
|
import { EFunctionalTestResult } from '#root/UE/EFunctionalTestResult.ts';
|
||||||
|
import { FunctionalTest } from '#root/UE/FunctionalTest.ts';
|
||||||
|
import { MathLibrary } from '#root/UE/MathLibrary.ts';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Functional Test: Surface Classification System
|
||||||
|
* Tests angle-based surface type detection across all boundary conditions
|
||||||
|
* Validates Walkable/SteepSlope/Wall/Ceiling classification accuracy
|
||||||
|
*/
|
||||||
|
export class FT_SurfaceClassification extends FunctionalTest {
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
// GRAPHS
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
// ────────────────────────────────────────────────────────────────────────────────────────
|
||||||
|
// EventGraph
|
||||||
|
// ────────────────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test preparation - converts angle thresholds to radians
|
||||||
|
* Called before main test execution to prepare test data
|
||||||
|
*/
|
||||||
|
EventPrepareTest(): void {
|
||||||
|
this.AngleThresholdsRads = {
|
||||||
|
Walkable: MathLibrary.DegreesToRadians(
|
||||||
|
this.MovementComponent.AngleThresholdsDegrees.Walkable
|
||||||
|
),
|
||||||
|
SteepSlope: MathLibrary.DegreesToRadians(
|
||||||
|
this.MovementComponent.AngleThresholdsDegrees.SteepSlope
|
||||||
|
),
|
||||||
|
Wall: MathLibrary.DegreesToRadians(
|
||||||
|
this.MovementComponent.AngleThresholdsDegrees.Wall
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test execution - validates surface classification for all test cases
|
||||||
|
* Tests boundary conditions and edge cases for each surface type
|
||||||
|
*/
|
||||||
|
EventStartTest(): void {
|
||||||
|
this.TestCases.forEach(
|
||||||
|
({ AngleDegrees, ExpectedType, Description }, arrayIndex) => {
|
||||||
|
const surfaceType = this.MovementComponent.ClassifySurface(
|
||||||
|
BFL_Vectors.GetNormalFromAngle(AngleDegrees),
|
||||||
|
this.AngleThresholdsRads
|
||||||
|
);
|
||||||
|
|
||||||
|
if (surfaceType === ExpectedType) {
|
||||||
|
this.FinishTest(EFunctionalTestResult.Succeeded);
|
||||||
|
} else {
|
||||||
|
this.FinishTest(
|
||||||
|
EFunctionalTestResult.Failed,
|
||||||
|
`Movement Component test ${arrayIndex + 1} FAIL: ${Description} (${AngleDegrees}°) expected ${ExpectedType}, got ${surfaceType}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
// VARIABLES
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Movement system component - provides surface classification functionality
|
||||||
|
* @category Components
|
||||||
|
*/
|
||||||
|
private MovementComponent = new AC_Movement();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Comprehensive test cases covering all surface type boundaries
|
||||||
|
* Tests edge cases and typical angles for each classification
|
||||||
|
* @category Test Data
|
||||||
|
*/
|
||||||
|
private TestCases: S_SurfaceTestCase[] = [
|
||||||
|
{
|
||||||
|
AngleDegrees: 0.0,
|
||||||
|
ExpectedType: E_SurfaceType.Walkable,
|
||||||
|
Description: 'Flat surface',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AngleDegrees: 25.0,
|
||||||
|
ExpectedType: E_SurfaceType.Walkable,
|
||||||
|
Description: 'Gentle slope',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AngleDegrees: 49.0,
|
||||||
|
ExpectedType: E_SurfaceType.Walkable,
|
||||||
|
Description: 'Max walkable',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AngleDegrees: 51.0,
|
||||||
|
ExpectedType: E_SurfaceType.SteepSlope,
|
||||||
|
Description: 'Steep slope',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AngleDegrees: 70.0,
|
||||||
|
ExpectedType: E_SurfaceType.SteepSlope,
|
||||||
|
Description: 'Very steep',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AngleDegrees: 84.0,
|
||||||
|
ExpectedType: E_SurfaceType.SteepSlope,
|
||||||
|
Description: 'Max steep',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AngleDegrees: 90.0,
|
||||||
|
ExpectedType: E_SurfaceType.Wall,
|
||||||
|
Description: 'Vertical wall',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AngleDegrees: 94.0,
|
||||||
|
ExpectedType: E_SurfaceType.Wall,
|
||||||
|
Description: 'Max wall',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AngleDegrees: 120.0,
|
||||||
|
ExpectedType: E_SurfaceType.Ceiling,
|
||||||
|
Description: 'Overhang',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AngleDegrees: 180.0,
|
||||||
|
ExpectedType: E_SurfaceType.Ceiling,
|
||||||
|
Description: 'Ceiling',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runtime cached angle thresholds in radians
|
||||||
|
* Converted during preparation for accurate classification testing
|
||||||
|
* @category Test State
|
||||||
|
*/
|
||||||
|
private AngleThresholdsRads: S_AngleThresholds = {
|
||||||
|
Walkable: 0.0,
|
||||||
|
SteepSlope: 0.0,
|
||||||
|
Wall: 0.0,
|
||||||
|
};
|
||||||
|
}
|
||||||
Binary file not shown.
|
|
@ -0,0 +1,271 @@
|
||||||
|
// Toasts/Components/AC_ToastSystem.ts
|
||||||
|
|
||||||
|
import type { S_ToastMessage } from '#root/Toasts/Structs/S_ToastMessage.ts';
|
||||||
|
import type { S_ToastSettings } from '#root/Toasts/Structs/S_ToastSettings.ts';
|
||||||
|
import type { WBP_Toast } from '#root/Toasts/UI/WBP_Toast.ts';
|
||||||
|
import { WBP_ToastContainer } from '#root/Toasts/UI/WBP_ToastContainer.ts';
|
||||||
|
import { ActorComponent } from '#root/UE/ActorComponent.ts';
|
||||||
|
import { CreateWidget } from '#root/UE/CteateWidget.ts';
|
||||||
|
import type { Float } from '#root/UE/Float.ts';
|
||||||
|
import type { Integer } from '#root/UE/Integer.ts';
|
||||||
|
import { SystemLibrary } from '#root/UE/SystemLibrary.ts';
|
||||||
|
import type { Text } from '#root/UE/Text.ts';
|
||||||
|
import { UEArray } from '#root/UE/UEArray.ts';
|
||||||
|
import { E_MessageType } from '#root/UI/Enums/E_MessageType.ts';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toast Notification System Component
|
||||||
|
* Manages temporary status messages with automatic positioning and lifecycle
|
||||||
|
* Provides visual feedback for debug operations and system events
|
||||||
|
*/
|
||||||
|
export class AC_ToastSystem extends ActorComponent {
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
// FUNCTIONS
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if toast system should process updates this frame
|
||||||
|
* @returns True if system is initialized and enabled
|
||||||
|
* @pure
|
||||||
|
* @category Toast Management
|
||||||
|
*/
|
||||||
|
private ShouldProcessToasts(): boolean {
|
||||||
|
/**
|
||||||
|
* Validate system state and settings
|
||||||
|
*/
|
||||||
|
const function1 = (isEnabled: boolean): boolean =>
|
||||||
|
this.IsInitialized && isEnabled;
|
||||||
|
|
||||||
|
return function1(this.ToastSettings.IsEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove expired toasts from the system
|
||||||
|
* @category Toast Lifecycle
|
||||||
|
*/
|
||||||
|
private RemoveExpiredToasts(): void {
|
||||||
|
let i = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if there are more toasts to process
|
||||||
|
*/
|
||||||
|
const HasMoreToastsToCheck = (activeToastsLength: Integer): boolean =>
|
||||||
|
i < activeToastsLength;
|
||||||
|
|
||||||
|
while (HasMoreToastsToCheck(this.ActiveToasts.length)) {
|
||||||
|
/**
|
||||||
|
* Check if toast has exceeded its display duration
|
||||||
|
*/
|
||||||
|
const IsToastExpired = (
|
||||||
|
currentTime: Float,
|
||||||
|
createdTime: Float,
|
||||||
|
duration: Float
|
||||||
|
): boolean => currentTime - createdTime > duration;
|
||||||
|
|
||||||
|
const el = this.ActiveToasts.Get(i);
|
||||||
|
|
||||||
|
if (
|
||||||
|
IsToastExpired(
|
||||||
|
SystemLibrary.GetGameTimeInSeconds(),
|
||||||
|
el.CreatedTime,
|
||||||
|
el.Duration
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
const widget = this.ToastWidgets.Get(i);
|
||||||
|
|
||||||
|
if (SystemLibrary.IsValid(widget)) {
|
||||||
|
if (SystemLibrary.IsValid(this.ToastContainer)) {
|
||||||
|
this.ToastContainer.RemoveToast(widget);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.ActiveToasts.RemoveIndex(i);
|
||||||
|
this.ToastWidgets.RemoveIndex(i);
|
||||||
|
} else {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enforce maximum visible toasts limit
|
||||||
|
* Removes oldest toasts when limit is exceeded
|
||||||
|
* @category Toast Lifecycle
|
||||||
|
*/
|
||||||
|
private EnforceToastLimit(): void {
|
||||||
|
/**
|
||||||
|
* Check if current toast count exceeds maximum allowed
|
||||||
|
*/
|
||||||
|
const ExceedsToastLimit = (
|
||||||
|
activeToastsLength: Integer,
|
||||||
|
maxVisibleToasts: Integer
|
||||||
|
): boolean => activeToastsLength >= maxVisibleToasts;
|
||||||
|
|
||||||
|
while (
|
||||||
|
ExceedsToastLimit(
|
||||||
|
this.ActiveToasts.length,
|
||||||
|
this.ToastSettings.MaxVisibleToasts
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
const widget = this.ToastWidgets.Get(0);
|
||||||
|
|
||||||
|
if (SystemLibrary.IsValid(widget)) {
|
||||||
|
if (SystemLibrary.IsValid(this.ToastContainer)) {
|
||||||
|
this.ToastContainer.RemoveToast(widget);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.ActiveToasts.RemoveIndex(0);
|
||||||
|
this.ToastWidgets.RemoveIndex(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create and display a new toast message
|
||||||
|
* @param Message - Text to display in the toast
|
||||||
|
* @param Type - Type of toast (affects color and styling)
|
||||||
|
* @param Duration - How long to display toast (optional, uses default)
|
||||||
|
* @returns Toast ID for tracking
|
||||||
|
* @example
|
||||||
|
* // Create success toast
|
||||||
|
* ShowToast("Test passed!", E_MessageType.Success, 2.0)
|
||||||
|
* // Create error toast with default duration
|
||||||
|
* ShowToast("Test failed!", E_MessageType.Error)
|
||||||
|
* @category Toast Creation
|
||||||
|
*/
|
||||||
|
public ShowToast(
|
||||||
|
Message: Text = '',
|
||||||
|
Type: E_MessageType = E_MessageType.Info,
|
||||||
|
Duration: Float = 0
|
||||||
|
): Integer {
|
||||||
|
if (this.ShouldProcessToasts()) {
|
||||||
|
const toastDuration =
|
||||||
|
Duration !== 0 ? Duration : this.ToastSettings.DefaultDuration;
|
||||||
|
|
||||||
|
const currentTime = SystemLibrary.GetGameTimeInSeconds();
|
||||||
|
|
||||||
|
const newToast: S_ToastMessage = {
|
||||||
|
ID: this.NextToastID++,
|
||||||
|
Message,
|
||||||
|
Type,
|
||||||
|
Duration: toastDuration,
|
||||||
|
CreatedTime: currentTime,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.EnforceToastLimit();
|
||||||
|
|
||||||
|
if (SystemLibrary.IsValid(this.ToastContainer)) {
|
||||||
|
const toastWidget = this.ToastContainer.AddToast(Message, Type);
|
||||||
|
|
||||||
|
if (SystemLibrary.IsValid(toastWidget)) {
|
||||||
|
this.ActiveToasts.Add(newToast);
|
||||||
|
this.ToastWidgets.Add(toastWidget);
|
||||||
|
this.LogToConsole(Type, Message);
|
||||||
|
return newToast.ID;
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.LogToConsole(Type, Message);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main update loop for toast system
|
||||||
|
* Removes expired toasts automatically
|
||||||
|
* @category System Main Loop
|
||||||
|
*/
|
||||||
|
public UpdateToastSystem(): void {
|
||||||
|
if (this.ShouldProcessToasts()) {
|
||||||
|
this.RemoveExpiredToasts();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get toast widgets for testing purposes
|
||||||
|
* @returns Array of active toast widgets
|
||||||
|
* @category Testing
|
||||||
|
*/
|
||||||
|
public GetTestData(): UEArray<WBP_Toast> {
|
||||||
|
return this.ToastWidgets;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize toast system with container widget
|
||||||
|
* @example
|
||||||
|
* // Initialize toast system in main character
|
||||||
|
* this.ToastSystemComponent.InitializeToastSystem();
|
||||||
|
* @category System Setup
|
||||||
|
*/
|
||||||
|
public InitializeToastSystem(): void {
|
||||||
|
this.ToastContainer = CreateWidget(WBP_ToastContainer);
|
||||||
|
this.ToastContainer.InitializeContainer();
|
||||||
|
this.IsInitialized = true;
|
||||||
|
this.NextToastID = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log toast message to console if enabled
|
||||||
|
* @param Type - Message type for formatting
|
||||||
|
* @param Message - Message text to log
|
||||||
|
* @category System Utilities
|
||||||
|
*/
|
||||||
|
public LogToConsole(Type: E_MessageType, Message: Text): void {
|
||||||
|
if (this.ToastSettings.AlsoLogToConsole) {
|
||||||
|
SystemLibrary.PrintString(`[${Type}] ${Message}`, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
// VARIABLES
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toast system configuration settings
|
||||||
|
* Controls capacity, duration, and logging behavior
|
||||||
|
* @category System Config
|
||||||
|
* @instanceEditable true
|
||||||
|
*/
|
||||||
|
public ToastSettings: S_ToastSettings = {
|
||||||
|
MaxVisibleToasts: 5,
|
||||||
|
DefaultDuration: 3.0,
|
||||||
|
AlsoLogToConsole: true,
|
||||||
|
IsEnabled: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* System initialization state flag
|
||||||
|
* Set to true after successful InitializeToastSystem call
|
||||||
|
* @category System State
|
||||||
|
*/
|
||||||
|
private IsInitialized: boolean = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Next unique ID for new toasts
|
||||||
|
* Incremented for each new toast
|
||||||
|
* @category System State
|
||||||
|
*/
|
||||||
|
private NextToastID: Integer = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toast container widget that handles positioning and layout
|
||||||
|
* @category Container Management
|
||||||
|
*/
|
||||||
|
private ToastContainer: WBP_ToastContainer | null = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array of currently active toast messages
|
||||||
|
* Contains all visible toasts with metadata
|
||||||
|
* @category Toast Management
|
||||||
|
*/
|
||||||
|
private ActiveToasts: UEArray<S_ToastMessage> = new UEArray([]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array of toast widget instances
|
||||||
|
* Corresponds to ActiveToasts array for UI display
|
||||||
|
* @category Widget Management
|
||||||
|
*/
|
||||||
|
private ToastWidgets: UEArray<WBP_Toast> = new UEArray([]);
|
||||||
|
}
|
||||||
Binary file not shown.
|
|
@ -1,192 +0,0 @@
|
||||||
// Content/Toasts/Components/AC_ToastSystem.ts
|
|
||||||
|
|
||||||
// TODO: Написать тесты
|
|
||||||
|
|
||||||
import {AddToArray, CreateWidget, GetGameTimeInSeconds, IsValid, Print, RemoveIndexFromArray} from "../../functions.js";
|
|
||||||
import {WBP_ToastContainer} from "../UI/WBP_ToastContainer.js";
|
|
||||||
import type {Float, Integer, Text} from "../../types.js";
|
|
||||||
import {E_MessageType} from "../../UI/Enums/E_MessageType.js";
|
|
||||||
import type {S_ToastMessage} from "../Structs/S_ToastMessage.js";
|
|
||||||
import type {S_ToastSettings} from "../Structs/S_ToastSettings.js";
|
|
||||||
import type {WBP_Toast} from "../UI/WBP_Toast.js";
|
|
||||||
|
|
||||||
export class AC_ToastSystem {
|
|
||||||
// Category: "System Configuration"
|
|
||||||
// Instance Editable: true
|
|
||||||
/**
|
|
||||||
* Toast system configuration settings
|
|
||||||
* Simplified - no positioning settings needed
|
|
||||||
* Instance editable for designer customization
|
|
||||||
*/
|
|
||||||
public ToastSettings: S_ToastSettings = {
|
|
||||||
MaxVisibleToasts: 5,
|
|
||||||
DefaultDuration: 3.0,
|
|
||||||
AlsoLogToConsole: true,
|
|
||||||
IsEnabled: true
|
|
||||||
};
|
|
||||||
|
|
||||||
// Category: "System State"
|
|
||||||
/**
|
|
||||||
* System initialization state flag
|
|
||||||
* Set to true after successful InitializeToastSystem call
|
|
||||||
*/
|
|
||||||
private IsInitialized: boolean = false;
|
|
||||||
|
|
||||||
// Category: "System State"
|
|
||||||
/**
|
|
||||||
* Next unique ID for new toasts
|
|
||||||
* Incremented for each new toast
|
|
||||||
*/
|
|
||||||
private NextToastID: Integer = 1;
|
|
||||||
|
|
||||||
// Category: "Container Management"
|
|
||||||
/**
|
|
||||||
* Toast container widget that handles all positioning
|
|
||||||
* Replaces manual position management
|
|
||||||
*/
|
|
||||||
private ToastContainer: WBP_ToastContainer | null = null;
|
|
||||||
|
|
||||||
// Category: "Toast Management"
|
|
||||||
/**
|
|
||||||
* Array of currently active toast messages
|
|
||||||
* Contains all visible toasts
|
|
||||||
*/
|
|
||||||
private ActiveToasts: S_ToastMessage[] = [];
|
|
||||||
|
|
||||||
// Category: "Widget Management"
|
|
||||||
/**
|
|
||||||
* Array of toast widget instances
|
|
||||||
* Corresponds to ActiveToasts array
|
|
||||||
*/
|
|
||||||
private ToastWidgets: WBP_Toast[] = [];
|
|
||||||
|
|
||||||
// Category: "Toast Management"
|
|
||||||
// Pure: true
|
|
||||||
/**
|
|
||||||
* Check if toast system should process updates this frame
|
|
||||||
* @returns True if system is initialized and enabled
|
|
||||||
* @pure Function has no side effects
|
|
||||||
*/
|
|
||||||
private ShouldProcessToasts(): boolean {
|
|
||||||
return this.IsInitialized && this.ToastSettings.IsEnabled && IsValid(this.ToastContainer);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Category: "Toast Lifecycle"
|
|
||||||
/**
|
|
||||||
* Remove expired toasts from the system
|
|
||||||
* @private Internal cleanup method
|
|
||||||
*/
|
|
||||||
private RemoveExpiredToasts(): void {
|
|
||||||
let i = 0;
|
|
||||||
|
|
||||||
while (i < this.ActiveToasts.length) {
|
|
||||||
if ((GetGameTimeInSeconds() - this.ActiveToasts[i].CreatedTime) > this.ActiveToasts[i].Duration) {
|
|
||||||
if (IsValid(this.ToastWidgets[i]) && IsValid(this.ToastContainer)) {
|
|
||||||
this.ToastContainer.RemoveToast(this.ToastWidgets[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
RemoveIndexFromArray(this.ActiveToasts, i);
|
|
||||||
RemoveIndexFromArray(this.ToastWidgets, i);
|
|
||||||
} else {
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Category: "Toast Lifecycle"
|
|
||||||
/**
|
|
||||||
* Enforce maximum visible toasts limit
|
|
||||||
* Uses container's capacity management
|
|
||||||
* @private Internal management method
|
|
||||||
*/
|
|
||||||
private EnforceToastLimit(): void {
|
|
||||||
while(this.ActiveToasts.length >= this.ToastSettings.MaxVisibleToasts) {
|
|
||||||
if (IsValid(this.ToastWidgets[0])) {
|
|
||||||
this.ToastContainer.RemoveToast(this.ToastWidgets[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
RemoveIndexFromArray(this.ActiveToasts, 0);
|
|
||||||
RemoveIndexFromArray(this.ToastWidgets, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Category: "Toast Creation"
|
|
||||||
/**
|
|
||||||
* Create and display a new toast message
|
|
||||||
* Uses container for automatic positioning
|
|
||||||
* @param Message - Text to display in the toast
|
|
||||||
* @param Type - Type of toast (affects color and styling)
|
|
||||||
* @param Duration - How long to display toast (optional, uses default)
|
|
||||||
* @returns Toast ID for tracking
|
|
||||||
* @public Main interface for creating toasts
|
|
||||||
* @example
|
|
||||||
* // Create success toast
|
|
||||||
* ShowToast("Test passed!", E_ToastType.Success, 2.0)
|
|
||||||
* // Create error toast with default duration
|
|
||||||
* ShowToast("Test failed!", E_ToastType.Error)
|
|
||||||
*/
|
|
||||||
public ShowToast(Message: Text, Type: E_MessageType, Duration?: Float): Integer {
|
|
||||||
if (this.ShouldProcessToasts()) {
|
|
||||||
const toastDuration = Duration === 0 ? this.ToastSettings.DefaultDuration : Duration;
|
|
||||||
const currentTime = GetGameTimeInSeconds();
|
|
||||||
const newToast: S_ToastMessage = {
|
|
||||||
ID: this.NextToastID++,
|
|
||||||
Message: Message,
|
|
||||||
Type: Type,
|
|
||||||
Duration: toastDuration,
|
|
||||||
CreatedTime: currentTime,
|
|
||||||
}
|
|
||||||
|
|
||||||
this.EnforceToastLimit();
|
|
||||||
|
|
||||||
const toastWidget = this.ToastContainer.AddToast(Message, Type);
|
|
||||||
|
|
||||||
if (IsValid(toastWidget)) {
|
|
||||||
AddToArray(this.ActiveToasts, newToast);
|
|
||||||
AddToArray(this.ToastWidgets, toastWidget);
|
|
||||||
|
|
||||||
if (this.ToastSettings.AlsoLogToConsole) {
|
|
||||||
Print(`[${Type}] ${Message}`, false, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return newToast.ID;
|
|
||||||
} else {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.ToastSettings.AlsoLogToConsole) {
|
|
||||||
Print(`[${Type}] ${Message}`, false, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Category: "System Main Loop"
|
|
||||||
/**
|
|
||||||
* Simplified update loop for toast system
|
|
||||||
* Container handles positioning automatically
|
|
||||||
* @public Called by BP_MainCharacter or game framework
|
|
||||||
*/
|
|
||||||
public UpdateToastSystem(): void {
|
|
||||||
if (this.ShouldProcessToasts()) {
|
|
||||||
this.RemoveExpiredToasts();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Category: "System Setup"
|
|
||||||
/**
|
|
||||||
* Initialize toast system with container
|
|
||||||
* @public Called by BP_MainCharacter during initialization
|
|
||||||
* @example
|
|
||||||
* // Initialize toast system in main character
|
|
||||||
* this.ToastSystemComponent.InitializeToastSystem();
|
|
||||||
*/
|
|
||||||
public InitializeToastSystem(): void {
|
|
||||||
this.ToastContainer = CreateWidget(new WBP_ToastContainer);
|
|
||||||
this.ToastContainer.InitializeContainer();
|
|
||||||
this.IsInitialized = true;
|
|
||||||
this.NextToastID = 1;
|
|
||||||
this.ShowToast(("Toast System Initialized" as Text), E_MessageType.Info);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BIN
Content/Toasts/Compontents/AC_ToastSystem.uasset (Stored with Git LFS)
BIN
Content/Toasts/Compontents/AC_ToastSystem.uasset (Stored with Git LFS)
Binary file not shown.
|
|
@ -1,7 +1,9 @@
|
||||||
// Content/Toasts/Structs/S_ToastMessage.ts
|
// Toasts/Structs/S_ToastMessage.ts
|
||||||
|
|
||||||
import type {Float, Integer, Text} from "../../types.js";
|
import type { Float } from '#root/UE/Float.ts';
|
||||||
import type {E_MessageType} from "../../UI/Enums/E_MessageType.js";
|
import type { Integer } from '#root/UE/Integer.ts';
|
||||||
|
import type { Text } from '#root/UE/Text.ts';
|
||||||
|
import type { E_MessageType } from '#root/UI/Enums/E_MessageType.ts';
|
||||||
|
|
||||||
export interface S_ToastMessage {
|
export interface S_ToastMessage {
|
||||||
ID: Integer;
|
ID: Integer;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// Content/Toasts/Structs/S_ToastSettings.ts
|
// Toasts/Structs/S_ToastSettings.ts
|
||||||
|
|
||||||
import type {Float, Integer} from "../../types.js";
|
import type { Float } from '#root/UE/Float.ts';
|
||||||
|
import type { Integer } from '#root/UE/Integer.ts';
|
||||||
|
|
||||||
export interface S_ToastSettings {
|
export interface S_ToastSettings {
|
||||||
MaxVisibleToasts: Integer;
|
MaxVisibleToasts: Integer;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
// Toasts/Tests/FT_ToastLimit.ts
|
||||||
|
|
||||||
|
import { AC_ToastSystem } from '#root/Toasts/Components/AC_ToastSystem.ts';
|
||||||
|
import { EFunctionalTestResult } from '#root/UE/EFunctionalTestResult.ts';
|
||||||
|
import { FunctionalTest } from '#root/UE/FunctionalTest.ts';
|
||||||
|
import { E_MessageType } from '#root/UI/Enums/E_MessageType.ts';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Functional Test: Toast System Capacity Management
|
||||||
|
* Validates that the toast system enforces MaxVisibleToasts limit correctly
|
||||||
|
* Tests that oldest toasts are removed when limit is exceeded
|
||||||
|
*/
|
||||||
|
export class FT_ToastLimit extends FunctionalTest {
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
// GRAPHS
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
// ────────────────────────────────────────────────────────────────────────────────────────
|
||||||
|
// EventGraph
|
||||||
|
// ────────────────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test execution - validates toast limit enforcement
|
||||||
|
* Creates more toasts than allowed and verifies limit is enforced
|
||||||
|
*/
|
||||||
|
EventStartTest(): void {
|
||||||
|
this.ToastComponent.InitializeToastSystem();
|
||||||
|
|
||||||
|
// Create MaxVisibleToasts + 3 toasts to test overflow handling
|
||||||
|
for (
|
||||||
|
let i = 1;
|
||||||
|
i <= this.ToastComponent.ToastSettings.MaxVisibleToasts + 3;
|
||||||
|
i++
|
||||||
|
) {
|
||||||
|
this.ToastComponent.ShowToast(
|
||||||
|
`Limit test toast ${i}`,
|
||||||
|
E_MessageType.Info,
|
||||||
|
10
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that only MaxVisibleToasts are actually visible
|
||||||
|
if (
|
||||||
|
this.ToastComponent.GetTestData().length ===
|
||||||
|
this.ToastComponent.ToastSettings.MaxVisibleToasts
|
||||||
|
) {
|
||||||
|
this.FinishTest(EFunctionalTestResult.Succeeded);
|
||||||
|
} else {
|
||||||
|
this.FinishTest(
|
||||||
|
EFunctionalTestResult.Failed,
|
||||||
|
`Expected ${this.ToastComponent.ToastSettings.MaxVisibleToasts} to display, got ${this.ToastComponent.GetTestData().length}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
// VARIABLES
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toast notification system - component under test
|
||||||
|
* @category Components
|
||||||
|
*/
|
||||||
|
private ToastComponent = new AC_ToastSystem();
|
||||||
|
}
|
||||||
Binary file not shown.
|
|
@ -0,0 +1,65 @@
|
||||||
|
// Toasts/Tests/FT_ToastsDurationHandling.ts
|
||||||
|
|
||||||
|
import { AC_ToastSystem } from '#root/Toasts/Components/AC_ToastSystem.ts';
|
||||||
|
import { EFunctionalTestResult } from '#root/UE/EFunctionalTestResult.ts';
|
||||||
|
import { FunctionalTest } from '#root/UE/FunctionalTest.ts';
|
||||||
|
import type { Integer } from '#root/UE/Integer.ts';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Functional Test: Toast Duration and Lifecycle Management
|
||||||
|
* Validates basic toast creation and ID assignment functionality
|
||||||
|
* Tests that toasts return valid IDs when created successfully
|
||||||
|
*/
|
||||||
|
export class FT_ToastsDurationHandling extends FunctionalTest {
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
// GRAPHS
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
// ────────────────────────────────────────────────────────────────────────────────────────
|
||||||
|
// EventGraph
|
||||||
|
// ────────────────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test execution - validates basic toast creation functionality
|
||||||
|
* Creates two toasts and verifies they return valid IDs
|
||||||
|
*/
|
||||||
|
EventStartTest(): void {
|
||||||
|
this.ToastComponent.InitializeToastSystem();
|
||||||
|
this.toast1 = this.ToastComponent.ShowToast();
|
||||||
|
this.toast2 = this.ToastComponent.ShowToast();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if both toasts were created successfully by verifying positive IDs
|
||||||
|
*/
|
||||||
|
const AreToastsCreatedSuccessfully = (): boolean =>
|
||||||
|
this.toast1 > 0 && this.toast2 > 0;
|
||||||
|
|
||||||
|
if (AreToastsCreatedSuccessfully()) {
|
||||||
|
this.FinishTest(EFunctionalTestResult.Succeeded);
|
||||||
|
} else {
|
||||||
|
this.FinishTest(EFunctionalTestResult.Failed, `Failed to create toasts`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
// VARIABLES
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toast notification system - component under test
|
||||||
|
* @category Components
|
||||||
|
*/
|
||||||
|
private ToastComponent = new AC_ToastSystem();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ID of first test toast
|
||||||
|
* @category Test State
|
||||||
|
*/
|
||||||
|
private toast1: Integer = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ID of second test toast
|
||||||
|
* @category Test State
|
||||||
|
*/
|
||||||
|
private toast2: Integer = 0;
|
||||||
|
}
|
||||||
Binary file not shown.
|
|
@ -0,0 +1,105 @@
|
||||||
|
// Toasts/Tests/FT_ToastsEdgeCases.ts
|
||||||
|
|
||||||
|
import { AC_ToastSystem } from '#root/Toasts/Components/AC_ToastSystem.ts';
|
||||||
|
import { EFunctionalTestResult } from '#root/UE/EFunctionalTestResult.ts';
|
||||||
|
import { FunctionalTest } from '#root/UE/FunctionalTest.ts';
|
||||||
|
import type { Integer } from '#root/UE/Integer.ts';
|
||||||
|
import { StringLibrary } from '#root/UE/StringLibrary.ts';
|
||||||
|
import type { Text } from '#root/UE/Text.ts';
|
||||||
|
import { TextLibrary } from '#root/UE/TextLibrary.ts';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Functional Test: Toast System Edge Cases
|
||||||
|
* Tests toast system robustness with unusual input conditions
|
||||||
|
* Validates handling of empty, very long, and multiline messages
|
||||||
|
*/
|
||||||
|
export class FT_ToastsEdgeCases extends FunctionalTest {
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
// GRAPHS
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
// ────────────────────────────────────────────────────────────────────────────────────────
|
||||||
|
// EventGraph
|
||||||
|
// ────────────────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test preparation - generates very long text for stress testing
|
||||||
|
* Creates 500-character string to test text handling limits
|
||||||
|
*/
|
||||||
|
EventPrepareTest(): void {
|
||||||
|
for (let i = 0; i < 500; i++) {
|
||||||
|
this.longText = TextLibrary.StringToText(
|
||||||
|
StringLibrary.Append(TextLibrary.TextToString(this.longText), 'A')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test execution - validates edge case handling
|
||||||
|
* Tests empty text, very long text, and multiline text scenarios
|
||||||
|
*/
|
||||||
|
EventStartTest(): void {
|
||||||
|
this.ToastComponent.InitializeToastSystem();
|
||||||
|
|
||||||
|
// Test empty message handling
|
||||||
|
this.emptyToast = this.ToastComponent.ShowToast();
|
||||||
|
|
||||||
|
// Test very long message handling (500 characters)
|
||||||
|
this.longToast = this.ToastComponent.ShowToast(this.longText);
|
||||||
|
|
||||||
|
// Test multiline message handling
|
||||||
|
this.specialToast = this.ToastComponent.ShowToast(`Test
|
||||||
|
Multiline
|
||||||
|
Message`);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if all edge case toasts were created successfully
|
||||||
|
*/
|
||||||
|
const AreToastsCreatedSuccessfully = (): boolean =>
|
||||||
|
this.emptyToast > 0 && this.longToast > 0 && this.specialToast > 0;
|
||||||
|
|
||||||
|
if (AreToastsCreatedSuccessfully()) {
|
||||||
|
this.FinishTest(EFunctionalTestResult.Succeeded);
|
||||||
|
} else {
|
||||||
|
this.FinishTest(
|
||||||
|
EFunctionalTestResult.Failed,
|
||||||
|
`Edge case failures: empty=${this.emptyToast}, long=${this.longToast}, special=${this.specialToast}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
// VARIABLES
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toast notification system - component under test
|
||||||
|
* @category Components
|
||||||
|
*/
|
||||||
|
private ToastComponent = new AC_ToastSystem();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ID of toast created with empty message
|
||||||
|
* @category Test State
|
||||||
|
*/
|
||||||
|
private emptyToast: Integer = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ID of toast created with very long message (500 chars)
|
||||||
|
* @category Test State
|
||||||
|
*/
|
||||||
|
private longToast: Integer = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ID of toast created with multiline message
|
||||||
|
* @category Test State
|
||||||
|
*/
|
||||||
|
private specialToast: Integer = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generated long text string for stress testing
|
||||||
|
* Built during test preparation phase
|
||||||
|
* @category Test Data
|
||||||
|
*/
|
||||||
|
private longText: Text = '';
|
||||||
|
}
|
||||||
Binary file not shown.
|
|
@ -0,0 +1,54 @@
|
||||||
|
// Toasts/Tests/FT_ToastsSystemInitialization.ts
|
||||||
|
|
||||||
|
import { AC_ToastSystem } from '#root/Toasts/Components/AC_ToastSystem.ts';
|
||||||
|
import { EFunctionalTestResult } from '#root/UE/EFunctionalTestResult.ts';
|
||||||
|
import { FunctionalTest } from '#root/UE/FunctionalTest.ts';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Functional Test: Toast System Initialization
|
||||||
|
* Validates that toast system initializes with correct default settings
|
||||||
|
* Tests IsEnabled state and MaxVisibleToasts configuration
|
||||||
|
*/
|
||||||
|
export class FT_ToastsSystemInitialization extends FunctionalTest {
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
// GRAPHS
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
// ────────────────────────────────────────────────────────────────────────────────────────
|
||||||
|
// EventGraph
|
||||||
|
// ────────────────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test execution - validates initialization and default settings
|
||||||
|
* Uses nested validation to check system state after initialization
|
||||||
|
*/
|
||||||
|
EventStartTest(): void {
|
||||||
|
this.ToastComponent.InitializeToastSystem();
|
||||||
|
|
||||||
|
if (this.ToastComponent.ToastSettings.IsEnabled) {
|
||||||
|
if (this.ToastComponent.ToastSettings.MaxVisibleToasts > 0) {
|
||||||
|
this.FinishTest(EFunctionalTestResult.Succeeded);
|
||||||
|
} else {
|
||||||
|
this.FinishTest(
|
||||||
|
EFunctionalTestResult.Failed,
|
||||||
|
'Invalid state: max visible toasts less then 1'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.FinishTest(
|
||||||
|
EFunctionalTestResult.Failed,
|
||||||
|
'Invalid state: enabled=false'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
// VARIABLES
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toast notification system - component under test
|
||||||
|
* @category Components
|
||||||
|
*/
|
||||||
|
private ToastComponent = new AC_ToastSystem();
|
||||||
|
}
|
||||||
Binary file not shown.
|
|
@ -0,0 +1,71 @@
|
||||||
|
// Toasts/Tests/FT_ToastsToastCreation.ts
|
||||||
|
|
||||||
|
import { AC_ToastSystem } from '#root/Toasts/Components/AC_ToastSystem.ts';
|
||||||
|
import { EFunctionalTestResult } from '#root/UE/EFunctionalTestResult.ts';
|
||||||
|
import { FunctionalTest } from '#root/UE/FunctionalTest.ts';
|
||||||
|
import { UEArray } from '#root/UE/UEArray.ts';
|
||||||
|
import { E_MessageType } from '#root/UI/Enums/E_MessageType.ts';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Functional Test: Toast Creation by Message Type
|
||||||
|
* Validates toast creation functionality for all message types
|
||||||
|
* Tests that each type (Info, Success, Warning, Error, Debug) creates valid toasts
|
||||||
|
*/
|
||||||
|
export class FT_ToastsToastCreation extends FunctionalTest {
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
// GRAPHS
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
// ────────────────────────────────────────────────────────────────────────────────────────
|
||||||
|
// EventGraph
|
||||||
|
// ────────────────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test execution - validates toast creation for each message type
|
||||||
|
* Iterates through all message types and verifies successful creation
|
||||||
|
*/
|
||||||
|
EventStartTest(): void {
|
||||||
|
this.ToastComponent.InitializeToastSystem();
|
||||||
|
|
||||||
|
this.ToastTypes.forEach(arrayElement => {
|
||||||
|
// Create toast for current message type and check if valid ID is returned
|
||||||
|
if (
|
||||||
|
this.ToastComponent.ShowToast(
|
||||||
|
`Test ${arrayElement} message`,
|
||||||
|
arrayElement,
|
||||||
|
1
|
||||||
|
) <= 0
|
||||||
|
) {
|
||||||
|
this.FinishTest(
|
||||||
|
EFunctionalTestResult.Failed,
|
||||||
|
`Failed to create ${arrayElement} toast`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.FinishTest(EFunctionalTestResult.Succeeded);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
// VARIABLES
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toast notification system - component under test
|
||||||
|
* @category Components
|
||||||
|
*/
|
||||||
|
private ToastComponent = new AC_ToastSystem();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array of all message types to test
|
||||||
|
* Covers complete range of supported toast types
|
||||||
|
* @category Test Data
|
||||||
|
*/
|
||||||
|
private ToastTypes: UEArray<E_MessageType> = new UEArray([
|
||||||
|
E_MessageType.Info,
|
||||||
|
E_MessageType.Success,
|
||||||
|
E_MessageType.Warning,
|
||||||
|
E_MessageType.Error,
|
||||||
|
E_MessageType.Debug,
|
||||||
|
]);
|
||||||
|
}
|
||||||
Binary file not shown.
|
|
@ -1,119 +1,113 @@
|
||||||
// Content/Toasts/UI/WBP_Toast.ts
|
// Toasts/UI/WBP_Toast.ts
|
||||||
|
|
||||||
import {Border, TextBox, Widget} from "../../classes.js";
|
import { Border } from '#root/UE/Border.ts';
|
||||||
import {E_MessageType} from "../../UI/Enums/E_MessageType.js";
|
import { MathLibrary } from '#root/UE/MathLibrary.ts';
|
||||||
import type {Color, Text} from "../../types.js";
|
import { SystemLibrary } from '#root/UE/SystemLibrary.ts';
|
||||||
import {IsValid} from "../../functions.js";
|
import type { Text } from '#root/UE/Text.ts';
|
||||||
|
import { TextBlock } from '#root/UE/TextBlock.ts';
|
||||||
|
import { UserWidget } from '#root/UE/UserWidget.ts';
|
||||||
|
import { E_MessageType } from '#root/UI/Enums/E_MessageType.ts';
|
||||||
|
import { BFL_Colors } from '#root/UI/Libraries/BFL_Colors.ts';
|
||||||
|
|
||||||
export class WBP_Toast extends Widget {
|
|
||||||
// Category: "Toast Data"
|
|
||||||
/**
|
/**
|
||||||
* Current message text displayed in this toast
|
* Individual Toast Notification Widget
|
||||||
|
* Displays a single toast message with type-based styling
|
||||||
|
* Managed by WBP_ToastContainer for positioning and lifecycle
|
||||||
*/
|
*/
|
||||||
private MessageText: Text = "" as Text;
|
export class WBP_Toast extends UserWidget {
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
// FUNCTIONS
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
// Category: "Toast Data"
|
|
||||||
/**
|
/**
|
||||||
* Toast type for color and styling
|
* Update background styling based on toast type
|
||||||
|
* Uses color library to get appropriate color for message type
|
||||||
|
* @category Display Updates
|
||||||
*/
|
*/
|
||||||
private ToastType: E_MessageType = E_MessageType.Info;
|
private UpdateBackgroundStyling(): void {
|
||||||
|
if (SystemLibrary.IsValid(this.BackgroundPanel)) {
|
||||||
// Category: "UI Components"
|
this.BackgroundPanel.SetBrushColor(
|
||||||
/**
|
MathLibrary.ColorToLinearColor(
|
||||||
* Text box widget for displaying toast message
|
BFL_Colors.GetColorByMessageType(this.ToastType, 100)
|
||||||
* Styled based on toast type
|
)
|
||||||
*/
|
);
|
||||||
private MessageTextBox = new TextBox();
|
|
||||||
|
|
||||||
// Category: "UI Components"
|
|
||||||
/**
|
|
||||||
* Background panel for toast styling
|
|
||||||
* Color changes based on toast type
|
|
||||||
*/
|
|
||||||
private BackgroundPanel = new Border();
|
|
||||||
|
|
||||||
// Category: "Toast Styling"
|
|
||||||
/**
|
|
||||||
* Get color based on toast type
|
|
||||||
* @param Type - Toast type
|
|
||||||
* @returns Color string in hex format
|
|
||||||
* @pure Function has no side effects
|
|
||||||
*/
|
|
||||||
private GetToastColor(Type: E_MessageType): Color {
|
|
||||||
switch (Type) {
|
|
||||||
case E_MessageType.Info:
|
|
||||||
return {R: 74, G: 144, B: 226, A: 100}; // Blue
|
|
||||||
case E_MessageType.Success:
|
|
||||||
return {R: 92, G: 184, B: 92, A: 100}; // Green
|
|
||||||
case E_MessageType.Warning:
|
|
||||||
return {R: 240, G: 173, B: 78, A: 100}; // Orange
|
|
||||||
case E_MessageType.Error:
|
|
||||||
return {R: 217, G: 83, B: 79, A: 100}; // Red
|
|
||||||
case E_MessageType.Debug:
|
|
||||||
return {R: 108, G: 177, B: 125, A: 100}; // Gray
|
|
||||||
case E_MessageType.Test:
|
|
||||||
return {R: 142, G: 68, B: 142, A: 100}; // Purple
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Category: "Display Updates"
|
|
||||||
/**
|
/**
|
||||||
* Update message text box with current message
|
* Update message text box with current message text
|
||||||
* @private Internal display update method
|
* @category Display Updates
|
||||||
*/
|
*/
|
||||||
private UpdateMessageDisplay(): void {
|
private UpdateMessageDisplay(): void {
|
||||||
if (IsValid(this.MessageTextBox)) {
|
if (SystemLibrary.IsValid(this.MessageTextBox)) {
|
||||||
this.MessageTextBox.SetText(this.MessageText);
|
this.MessageTextBox.SetText(this.MessageText);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Category: "Display Updates"
|
|
||||||
/**
|
|
||||||
* Update background styling based on toast type
|
|
||||||
* @private Internal display update method
|
|
||||||
*/
|
|
||||||
private UpdateBackgroundStyling(): void {
|
|
||||||
if (IsValid(this.BackgroundPanel)) {
|
|
||||||
this.BackgroundPanel.SetBrushColor(this.GetToastColor(this.ToastType));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Category: "Public Interface"
|
|
||||||
/**
|
/**
|
||||||
* Set toast message and update display
|
* Set toast message and update display
|
||||||
* @param Message - New message text
|
* @param Message - New message text
|
||||||
* @example
|
* @example
|
||||||
* // Set success message
|
* // Set success message
|
||||||
* SetMessage("Test completed successfully!")
|
* SetMessage("Test completed successfully!")
|
||||||
|
* @category Public Interface
|
||||||
*/
|
*/
|
||||||
public SetMessage(Message: Text): void {
|
public SetMessage(Message: Text): void {
|
||||||
this.MessageText = Message;
|
this.MessageText = Message;
|
||||||
this.UpdateMessageDisplay();
|
this.UpdateMessageDisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Category: "Public Interface"
|
|
||||||
/**
|
/**
|
||||||
* Set toast type and update styling
|
* Set toast type and update styling
|
||||||
* @param Type - New toast type
|
* @param Type - New toast type
|
||||||
* @example
|
* @example
|
||||||
* // Set as error toast
|
* // Set as error toast
|
||||||
* SetToastType(E_ToastType.Error)
|
* SetToastType(E_MessageType.Error)
|
||||||
|
* @category Public Interface
|
||||||
*/
|
*/
|
||||||
public SetToastType(Type: E_MessageType): void {
|
public SetToastType(Type: E_MessageType): void {
|
||||||
this.ToastType = Type;
|
this.ToastType = Type;
|
||||||
this.UpdateBackgroundStyling();
|
this.UpdateBackgroundStyling();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Category: "Widget Lifecycle"
|
|
||||||
/**
|
/**
|
||||||
* Initialize toast widget with message and type
|
* Initialize toast widget with message and type
|
||||||
|
* Adds widget to viewport for display
|
||||||
* @param Message - Initial message text
|
* @param Message - Initial message text
|
||||||
* @param Type - Initial toast type
|
* @param Type - Initial toast type
|
||||||
* @public Called when creating new toast
|
* @category Widget Lifecycle
|
||||||
*/
|
*/
|
||||||
public InitializeToast(Message: Text, Type: E_MessageType): void {
|
public InitializeToast(Message: Text, Type: E_MessageType): void {
|
||||||
this.SetMessage(Message);
|
this.SetMessage(Message);
|
||||||
this.SetToastType(Type);
|
this.SetToastType(Type);
|
||||||
this.AddToViewport();
|
this.AddToViewport();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
// VARIABLES
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current message text displayed in this toast
|
||||||
|
* @category Toast Data
|
||||||
|
*/
|
||||||
|
private MessageText: Text = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toast type for color and styling determination
|
||||||
|
* @category Toast Data
|
||||||
|
*/
|
||||||
|
private ToastType: E_MessageType = E_MessageType.Info;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Text widget for displaying the toast message
|
||||||
|
* @category UI Components
|
||||||
|
*/
|
||||||
|
private MessageTextBox = new TextBlock();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Background border widget for toast styling and color
|
||||||
|
* @category UI Components
|
||||||
|
*/
|
||||||
|
private BackgroundPanel = new Border();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
BIN
Content/Toasts/UI/WBP_Toast.uasset (Stored with Git LFS)
BIN
Content/Toasts/UI/WBP_Toast.uasset (Stored with Git LFS)
Binary file not shown.
|
|
@ -1,61 +1,76 @@
|
||||||
// Content/Toasts/UI/WBP_ToastContainer.ts
|
// Toasts/UI/WBP_ToastContainer.ts
|
||||||
|
|
||||||
import {VerticalBox, Widget} from "../../classes.js";
|
import { WBP_Toast } from '#root/Toasts/UI/WBP_Toast.ts';
|
||||||
import {ESlateVisibility} from "../../enums.js";
|
import { CreateWidget } from '#root/UE/CteateWidget.ts';
|
||||||
import type {Text} from "../../types.js";
|
import { ESlateVisibility } from '#root/UE/ESlateVisibility.ts';
|
||||||
import type {E_MessageType} from "../../UI/Enums/E_MessageType.js";
|
import { SystemLibrary } from '#root/UE/SystemLibrary.ts';
|
||||||
import {WBP_Toast} from "./WBP_Toast.js";
|
import type { Text } from '#root/UE/Text.ts';
|
||||||
import {AddToArray, CreateWidget, IsValid, RemoveItemFromArray} from "../../functions.js";
|
import { UEArray } from '#root/UE/UEArray.ts';
|
||||||
|
import { UserWidget } from '#root/UE/UserWidget.ts';
|
||||||
|
import { VerticalBox } from '#root/UE/VerticalBox.ts';
|
||||||
|
import type { E_MessageType } from '#root/UI/Enums/E_MessageType.ts';
|
||||||
|
|
||||||
export class WBP_ToastContainer extends Widget {
|
|
||||||
// Category: "Layout"
|
|
||||||
/**
|
/**
|
||||||
* Vertical box container for automatic toast stacking
|
* Toast Container Widget
|
||||||
* Handles spacing and positioning automatically
|
* Manages layout and positioning of multiple toast notifications
|
||||||
|
* Automatically stacks toasts vertically with proper spacing
|
||||||
*/
|
*/
|
||||||
private ToastVerticalBox = new VerticalBox();
|
export class WBP_ToastContainer extends UserWidget {
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
// FUNCTIONS
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
// Category: "Container Management"
|
|
||||||
/**
|
/**
|
||||||
* Array of child toast widgets for management
|
* Create and add new toast to container
|
||||||
|
* @param Message - Toast message text
|
||||||
|
* @param Type - Toast type for styling
|
||||||
|
* @returns Created toast widget reference
|
||||||
|
* @category Toast Management
|
||||||
*/
|
*/
|
||||||
private ChildToasts: WBP_Toast[] = [];
|
public AddToast(Message: Text, Type: E_MessageType): WBP_Toast {
|
||||||
|
const toast = CreateWidget(WBP_Toast);
|
||||||
|
toast.InitializeToast(Message, Type);
|
||||||
|
this.ToastVerticalBox.AddChild(toast);
|
||||||
|
this.ChildToasts.Add(toast);
|
||||||
|
return toast;
|
||||||
|
}
|
||||||
|
|
||||||
// Category: "Container Setup"
|
|
||||||
/**
|
/**
|
||||||
* Initialize toast container
|
* Remove toast from container and hide it
|
||||||
* Sets up vertical box with proper spacing
|
* @param Toast - Toast widget to remove
|
||||||
|
* @category Toast Management
|
||||||
|
*/
|
||||||
|
public RemoveToast(Toast: WBP_Toast): void {
|
||||||
|
if (SystemLibrary.IsValid(Toast)) {
|
||||||
|
this.ChildToasts.Remove(Toast);
|
||||||
|
Toast.SetVisibility(ESlateVisibility.Hidden);
|
||||||
|
Toast.RemoveFromParent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize container widget and add to viewport
|
||||||
|
* @category Container Setup
|
||||||
*/
|
*/
|
||||||
public InitializeContainer(): void {
|
public InitializeContainer(): void {
|
||||||
this.SetVisibility(ESlateVisibility.Visible);
|
this.SetVisibility(ESlateVisibility.Visible);
|
||||||
this.AddToViewport();
|
this.AddToViewport();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Category: "Toast Management"
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
/**
|
// VARIABLES
|
||||||
* Add new toast to container
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
* @param Message - Toast message
|
|
||||||
* @param Type - Toast type
|
|
||||||
* @returns Created toast widget reference
|
|
||||||
*/
|
|
||||||
public AddToast(Message: Text, Type: E_MessageType): WBP_Toast {
|
|
||||||
const toast = CreateWidget(new WBP_Toast());
|
|
||||||
toast.InitializeToast(Message, Type);
|
|
||||||
this.ToastVerticalBox.AddChild(toast);
|
|
||||||
AddToArray(this.ChildToasts, toast);
|
|
||||||
return toast;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Category: "Toast Management"
|
|
||||||
/**
|
/**
|
||||||
* Remove toast from container
|
* Vertical layout container for automatic toast stacking
|
||||||
* @param Toast - Toast widget to remove
|
* Handles spacing and positioning automatically
|
||||||
|
* @category Layout
|
||||||
*/
|
*/
|
||||||
public RemoveToast(Toast: WBP_Toast): void {
|
private ToastVerticalBox = new VerticalBox();
|
||||||
if (IsValid(Toast)) {
|
|
||||||
RemoveItemFromArray(this.ChildToasts, Toast);
|
/**
|
||||||
Toast.SetVisibility(ESlateVisibility.Hidden);
|
* Array of managed child toast widgets
|
||||||
Toast.RemoveFromParent();
|
* @category Container Management
|
||||||
}
|
*/
|
||||||
}
|
private ChildToasts: UEArray<WBP_Toast> = new UEArray([]);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
BIN
Content/Toasts/UI/WBP_ToastContainer.uasset (Stored with Git LFS)
BIN
Content/Toasts/UI/WBP_ToastContainer.uasset (Stored with Git LFS)
Binary file not shown.
|
|
@ -0,0 +1,10 @@
|
||||||
|
// UE/Actor.ts
|
||||||
|
|
||||||
|
import { Name } from '#root/UE/Name.ts';
|
||||||
|
import { UEObject } from '#root/UE/UEObject.ts';
|
||||||
|
|
||||||
|
export class Actor extends UEObject {
|
||||||
|
constructor(outer: UEObject | null = null, name: Name | string = Name.NONE) {
|
||||||
|
super(outer, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
// UE/ActorComponent.ts
|
||||||
|
|
||||||
|
import { Name } from '#root/UE/Name.ts';
|
||||||
|
import { UEObject } from '#root/UE/UEObject.ts';
|
||||||
|
|
||||||
|
export class ActorComponent extends UEObject {
|
||||||
|
constructor(outer: UEObject | null = null, name: Name | string = Name.NONE) {
|
||||||
|
super(outer, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
// UE/BlueprintFunctionLibrary.ts
|
||||||
|
|
||||||
|
import { UEObject } from '#root/UE/UEObject.ts';
|
||||||
|
|
||||||
|
export class BlueprintFunctionLibrary extends UEObject {
|
||||||
|
constructor(
|
||||||
|
outer: UEObject | null = null,
|
||||||
|
name: string = 'BlueprintFunctionLibrary'
|
||||||
|
) {
|
||||||
|
super(outer, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
// UE/Border.ts
|
||||||
|
|
||||||
|
import { ContentWidget } from '#root/UE/ContentWidget.ts';
|
||||||
|
import type { LinearColor } from '#root/UE/LinearColor.ts';
|
||||||
|
import { Name } from '#root/UE/Name.ts';
|
||||||
|
import { UEObject } from '#root/UE/UEObject.ts';
|
||||||
|
|
||||||
|
export class Border extends ContentWidget {
|
||||||
|
constructor(outer: UEObject | null = null, name: Name | string = Name.NONE) {
|
||||||
|
super(outer, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SetBrushColor(color: LinearColor): void {
|
||||||
|
console.log(color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
// UE/Byte.ts
|
||||||
|
|
||||||
|
export type Byte = number;
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
// Content/Cast.ts
|
||||||
|
|
||||||
|
export function Cast<T>(obj: unknown): T | null {
|
||||||
|
return (obj as T) || null;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
// UE/Color.ts
|
||||||
|
|
||||||
|
import { StructBase } from '#root/UE/StructBase.ts';
|
||||||
|
import type { UInt8 } from '#root/UE/UInt8.ts';
|
||||||
|
|
||||||
|
export class Color extends StructBase {
|
||||||
|
constructor(
|
||||||
|
public B: UInt8 = 0,
|
||||||
|
public G: UInt8 = 0,
|
||||||
|
public R: UInt8 = 0,
|
||||||
|
public A: UInt8 = 0
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
// UE/ContentWidget.ts
|
||||||
|
|
||||||
|
import { Name } from '#root/UE/Name.ts';
|
||||||
|
import { PanelWidget } from '#root/UE/PanelWidget.ts';
|
||||||
|
import { UEObject } from '#root/UE/UEObject.ts';
|
||||||
|
|
||||||
|
export class ContentWidget extends PanelWidget {
|
||||||
|
constructor(outer: UEObject | null = null, name: Name | string = Name.NONE) {
|
||||||
|
super(outer, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
// UE/Controller.ts
|
||||||
|
|
||||||
|
import { Actor } from '#root/UE/Actor.ts';
|
||||||
|
import { Name } from '#root/UE/Name.ts';
|
||||||
|
import { UEObject } from '#root/UE/UEObject.ts';
|
||||||
|
|
||||||
|
export class Controller extends Actor {
|
||||||
|
constructor(outer: UEObject | null = null, name: Name | string = Name.NONE) {
|
||||||
|
super(outer, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CastToPlayerController(): Controller {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
// UE/CreateWidget.ts
|
||||||
|
|
||||||
|
import type { UserWidget } from '#root/UE/UserWidget.ts';
|
||||||
|
|
||||||
|
type WidgetConstructor<T extends UserWidget> = new () => T;
|
||||||
|
|
||||||
|
export function CreateWidget<T extends UserWidget>(
|
||||||
|
widgetClass: WidgetConstructor<T>
|
||||||
|
): T {
|
||||||
|
return new widgetClass();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
// UE/DataAsset.ts
|
||||||
|
|
||||||
|
import { Name } from '#root/UE/Name.ts';
|
||||||
|
import { UEObject } from '#root/UE/UEObject.ts';
|
||||||
|
|
||||||
|
export class DataAsset extends UEObject {
|
||||||
|
constructor(outer: UEObject | null = null, name: Name | string = Name.NONE) {
|
||||||
|
super(outer, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
// UE/DataTable.ts
|
||||||
|
|
||||||
|
import { Name } from '#root/UE/Name.ts';
|
||||||
|
import type { UEArray } from '#root/UE/UEArray.ts';
|
||||||
|
import { UEObject } from '#root/UE/UEObject.ts';
|
||||||
|
|
||||||
|
export class DataTable<T> extends UEObject {
|
||||||
|
private rows: Map<Name, T> = new Map();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
outer: UEObject | null = null,
|
||||||
|
name: Name = Name.NONE,
|
||||||
|
initialData: UEArray<T & { Name: Name }> = [] as unknown as UEArray<
|
||||||
|
T & { Name: Name }
|
||||||
|
>
|
||||||
|
) {
|
||||||
|
super(outer, name);
|
||||||
|
|
||||||
|
initialData.forEach(row => {
|
||||||
|
this.rows.set(row.Name, row);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public GetDataTableRow(
|
||||||
|
rowName: Name,
|
||||||
|
rowFound?: (row: T) => void,
|
||||||
|
rowNotFound?: () => void
|
||||||
|
): void | T | undefined {
|
||||||
|
const row = this.rows.get(rowName);
|
||||||
|
|
||||||
|
if (!rowFound && !rowNotFound) {
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (row) {
|
||||||
|
rowFound?.(row);
|
||||||
|
} else {
|
||||||
|
rowNotFound?.();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
// UE/DataTableFunctionLibrary.ts
|
||||||
|
|
||||||
|
import { BlueprintFunctionLibrary } from '#root/UE/BlueprintFunctionLibrary.ts';
|
||||||
|
import type { DataTable } from '#root/UE/DataTable.ts';
|
||||||
|
import { Name } from '#root/UE/Name.ts';
|
||||||
|
|
||||||
|
class DataTableFunctionLibraryClass extends BlueprintFunctionLibrary {
|
||||||
|
constructor(
|
||||||
|
outer: null | BlueprintFunctionLibrary = null,
|
||||||
|
name: string = 'DataTableFunctionLibrary'
|
||||||
|
) {
|
||||||
|
super(outer, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GetDataTableRowNames<T>(table: DataTable<T>): Name[] {
|
||||||
|
return Array.from(table['rows'].keys());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DataTableFunctionLibrary = new DataTableFunctionLibraryClass();
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
// UE/EFunctionalTestResult.ts
|
||||||
|
|
||||||
|
export enum EFunctionalTestResult {
|
||||||
|
'Default' = 'Default',
|
||||||
|
'Invalid' = 'Invalid',
|
||||||
|
'Error' = 'Error',
|
||||||
|
'Running' = 'Running',
|
||||||
|
'Failed' = 'Failed',
|
||||||
|
'Succeeded' = 'Succeeded',
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
// UE/ESlateVisibility.ts
|
||||||
|
|
||||||
|
export enum ESlateVisibility {
|
||||||
|
Visible = 'Visible',
|
||||||
|
Collapsed = 'Collapsed',
|
||||||
|
Hidden = 'Hidden',
|
||||||
|
NotHitTestableSelfAndAllChildren = 'NotHitTestableSelfAndAllChildren',
|
||||||
|
NotHitTestableSelfOnly = 'NotHitTestableSelfOnly',
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
// UE/EnhancedActionKeyMapping.ts
|
||||||
|
|
||||||
|
import type { InputAction } from '#root/UE/InputAction.ts';
|
||||||
|
import type { InputModifier } from '#root/UE/InputModifier.ts';
|
||||||
|
import type { InputTrigger } from '#root/UE/InputTrigger.ts';
|
||||||
|
import type { Key } from '#root/UE/Key.ts';
|
||||||
|
import { StructBase } from '#root/UE/StructBase.ts';
|
||||||
|
import type { UEArray } from '#root/UE/UEArray.ts';
|
||||||
|
|
||||||
|
export class EnhancedActionKeyMapping extends StructBase {
|
||||||
|
public action: InputAction;
|
||||||
|
public key: Key;
|
||||||
|
public modifiers: UEArray<InputModifier>;
|
||||||
|
public triggers: UEArray<InputTrigger>;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
triggers: UEArray<InputTrigger>,
|
||||||
|
modifiers: UEArray<InputModifier>,
|
||||||
|
action: InputAction,
|
||||||
|
key: Key
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.action = action;
|
||||||
|
this.key = key;
|
||||||
|
this.modifiers = modifiers;
|
||||||
|
this.triggers = triggers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
// UE/EnhancedInputLocalPlayerSubsystem.ts
|
||||||
|
|
||||||
|
import type { InputMappingContext } from '#root/UE/InputMappingContext.ts';
|
||||||
|
import type { Integer } from '#root/UE/Integer.ts';
|
||||||
|
import { LocalPlayerSubsystem } from '#root/UE/LocalPlayerSubsystem.ts';
|
||||||
|
import { ModifyContextOptions } from '#root/UE/ModifyContextOptions.ts';
|
||||||
|
import { Name } from '#root/UE/Name.ts';
|
||||||
|
import { UEObject } from '#root/UE/UEObject.ts';
|
||||||
|
|
||||||
|
export class EnhancedInputLocalPlayerSubsystem extends LocalPlayerSubsystem {
|
||||||
|
constructor(outer: UEObject | null = null, name: Name | string = Name.NONE) {
|
||||||
|
super(outer, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AddMappingContext(
|
||||||
|
mappingContent: InputMappingContext,
|
||||||
|
priority: Integer = 0,
|
||||||
|
options: ModifyContextOptions = new ModifyContextOptions(true, false, false)
|
||||||
|
): void {
|
||||||
|
console.log(mappingContent, priority, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
// UE/Float.ts
|
||||||
|
|
||||||
|
export type Float = number;
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
// UE/FunctionalTest.ts
|
||||||
|
|
||||||
|
import { Actor } from '#root/UE/Actor.ts';
|
||||||
|
import { EFunctionalTestResult } from '#root/UE/EFunctionalTestResult.ts';
|
||||||
|
import { Name } from '#root/UE/Name.ts';
|
||||||
|
import { UEObject } from '#root/UE/UEObject.ts';
|
||||||
|
|
||||||
|
export class FunctionalTest extends Actor {
|
||||||
|
constructor(outer: UEObject | null = null, name: Name | string = Name.NONE) {
|
||||||
|
super(outer, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FinishTest(
|
||||||
|
testResult: EFunctionalTestResult = EFunctionalTestResult.Default,
|
||||||
|
message: string = ''
|
||||||
|
): void {
|
||||||
|
console.log(
|
||||||
|
`Test Finished with result: ${testResult}. Message: ${message}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
// Content/GameModeBase.ts
|
||||||
|
|
||||||
|
import { Info } from '#root/UE/Info.ts';
|
||||||
|
import { Name } from '#root/UE/Name.ts';
|
||||||
|
import { UEObject } from '#root/UE/UEObject.ts';
|
||||||
|
|
||||||
|
export class GameModeBase extends Info {
|
||||||
|
constructor(outer: UEObject | null = null, name: Name | string = Name.NONE) {
|
||||||
|
super(outer, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
// UE/Info.ts
|
||||||
|
|
||||||
|
import { Actor } from '#root/UE/Actor.ts';
|
||||||
|
import { Name } from '#root/UE/Name.ts';
|
||||||
|
import { UEObject } from '#root/UE/UEObject.ts';
|
||||||
|
|
||||||
|
export class Info extends Actor {
|
||||||
|
constructor(outer: UEObject | null = null, name: Name | string = Name.NONE) {
|
||||||
|
super(outer, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
// UE/InputAction.ts
|
||||||
|
|
||||||
|
import { DataAsset } from '#root/UE/DataAsset.ts';
|
||||||
|
import { Name } from '#root/UE/Name.ts';
|
||||||
|
import type { UEObject } from '#root/UE/UEObject.ts';
|
||||||
|
|
||||||
|
export class InputAction extends DataAsset {
|
||||||
|
constructor(outer: UEObject | null = null, name: Name | string = Name.NONE) {
|
||||||
|
super(outer, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
// UE/InputMappingContext.ts
|
||||||
|
|
||||||
|
import { DataAsset } from '#root/UE/DataAsset.ts';
|
||||||
|
import { EnhancedActionKeyMapping } from '#root/UE/EnhancedActionKeyMapping.ts';
|
||||||
|
import type { InputAction } from '#root/UE/InputAction.ts';
|
||||||
|
import type { InputModifier } from '#root/UE/InputModifier.ts';
|
||||||
|
import type { InputTrigger } from '#root/UE/InputTrigger.ts';
|
||||||
|
import type { Key } from '#root/UE/Key.ts';
|
||||||
|
import { Name } from '#root/UE/Name.ts';
|
||||||
|
import type { UEArray } from '#root/UE/UEArray.ts';
|
||||||
|
import type { UEObject } from '#root/UE/UEObject.ts';
|
||||||
|
|
||||||
|
export class InputMappingContext extends DataAsset {
|
||||||
|
constructor(outer: UEObject | null = null, name: Name | string = Name.NONE) {
|
||||||
|
super(outer, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public mapKey(action: InputAction, toKey: Key): EnhancedActionKeyMapping {
|
||||||
|
return new EnhancedActionKeyMapping(
|
||||||
|
[] as unknown as UEArray<InputTrigger>,
|
||||||
|
[] as unknown as UEArray<InputModifier>,
|
||||||
|
action,
|
||||||
|
toKey
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
// UE/InputModifier.ts
|
||||||
|
|
||||||
|
import { Name } from '#root/UE/Name.ts';
|
||||||
|
import { UEObject } from '#root/UE/UEObject.ts';
|
||||||
|
|
||||||
|
export class InputModifier extends UEObject {
|
||||||
|
constructor(outer: UEObject | null = null, name: Name | string = Name.NONE) {
|
||||||
|
super(outer, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
// UE/InputTrigger.ts
|
||||||
|
|
||||||
|
import { Name } from '#root/UE/Name.ts';
|
||||||
|
import { UEObject } from '#root/UE/UEObject.ts';
|
||||||
|
|
||||||
|
export class InputTrigger extends UEObject {
|
||||||
|
constructor(outer: UEObject | null = null, name: Name | string = Name.NONE) {
|
||||||
|
super(outer, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
// UE/Integer.ts
|
||||||
|
|
||||||
|
export type Integer = number;
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
// Content/UE/Key.ts
|
||||||
|
|
||||||
|
import { Name } from '#root/UE/Name.ts';
|
||||||
|
import { StructBase } from '#root/UE/StructBase.ts';
|
||||||
|
|
||||||
|
export class Key extends StructBase {
|
||||||
|
public keyName: Name;
|
||||||
|
|
||||||
|
constructor(keyName: Name | string = Name.NONE) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
if (keyName instanceof Name) {
|
||||||
|
this.keyName = keyName;
|
||||||
|
} else {
|
||||||
|
this.keyName = new Name(keyName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
// UE/LinearColor.ts
|
||||||
|
|
||||||
|
import type { Float } from '#root/UE/Float.ts';
|
||||||
|
import { StructBase } from '#root/UE/StructBase.ts';
|
||||||
|
|
||||||
|
export class LinearColor extends StructBase {
|
||||||
|
public A: Float = 0;
|
||||||
|
public R: Float = 0;
|
||||||
|
public G: Float = 0;
|
||||||
|
public B: Float = 0;
|
||||||
|
|
||||||
|
constructor(r: Float = 0, g: Float = 0, b: Float = 0, a: Float = 0) {
|
||||||
|
super();
|
||||||
|
this.A = a;
|
||||||
|
this.R = r;
|
||||||
|
this.G = g;
|
||||||
|
this.B = b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
// UE/LocalPlayerSubsystem.ts
|
||||||
|
|
||||||
|
import { Name } from '#root/UE/Name.ts';
|
||||||
|
import { Subsystem } from '#root/UE/Subsystem.ts';
|
||||||
|
import { UEObject } from '#root/UE/UEObject.ts';
|
||||||
|
|
||||||
|
export class LocalPlayerSubsystem extends Subsystem {
|
||||||
|
constructor(outer: UEObject | null = null, name: Name | string = Name.NONE) {
|
||||||
|
super(outer, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue