Add UnrealDocGenerator tool and UE API skill
- ue_parser.py: position-based UE C++ header parser - ue_markdown.py: compact agent-optimised Markdown renderer - generate.py: two-pass CLI (parse-all → type index → render-all) - samples/: representative UE headers (GeomUtils, AIController, GameplayTagsManager) - .claude/skills/ue-api/: Claude Code skill for querying UE docs + source headers - CLAUDE.md: architecture notes, usage, critical gotchas Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
5
.claude/plugins/ue-api/.claude-plugin/plugin.json
Normal file
5
.claude/plugins/ue-api/.claude-plugin/plugin.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "ue-api",
|
||||
"description": "Unreal Engine API documentation skill — queries generated docs and source headers to answer questions about UE C++ APIs, class hierarchies, and system flows.",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
109
.claude/plugins/ue-api/skills/ue-api/SKILL.md
Normal file
109
.claude/plugins/ue-api/skills/ue-api/SKILL.md
Normal file
@@ -0,0 +1,109 @@
|
||||
---
|
||||
name: ue-api
|
||||
description: >
|
||||
Use this skill when the user asks anything about Unreal Engine C++ APIs,
|
||||
class hierarchies, system flows, or how engine subsystems work together.
|
||||
Trigger phrases include: "how does X work in UE", "what is the flow for",
|
||||
"which class handles", "what virtual functions", "UE API for", "Unreal Engine
|
||||
architecture", and any question that names UE types like ACharacter,
|
||||
UActorComponent, APlayerController, UBehaviorTreeComponent, APawn, etc.
|
||||
Always use this skill for Unreal Engine questions — don't rely on training
|
||||
data alone when local documentation is available.
|
||||
---
|
||||
|
||||
# UE API Skill
|
||||
|
||||
Answer questions about Unreal Engine C++ APIs and system flows using the local
|
||||
documentation corpus as the fast primary source, then source headers for depth.
|
||||
|
||||
## Configuration
|
||||
|
||||
Two environment variables control where to look:
|
||||
|
||||
| Variable | Purpose | Example |
|
||||
|---|---|---|
|
||||
| `UE_DOCS_PATH` | Root of the generated documentation | `/home/user/ue-docs` |
|
||||
| `UE_ENGINE_ROOT` | UE engine source root (for header fallback) | `/home/user/UnrealEngine` |
|
||||
|
||||
**Resolve them at the start of every query:**
|
||||
|
||||
```bash
|
||||
echo "$UE_DOCS_PATH"
|
||||
echo "$UE_ENGINE_ROOT"
|
||||
```
|
||||
|
||||
If `UE_DOCS_PATH` is unset, ask the user where their generated docs are before
|
||||
proceeding. If `UE_ENGINE_ROOT` is unset, only ask when a question actually
|
||||
requires source headers — don't interrupt doc-only queries.
|
||||
|
||||
The type index is always at `$UE_DOCS_PATH/type-index.txt`.
|
||||
|
||||
## Step 1 — Identify types, resolve paths
|
||||
|
||||
Extract UE type names from the question (e.g. `APlayerController`, `APawn`,
|
||||
`UBehaviorTreeComponent`). Resolve all of them in a single grep:
|
||||
|
||||
```bash
|
||||
grep "^APlayerController:\|^APawn:\|^ACharacter:" "$UE_DOCS_PATH/type-index.txt"
|
||||
```
|
||||
|
||||
Paths in the index are relative to `$UE_DOCS_PATH` — prepend it when reading:
|
||||
|
||||
```bash
|
||||
# index returns: AController: Engine/Classes/GameFramework/Controller.md
|
||||
# read as:
|
||||
Read "$UE_DOCS_PATH/Engine/Classes/GameFramework/Controller.md"
|
||||
```
|
||||
|
||||
The `.md` files are compact by design — only items with C++ doc comments,
|
||||
no deprecated entries, enums collapsed when undescribed.
|
||||
|
||||
## Step 2 — Follow the trail
|
||||
|
||||
Inline links in `*Inherits*:` lines and function signatures point to related
|
||||
types. Follow them when the question spans multiple classes. A second grep on
|
||||
`type-index.txt` is always cheaper than guessing paths.
|
||||
|
||||
## Step 3 — Escalate to source headers when docs aren't enough
|
||||
|
||||
The docs only surface items with C++ doc comments. Go to `.h` files when:
|
||||
|
||||
- The exact call order or implementation logic isn't described in any comment
|
||||
- A function or member is absent from `.md` files (no doc comment)
|
||||
- The question involves macros: `UCLASS`, `UPROPERTY`, `UFUNCTION`,
|
||||
`DECLARE_DELEGATE_*`, `GENERATED_BODY`, etc.
|
||||
- Private or protected members are relevant to the answer
|
||||
- The user asks about edge-case behaviour ("what happens when X is null?")
|
||||
|
||||
Search under `$UE_ENGINE_ROOT/Engine/Source/` — e.g.:
|
||||
|
||||
```bash
|
||||
Glob("**/*Controller*.h", path="$UE_ENGINE_ROOT/Engine/Source/Runtime/Engine")
|
||||
Grep("void Possess", path="$UE_ENGINE_ROOT/Engine/Source")
|
||||
```
|
||||
|
||||
## Output format
|
||||
|
||||
Lead with the direct answer or a concise flow description. For multi-step
|
||||
flows use an ASCII sequence or numbered list. For single-class API questions,
|
||||
a brief prose answer with the key function signatures is enough.
|
||||
|
||||
Cite every substantive claim — `(Controller.md)` for docs, `(Controller.h:142)`
|
||||
for source. Mark source-derived facts as *implementation detail* since they can
|
||||
change across engine versions; doc-derived facts reflect the stable API contract.
|
||||
|
||||
## Examples
|
||||
|
||||
**"How does APlayerController possess APawn?"**
|
||||
→ check `$UE_DOCS_PATH` → grep type-index for AController, APawn,
|
||||
APlayerController, ACharacter → read the four `.md` files → ASCII diagram
|
||||
|
||||
**"What virtual functions does ACharacter expose for movement?"**
|
||||
→ grep for ACharacter, UCharacterMovementComponent → read docs → list virtuals
|
||||
|
||||
**"What does UFUNCTION(BlueprintCallable) expand to?"**
|
||||
→ docs won't help (macro) → search `$UE_ENGINE_ROOT` for `UFUNCTION` definition
|
||||
|
||||
**"How does the behavior tree pick which task to run?"**
|
||||
→ grep for UBehaviorTreeComponent, UBTCompositeNode, UBTDecorator → read docs
|
||||
→ if execution order is still unclear, escalate to `BehaviorTreeComponent.h`
|
||||
109
.claude/skills/ue-api/SKILL.md
Normal file
109
.claude/skills/ue-api/SKILL.md
Normal file
@@ -0,0 +1,109 @@
|
||||
---
|
||||
name: ue-api
|
||||
description: >
|
||||
Use this skill when the user asks anything about Unreal Engine C++ APIs,
|
||||
class hierarchies, system flows, or how engine subsystems work together.
|
||||
Trigger phrases include: "how does X work in UE", "what is the flow for",
|
||||
"which class handles", "what virtual functions", "UE API for", "Unreal Engine
|
||||
architecture", and any question that names UE types like ACharacter,
|
||||
UActorComponent, APlayerController, UBehaviorTreeComponent, APawn, etc.
|
||||
Always use this skill for Unreal Engine questions — don't rely on training
|
||||
data alone when local documentation is available.
|
||||
---
|
||||
|
||||
# UE API Skill
|
||||
|
||||
Answer questions about Unreal Engine C++ APIs and system flows using the local
|
||||
documentation corpus as the fast primary source, then source headers for depth.
|
||||
|
||||
## Configuration
|
||||
|
||||
Two environment variables control where to look:
|
||||
|
||||
| Variable | Purpose | Example |
|
||||
|---|---|---|
|
||||
| `UE_DOCS_PATH` | Root of the generated documentation | `/home/user/ue-docs` |
|
||||
| `UE_ENGINE_ROOT` | UE engine source root (for header fallback) | `/home/user/UnrealEngine` |
|
||||
|
||||
**Resolve them at the start of every query:**
|
||||
|
||||
```bash
|
||||
echo "$UE_DOCS_PATH"
|
||||
echo "$UE_ENGINE_ROOT"
|
||||
```
|
||||
|
||||
If `UE_DOCS_PATH` is unset, ask the user where their generated docs are before
|
||||
proceeding. If `UE_ENGINE_ROOT` is unset, only ask when a question actually
|
||||
requires source headers — don't interrupt doc-only queries.
|
||||
|
||||
The type index is always at `$UE_DOCS_PATH/type-index.txt`.
|
||||
|
||||
## Step 1 — Identify types, resolve paths
|
||||
|
||||
Extract UE type names from the question (e.g. `APlayerController`, `APawn`,
|
||||
`UBehaviorTreeComponent`). Resolve all of them in a single grep:
|
||||
|
||||
```bash
|
||||
grep "^APlayerController:\|^APawn:\|^ACharacter:" "$UE_DOCS_PATH/type-index.txt"
|
||||
```
|
||||
|
||||
Paths in the index are relative to `$UE_DOCS_PATH` — prepend it when reading:
|
||||
|
||||
```bash
|
||||
# index returns: AController: Engine/Classes/GameFramework/Controller.md
|
||||
# read as:
|
||||
Read "$UE_DOCS_PATH/Engine/Classes/GameFramework/Controller.md"
|
||||
```
|
||||
|
||||
The `.md` files are compact by design — only items with C++ doc comments,
|
||||
no deprecated entries, enums collapsed when undescribed.
|
||||
|
||||
## Step 2 — Follow the trail
|
||||
|
||||
Inline links in `*Inherits*:` lines and function signatures point to related
|
||||
types. Follow them when the question spans multiple classes. A second grep on
|
||||
`type-index.txt` is always cheaper than guessing paths.
|
||||
|
||||
## Step 3 — Escalate to source headers when docs aren't enough
|
||||
|
||||
The docs only surface items with C++ doc comments. Go to `.h` files when:
|
||||
|
||||
- The exact call order or implementation logic isn't described in any comment
|
||||
- A function or member is absent from `.md` files (no doc comment)
|
||||
- The question involves macros: `UCLASS`, `UPROPERTY`, `UFUNCTION`,
|
||||
`DECLARE_DELEGATE_*`, `GENERATED_BODY`, etc.
|
||||
- Private or protected members are relevant to the answer
|
||||
- The user asks about edge-case behaviour ("what happens when X is null?")
|
||||
|
||||
Search under `$UE_ENGINE_ROOT/Engine/Source/` — e.g.:
|
||||
|
||||
```bash
|
||||
Glob("**/*Controller*.h", path="$UE_ENGINE_ROOT/Engine/Source/Runtime/Engine")
|
||||
Grep("void Possess", path="$UE_ENGINE_ROOT/Engine/Source")
|
||||
```
|
||||
|
||||
## Output format
|
||||
|
||||
Lead with the direct answer or a concise flow description. For multi-step
|
||||
flows use an ASCII sequence or numbered list. For single-class API questions,
|
||||
a brief prose answer with the key function signatures is enough.
|
||||
|
||||
Cite every substantive claim — `(Controller.md)` for docs, `(Controller.h:142)`
|
||||
for source. Mark source-derived facts as *implementation detail* since they can
|
||||
change across engine versions; doc-derived facts reflect the stable API contract.
|
||||
|
||||
## Examples
|
||||
|
||||
**"How does APlayerController possess APawn?"**
|
||||
→ check `$UE_DOCS_PATH` → grep type-index for AController, APawn,
|
||||
APlayerController, ACharacter → read the four `.md` files → ASCII diagram
|
||||
|
||||
**"What virtual functions does ACharacter expose for movement?"**
|
||||
→ grep for ACharacter, UCharacterMovementComponent → read docs → list virtuals
|
||||
|
||||
**"What does UFUNCTION(BlueprintCallable) expand to?"**
|
||||
→ docs won't help (macro) → search `$UE_ENGINE_ROOT` for `UFUNCTION` definition
|
||||
|
||||
**"How does the behavior tree pick which task to run?"**
|
||||
→ grep for UBehaviorTreeComponent, UBTCompositeNode, UBTDecorator → read docs
|
||||
→ if execution order is still unclear, escalate to `BehaviorTreeComponent.h`
|
||||
740
.gitignore
vendored
Normal file
740
.gitignore
vendored
Normal file
@@ -0,0 +1,740 @@
|
||||
# ---> Python
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# UV
|
||||
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
#uv.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
#poetry.lock
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
#pdm.lock
|
||||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||
# in version control.
|
||||
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
|
||||
.pdm.toml
|
||||
.pdm-python
|
||||
.pdm-build/
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
|
||||
# Ruff stuff:
|
||||
.ruff_cache/
|
||||
|
||||
# PyPI configuration file
|
||||
.pypirc
|
||||
|
||||
# ---> JetBrains
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/**/usage.statistics.xml
|
||||
.idea/**/dictionaries
|
||||
.idea/**/shelf
|
||||
|
||||
# AWS User-specific
|
||||
.idea/**/aws.xml
|
||||
|
||||
# Generated files
|
||||
.idea/**/contentModel.xml
|
||||
|
||||
# Sensitive or high-churn files
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
.idea/**/dbnavigator.xml
|
||||
|
||||
# Gradle
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# Gradle and Maven with auto-import
|
||||
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||
# since they will be recreated, and may cause churn. Uncomment if using
|
||||
# auto-import.
|
||||
# .idea/artifacts
|
||||
# .idea/compiler.xml
|
||||
# .idea/jarRepositories.xml
|
||||
# .idea/modules.xml
|
||||
# .idea/*.iml
|
||||
# .idea/modules
|
||||
# *.iml
|
||||
# *.ipr
|
||||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
|
||||
# Mongo Explorer plugin
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
# File-based project format
|
||||
*.iws
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# SonarLint plugin
|
||||
.idea/sonarlint/
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
# Editor-based Rest Client
|
||||
.idea/httpRequests
|
||||
|
||||
# Android studio 3.1+ serialized cache file
|
||||
.idea/caches/build_file_checksums.ser
|
||||
|
||||
# ---> VisualStudio
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
|
||||
|
||||
# User-specific files
|
||||
*.rsuser
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Mono auto generated files
|
||||
mono_crash.*
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
[Ww][Ii][Nn]32/
|
||||
[Aa][Rr][Mm]/
|
||||
[Aa][Rr][Mm]64/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
[Ll]ogs/
|
||||
|
||||
# Visual Studio 2015/2017 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# Visual Studio 2017 auto generated files
|
||||
Generated\ Files/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUnit
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
nunit-*.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# Benchmark Results
|
||||
BenchmarkDotNet.Artifacts/
|
||||
|
||||
# .NET Core
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
|
||||
# ASP.NET Scaffolding
|
||||
ScaffoldingReadMe.txt
|
||||
|
||||
# StyleCop
|
||||
StyleCopReport.xml
|
||||
|
||||
# Files built by Visual Studio
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_h.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.iobj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.ipdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
# but not Directory.Build.rsp, as it configures directory-level build defaults
|
||||
!Directory.Build.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*_wpftmp.csproj
|
||||
*.log
|
||||
*.tlog
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# Visual Studio Trace Files
|
||||
*.e2e
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# AxoCover is a Code Coverage Tool
|
||||
.axoCover/*
|
||||
!.axoCover/settings.json
|
||||
|
||||
# Coverlet is a free, cross platform Code Coverage Tool
|
||||
coverage*.json
|
||||
coverage*.xml
|
||||
coverage*.info
|
||||
|
||||
# Visual Studio code coverage results
|
||||
*.coverage
|
||||
*.coveragexml
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# NuGet Symbol Packages
|
||||
*.snupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/[Pp]ackages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/[Pp]ackages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/[Pp]ackages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignorable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
*.appx
|
||||
*.appxbundle
|
||||
*.appxupload
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!?*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
orleans.codegen.cs
|
||||
|
||||
# Including strong name files can present a security risk
|
||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||
#*.snk
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
ServiceFabricBackup/
|
||||
*.rptproj.bak
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
*.ndf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
*.rptproj.rsuser
|
||||
*- [Bb]ackup.rdl
|
||||
*- [Bb]ackup ([0-9]).rdl
|
||||
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
node_modules/
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||
*.vbw
|
||||
|
||||
# Visual Studio 6 auto-generated project file (contains which files were open etc.)
|
||||
*.vbp
|
||||
|
||||
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
|
||||
*.dsw
|
||||
*.dsp
|
||||
|
||||
# Visual Studio 6 technical files
|
||||
*.ncb
|
||||
*.aps
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# CodeRush personal settings
|
||||
.cr/personal
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Cake - Uncomment if you are using it
|
||||
# tools/**
|
||||
# !tools/packages.config
|
||||
|
||||
# Tabs Studio
|
||||
*.tss
|
||||
|
||||
# Telerik's JustMock configuration file
|
||||
*.jmconfig
|
||||
|
||||
# BizTalk build output
|
||||
*.btp.cs
|
||||
*.btm.cs
|
||||
*.odx.cs
|
||||
*.xsd.cs
|
||||
|
||||
# OpenCover UI analysis results
|
||||
OpenCover/
|
||||
|
||||
# Azure Stream Analytics local run output
|
||||
ASALocalRun/
|
||||
|
||||
# MSBuild Binary and Structured Log
|
||||
*.binlog
|
||||
|
||||
# NVidia Nsight GPU debugger configuration file
|
||||
*.nvuser
|
||||
|
||||
# MFractors (Xamarin productivity tool) working folder
|
||||
.mfractor/
|
||||
|
||||
# Local History for Visual Studio
|
||||
.localhistory/
|
||||
|
||||
# Visual Studio History (VSHistory) files
|
||||
.vshistory/
|
||||
|
||||
# BeatPulse healthcheck temp database
|
||||
healthchecksdb
|
||||
|
||||
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||
MigrationBackup/
|
||||
|
||||
# Ionide (cross platform F# VS Code tools) working folder
|
||||
.ionide/
|
||||
|
||||
# Fody - auto-generated XML schema
|
||||
FodyWeavers.xsd
|
||||
|
||||
# VS Code files for those working on multiple tools
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
*.code-workspace
|
||||
|
||||
# Local History for Visual Studio Code
|
||||
.history/
|
||||
|
||||
# Windows Installer files from build outputs
|
||||
*.cab
|
||||
*.msi
|
||||
*.msix
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
# JetBrains Rider
|
||||
*.sln.iml
|
||||
|
||||
# ---> VisualStudioCode
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
!.vscode/*.code-snippets
|
||||
|
||||
# Local History for Visual Studio Code
|
||||
.history/
|
||||
|
||||
# Built Visual Studio Code Extensions
|
||||
*.vsix
|
||||
|
||||
# ---> macOS
|
||||
# General
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
# ---> Linux
|
||||
*~
|
||||
|
||||
# temporary files which can be created if a process still has a handle open of a deleted file
|
||||
.fuse_hidden*
|
||||
|
||||
# KDE directory preferences
|
||||
.directory
|
||||
|
||||
# Linux trash folder which might appear on any partition or disk
|
||||
.Trash-*
|
||||
|
||||
# .nfs files are created when an open file is removed but is still being accessed
|
||||
.nfs*
|
||||
|
||||
# ---> Windows
|
||||
# Windows thumbnail cache files
|
||||
Thumbs.db
|
||||
Thumbs.db:encryptable
|
||||
ehthumbs.db
|
||||
ehthumbs_vista.db
|
||||
|
||||
# Dump file
|
||||
*.stackdump
|
||||
|
||||
# Folder config file
|
||||
[Dd]esktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Windows Installer files
|
||||
*.cab
|
||||
*.msi
|
||||
*.msix
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
# Windows shortcuts
|
||||
*.lnk
|
||||
49
CLAUDE.md
Normal file
49
CLAUDE.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Purpose
|
||||
|
||||
UnrealDocGenerator is a tool (in development) for generating documentation from Unreal Engine C++ header files. The `samples/` directory contains representative Unreal Engine headers used as test inputs:
|
||||
|
||||
- `samples/GeomUtils.h` — Small utility header (~6 KB): geometry/math helpers in `UE::AI` namespace
|
||||
- `samples/AIController.h` — Medium header (~22 KB): `AAIController` class with movement, behavior tree, perception, and blackboard integration
|
||||
- `samples/GameplayTagsManager.h` — Large/complex header (~43 KB): `UGameplayTagsManager` singleton with tag trees, INI/DataTable loading, and editor support
|
||||
|
||||
These samples cover a range of complexity (free functions, large classes, deeply nested types, macros, delegates) suitable for stress-testing a parser or doc generator.
|
||||
|
||||
## Current State
|
||||
|
||||
Implementation complete. Three Python files:
|
||||
- `ue_parser.py` — Parses UE headers into dataclasses
|
||||
- `ue_markdown.py` — Renders parsed data as Markdown (ultra-compact format, documented items only)
|
||||
- `generate.py` — CLI entry point; two-pass pipeline (parse-all → build type index → render-all)
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
python generate.py samples/ docs/ # process directory → docs/
|
||||
python generate.py samples/AIController.h docs/ # single file
|
||||
```
|
||||
|
||||
Output: one `.md` per `.h` + `docs/type-index.txt` (compact `TypeName: path/to/File.md` lookup).
|
||||
|
||||
Agent lookup pattern: `grep "^AController:" docs/type-index.txt` → instant single-line result.
|
||||
|
||||
## Architecture Notes
|
||||
|
||||
- Parser uses a **position-based scanner**, not line-by-line regex, to handle nested braces correctly.
|
||||
- `find_matching_close()` extracts balanced `{}`/`()` while skipping `//`, `/* */`, and string literals.
|
||||
- Only items with actual C++ doc comments (`/** */`) are included in output (`_has_doc()` filter).
|
||||
- Two-pass pipeline in `generate.py`: Pass 1 parses all files, Pass 2 renders with cross-reference links.
|
||||
- Inline cross-references: base class names linked to their `.md` when in corpus (`_make_type_link()`).
|
||||
- No "See also" section — inline links in `*Inherits*:` lines cover cross-file navigation.
|
||||
- Deprecated functions excluded from output (`not f.is_deprecated` in visibility filter).
|
||||
- Enums with no value descriptions use compact inline format (`Values: A, B, C`) instead of a table.
|
||||
- Placeholder descriptions (`------//`) filtered by `_PLACEHOLDER_RE` in `_fn_body()`.
|
||||
|
||||
## Critical Gotchas
|
||||
|
||||
- **Never** use `\s` inside a `[...]` character class with `*?` on C++ source — causes catastrophic regex backtracking (hangs on `AIController.h`). Use line-based scanning instead.
|
||||
- Identifier regex must be `r'\w+'`, **not** `r'[\w:~]+'` — the latter consumes `public:` as one token, breaking access specifier detection (all members become `private`).
|
||||
- Module name casing: use `_caps_to_camel()` (not `.title()`) — `AIMODULE.title()` → `Aimodule` instead of `AIModule`.
|
||||
135
generate.py
Normal file
135
generate.py
Normal file
@@ -0,0 +1,135 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
generate.py — CLI for UnrealDocGenerator.
|
||||
|
||||
Usage:
|
||||
python generate.py <input> <output_dir>
|
||||
|
||||
<input> can be a single .h file or a directory (processed recursively).
|
||||
|
||||
Two-pass pipeline:
|
||||
Pass 1 — parse every header, build a corpus-wide type index
|
||||
Pass 2 — render each header with cross-reference links injected
|
||||
"""
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
from ue_parser import parse_header, ParsedHeader
|
||||
from ue_markdown import render_header
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Type index
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def build_type_index(parsed_list: list[tuple[Path, ParsedHeader]],
|
||||
input_base: Path) -> dict[str, str]:
|
||||
"""
|
||||
Returns {TypeName: md_path_relative_to_docs_root} for every
|
||||
class, struct, enum, and delegate in the corpus.
|
||||
"""
|
||||
index: dict[str, str] = {}
|
||||
for h, parsed in parsed_list:
|
||||
try:
|
||||
rel = h.relative_to(input_base)
|
||||
except ValueError:
|
||||
rel = Path(h.name)
|
||||
md_rel = str(rel.with_suffix('.md'))
|
||||
|
||||
for ci in parsed.classes:
|
||||
index[ci.name] = md_rel
|
||||
for ei in parsed.enums:
|
||||
index[ei.name] = md_rel
|
||||
for di in parsed.delegates:
|
||||
index[di.name] = md_rel
|
||||
# namespace names are not types — skip
|
||||
return index
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Type index file
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def write_type_index(type_index: dict[str, str], output_dir: Path) -> None:
|
||||
"""
|
||||
Write docs/type-index.txt — compact TypeName: path/to/File.md lookup.
|
||||
One entry per line, alphabetically sorted. Agents can grep this file
|
||||
to resolve a type name to its documentation path.
|
||||
"""
|
||||
_valid_name = re.compile(r'^[A-Za-z_][A-Za-z0-9_]*$')
|
||||
lines = sorted(f"{name}: {path}" for name, path in type_index.items()
|
||||
if _valid_name.match(name))
|
||||
out = output_dir / "type-index.txt"
|
||||
out.write_text('\n'.join(lines) + '\n', encoding='utf-8')
|
||||
print(f"Written {out}")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Main
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 3:
|
||||
print("Usage: python generate.py <input> <output_dir>", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
input_arg = Path(sys.argv[1])
|
||||
output_dir = Path(sys.argv[2])
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Collect input files
|
||||
if input_arg.is_file():
|
||||
headers = [input_arg]
|
||||
input_base = input_arg.parent
|
||||
elif input_arg.is_dir():
|
||||
headers = sorted(input_arg.rglob('*.h'))
|
||||
input_base = input_arg
|
||||
else:
|
||||
print(f"Error: {input_arg} is not a file or directory", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
if not headers:
|
||||
print("No .h files found.", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# --- Pass 1: parse all ---
|
||||
parsed_list: list[tuple[Path, ParsedHeader]] = []
|
||||
for h in headers:
|
||||
print(f"Parsing {h} ...")
|
||||
try:
|
||||
parsed = parse_header(str(h))
|
||||
parsed_list.append((h, parsed))
|
||||
except Exception as exc:
|
||||
print(f" ERROR parsing {h}: {exc}", file=sys.stderr)
|
||||
|
||||
# --- Build corpus-wide type index ---
|
||||
type_index = build_type_index(parsed_list, input_base)
|
||||
|
||||
# --- Pass 2: render all ---
|
||||
success = 0
|
||||
for h, parsed in parsed_list:
|
||||
print(f"Rendering {h} ...")
|
||||
try:
|
||||
rel = h.relative_to(input_base)
|
||||
except ValueError:
|
||||
rel = Path(h.name)
|
||||
|
||||
current_md = str(rel.with_suffix('.md'))
|
||||
out_path = output_dir / current_md
|
||||
out_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
try:
|
||||
md = render_header(parsed, type_index=type_index, current_md=current_md)
|
||||
out_path.write_text(md, encoding='utf-8')
|
||||
success += 1
|
||||
except Exception as exc:
|
||||
print(f" ERROR rendering {h}: {exc}", file=sys.stderr)
|
||||
|
||||
write_type_index(type_index, output_dir)
|
||||
print(f"\nGenerated {success}/{len(parsed_list)} files + type-index.txt in {output_dir}/")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
465
samples/AIController.h
Normal file
465
samples/AIController.h
Normal file
@@ -0,0 +1,465 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/ObjectMacros.h"
|
||||
#include "UObject/UObjectGlobals.h"
|
||||
#include "Templates/SubclassOf.h"
|
||||
#include "EngineDefines.h"
|
||||
#if UE_ENABLE_INCLUDE_ORDER_DEPRECATED_IN_5_4
|
||||
#include "NavFilters/NavigationQueryFilter.h"
|
||||
#endif
|
||||
#include "AITypes.h"
|
||||
#include "GameplayTaskOwnerInterface.h"
|
||||
#include "GameplayTask.h"
|
||||
#include "GameFramework/Pawn.h"
|
||||
#include "GameFramework/Controller.h"
|
||||
#include "Perception/AIPerceptionListenerInterface.h"
|
||||
#include "GenericTeamAgentInterface.h"
|
||||
#include "VisualLogger/VisualLoggerDebugSnapshotInterface.h"
|
||||
#include "AIController.generated.h"
|
||||
|
||||
class FDebugDisplayInfo;
|
||||
class UAIPerceptionComponent;
|
||||
class UBehaviorTree;
|
||||
class UBlackboardComponent;
|
||||
class UBlackboardData;
|
||||
class UBrainComponent;
|
||||
class UCanvas;
|
||||
class UGameplayTaskResource;
|
||||
class UGameplayTasksComponent;
|
||||
class UPathFollowingComponent;
|
||||
|
||||
namespace EPathFollowingRequestResult { enum Type : int; }
|
||||
namespace EPathFollowingResult { enum Type : int; }
|
||||
namespace EPathFollowingStatus { enum Type : int; }
|
||||
|
||||
#if ENABLE_VISUAL_LOG
|
||||
struct FVisualLogEntry;
|
||||
#endif // ENABLE_VISUAL_LOG
|
||||
struct FPathFindingQuery;
|
||||
struct FPathFollowingRequestResult;
|
||||
struct FPathFollowingResult;
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FAIMoveCompletedSignature, FAIRequestID, RequestID, EPathFollowingResult::Type, Result);
|
||||
|
||||
// the reason for this being namespace instead of a regular enum is
|
||||
// so that it can be expanded in game-specific code
|
||||
// @todo this is a bit messy, needs to be refactored
|
||||
namespace EAIFocusPriority
|
||||
{
|
||||
typedef uint8 Type;
|
||||
|
||||
inline const Type Default = 0;
|
||||
inline const Type Move = 1;
|
||||
inline const Type Gameplay = 2;
|
||||
|
||||
inline const Type LastFocusPriority = Gameplay;
|
||||
}
|
||||
|
||||
struct FFocusKnowledge
|
||||
{
|
||||
struct FFocusItem
|
||||
{
|
||||
TWeakObjectPtr<AActor> Actor;
|
||||
FVector Position;
|
||||
|
||||
FFocusItem()
|
||||
{
|
||||
Actor = nullptr;
|
||||
Position = FAISystem::InvalidLocation;
|
||||
}
|
||||
};
|
||||
|
||||
TArray<FFocusItem> Priorities;
|
||||
};
|
||||
|
||||
//~=============================================================================
|
||||
/**
|
||||
* AIController is the base class of controllers for AI-controlled Pawns.
|
||||
*
|
||||
* Controllers are non-physical actors that can be attached to a pawn to control its actions.
|
||||
* AIControllers manage the artificial intelligence for the pawns they control.
|
||||
* In networked games, they only exist on the server.
|
||||
*
|
||||
* @see https://docs.unrealengine.com/latest/INT/Gameplay/Framework/Controller/
|
||||
*/
|
||||
|
||||
UCLASS(ClassGroup = AI, BlueprintType, Blueprintable, MinimalAPI)
|
||||
class AAIController : public AController, public IAIPerceptionListenerInterface, public IGameplayTaskOwnerInterface, public IGenericTeamAgentInterface, public IVisualLoggerDebugSnapshotInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
FGameplayResourceSet ScriptClaimedResources;
|
||||
protected:
|
||||
FFocusKnowledge FocusInformation;
|
||||
|
||||
/** By default AI's logic does not start when controlled Pawn is possessed. Setting this flag to true
|
||||
* will make AI logic start when pawn is possessed */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AI)
|
||||
uint32 bStartAILogicOnPossess : 1;
|
||||
|
||||
/** By default AI's logic gets stopped when controlled Pawn is unpossessed. Setting this flag to false
|
||||
* will make AI logic persist past losing control over a pawn */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AI)
|
||||
uint32 bStopAILogicOnUnposses : 1;
|
||||
|
||||
public:
|
||||
/** used for alternating LineOfSight traces */
|
||||
UPROPERTY()
|
||||
mutable uint32 bLOSflag : 1;
|
||||
|
||||
/** Skip extra line of sight traces to extremities of target being checked. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AI)
|
||||
uint32 bSkipExtraLOSChecks : 1;
|
||||
|
||||
/** Is strafing allowed during movement? */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AI)
|
||||
uint32 bAllowStrafe : 1;
|
||||
|
||||
/** Specifies if this AI wants its own PlayerState. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AI)
|
||||
uint32 bWantsPlayerState : 1;
|
||||
|
||||
/** Copy Pawn rotation to ControlRotation, if there is no focus point. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AI)
|
||||
uint32 bSetControlRotationFromPawnOrientation:1;
|
||||
|
||||
private:
|
||||
|
||||
/** Component used for moving along a path. */
|
||||
UPROPERTY(VisibleDefaultsOnly, Category = AI)
|
||||
TObjectPtr<UPathFollowingComponent> PathFollowingComponent;
|
||||
|
||||
public:
|
||||
|
||||
/** Component responsible for behaviors. */
|
||||
UPROPERTY(BlueprintReadWrite, Category = AI)
|
||||
TObjectPtr<UBrainComponent> BrainComponent;
|
||||
|
||||
UPROPERTY(VisibleDefaultsOnly, Category = AI)
|
||||
TObjectPtr<UAIPerceptionComponent> PerceptionComponent;
|
||||
|
||||
protected:
|
||||
/** blackboard */
|
||||
UPROPERTY(BlueprintReadOnly, Category = AI, meta = (AllowPrivateAccess = "true"))
|
||||
TObjectPtr<UBlackboardComponent> Blackboard;
|
||||
|
||||
UPROPERTY()
|
||||
TObjectPtr<UGameplayTasksComponent> CachedGameplayTasksComponent;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AI)
|
||||
TSubclassOf<UNavigationQueryFilter> DefaultNavigationFilterClass;
|
||||
|
||||
public:
|
||||
|
||||
AIMODULE_API AAIController(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
|
||||
|
||||
AIMODULE_API virtual void SetPawn(APawn* InPawn) override;
|
||||
|
||||
/** Makes AI go toward specified Goal actor (destination will be continuously updated), aborts any active path following
|
||||
* @param AcceptanceRadius - finish move if pawn gets close enough
|
||||
* @param bStopOnOverlap - add pawn's radius to AcceptanceRadius
|
||||
* @param bUsePathfinding - use navigation data to calculate path (otherwise it will go in straight line)
|
||||
* @param bCanStrafe - set focus related flag: bAllowStrafe
|
||||
* @param FilterClass - navigation filter for pathfinding adjustments. If none specified DefaultNavigationFilterClass will be used
|
||||
* @param bAllowPartialPath - use incomplete path when goal can't be reached
|
||||
* @note AcceptanceRadius has default value or -1 due to Header Parser not being able to recognize UPathFollowingComponent::DefaultAcceptanceRadius
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "AI|Navigation", Meta = (AdvancedDisplay = "bStopOnOverlap,bCanStrafe,bAllowPartialPath"))
|
||||
AIMODULE_API EPathFollowingRequestResult::Type MoveToActor(AActor* Goal, float AcceptanceRadius = -1, bool bStopOnOverlap = true,
|
||||
bool bUsePathfinding = true, bool bCanStrafe = true,
|
||||
TSubclassOf<UNavigationQueryFilter> FilterClass = {}, bool bAllowPartialPath = true);
|
||||
|
||||
/** Makes AI go toward specified Dest location, aborts any active path following
|
||||
* @param AcceptanceRadius - finish move if pawn gets close enough
|
||||
* @param bStopOnOverlap - add pawn's radius to AcceptanceRadius
|
||||
* @param bUsePathfinding - use navigation data to calculate path (otherwise it will go in straight line)
|
||||
* @param bProjectDestinationToNavigation - project location on navigation data before using it
|
||||
* @param bCanStrafe - set focus related flag: bAllowStrafe
|
||||
* @param FilterClass - navigation filter for pathfinding adjustments. If none specified DefaultNavigationFilterClass will be used
|
||||
* @param bAllowPartialPath - use incomplete path when goal can't be reached
|
||||
* @note AcceptanceRadius has default value or -1 due to Header Parser not being able to recognize UPathFollowingComponent::DefaultAcceptanceRadius
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "AI|Navigation", Meta = (AdvancedDisplay = "bStopOnOverlap,bCanStrafe,bAllowPartialPath"))
|
||||
AIMODULE_API EPathFollowingRequestResult::Type MoveToLocation(const FVector& Dest, float AcceptanceRadius = -1, bool bStopOnOverlap = true,
|
||||
bool bUsePathfinding = true, bool bProjectDestinationToNavigation = false, bool bCanStrafe = true,
|
||||
TSubclassOf<UNavigationQueryFilter> FilterClass = {}, bool bAllowPartialPath = true);
|
||||
|
||||
/** Makes AI go toward specified destination
|
||||
* @param MoveRequest - details about move
|
||||
* @param OutPath - optional output param, filled in with assigned path
|
||||
* @return struct holding MoveId and enum code
|
||||
*/
|
||||
AIMODULE_API virtual FPathFollowingRequestResult MoveTo(const FAIMoveRequest& MoveRequest, FNavPathSharedPtr* OutPath = nullptr);
|
||||
|
||||
/** Passes move request and path object to path following */
|
||||
AIMODULE_API virtual FAIRequestID RequestMove(const FAIMoveRequest& MoveRequest, FNavPathSharedPtr Path);
|
||||
|
||||
/** Finds path for given move request
|
||||
* @param MoveRequest - details about move
|
||||
* @param Query - pathfinding query for navigation system
|
||||
* @param OutPath - generated path
|
||||
*/
|
||||
AIMODULE_API virtual void FindPathForMoveRequest(const FAIMoveRequest& MoveRequest, FPathFindingQuery& Query, FNavPathSharedPtr& OutPath) const;
|
||||
|
||||
/** Helper function for creating pathfinding query for this agent from move request data */
|
||||
AIMODULE_API bool BuildPathfindingQuery(const FAIMoveRequest& MoveRequest, FPathFindingQuery& OutQuery) const;
|
||||
|
||||
/** Helper function for creating pathfinding query for this agent from move request data and starting location */
|
||||
AIMODULE_API bool BuildPathfindingQuery(const FAIMoveRequest& MoveRequest, const FVector& StartLocation, FPathFindingQuery& OutQuery) const;
|
||||
|
||||
UE_DEPRECATED_FORGAME(4.13, "This function is now deprecated, please use FindPathForMoveRequest() for adjusting Query or BuildPathfindingQuery() for getting one.")
|
||||
AIMODULE_API virtual bool PreparePathfinding(const FAIMoveRequest& MoveRequest, FPathFindingQuery& Query);
|
||||
|
||||
UE_DEPRECATED_FORGAME(4.13, "This function is now deprecated, please use FindPathForMoveRequest() for adjusting pathfinding or path postprocess.")
|
||||
AIMODULE_API virtual FAIRequestID RequestPathAndMove(const FAIMoveRequest& MoveRequest, FPathFindingQuery& Query);
|
||||
|
||||
/** if AI is currently moving due to request given by RequestToPause, then the move will be paused */
|
||||
AIMODULE_API bool PauseMove(FAIRequestID RequestToPause);
|
||||
|
||||
/** resumes last AI-performed, paused request provided it's ID was equivalent to RequestToResume */
|
||||
AIMODULE_API bool ResumeMove(FAIRequestID RequestToResume);
|
||||
|
||||
/** Aborts the move the controller is currently performing */
|
||||
AIMODULE_API virtual void StopMovement() override;
|
||||
|
||||
/** Called on completing current movement request */
|
||||
AIMODULE_API virtual void OnMoveCompleted(FAIRequestID RequestID, const FPathFollowingResult& Result);
|
||||
|
||||
UE_DEPRECATED_FORGAME(4.13, "This function is now deprecated, please use version with EPathFollowingResultDetails parameter.")
|
||||
AIMODULE_API virtual void OnMoveCompleted(FAIRequestID RequestID, EPathFollowingResult::Type Result);
|
||||
|
||||
/** Returns the Move Request ID for the current move */
|
||||
AIMODULE_API FAIRequestID GetCurrentMoveRequestID() const;
|
||||
|
||||
/** Blueprint notification that we've completed the current movement request */
|
||||
UPROPERTY(BlueprintAssignable, meta = (DisplayName = "MoveCompleted"))
|
||||
FAIMoveCompletedSignature ReceiveMoveCompleted;
|
||||
|
||||
TSubclassOf<UNavigationQueryFilter> GetDefaultNavigationFilterClass() const { return DefaultNavigationFilterClass; }
|
||||
|
||||
/** Returns status of path following */
|
||||
UFUNCTION(BlueprintCallable, Category = "AI|Navigation")
|
||||
AIMODULE_API EPathFollowingStatus::Type GetMoveStatus() const;
|
||||
|
||||
/** Returns true if the current PathFollowingComponent's path is partial (does not reach desired destination). */
|
||||
UFUNCTION(BlueprintCallable, Category = "AI|Navigation")
|
||||
AIMODULE_API bool HasPartialPath() const;
|
||||
|
||||
/** Returns position of current path segment's end. */
|
||||
UFUNCTION(BlueprintCallable, Category = "AI|Navigation")
|
||||
AIMODULE_API FVector GetImmediateMoveDestination() const;
|
||||
|
||||
/** Updates state of movement block detection. */
|
||||
UFUNCTION(BlueprintCallable, Category = "AI|Navigation")
|
||||
AIMODULE_API void SetMoveBlockDetection(bool bEnable);
|
||||
|
||||
/** Starts executing behavior tree. */
|
||||
UFUNCTION(BlueprintCallable, Category = "AI")
|
||||
AIMODULE_API virtual bool RunBehaviorTree(UBehaviorTree* BTAsset);
|
||||
|
||||
protected:
|
||||
AIMODULE_API virtual void CleanupBrainComponent();
|
||||
|
||||
/** Merges the remaining points of InitialPath, with the points of InOutMergedPath. The resulting merged path is outputted into InOutMergedPath. */
|
||||
AIMODULE_API virtual void MergePaths(const FNavPathSharedPtr& InitialPath, FNavPathSharedPtr& InOutMergedPath) const;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Makes AI use the specified Blackboard asset & creates a Blackboard Component if one does not already exist.
|
||||
* @param BlackboardAsset The Blackboard asset to use.
|
||||
* @param BlackboardComponent The Blackboard component that was used or created to work with the passed-in Blackboard Asset.
|
||||
* @return true if we successfully linked the blackboard asset to the blackboard component.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "AI")
|
||||
AIMODULE_API bool UseBlackboard(UBlackboardData* BlackboardAsset, UBlackboardComponent*& BlackboardComponent);
|
||||
|
||||
/** does this AIController allow given UBlackboardComponent sync data with it */
|
||||
AIMODULE_API virtual bool ShouldSyncBlackboardWith(const UBlackboardComponent& OtherBlackboardComponent) const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "AI|Tasks")
|
||||
AIMODULE_API void ClaimTaskResource(TSubclassOf<UGameplayTaskResource> ResourceClass);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "AI|Tasks")
|
||||
AIMODULE_API void UnclaimTaskResource(TSubclassOf<UGameplayTaskResource> ResourceClass);
|
||||
|
||||
protected:
|
||||
UFUNCTION(BlueprintImplementableEvent)
|
||||
AIMODULE_API void OnUsingBlackBoard(UBlackboardComponent* BlackboardComp, UBlackboardData* BlackboardAsset);
|
||||
|
||||
AIMODULE_API virtual bool InitializeBlackboard(UBlackboardComponent& BlackboardComp, UBlackboardData& BlackboardAsset);
|
||||
|
||||
public:
|
||||
/** Retrieve the final position that controller should be looking at. */
|
||||
UFUNCTION(BlueprintCallable, Category = "AI")
|
||||
AIMODULE_API FVector GetFocalPoint() const;
|
||||
|
||||
AIMODULE_API FVector GetFocalPointForPriority(EAIFocusPriority::Type InPriority) const;
|
||||
|
||||
/** Retrieve the focal point this controller should focus to on given actor. */
|
||||
UFUNCTION(BlueprintCallable, Category = "AI")
|
||||
AIMODULE_API virtual FVector GetFocalPointOnActor(const AActor *Actor) const;
|
||||
|
||||
/** Set the position that controller should be looking at. */
|
||||
UFUNCTION(BlueprintCallable, Category = "AI", meta = (DisplayName = "SetFocalPoint", ScriptName = "SetFocalPoint", Keywords = "focus"))
|
||||
AIMODULE_API void K2_SetFocalPoint(FVector FP);
|
||||
|
||||
/** Set Focus for actor, will set FocalPoint as a result. */
|
||||
UFUNCTION(BlueprintCallable, Category = "AI", meta = (DisplayName = "SetFocus", ScriptName = "SetFocus"))
|
||||
AIMODULE_API void K2_SetFocus(AActor* NewFocus);
|
||||
|
||||
/** Get the focused actor. */
|
||||
UFUNCTION(BlueprintCallable, Category = "AI")
|
||||
AIMODULE_API AActor* GetFocusActor() const;
|
||||
|
||||
inline AActor* GetFocusActorForPriority(EAIFocusPriority::Type InPriority) const { return FocusInformation.Priorities.IsValidIndex(InPriority) ? FocusInformation.Priorities[InPriority].Actor.Get() : nullptr; }
|
||||
|
||||
/** Clears Focus, will also clear FocalPoint as a result */
|
||||
UFUNCTION(BlueprintCallable, Category = "AI", meta = (DisplayName = "ClearFocus", ScriptName = "ClearFocus"))
|
||||
AIMODULE_API void K2_ClearFocus();
|
||||
|
||||
|
||||
/**
|
||||
* Computes a launch velocity vector to toss a projectile and hit the given destination.
|
||||
* Performance note: Potentially expensive. Nonzero CollisionRadius and bOnlyTraceUp=false are the more expensive options.
|
||||
*
|
||||
* @param OutTossVelocity - out param stuffed with the computed velocity to use
|
||||
* @param Start - desired start point of arc
|
||||
* @param End - desired end point of arc
|
||||
* @param TossSpeed - Initial speed of the theoretical projectile. Assumed to only change due to gravity for the entire lifetime of the projectile
|
||||
* @param CollisionSize (optional) - is the size of bounding box of the tossed actor (defaults to (0,0,0)
|
||||
* @param bOnlyTraceUp (optional) - when true collision checks verifying the arc will only be done along the upward portion of the arc
|
||||
* @return - true if a valid arc was computed, false if no valid solution could be found
|
||||
*/
|
||||
AIMODULE_API bool SuggestTossVelocity(FVector& OutTossVelocity, FVector Start, FVector End, float TossSpeed, bool bPreferHighArc, float CollisionRadius = 0, bool bOnlyTraceUp = false);
|
||||
|
||||
//~ Begin AActor Interface
|
||||
AIMODULE_API virtual void Tick(float DeltaTime) override;
|
||||
AIMODULE_API virtual void PostInitializeComponents() override;
|
||||
AIMODULE_API virtual void PostRegisterAllComponents() override;
|
||||
//~ End AActor Interface
|
||||
|
||||
//~ Begin AController Interface
|
||||
protected:
|
||||
AIMODULE_API virtual void OnPossess(APawn* InPawn) override;
|
||||
AIMODULE_API virtual void OnUnPossess() override;
|
||||
|
||||
public:
|
||||
AIMODULE_API virtual bool ShouldPostponePathUpdates() const override;
|
||||
AIMODULE_API virtual void DisplayDebug(UCanvas* Canvas, const FDebugDisplayInfo& DebugDisplay, float& YL, float& YPos) override;
|
||||
|
||||
#if ENABLE_VISUAL_LOG
|
||||
AIMODULE_API virtual void GrabDebugSnapshot(FVisualLogEntry* Snapshot) const override;
|
||||
#endif
|
||||
|
||||
AIMODULE_API virtual void Reset() override;
|
||||
|
||||
/**
|
||||
* Checks line to center and top of other actor
|
||||
* @param Other is the actor whose visibility is being checked.
|
||||
* @param ViewPoint is eye position visibility is being checked from. If vect(0,0,0) passed in, uses current viewtarget's eye position.
|
||||
* @param bAlternateChecks used only in AIController implementation
|
||||
* @return true if controller's pawn can see Other actor.
|
||||
*/
|
||||
AIMODULE_API virtual bool LineOfSightTo(const AActor* Other, FVector ViewPoint = FVector(ForceInit), bool bAlternateChecks = false) const override;
|
||||
//~ End AController Interface
|
||||
|
||||
/** Notifies AIController of changes in given actors' perception */
|
||||
AIMODULE_API virtual void ActorsPerceptionUpdated(const TArray<AActor*>& UpdatedActors);
|
||||
|
||||
/** Update direction AI is looking based on FocalPoint */
|
||||
AIMODULE_API virtual void UpdateControlRotation(float DeltaTime, bool bUpdatePawn = true);
|
||||
|
||||
/** Set FocalPoint for given priority as absolute position or offset from base. */
|
||||
AIMODULE_API virtual void SetFocalPoint(FVector NewFocus, EAIFocusPriority::Type InPriority = EAIFocusPriority::Gameplay);
|
||||
|
||||
/* Set Focus actor for given priority, will set FocalPoint as a result. */
|
||||
AIMODULE_API virtual void SetFocus(AActor* NewFocus, EAIFocusPriority::Type InPriority = EAIFocusPriority::Gameplay);
|
||||
|
||||
/** Clears Focus for given priority, will also clear FocalPoint as a result
|
||||
* @param InPriority focus priority to clear. If you don't know what to use you probably mean EAIFocusPriority::Gameplay*/
|
||||
AIMODULE_API virtual void ClearFocus(EAIFocusPriority::Type InPriority);
|
||||
|
||||
AIMODULE_API void SetPerceptionComponent(UAIPerceptionComponent& InPerceptionComponent);
|
||||
//----------------------------------------------------------------------//
|
||||
// IAIPerceptionListenerInterface
|
||||
//----------------------------------------------------------------------//
|
||||
virtual UAIPerceptionComponent* GetPerceptionComponent() override { return GetAIPerceptionComponent(); }
|
||||
|
||||
//----------------------------------------------------------------------//
|
||||
// INavAgentInterface
|
||||
//----------------------------------------------------------------------//
|
||||
AIMODULE_API virtual bool IsFollowingAPath() const override;
|
||||
AIMODULE_API virtual IPathFollowingAgentInterface* GetPathFollowingAgent() const override;
|
||||
|
||||
//----------------------------------------------------------------------//
|
||||
// IGenericTeamAgentInterface
|
||||
//----------------------------------------------------------------------//
|
||||
private:
|
||||
FGenericTeamId TeamID;
|
||||
public:
|
||||
AIMODULE_API virtual void SetGenericTeamId(const FGenericTeamId& NewTeamID) override;
|
||||
virtual FGenericTeamId GetGenericTeamId() const override { return TeamID; }
|
||||
|
||||
//----------------------------------------------------------------------//
|
||||
// IGameplayTaskOwnerInterface
|
||||
//----------------------------------------------------------------------//
|
||||
virtual UGameplayTasksComponent* GetGameplayTasksComponent(const UGameplayTask& Task) const override { return GetGameplayTasksComponent(); }
|
||||
virtual AActor* GetGameplayTaskOwner(const UGameplayTask* Task) const override { return const_cast<AAIController*>(this); }
|
||||
virtual AActor* GetGameplayTaskAvatar(const UGameplayTask* Task) const override { return GetPawn(); }
|
||||
virtual uint8 GetGameplayTaskDefaultPriority() const { return FGameplayTasks::DefaultPriority - 1; }
|
||||
|
||||
inline UGameplayTasksComponent* GetGameplayTasksComponent() const { return CachedGameplayTasksComponent; }
|
||||
|
||||
// add empty overrides to fix linker errors if project implements a child class without adding GameplayTasks module dependency
|
||||
virtual void OnGameplayTaskInitialized(UGameplayTask& Task) override {}
|
||||
virtual void OnGameplayTaskActivated(UGameplayTask& Task) override {}
|
||||
virtual void OnGameplayTaskDeactivated(UGameplayTask& Task) override {}
|
||||
|
||||
UFUNCTION()
|
||||
AIMODULE_API virtual void OnGameplayTaskResourcesClaimed(FGameplayResourceSet NewlyClaimed, FGameplayResourceSet FreshlyReleased);
|
||||
|
||||
//----------------------------------------------------------------------//
|
||||
// debug/dev-time
|
||||
//----------------------------------------------------------------------//
|
||||
AIMODULE_API virtual FString GetDebugIcon() const;
|
||||
|
||||
// Cheat/debugging functions
|
||||
static void ToggleAIIgnorePlayers() { bAIIgnorePlayers = !bAIIgnorePlayers; }
|
||||
static bool AreAIIgnoringPlayers() { return bAIIgnorePlayers; }
|
||||
|
||||
/** If true, AI controllers will ignore players. */
|
||||
static AIMODULE_API bool bAIIgnorePlayers;
|
||||
|
||||
public:
|
||||
/** Returns PathFollowingComponent subobject **/
|
||||
UFUNCTION(BlueprintCallable, Category="AI|Navigation")
|
||||
UPathFollowingComponent* GetPathFollowingComponent() const { return PathFollowingComponent; }
|
||||
UFUNCTION(BlueprintPure, Category = "AI|Perception")
|
||||
UAIPerceptionComponent* GetAIPerceptionComponent() { return PerceptionComponent; }
|
||||
|
||||
const UAIPerceptionComponent* GetAIPerceptionComponent() const { return PerceptionComponent; }
|
||||
|
||||
UBrainComponent* GetBrainComponent() const { return BrainComponent; }
|
||||
const UBlackboardComponent* GetBlackboardComponent() const { return Blackboard; }
|
||||
UBlackboardComponent* GetBlackboardComponent() { return Blackboard; }
|
||||
|
||||
/** Note that this function does not do any pathfollowing state transfer.
|
||||
* Intended to be called as part of initialization/setup process */
|
||||
UFUNCTION(BlueprintCallable, Category = "AI|Navigation")
|
||||
AIMODULE_API void SetPathFollowingComponent(UPathFollowingComponent* NewPFComponent);
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------//
|
||||
// forceinlines
|
||||
//----------------------------------------------------------------------//
|
||||
namespace FAISystem
|
||||
{
|
||||
inline bool IsValidControllerAndHasValidPawn(const AController* Controller)
|
||||
{
|
||||
return Controller != nullptr && Controller->IsPendingKillPending() == false
|
||||
&& Controller->GetPawn() != nullptr && Controller->GetPawn()->IsPendingKillPending() == false;
|
||||
}
|
||||
}
|
||||
1010
samples/GameplayTagsManager.h
Normal file
1010
samples/GameplayTagsManager.h
Normal file
File diff suppressed because it is too large
Load Diff
175
samples/GeomUtils.h
Normal file
175
samples/GeomUtils.h
Normal file
@@ -0,0 +1,175 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Containers/ContainersFwd.h"
|
||||
#include "Math/UnrealMathSSE.h"
|
||||
#include "Math/Vector.h"
|
||||
#include "Math/Vector2D.h"
|
||||
|
||||
namespace UE::AI
|
||||
{
|
||||
/** @return 2D cross product. Using X and Y components of 3D vectors. */
|
||||
inline FVector::FReal Cross2D(const FVector& A, const FVector& B)
|
||||
{
|
||||
return A.X * B.Y - A.Y * B.X;
|
||||
}
|
||||
|
||||
/** @return 2D cross product. */
|
||||
inline FVector2D::FReal Cross2D(const FVector2D& A, const FVector2D& B)
|
||||
{
|
||||
return A.X * B.Y - A.Y * B.X;
|
||||
}
|
||||
|
||||
/** @return 2D area of triangle. Using X and Y components of 3D vectors. */
|
||||
inline FVector::FReal TriArea2D(const FVector& A, const FVector& B, const FVector& C)
|
||||
{
|
||||
const FVector AB = B - A;
|
||||
const FVector AC = C - A;
|
||||
return (AC.X * AB.Y - AB.X * AC.Y) * 0.5;
|
||||
}
|
||||
|
||||
/** @return 2D area of triangle. */
|
||||
inline FVector2D::FReal TriArea2D(const FVector2D& A, const FVector2D& B, const FVector2D& C)
|
||||
{
|
||||
const FVector2D AB = B - A;
|
||||
const FVector2D AC = C - A;
|
||||
return (AC.X * AB.Y - AB.X * AC.Y) * 0.5;
|
||||
}
|
||||
|
||||
/** @return value in range [0..1] of the 'Point' project on segment 'Start-End'. Using X and Y components of 3D vectors. */
|
||||
inline FVector2D::FReal ProjectPointOnSegment2D(const FVector Point, const FVector Start, const FVector End)
|
||||
{
|
||||
using FReal = FVector::FReal;
|
||||
|
||||
const FVector2D Seg(End - Start);
|
||||
const FVector2D Dir(Point - Start);
|
||||
const FReal D = Seg.SquaredLength();
|
||||
const FReal T = FVector2D::DotProduct(Seg, Dir);
|
||||
|
||||
if (T < 0.0)
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
else if (T > D)
|
||||
{
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
return D > UE_KINDA_SMALL_NUMBER ? (T / D) : 0.0;
|
||||
}
|
||||
|
||||
/** @return value of the 'Point' project on infinite line defined by segment 'Start-End'. Using X and Y components of 3D vectors. */
|
||||
inline FVector::FReal ProjectPointOnLine2D(const FVector Point, const FVector Start, const FVector End)
|
||||
{
|
||||
using FReal = FVector::FReal;
|
||||
|
||||
const FVector2D Seg(End - Start);
|
||||
const FVector2D Dir(Point - Start);
|
||||
const FReal D = Seg.SquaredLength();
|
||||
const FReal T = FVector2D::DotProduct(Seg, Dir);
|
||||
return D > UE_KINDA_SMALL_NUMBER ? (T / D) : 0.0;
|
||||
}
|
||||
|
||||
/** @return signed distance of the 'Point' to infinite line defined by segment 'Start-End'. Using X and Y components of 3D vectors. */
|
||||
inline FVector::FReal SignedDistancePointLine2D(const FVector Point, const FVector Start, const FVector End)
|
||||
{
|
||||
using FReal = FVector::FReal;
|
||||
|
||||
const FVector2D Seg(End - Start);
|
||||
const FVector2D Dir(Point - Start);
|
||||
const FReal Nom = Cross2D(Seg, Dir);
|
||||
const FReal Den = Seg.SquaredLength();
|
||||
const FReal Dist = Den > UE_KINDA_SMALL_NUMBER ? (Nom / FMath::Sqrt(Den)) : 0.0;
|
||||
return Dist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Intersects infinite lines defined by segments A and B in 2D. Using X and Y components of 3D vectors.
|
||||
* @param StartA start point of segment A
|
||||
* @param EndA end point of segment A
|
||||
* @param StartB start point of segment B
|
||||
* @param EndB end point of segment B
|
||||
* @param OutTA intersection value along segment A
|
||||
* @param OutTB intersection value along segment B
|
||||
* @return if segments A and B intersect in 2D
|
||||
*/
|
||||
inline bool IntersectLineLine2D(const FVector& StartA, const FVector& EndA, const FVector& StartB, const FVector& EndB, FVector2D::FReal& OutTA, FVector2D::FReal& OutTB)
|
||||
{
|
||||
using FReal = FVector::FReal;
|
||||
|
||||
const FVector U = EndA - StartA;
|
||||
const FVector V = EndB - StartB;
|
||||
const FVector W = StartA - StartB;
|
||||
|
||||
const FReal D = Cross2D(U, V);
|
||||
if (FMath::Abs(D) < UE_KINDA_SMALL_NUMBER)
|
||||
{
|
||||
OutTA = 0.0;
|
||||
OutTB = 0.0;
|
||||
return false;
|
||||
}
|
||||
|
||||
OutTA = Cross2D(V, W) / D;
|
||||
OutTB = Cross2D(U, W) / D;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates intersection of segment Start-End with convex polygon Poly in 2D. Using X and Y components of 3D vectors.
|
||||
* @param Start start point of the segment
|
||||
* @param End end point of the segment
|
||||
* @param Poly convex polygon
|
||||
* @param OutTMin value along the segment of the first intersection point [0..1]
|
||||
* @param OutTMax value along the segment of the second intersection point [0..1]
|
||||
* @param OutSegMin index of the polygon segment of the first intersection point
|
||||
* @param OutSegMax index of the polygon segment of the second intersection point
|
||||
* @return true if the segment inside or intersects with the polygon.
|
||||
*/
|
||||
extern AIMODULE_API bool IntersectSegmentPoly2D(const FVector& Start, const FVector& End, TConstArrayView<FVector> Poly,
|
||||
FVector2D::FReal& OutTMin, FVector2D::FReal& OutTMax, int32& OutSegMin, int32& OutSegMax);
|
||||
|
||||
/**
|
||||
* Interpolates bilinear patch A,B,C,D. U interpolates from A->B, and C->D, and V interpolates from AB->CD.
|
||||
* @param UV interpolation coordinates [0..1] range
|
||||
* @param VertexA first corner
|
||||
* @param VertexB second corner
|
||||
* @param VertexC third corner
|
||||
* @param VertexD fourth corner
|
||||
* @return interpolated value.
|
||||
*/
|
||||
inline FVector Bilinear(const FVector2D UV, const FVector VertexA, const FVector VertexB, const FVector VertexC, const FVector VertexD)
|
||||
{
|
||||
const FVector AB = FMath::Lerp(VertexA, VertexB, UV.X);
|
||||
const FVector CD = FMath::Lerp(VertexD, VertexC, UV.X);
|
||||
return FMath::Lerp(AB, CD, UV.Y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the UV coordinates of the 'Point' on bilinear patch A,B,C,D. U interpolates from A->B, and C->D, and V interpolates from AB->CD.
|
||||
* @param Point location inside or close to the bilinear patch
|
||||
* @param VertexA first corner
|
||||
* @param VertexB second corner
|
||||
* @param VertexC third corner
|
||||
* @param VertexD fourth corner
|
||||
* @return UV interpolation coordinates of the 'Point'.
|
||||
*/
|
||||
extern AIMODULE_API FVector2D InvBilinear2D(const FVector Point, const FVector VertexA, const FVector VertexB, const FVector VertexC, const FVector VertexD);
|
||||
|
||||
/**
|
||||
* Finds the UV coordinates of the 'Point' on bilinear patch A,B,C,D. U interpolates from A->B, and C->D, and V interpolates from AB->CD.
|
||||
* The UV coordinate is clamped to [0..1] range after inversion.
|
||||
* @param Point location inside or close to the bilinear patch
|
||||
* @param VertexA first corner
|
||||
* @param VertexB second corner
|
||||
* @param VertexC third corner
|
||||
* @param VertexD fourth corner
|
||||
* @return UV interpolation coordinates of the 'Point' in [0..1] range.
|
||||
*/
|
||||
inline FVector2D InvBilinear2DClamped(const FVector Point, const FVector VertexA, const FVector VertexB, const FVector VertexC, const FVector VertexD)
|
||||
{
|
||||
return InvBilinear2D(Point, VertexA, VertexB, VertexC, VertexD).ClampAxes(0.0, 1.0);
|
||||
}
|
||||
|
||||
}; // UE::AI
|
||||
405
ue_markdown.py
Normal file
405
ue_markdown.py
Normal file
@@ -0,0 +1,405 @@
|
||||
"""
|
||||
ue_markdown.py — Render a ParsedHeader into compact, agent-readable Markdown.
|
||||
|
||||
Format goals:
|
||||
- Ultra-compact function entries: signature in heading, params folded into prose
|
||||
- Bullet-list properties (no tables)
|
||||
- Only items with actual C++ doc comments (or deprecated annotations) are emitted
|
||||
- Cross-reference links to other files in the corpus (via type_index)
|
||||
"""
|
||||
from __future__ import annotations
|
||||
import os
|
||||
import re
|
||||
from typing import Optional
|
||||
from ue_parser import (
|
||||
ParsedHeader, ClassInfo, EnumInfo, DelegateInfo, NamespaceInfo,
|
||||
FreeFunction, FunctionInfo, PropertyInfo, DocComment, _split_params,
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Cross-reference utilities
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _rel_link(current_md: str, target_md: str) -> str:
|
||||
"""
|
||||
Compute a relative markdown link from the directory of current_md to target_md.
|
||||
Both paths are relative to the docs root.
|
||||
"""
|
||||
cur_dir = os.path.dirname(current_md) or '.'
|
||||
return os.path.relpath(target_md, cur_dir).replace('\\', '/')
|
||||
|
||||
|
||||
def _make_type_link(name: str, type_index: dict[str, str],
|
||||
current_md: str) -> str:
|
||||
"""Return '[Name](relative/path.md)' if in corpus, else '`Name`'."""
|
||||
if name in type_index:
|
||||
link = _rel_link(current_md, type_index[name])
|
||||
return f"[{name}]({link})"
|
||||
return f"`{name}`"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _has_doc(comment: Optional[DocComment]) -> bool:
|
||||
if comment is None:
|
||||
return False
|
||||
return bool(comment.description or comment.params or comment.returns)
|
||||
|
||||
|
||||
def _uf_category(uf_specs: str) -> str:
|
||||
m = re.search(r'Category\s*=\s*"([^"]*)"', uf_specs)
|
||||
if m:
|
||||
return m.group(1)
|
||||
m = re.search(r'Category\s*=\s*([\w|]+)', uf_specs)
|
||||
return m.group(1) if m else ""
|
||||
|
||||
|
||||
def _uf_flags(uf_specs: str) -> list:
|
||||
flags = []
|
||||
for flag in ('BlueprintCallable', 'BlueprintPure', 'BlueprintImplementableEvent',
|
||||
'BlueprintNativeEvent', 'Exec', 'NetMulticast', 'Server', 'Client',
|
||||
'CallInEditor'):
|
||||
if re.search(r'\b' + flag + r'\b', uf_specs):
|
||||
flags.append(flag)
|
||||
return flags
|
||||
|
||||
|
||||
def _up_flags(specs: str) -> list:
|
||||
notable = [
|
||||
'EditAnywhere', 'EditDefaultsOnly', 'EditInstanceOnly',
|
||||
'VisibleAnywhere', 'VisibleDefaultsOnly',
|
||||
'BlueprintReadWrite', 'BlueprintReadOnly', 'BlueprintAssignable',
|
||||
'Replicated', 'ReplicatedUsing', 'Transient', 'SaveGame', 'Config',
|
||||
]
|
||||
return [f for f in notable if re.search(r'\b' + f + r'\b', specs)]
|
||||
|
||||
|
||||
def _compact_params(raw_params: str, max_count: int = 3) -> str:
|
||||
params = _split_params(raw_params)
|
||||
if not params:
|
||||
return ""
|
||||
shown = []
|
||||
for p in params[:max_count]:
|
||||
p = p.strip()
|
||||
p = re.sub(r'\s*=\s*', '=', p)
|
||||
shown.append(p)
|
||||
result = ', '.join(shown)
|
||||
if len(params) > max_count:
|
||||
result += ', ...'
|
||||
return result
|
||||
|
||||
|
||||
def _fn_flags_str(fn: FunctionInfo) -> str:
|
||||
parts = []
|
||||
if fn.is_deprecated:
|
||||
ver = fn.deprecated_version or "?"
|
||||
parts.append(f"Deprecated {ver}")
|
||||
if fn.uf_specifiers:
|
||||
uf = _uf_flags(fn.uf_specifiers)
|
||||
cat = _uf_category(fn.uf_specifiers)
|
||||
annotation = ', '.join(uf)
|
||||
if cat:
|
||||
annotation = (annotation + ' — ' if annotation else '') + cat
|
||||
if annotation:
|
||||
parts.append(annotation)
|
||||
for mod in fn.modifiers:
|
||||
if mod in ('virtual', 'static', 'inline', 'constexpr'):
|
||||
parts.append(mod)
|
||||
if fn.editor_only:
|
||||
parts.append("Editor only")
|
||||
return f" *({', '.join(parts)})*" if parts else ""
|
||||
|
||||
|
||||
_PLACEHOLDER_RE = re.compile(r'^[-_/\\]{6,}')
|
||||
|
||||
|
||||
def _fn_body(fn: FunctionInfo) -> str:
|
||||
parts = []
|
||||
c = fn.comment
|
||||
if c:
|
||||
if c.description and not _PLACEHOLDER_RE.match(c.description):
|
||||
desc = c.description.rstrip('.')
|
||||
parts.append(desc + '.')
|
||||
for pname, pdesc in c.params.items():
|
||||
pdesc = pdesc.rstrip('.')
|
||||
parts.append(f"`{pname}`: {pdesc}.")
|
||||
if c.returns:
|
||||
ret = c.returns.rstrip('.')
|
||||
parts.append(f"**Returns** {ret}.")
|
||||
return ' '.join(parts)
|
||||
|
||||
|
||||
def _render_function_compact(fn: FunctionInfo) -> str:
|
||||
params_str = _compact_params(fn.raw_params)
|
||||
heading = f"##### `{fn.name}({params_str})`"
|
||||
if fn.return_type:
|
||||
heading += f" → `{fn.return_type}`"
|
||||
heading += _fn_flags_str(fn)
|
||||
body = _fn_body(fn)
|
||||
return heading + ('\n' + body if body else '')
|
||||
|
||||
|
||||
def _render_ff_compact(fn: FreeFunction, overloads: list = None) -> str:
|
||||
overloads = overloads or [fn]
|
||||
fn0 = overloads[0]
|
||||
params_str = _compact_params(fn0.raw_params)
|
||||
heading = f"##### `{fn0.name}({params_str})`"
|
||||
if fn0.return_type:
|
||||
heading += f" → `{fn0.return_type}`"
|
||||
flag_parts = [m for m in fn0.modifiers if m in ('inline', 'static', 'constexpr')]
|
||||
if len(overloads) > 1:
|
||||
flag_parts.append(f"{len(overloads)} overloads")
|
||||
if flag_parts:
|
||||
heading += f" *({', '.join(flag_parts)})*"
|
||||
body = ""
|
||||
for f in overloads:
|
||||
b = _fn_body(f)
|
||||
if b:
|
||||
body = b
|
||||
break
|
||||
return heading + ('\n' + body if body else '')
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Section renderers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _render_delegates(delegates: list) -> str:
|
||||
if not delegates:
|
||||
return ""
|
||||
lines = ["## Delegates"]
|
||||
for d in delegates:
|
||||
suffix_parts = []
|
||||
if d.is_dynamic:
|
||||
suffix_parts.append("Dynamic")
|
||||
if d.is_multicast:
|
||||
suffix_parts.append("Multicast")
|
||||
suffix = ", ".join(suffix_parts)
|
||||
head = f"### `{d.name}`"
|
||||
if suffix:
|
||||
head += f" *({suffix})*"
|
||||
lines.append(head)
|
||||
if d.comment and d.comment.description:
|
||||
lines.append(d.comment.description)
|
||||
if d.params:
|
||||
param_str = ", ".join(f"`{t} {n}`" for t, n in d.params)
|
||||
lines.append(f"Params: {param_str}")
|
||||
return '\n'.join(lines)
|
||||
|
||||
|
||||
def _render_enum(ei: EnumInfo) -> str:
|
||||
head = f"### `{ei.name}`"
|
||||
if ei.editor_only:
|
||||
head += " *(Editor only)*"
|
||||
lines = [head]
|
||||
if ei.comment and ei.comment.description:
|
||||
lines.append(ei.comment.description)
|
||||
if ei.values:
|
||||
has_descriptions = any(v.comment for v in ei.values)
|
||||
if has_descriptions:
|
||||
lines.append("| Value | Description |")
|
||||
lines.append("|-------|-------------|")
|
||||
for v in ei.values:
|
||||
val_str = f"`{v.name}`"
|
||||
if v.value:
|
||||
val_str += f" (={v.value})"
|
||||
lines.append(f"| {val_str} | {v.comment} |")
|
||||
else:
|
||||
# Compact inline list when no value has a description
|
||||
vals = [v.name for v in ei.values]
|
||||
lines.append("Values: " + ", ".join(f"`{v}`" for v in vals))
|
||||
return '\n'.join(lines)
|
||||
|
||||
|
||||
def _render_enums(enums: list, heading: str = "## Enums") -> str:
|
||||
if not enums:
|
||||
return ""
|
||||
parts = [heading]
|
||||
for ei in enums:
|
||||
parts.append(_render_enum(ei))
|
||||
return '\n'.join(parts)
|
||||
|
||||
|
||||
def _render_properties(props: list) -> str:
|
||||
visible = [p for p in props
|
||||
if p.access in ('public', 'protected') and _has_doc(p.comment)]
|
||||
if not visible:
|
||||
return ""
|
||||
lines = ["#### Properties"]
|
||||
for p in visible:
|
||||
flags = ', '.join(_up_flags(p.specifiers))
|
||||
desc = p.comment.description if p.comment else ""
|
||||
line = f"- `{p.name}` `{p.type}`"
|
||||
if flags:
|
||||
line += f" *({flags})*"
|
||||
if desc:
|
||||
line += f" — {desc}"
|
||||
if p.editor_only:
|
||||
line += " *(Editor only)*"
|
||||
lines.append(line)
|
||||
return '\n'.join(lines)
|
||||
|
||||
|
||||
def _render_functions(fns: list) -> str:
|
||||
visible = [f for f in fns
|
||||
if f.access in ('public', 'protected')
|
||||
and _has_doc(f.comment)
|
||||
and not f.is_deprecated]
|
||||
if not visible:
|
||||
return ""
|
||||
|
||||
lines = ["#### Functions"]
|
||||
seen: dict[str, list] = {}
|
||||
ordered: list[str] = []
|
||||
for fn in visible:
|
||||
if fn.name not in seen:
|
||||
seen[fn.name] = []
|
||||
ordered.append(fn.name)
|
||||
seen[fn.name].append(fn)
|
||||
|
||||
for name in ordered:
|
||||
group = seen[name]
|
||||
if len(group) == 1:
|
||||
lines.append(_render_function_compact(group[0]))
|
||||
else:
|
||||
fn0 = group[0]
|
||||
params_str = _compact_params(fn0.raw_params)
|
||||
heading = f"##### `{fn0.name}({params_str})`"
|
||||
if fn0.return_type:
|
||||
heading += f" → `{fn0.return_type}`"
|
||||
flag_parts = []
|
||||
if fn0.is_deprecated:
|
||||
flag_parts.append(f"Deprecated {fn0.deprecated_version or '?'}")
|
||||
if fn0.uf_specifiers:
|
||||
uf = _uf_flags(fn0.uf_specifiers)
|
||||
cat = _uf_category(fn0.uf_specifiers)
|
||||
ann = ', '.join(uf)
|
||||
if cat:
|
||||
ann = (ann + ' — ' if ann else '') + cat
|
||||
if ann:
|
||||
flag_parts.append(ann)
|
||||
flag_parts.append(f"{len(group)} overloads")
|
||||
heading += f" *({', '.join(flag_parts)})*"
|
||||
lines.append(heading)
|
||||
for fn in group:
|
||||
body = _fn_body(fn)
|
||||
if body:
|
||||
lines.append(body)
|
||||
break
|
||||
|
||||
return '\n'.join(lines)
|
||||
|
||||
|
||||
def _class_has_content(ci: ClassInfo) -> bool:
|
||||
if _has_doc(ci.comment):
|
||||
return True
|
||||
if any(p.access in ('public', 'protected') and _has_doc(p.comment)
|
||||
for p in ci.properties):
|
||||
return True
|
||||
if any(f.access in ('public', 'protected') and (_has_doc(f.comment) or f.is_deprecated)
|
||||
for f in ci.functions):
|
||||
return True
|
||||
if ci.nested_enums:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def _render_class(ci: ClassInfo,
|
||||
type_index: dict[str, str], current_md: str) -> str:
|
||||
lines = []
|
||||
head = f"### `{ci.name}` *({ci.kind})*"
|
||||
lines.append(head)
|
||||
|
||||
if ci.bases:
|
||||
linked = [_make_type_link(b, type_index, current_md) for b in ci.bases]
|
||||
lines.append("*Inherits*: " + ", ".join(linked))
|
||||
|
||||
if ci.module_api:
|
||||
lines.append(f"*API*: `{ci.module_api}`")
|
||||
if ci.comment and ci.comment.description:
|
||||
lines.append(ci.comment.description)
|
||||
|
||||
if ci.nested_enums:
|
||||
lines.append(_render_enums(ci.nested_enums, "#### Enums"))
|
||||
|
||||
props_section = _render_properties(ci.properties)
|
||||
if props_section:
|
||||
lines.append(props_section)
|
||||
|
||||
fns_section = _render_functions(ci.functions)
|
||||
if fns_section:
|
||||
lines.append(fns_section)
|
||||
|
||||
return '\n'.join(lines)
|
||||
|
||||
|
||||
def _render_namespace(ns: NamespaceInfo) -> str:
|
||||
doc_fns = [f for f in ns.functions if _has_doc(f.comment)]
|
||||
if not doc_fns:
|
||||
return ""
|
||||
lines = [f"## Free Functions — `{ns.name}`"]
|
||||
seen: dict[str, list] = {}
|
||||
ordered: list[str] = []
|
||||
for fn in doc_fns:
|
||||
if fn.name not in seen:
|
||||
seen[fn.name] = []
|
||||
ordered.append(fn.name)
|
||||
seen[fn.name].append(fn)
|
||||
for name in ordered:
|
||||
group = seen[name]
|
||||
lines.append(_render_ff_compact(group[0], group))
|
||||
return '\n'.join(lines)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Main render function
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def render_header(parsed: ParsedHeader,
|
||||
type_index: dict[str, str] = None,
|
||||
current_md: str = "") -> str:
|
||||
if type_index is None:
|
||||
type_index = {}
|
||||
|
||||
lines = []
|
||||
lines.append(f"# `{parsed.filename}`")
|
||||
lines.append(f"**Module**: `{parsed.module_name}`")
|
||||
lines.append("")
|
||||
|
||||
sections = []
|
||||
|
||||
d_sec = _render_delegates(parsed.delegates)
|
||||
if d_sec.strip():
|
||||
sections.append(d_sec)
|
||||
|
||||
e_sec = _render_enums(parsed.enums)
|
||||
if e_sec.strip():
|
||||
sections.append(e_sec)
|
||||
|
||||
if parsed.classes:
|
||||
doc_classes = [ci for ci in parsed.classes if _class_has_content(ci)]
|
||||
if doc_classes:
|
||||
cls_lines = ["## Classes"]
|
||||
for ci in doc_classes:
|
||||
cls_lines.append(_render_class(ci, type_index, current_md))
|
||||
sections.append('\n'.join(cls_lines))
|
||||
|
||||
for ns in parsed.namespaces:
|
||||
ns_sec = _render_namespace(ns)
|
||||
if ns_sec.strip():
|
||||
sections.append(ns_sec)
|
||||
|
||||
if parsed.free_functions:
|
||||
doc_fns = [f for f in parsed.free_functions if _has_doc(f.comment)]
|
||||
if doc_fns:
|
||||
ff_lines = ["## Free Functions"]
|
||||
for fn in doc_fns:
|
||||
ff_lines.append(_render_ff_compact(fn))
|
||||
sections.append('\n'.join(ff_lines))
|
||||
|
||||
lines.append('\n\n---\n\n'.join(sections))
|
||||
|
||||
return '\n'.join(lines)
|
||||
1183
ue_parser.py
Normal file
1183
ue_parser.py
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user