Moved core into my new style
This commit is contained in:
parent
bfbee0c9db
commit
3ca06726c0
1
.gitignore
vendored
1
.gitignore
vendored
@ -15,3 +15,4 @@ export_presets.cfg
|
|||||||
data_*/
|
data_*/
|
||||||
mono_crash.*.json
|
mono_crash.*.json
|
||||||
|
|
||||||
|
.DS_Store
|
||||||
285
addons/core/.gitattributes
vendored
Normal file
285
addons/core/.gitattributes
vendored
Normal file
@ -0,0 +1,285 @@
|
|||||||
|
## GITATTRIBUTES FOR WEB PROJECTS
|
||||||
|
#
|
||||||
|
# These settings are for any web project.
|
||||||
|
#
|
||||||
|
# Details per file setting:
|
||||||
|
# text These files should be normalized (i.e. convert CRLF to LF).
|
||||||
|
# binary These files are binary and should be left untouched.
|
||||||
|
#
|
||||||
|
# Note that binary is a macro for -text -diff.
|
||||||
|
######################################################################
|
||||||
|
# Auto detect
|
||||||
|
## Handle line endings automatically for files detected as
|
||||||
|
## text and leave all files detected as binary untouched.
|
||||||
|
## This will handle all files NOT defined below.
|
||||||
|
## * text=lf
|
||||||
|
# Set the default behavior for all files.
|
||||||
|
* text=auto eol=lf
|
||||||
|
|
||||||
|
# Godot
|
||||||
|
# Godot
|
||||||
|
# Godot
|
||||||
|
*.bin filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.res filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.glb filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.lmbake filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
|
||||||
|
|
||||||
|
# Source code
|
||||||
|
# Source code
|
||||||
|
# Source code
|
||||||
|
*.rs text
|
||||||
|
*.bash text eol=lf
|
||||||
|
*.bat text eol=crlf
|
||||||
|
*.cmd text eol=crlf
|
||||||
|
*.coffee text
|
||||||
|
*.css text diff=css
|
||||||
|
*.htm text diff=html
|
||||||
|
*.html text diff=html
|
||||||
|
*.inc text
|
||||||
|
*.ini text
|
||||||
|
*.js text
|
||||||
|
*.json text
|
||||||
|
*.jsx text
|
||||||
|
*.less text
|
||||||
|
*.ls text
|
||||||
|
*.map text -diff
|
||||||
|
*.od text
|
||||||
|
*.onlydata text
|
||||||
|
*.php text diff=php
|
||||||
|
*.pl text
|
||||||
|
*.ps1 text eol=crlf
|
||||||
|
*.py text diff=python
|
||||||
|
*.rb text diff=ruby
|
||||||
|
*.sass text
|
||||||
|
*.scm text
|
||||||
|
*.scss text diff=css
|
||||||
|
*.sh text eol=lf
|
||||||
|
.husky/* text eol=lf
|
||||||
|
*.sql text
|
||||||
|
*.styl text
|
||||||
|
*.tag text
|
||||||
|
*.ts text
|
||||||
|
*.tsx text
|
||||||
|
*.xml text
|
||||||
|
*.xhtml text diff=html
|
||||||
|
|
||||||
|
# Docker
|
||||||
|
# Docker
|
||||||
|
# Docker
|
||||||
|
Dockerfile text
|
||||||
|
|
||||||
|
# Documentation
|
||||||
|
# Documentation
|
||||||
|
# Documentation
|
||||||
|
*.ipynb text eol=lf
|
||||||
|
*.markdown text diff=markdown
|
||||||
|
*.md text diff=markdown
|
||||||
|
*.mdwn text diff=markdown
|
||||||
|
*.mdown text diff=markdown
|
||||||
|
*.mkd text diff=markdown
|
||||||
|
*.mkdn text diff=markdown
|
||||||
|
*.mdtxt text
|
||||||
|
*.mdtext text
|
||||||
|
*.txt text
|
||||||
|
AUTHORS text
|
||||||
|
CHANGELOG text
|
||||||
|
CHANGES text
|
||||||
|
CONTRIBUTING text
|
||||||
|
COPYING text
|
||||||
|
copyright text
|
||||||
|
*COPYRIGHT* text
|
||||||
|
INSTALL text
|
||||||
|
license text
|
||||||
|
LICENSE text
|
||||||
|
NEWS text
|
||||||
|
readme text
|
||||||
|
*README* text
|
||||||
|
TODO text
|
||||||
|
|
||||||
|
# Templates
|
||||||
|
# Templates
|
||||||
|
# Templates
|
||||||
|
*.dot text
|
||||||
|
*.ejs text
|
||||||
|
*.erb text
|
||||||
|
*.haml text
|
||||||
|
*.handlebars text
|
||||||
|
*.hbs text
|
||||||
|
*.hbt text
|
||||||
|
*.jade text
|
||||||
|
*.latte text
|
||||||
|
*.mustache text
|
||||||
|
*.njk text
|
||||||
|
*.phtml text
|
||||||
|
*.svelte text
|
||||||
|
*.tmpl text
|
||||||
|
*.tpl text
|
||||||
|
*.twig text
|
||||||
|
*.vue text
|
||||||
|
|
||||||
|
# Configs
|
||||||
|
# Configs
|
||||||
|
# Configs
|
||||||
|
*.cnf text
|
||||||
|
*.conf text
|
||||||
|
*.config text
|
||||||
|
.editorconfig text
|
||||||
|
.env text
|
||||||
|
.gitattributes text eol=lf
|
||||||
|
.gitconfig text
|
||||||
|
.htaccess text
|
||||||
|
*.lock text -diff
|
||||||
|
package.json text eol=lf
|
||||||
|
package-lock.json text eol=lf -diff
|
||||||
|
pnpm-lock.yaml text eol=lf -diff
|
||||||
|
.prettierrc text
|
||||||
|
yarn.lock text -diff
|
||||||
|
*.toml text
|
||||||
|
*.yaml text
|
||||||
|
*.yml text
|
||||||
|
browserslist text
|
||||||
|
Makefile text
|
||||||
|
makefile text
|
||||||
|
|
||||||
|
# Heroku
|
||||||
|
# Heroku
|
||||||
|
# Heroku
|
||||||
|
Procfile text
|
||||||
|
|
||||||
|
# Graphics
|
||||||
|
# Graphics
|
||||||
|
# Graphics
|
||||||
|
*.ai filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.bmp filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.eps filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.gif filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.gifv filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.ico filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.jng filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.jp2 filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.jpg filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.jpeg filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.jpx filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.jxr filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.pdf filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.png filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.psb filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.psd filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
# SVG treated as an asset (binary) by default.
|
||||||
|
*.svg text
|
||||||
|
# If you want to treat it as binary,
|
||||||
|
# use the following line instead.
|
||||||
|
# *.svg filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.svgz filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.tif filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.tiff filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.wbmp filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.webp filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.exr filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.hdr filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.iff filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.pict filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# 3d models
|
||||||
|
# 3d models
|
||||||
|
# 3d models
|
||||||
|
*.3dm filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.3ds filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.blend filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.c4d filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.collada filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.dae filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.dxf filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.fbx filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.jas filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.lws filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.lxo filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.ma filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.max filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.mb filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.obj filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.ply filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.skp filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.stl filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.ztl filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
|
||||||
|
|
||||||
|
# Audio
|
||||||
|
# Audio
|
||||||
|
# Audio
|
||||||
|
*.kar filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.m4a filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.mid filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.midi filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.mp3 filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.ogg filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.ra filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.aif filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.aiff filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.it filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.mod filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.mp3 filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.ogg filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.s3m filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.wav filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.xm filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
|
||||||
|
# Fonts
|
||||||
|
# Fonts
|
||||||
|
# Fonts
|
||||||
|
*.otf filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.ttf filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
|
||||||
|
# Video
|
||||||
|
# Video
|
||||||
|
# Video
|
||||||
|
*.3gpp filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.3gp filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.as filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.asf filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.asx filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.avi filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.fla filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.flv filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.m4v filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.mng filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.mov filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.mp4 filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.mpeg filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.mpg filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.ogv filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.swc filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.swf filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.webm filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
|
||||||
|
# Archives
|
||||||
|
# Archives
|
||||||
|
# Archives
|
||||||
|
*.7z filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.gz filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.jar filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.rar filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.tar filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.zip filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
|
||||||
|
# Fonts
|
||||||
|
# Fonts
|
||||||
|
# Fonts
|
||||||
|
*.ttf filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.eot filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.otf filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.woff filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.woff2 filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
|
||||||
|
# Executables
|
||||||
|
# Executables
|
||||||
|
# Executables
|
||||||
|
*.exe filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
*.pyc filter=lfs diff=lfs merge=lfs -text lockable
|
||||||
|
# RC files (like .babelrc or .eslintrc)
|
||||||
|
*.*rc text
|
||||||
|
# Ignore files (like .npmignore or .gitignore)
|
||||||
|
*.*ignore text
|
||||||
67
addons/core/.gitignore
vendored
Normal file
67
addons/core/.gitignore
vendored
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
|
||||||
|
|
||||||
|
# Group Ignores
|
||||||
|
ignored/
|
||||||
|
ignore/
|
||||||
|
|
||||||
|
|
||||||
|
# Personal ignores
|
||||||
|
# ignore the addons folder
|
||||||
|
addons/
|
||||||
|
exports/
|
||||||
|
log/
|
||||||
|
save/
|
||||||
|
samples/
|
||||||
|
|
||||||
|
# Godot 4+ specific ignores
|
||||||
|
.godot/
|
||||||
|
|
||||||
|
*.swp
|
||||||
|
*.*~
|
||||||
|
project.lock.json
|
||||||
|
.DS_Store
|
||||||
|
*.pyc
|
||||||
|
nupkg/
|
||||||
|
.vs/
|
||||||
|
|
||||||
|
# Visual Studio Code
|
||||||
|
# Created by https://www.gitignore.io/api/visualstudiocode
|
||||||
|
# Edit at https://www.gitignore.io/?templates=visualstudiocode
|
||||||
|
|
||||||
|
### VisualStudioCode ###
|
||||||
|
.vscode/* # Maybe .vscode/**/* instead - see comments
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
|
||||||
|
### VisualStudioCode Patch ###
|
||||||
|
# Ignore all local history of files
|
||||||
|
**/.history
|
||||||
|
|
||||||
|
# End of https://www.gitignore.io/api/visualstudiocode
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# User-specific files
|
||||||
|
*.suo
|
||||||
|
*.user
|
||||||
|
*.userosscache
|
||||||
|
*.sln.docstates
|
||||||
|
|
||||||
|
# Build results
|
||||||
|
[Dd]ebug/
|
||||||
|
[Dd]ebugPublic/
|
||||||
|
[Rr]elease/
|
||||||
|
[Rr]eleases/
|
||||||
|
x64/
|
||||||
|
x86/
|
||||||
|
build/
|
||||||
|
bld/
|
||||||
|
[Bb]in/
|
||||||
|
[Oo]bj/
|
||||||
|
[Oo]ut/
|
||||||
|
msbuild.log
|
||||||
|
msbuild.err
|
||||||
|
msbuild.wrn
|
||||||
106
addons/core/ai/AStarGraph.cs
Normal file
106
addons/core/ai/AStarGraph.cs
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// D E R E L I C T
|
||||||
|
//
|
||||||
|
/// // (c) 2003..2025
|
||||||
|
|
||||||
|
using Godot;
|
||||||
|
using gdcoll = Godot.Collections;
|
||||||
|
//using System.Linq;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[Tool, GlobalClass]
|
||||||
|
public partial class AStarGraph : Resource
|
||||||
|
{
|
||||||
|
[Export]
|
||||||
|
public gdcoll.Array<Vector3> Points { get; set; } = new();
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public gdcoll.Array<gdcoll.Array<int>> Connections { get; set; } = new();
|
||||||
|
|
||||||
|
private int _nextId = 0;
|
||||||
|
|
||||||
|
public int AddPoint( Vector3 position )
|
||||||
|
{
|
||||||
|
var id = _nextId++;
|
||||||
|
|
||||||
|
if( Points.Count <= _nextId )
|
||||||
|
{
|
||||||
|
Points.Resize( _nextId );
|
||||||
|
Connections.Resize( _nextId );
|
||||||
|
}
|
||||||
|
Points[id] = position;
|
||||||
|
Connections[id] = new gdcoll.Array<int>();
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void ConnectPoints( int idFrom, int idTo, bool bidirectional = true )
|
||||||
|
{
|
||||||
|
//if (!Points.ContainsKey(idFrom) || !Points.ContainsKey(idTo)) return;
|
||||||
|
|
||||||
|
//if (!Connections.ContainsKey(idFrom)) Connections[idFrom] = new();
|
||||||
|
Connections[idFrom].Add( idTo );
|
||||||
|
|
||||||
|
if( bidirectional )
|
||||||
|
{
|
||||||
|
//if (!Connections.ContainsKey(idTo)) Connections[idTo] = new();
|
||||||
|
Connections[idTo].Add( idFrom );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ArePointsConnected( int idFrom, int idTo )
|
||||||
|
{
|
||||||
|
return Connections[idFrom].Contains( idTo );
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetAvailablePointId() => _nextId++;
|
||||||
|
|
||||||
|
public Vector3 GetPointPosition( int id ) => Points[id];
|
||||||
|
|
||||||
|
public gdcoll.Array<int> GetPointConnections( int id ) => Connections[id];
|
||||||
|
|
||||||
|
//public int[] GetPointIds() => Points.Keys.ToArray();
|
||||||
|
|
||||||
|
|
||||||
|
public int GetClosestPoint( Vector3 position )
|
||||||
|
{
|
||||||
|
int closestId = -1;
|
||||||
|
float closestDistSq = float.MaxValue;
|
||||||
|
|
||||||
|
int curId = 0;
|
||||||
|
foreach( var pos in Points )
|
||||||
|
{
|
||||||
|
if( !pos.IsFinite() )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
float distSq = position.DistanceSquaredTo( pos );
|
||||||
|
if( distSq < closestDistSq )
|
||||||
|
{
|
||||||
|
if( Connections[curId].Count > 0 )
|
||||||
|
{
|
||||||
|
closestDistSq = distSq;
|
||||||
|
closestId = curId;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Points[curId] = Vector3.Inf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
curId++;
|
||||||
|
}
|
||||||
|
return closestId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
Points.Clear();
|
||||||
|
Connections.Clear();
|
||||||
|
_nextId = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
1
addons/core/ai/AStarGraph.cs.uid
Normal file
1
addons/core/ai/AStarGraph.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://b500r6swmdc3w
|
||||||
79
addons/core/ai/AStarSolver.cs
Normal file
79
addons/core/ai/AStarSolver.cs
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// D E R E L I C T
|
||||||
|
//
|
||||||
|
/// // (c) 2003..2025
|
||||||
|
|
||||||
|
using Godot;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
public static class AStarSolver
|
||||||
|
{
|
||||||
|
public static Vector3[] FindPath( AStarGraph graph, Vector3 startPos, Vector3 endPos )
|
||||||
|
{
|
||||||
|
int startId = graph.GetClosestPoint( startPos );
|
||||||
|
int endId = graph.GetClosestPoint( endPos );
|
||||||
|
|
||||||
|
log.info( $"Finding path from {startPos.Log}->{endPos.Log} ({startId}->{endId})" );
|
||||||
|
|
||||||
|
if( startId == -1 || endId == -1 )
|
||||||
|
return new Vector3[0];
|
||||||
|
|
||||||
|
var openSet = new PriorityQueue<int, float>();
|
||||||
|
var cameFrom = new Dictionary<int, int>();
|
||||||
|
|
||||||
|
var gScore = new Dictionary<int, float>();
|
||||||
|
gScore[startId] = 0;
|
||||||
|
|
||||||
|
var fScore = new Dictionary<int, float>();
|
||||||
|
fScore[startId] = startPos.DistanceTo( endPos );
|
||||||
|
|
||||||
|
openSet.Enqueue( startId, fScore[startId] );
|
||||||
|
|
||||||
|
while( openSet.Count > 0 )
|
||||||
|
{
|
||||||
|
int currentId = openSet.Dequeue();
|
||||||
|
if( currentId == endId )
|
||||||
|
{
|
||||||
|
log.debug( $"Found path!" );
|
||||||
|
return ReconstructPath( startPos, endPos, cameFrom, currentId, graph );
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach( int neighborId in graph.GetPointConnections( currentId ) )
|
||||||
|
{
|
||||||
|
Vector3 currentPos = graph.GetPointPosition( currentId );
|
||||||
|
Vector3 neighborPos = graph.GetPointPosition( neighborId );
|
||||||
|
float tentativeGScore = gScore.GetValueOrDefault( currentId, float.MaxValue ) + currentPos.DistanceTo( neighborPos );
|
||||||
|
|
||||||
|
if( tentativeGScore < gScore.GetValueOrDefault( neighborId, float.MaxValue ) )
|
||||||
|
{
|
||||||
|
cameFrom[neighborId] = currentId;
|
||||||
|
gScore[neighborId] = tentativeGScore;
|
||||||
|
fScore[neighborId] = tentativeGScore + neighborPos.DistanceTo( endPos );
|
||||||
|
|
||||||
|
openSet.Enqueue( neighborId, fScore[neighborId] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Vector3[0]; // No path found
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Vector3[] ReconstructPath( Vector3 startPos, Vector3 endPos, Dictionary<int, int> cameFrom, int currentId, AStarGraph graph )
|
||||||
|
{
|
||||||
|
var path = new List<Vector3> { endPos, graph.GetPointPosition( currentId ) };
|
||||||
|
while( cameFrom.ContainsKey( currentId ) )
|
||||||
|
{
|
||||||
|
currentId = cameFrom[currentId];
|
||||||
|
path.Add( graph.GetPointPosition( currentId ) );
|
||||||
|
}
|
||||||
|
path.Add( startPos );
|
||||||
|
|
||||||
|
path.Reverse();
|
||||||
|
|
||||||
|
log.debug( $"Path is {path.Count} long." );
|
||||||
|
|
||||||
|
|
||||||
|
return path.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
1
addons/core/ai/AStarSolver.cs.uid
Normal file
1
addons/core/ai/AStarSolver.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://c7e80my8xas6l
|
||||||
175
addons/core/ai/SpherePathFollower.cs
Normal file
175
addons/core/ai/SpherePathFollower.cs
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// D E R E L I C T
|
||||||
|
//
|
||||||
|
/// // (c) 2003..2025
|
||||||
|
|
||||||
|
using Godot;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A path follower that treats the agent as a sphere. The target point is the
|
||||||
|
/// intersection of this sphere with the current path segment.
|
||||||
|
/// </summary>
|
||||||
|
public class SpherePathFollower
|
||||||
|
{
|
||||||
|
private Vector3[] _path;
|
||||||
|
private float _radius = 4.0f;
|
||||||
|
private int _currentIndex = 0;
|
||||||
|
|
||||||
|
public Vector3[] Path => _path;
|
||||||
|
|
||||||
|
public int Index => _currentIndex;
|
||||||
|
|
||||||
|
public int MaxIndex => _path.Length;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if the agent has reached the end of the path.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsFinished => _currentIndex >= _path.Length;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if the agent has reached the end of the path.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsNearlyFinished => _currentIndex >= ( _path.Length - 1 );
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new path follower with a path and a sphere radius.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">The array of Vector3 points to follow.</param>
|
||||||
|
/// <param name="sphereRadius">The radius of the agent's collision/interaction sphere.</param>
|
||||||
|
public SpherePathFollower( float sphereRadius )
|
||||||
|
{
|
||||||
|
_radius = sphereRadius;
|
||||||
|
SetPath( new Vector3[0], sphereRadius );
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new path follower with a path and a sphere radius.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">The array of Vector3 points to follow.</param>
|
||||||
|
/// <param name="sphereRadius">The radius of the agent's collision/interaction sphere.</param>
|
||||||
|
public SpherePathFollower( Vector3[] path, float sphereRadius )
|
||||||
|
{
|
||||||
|
SetPath( path, sphereRadius );
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Assigns a new path and radius, resetting the follower's progress.
|
||||||
|
/// </summary>
|
||||||
|
public void SetPath( Vector3[] newPath )
|
||||||
|
{
|
||||||
|
SetPath( newPath, _radius );
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Assigns a new path and radius, resetting the follower's progress.
|
||||||
|
/// </summary>
|
||||||
|
public void SetPath( Vector3[] newPath, float sphereRadius )
|
||||||
|
{
|
||||||
|
_path = newPath ?? new Vector3[0];
|
||||||
|
_radius = Mathf.Max( 0.01f, sphereRadius ); // Ensure radius is positive
|
||||||
|
_currentIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculates the next target point on the current path segment for the agent to move towards.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="agentPosition">The current world position of the agent.</param>
|
||||||
|
/// <returns>The world-space target point on the path segment.</returns>
|
||||||
|
public Vector3 GetTargetPoint( Vector3 agentPos, Vector3 goalPos, out bool advance )
|
||||||
|
{
|
||||||
|
advance = false;
|
||||||
|
if( IsNearlyFinished )
|
||||||
|
{
|
||||||
|
return goalPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3 p1 = _path[_currentIndex];
|
||||||
|
Vector3 p2 = _path[_currentIndex + 1];
|
||||||
|
|
||||||
|
//DebugDraw3D.DrawLine(p1, p2, Colors.Blue);
|
||||||
|
|
||||||
|
Vector3 intersects = p2;
|
||||||
|
|
||||||
|
var intersectLine = Geo.SphereSegment( agentPos, _radius, p1, p2, out intersects, out var intersectType );
|
||||||
|
|
||||||
|
/*
|
||||||
|
if (!intersectLine)
|
||||||
|
{
|
||||||
|
intersects = p1;
|
||||||
|
intersectType = Geo.IntersectType.Before;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
//log.info($"{log.loc().Log}: Seg:({p1.Log})->({p2.Log}) On {intersectLine} Type {intersectType} To {intersects.Log}");
|
||||||
|
|
||||||
|
switch( intersectType )
|
||||||
|
{
|
||||||
|
case Geo.IntersectType.After:
|
||||||
|
advance = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return intersects;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the follower's progress along the path. Call this after the agent moves.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="agentPosition">The agent's new world position after moving.</param>
|
||||||
|
/// <returns>True if updated</returns>
|
||||||
|
public int Update( Vector3 agentPos, Vector3 goalPos, bool advance )
|
||||||
|
{
|
||||||
|
if( IsNearlyFinished )
|
||||||
|
{
|
||||||
|
if( !IsFinished )
|
||||||
|
return ++_currentIndex;
|
||||||
|
|
||||||
|
return _currentIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3 endOfSegment = _path[_currentIndex + 1];
|
||||||
|
|
||||||
|
// Check if the agent's sphere has reached the end of the current segment
|
||||||
|
if( advance || agentPos.DistanceSquaredTo( endOfSegment ) <= _radius * _radius )
|
||||||
|
{
|
||||||
|
++_currentIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
var targetPos = GetTargetPoint( agentPos, goalPos, out advance );
|
||||||
|
|
||||||
|
return _currentIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the follower's progress along the path. Call this after the agent moves.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="agentPosition">The agent's new world position after moving.</param>
|
||||||
|
/// <returns>True if updated</returns>
|
||||||
|
public int Update_orig( Vector3 agentPosition, out bool advance )
|
||||||
|
{
|
||||||
|
advance = false;
|
||||||
|
|
||||||
|
if( IsFinished )
|
||||||
|
return _currentIndex;
|
||||||
|
|
||||||
|
Vector3 endOfSegment = _path[_currentIndex + 1];
|
||||||
|
|
||||||
|
// Check if the agent's sphere has reached the end of the current segment
|
||||||
|
while( agentPosition.DistanceTo( endOfSegment ) <= _radius )
|
||||||
|
{
|
||||||
|
_currentIndex++;
|
||||||
|
|
||||||
|
advance = true;
|
||||||
|
|
||||||
|
if( IsFinished )
|
||||||
|
return _currentIndex;
|
||||||
|
|
||||||
|
endOfSegment = _path[_currentIndex + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return _currentIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
1
addons/core/ai/SpherePathFollower.cs.uid
Normal file
1
addons/core/ai/SpherePathFollower.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://dlfmqjpqpduxn
|
||||||
219
addons/core/core.cs
Normal file
219
addons/core/core.cs
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// D E R E L I C T
|
||||||
|
//
|
||||||
|
/// // (c) 2003..2024
|
||||||
|
|
||||||
|
#if TOOLS
|
||||||
|
using Godot;
|
||||||
|
using Godot.NativeInterop;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Logging.Abstractions;
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.Loader;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
//Currently doesnt _quiiiite_ work
|
||||||
|
|
||||||
|
|
||||||
|
[GlobalClass, Tool]
|
||||||
|
public partial class core : EditorPlugin
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
#if DONT_RUN
|
||||||
|
|
||||||
|
public static void SetupUnloads()
|
||||||
|
{
|
||||||
|
GD.Print($"SetupUnloads");
|
||||||
|
|
||||||
|
//AppDomain.CurrentDomain.FirstChanceException +=CurrentDomainOnFirstChanceException;
|
||||||
|
|
||||||
|
var assContext = System.Runtime.Loader.AssemblyLoadContext.GetLoadContext(System.Reflection.Assembly.GetExecutingAssembly());
|
||||||
|
|
||||||
|
assContext.Unloading += alc =>
|
||||||
|
{
|
||||||
|
GD.Print($"Unloading (from Initialize)");
|
||||||
|
|
||||||
|
//Debug.WriteLine("Unloading (from Initialize)");
|
||||||
|
|
||||||
|
{
|
||||||
|
////complete godot async tasks, which may have been left hanging
|
||||||
|
var isDone = false;
|
||||||
|
while (isDone is false)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Dispatcher.SynchronizationContext.ExecutePendingContinuations();
|
||||||
|
isDone = true;
|
||||||
|
GD.Print("Godot async tasks completed.");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine("Exception: " + ex);
|
||||||
|
/*if (ex._IsRoutineControlFlow() is false)
|
||||||
|
{
|
||||||
|
__.Assert(ex);
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//need to detach the handler, or it will keep the assembly alive.
|
||||||
|
//AppDomain.CurrentDomain.FirstChanceException -= CurrentDomainOnFirstChanceException;
|
||||||
|
|
||||||
|
{
|
||||||
|
//unload STJ cached assemblies,
|
||||||
|
//see https://github.com/dotnet/runtime/issues/65323#issuecomment-1320949911
|
||||||
|
//and https://github.com/godotengine/godot/issues/78513#issuecomment-1624682104
|
||||||
|
GD.Print($"Clearing STJ cache");
|
||||||
|
var assembly = typeof(JsonSerializerOptions).Assembly;
|
||||||
|
var updateHandlerType = assembly.GetType("System.Text.Json.JsonSerializerOptionsUpdateHandler");
|
||||||
|
var clearCacheMethod = updateHandlerType?.GetMethod("ClearCache", BindingFlags.Static | BindingFlags.Public);
|
||||||
|
clearCacheMethod?.Invoke(null, new object?[] { null });
|
||||||
|
GD.Print($"Cleared STJ cache");
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public override void _EnterTree()
|
||||||
|
{
|
||||||
|
GD.Print($"_EnterTree");
|
||||||
|
var assContext = System.Runtime.Loader.AssemblyLoadContext.GetLoadContext(System.Reflection.Assembly.GetExecutingAssembly());
|
||||||
|
|
||||||
|
assContext.Unloading += OnUnloading;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
//res.Mgr.startup();
|
||||||
|
lib.Config.startup("editor.xml");
|
||||||
|
ent.Util.RegisterData();
|
||||||
|
Util.SetupSaveState();
|
||||||
|
|
||||||
|
GD.Print($"DONE Setting up core.");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
GD.PrintErr($"Ruh roh, ex {ex.GetType().Name} {ex.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[ModuleInitializer]
|
||||||
|
public static void OnLoading()
|
||||||
|
{
|
||||||
|
GD.Print($"OnLoading");
|
||||||
|
|
||||||
|
SetupUnloads();
|
||||||
|
SetupCore();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnUnloading(AssemblyLoadContext context)
|
||||||
|
{
|
||||||
|
GD.Print($"OnUnloading");
|
||||||
|
|
||||||
|
Game.shutdownLogging();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void _ExitTree()
|
||||||
|
{
|
||||||
|
GD.Print($"_ExitTree");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void SetupCore()
|
||||||
|
{
|
||||||
|
var id = Process.GetCurrentProcess().Id;
|
||||||
|
|
||||||
|
GD.Print($"Setting up core...");
|
||||||
|
|
||||||
|
// TWICE (down below)
|
||||||
|
Game.setupLogging($"editor", $"");
|
||||||
|
|
||||||
|
// Initialization of the plugin goes here.
|
||||||
|
log.high($"Starting up core...");
|
||||||
|
|
||||||
|
var windowTitle = $"D E R E L I C T ({id})";
|
||||||
|
|
||||||
|
log.high(windowTitle);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#if DONT_RUN
|
||||||
|
public override void _EnterTree()
|
||||||
|
{
|
||||||
|
var id = Process.GetCurrentProcess().Id;
|
||||||
|
|
||||||
|
GD.Print($"Setting up core...");
|
||||||
|
|
||||||
|
// TWICE (down below)
|
||||||
|
Game.setupLogging($"editor", $"");
|
||||||
|
|
||||||
|
// Initialization of the plugin goes here.
|
||||||
|
log.high($"Starting up core...");
|
||||||
|
|
||||||
|
var windowTitle = $"D E R E L I C T ({id})";
|
||||||
|
|
||||||
|
log.high(windowTitle);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
//res.Mgr.startup();
|
||||||
|
lib.Config.startup("editor.xml");
|
||||||
|
ent.Util.RegisterData();
|
||||||
|
Util.SetupSaveState();
|
||||||
|
|
||||||
|
var curAss = Assembly.GetExecutingAssembly();
|
||||||
|
AssemblyLoadContext.GetLoadContext(curAss).Unloading += OnUnloading;
|
||||||
|
|
||||||
|
GD.Print($"DONE Setting up core.");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
GD.PrintErr($"Ruh roh, ex {ex.GetType().Name} {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[ModuleInitializer]
|
||||||
|
static public void OnLoading()
|
||||||
|
{
|
||||||
|
GD.Print($"On LOADing: Unpause thread");
|
||||||
|
//log.unpauseThread();
|
||||||
|
Game.setupLogging($"editor", $"");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnUnloading(AssemblyLoadContext context)
|
||||||
|
{
|
||||||
|
GD.Print($"On UNloading: Pause thread");
|
||||||
|
|
||||||
|
//log.pauseThread();
|
||||||
|
Game.shutdownLogging();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void _ExitTree()
|
||||||
|
{
|
||||||
|
// Clean-up of the plugin goes here.
|
||||||
|
log.high($"Shutting down core . . .");
|
||||||
|
|
||||||
|
Game.shutdownLogging();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
log.high($"D O N E Shutting down core.");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif
|
||||||
1
addons/core/core.cs.uid
Normal file
1
addons/core/core.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://t5s8xt1qqpoq
|
||||||
391
addons/core/core/Game.cs
Normal file
391
addons/core/core/Game.cs
Normal file
@ -0,0 +1,391 @@
|
|||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// D E R E L I C T
|
||||||
|
//
|
||||||
|
/// // (c) 2003..2024
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Net.NetworkInformation;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using Godot;
|
||||||
|
|
||||||
|
using Godot.Sharp.Extras;
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Logging.Abstractions;
|
||||||
|
using prof;
|
||||||
|
|
||||||
|
|
||||||
|
[GlobalClass]
|
||||||
|
public partial class Game : CanvasLayer
|
||||||
|
{
|
||||||
|
static public Game _ { get; private set; }
|
||||||
|
|
||||||
|
|
||||||
|
[Prefix( "AR" )]
|
||||||
|
public Container Display;
|
||||||
|
public Node DisplayNode;
|
||||||
|
public string _reason = "";
|
||||||
|
|
||||||
|
|
||||||
|
private Node _test;
|
||||||
|
|
||||||
|
/*
|
||||||
|
// @@@@ This isnt great. Really need to refactor
|
||||||
|
// @@@@ This isnt great. Really need to refactor
|
||||||
|
// @@@@ This isnt great. Really need to refactor
|
||||||
|
[Prefix("../game")]
|
||||||
|
public PackedScene MainMenu;
|
||||||
|
|
||||||
|
[Prefix("../game")]
|
||||||
|
public PackedScene Gameplay;
|
||||||
|
*/
|
||||||
|
|
||||||
|
//private Button XButton;
|
||||||
|
|
||||||
|
private Label FPSLabel;
|
||||||
|
|
||||||
|
#region Logging
|
||||||
|
|
||||||
|
static string s_extraHeader = $"";
|
||||||
|
|
||||||
|
|
||||||
|
static public void PrintToGDPrint( log.LogEvent evt )
|
||||||
|
{
|
||||||
|
var color = "";
|
||||||
|
var bold = false;
|
||||||
|
|
||||||
|
switch( evt.LogType )
|
||||||
|
{
|
||||||
|
case log.LogType.Invalid:
|
||||||
|
color = "yellow";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case log.LogType.Trace:
|
||||||
|
color = "grey";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case log.LogType.Debug:
|
||||||
|
color = "grey";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case log.LogType.Info:
|
||||||
|
color = "white";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case log.LogType.High:
|
||||||
|
color = "white";
|
||||||
|
bold = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case log.LogType.Warn:
|
||||||
|
color = "yellow";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case log.LogType.Error:
|
||||||
|
color = "red";
|
||||||
|
bold = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case log.LogType.Fatal:
|
||||||
|
color = "red";
|
||||||
|
bold = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
var msgHeader = log.headerPrint( evt );
|
||||||
|
|
||||||
|
var msg = $"{( bold ? "[b]" : "" )}[color={color}]{evt.Msg}[/color]{( bold ? "[/b]" : "" )}";
|
||||||
|
|
||||||
|
// @@ PERF PrintRich is quite slow and uses .replace to parse (as it were) the bbcode (ie html with [] tags)
|
||||||
|
GD.PrintRich( $"{s_extraHeader,10}| {msgHeader}{msg}" );
|
||||||
|
|
||||||
|
/*
|
||||||
|
if( evt.LogType == log.LogType.Warn )
|
||||||
|
{
|
||||||
|
GD.PushWarning( $"| {evt.Msg}", evt.Path, evt.Line, evt.Member );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( evt.LogType == log.LogType.Error )
|
||||||
|
{
|
||||||
|
GD.PushError( $"| {evt.Msg}" );
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void SetTitle( string details )
|
||||||
|
{
|
||||||
|
var id = Process.GetCurrentProcess().Id;
|
||||||
|
var windowTitle = $"D E R E L I C T ({id}) Scene[{details}]";
|
||||||
|
DisplayServer.WindowSetTitle( windowTitle );
|
||||||
|
}
|
||||||
|
|
||||||
|
static public string s_logDir = "log/";
|
||||||
|
|
||||||
|
static public void setupLogging( string prefix, string suffix )
|
||||||
|
{
|
||||||
|
if( log.IsLogging )
|
||||||
|
{
|
||||||
|
log.warn( $"Logging already started, cannot start again" );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var logFilename = LogFilename( prefix, suffix );
|
||||||
|
|
||||||
|
/*
|
||||||
|
var file = new FileInfo( $"{logDir}{logFilename}" );
|
||||||
|
if( file.Exists )
|
||||||
|
{
|
||||||
|
if( file.Length > 10 * 1024 * 1024 )
|
||||||
|
{
|
||||||
|
logFilename = $"{logFilePrefix}_{now.Hour:00}.log";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//*/
|
||||||
|
|
||||||
|
// Setup .warn and .error so that they are directly sent to GDPrint
|
||||||
|
// This makes it so the real causes are in the stack trace in Godot and can be clicked
|
||||||
|
//log.addDirectCallback( log.LogType.Warn, PrintWarning );
|
||||||
|
|
||||||
|
//log.addDirectCallback( log.LogType.Error, PrintError );
|
||||||
|
|
||||||
|
GD.Print( $"***** Starting {prefix} logging system for file {logFilename}" );
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
s_extraHeader = suffix;
|
||||||
|
|
||||||
|
//log.create( $"{logDir}{logFilename}", log.Endpoints.File );
|
||||||
|
log.startup( $"{s_logDir}{logFilename}", log.Endpoints.File );
|
||||||
|
|
||||||
|
log.addDelegate( PrintToGDPrint );
|
||||||
|
|
||||||
|
|
||||||
|
log.high( $"Machine and C# Runtime Environment" );
|
||||||
|
log.var( System.Environment.ProcessId );
|
||||||
|
|
||||||
|
log.var( System.Environment.CommandLine );
|
||||||
|
|
||||||
|
log.var( System.Environment.CurrentDirectory );
|
||||||
|
log.var( System.Environment.SystemDirectory );
|
||||||
|
|
||||||
|
log.var( System.Environment.GetEnvironmentVariable( "PATH" ) );
|
||||||
|
log.var( System.Environment.GetEnvironmentVariable( "USER" ) );
|
||||||
|
log.var( System.Environment.GetEnvironmentVariable( "HOME" ) );
|
||||||
|
|
||||||
|
log.var( System.Environment.ProcessorCount );
|
||||||
|
log.var( System.Environment.Is64BitOperatingSystem );
|
||||||
|
log.var( System.Environment.Is64BitProcess );
|
||||||
|
|
||||||
|
log.var( System.Environment.MachineName );
|
||||||
|
|
||||||
|
log.var( System.Environment.Version );
|
||||||
|
log.var( System.Environment.OSVersion );
|
||||||
|
|
||||||
|
|
||||||
|
GD.Print( $"* End setupLogging" );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string LogFilename( string prefix, string suffix )
|
||||||
|
{
|
||||||
|
var now = DateTime.Now;
|
||||||
|
|
||||||
|
//var logFilePrefix = $"{prefix}_{now.Year}{now.Month:00}{now.Day:00}_{suffix}";
|
||||||
|
var logFilePrefix = $"{prefix}_{now.Year}{now.Month:00}{now.Day:00}{( suffix.NOW() ? "" : "_" )}{suffix}";
|
||||||
|
|
||||||
|
//New log file every 10 minutes;
|
||||||
|
//var logFilename = $"{logFilePrefix}_{now.Hour:00}_{now.Minute / 10:0}0.log";
|
||||||
|
var logFilename = $"{logFilePrefix}.log";
|
||||||
|
return logFilename;
|
||||||
|
}
|
||||||
|
|
||||||
|
static public void shutdownLogging()
|
||||||
|
{
|
||||||
|
log.high( $"Shutting down logging" );
|
||||||
|
log.shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
[StackTraceHidden]
|
||||||
|
private static void PrintError( log.LogEvent le )
|
||||||
|
{
|
||||||
|
PrintToGDPrint( le );
|
||||||
|
GD.PushError( $"| {le.Msg}" );
|
||||||
|
}
|
||||||
|
|
||||||
|
[StackTraceHidden]
|
||||||
|
private static void PrintWarning( log.LogEvent le )
|
||||||
|
{
|
||||||
|
PrintToGDPrint( le );
|
||||||
|
GD.PushWarning( $"| {le.Msg}", le.Path, le.Line, le.Member );
|
||||||
|
}
|
||||||
|
|
||||||
|
static public void LogCallable( Variant dictVar )
|
||||||
|
{
|
||||||
|
var dict = dictVar.AsGodotDictionary();
|
||||||
|
|
||||||
|
var msg = dict["text"].AsString();
|
||||||
|
|
||||||
|
if( msg[0] == '|' )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The dictionary that comes in has almost nothing. Currently Ive only seen text: the message and type: the log type
|
||||||
|
// It has no other keys.
|
||||||
|
log.info( $"{msg}", cat: "engine" );
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Logging
|
||||||
|
|
||||||
|
public Game()
|
||||||
|
{
|
||||||
|
_ = this;
|
||||||
|
|
||||||
|
//log.trace( $"Notification: Game()" );
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void _Notification( int note )
|
||||||
|
{
|
||||||
|
base._Notification( note );
|
||||||
|
//log.trace( $"Notification: {note}" );
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void _EnterTree()
|
||||||
|
{
|
||||||
|
//log.trace( $"Notification: _EnterTree" );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public override void _Ready()
|
||||||
|
{
|
||||||
|
|
||||||
|
this.OnReady();
|
||||||
|
|
||||||
|
|
||||||
|
// Only half useful
|
||||||
|
//var callable = Callable.From( ( Variant v ) => LogCallable( v ) );
|
||||||
|
//Godot.LogManager.RegisterLogCaptureNonThreadSafe( callable );
|
||||||
|
|
||||||
|
|
||||||
|
//this.OnReady();
|
||||||
|
|
||||||
|
/*
|
||||||
|
log.trace( $"Trace" );
|
||||||
|
log.debug( $"Debug" );
|
||||||
|
log.info ( $"Info" );
|
||||||
|
log.high ( $"High" );
|
||||||
|
log.warn ( $"Warn" );
|
||||||
|
log.error( $"Error" );
|
||||||
|
/*/
|
||||||
|
log.high( $"Skipping log demo" );
|
||||||
|
// */
|
||||||
|
|
||||||
|
//log.fatal( $"Fatal" );
|
||||||
|
|
||||||
|
|
||||||
|
Tools.ReadyType<Game>( this );
|
||||||
|
|
||||||
|
|
||||||
|
//Util.SetupSaveState();
|
||||||
|
|
||||||
|
if( DisplayNode != null && string.IsNullOrEmpty( DisplayNode.Name ) && DisplayNode.NamePath != null )
|
||||||
|
{
|
||||||
|
log.info( $"Adding DisplayNode:{DisplayNode.Name} SceneFilePath: {DisplayNode.SceneFilePath} to Display" );
|
||||||
|
Display.AddChild( DisplayNode );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
log.warn( $"DisplayNode is null, not adding to Display" );
|
||||||
|
}
|
||||||
|
|
||||||
|
log.high( $"**** Game scene loaded" );
|
||||||
|
}
|
||||||
|
|
||||||
|
override public void _Process( double dt )
|
||||||
|
{
|
||||||
|
using var zone = new prof.Zone();
|
||||||
|
|
||||||
|
var fpsF = 1.0 / dt;
|
||||||
|
|
||||||
|
int fps = (int)fpsF;
|
||||||
|
|
||||||
|
FPSLabel.Text = $"FPS: {fps}";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void SwitchToScene( PackedScene scene, string reason,
|
||||||
|
[CallerFilePath] string dbgPath = "", [CallerLineNumber] int dbgLine = -1, [CallerMemberName] string dbgMethod = "", [CallerArgumentExpression( "scene" )] string dbgExpScene = "" )
|
||||||
|
{
|
||||||
|
if( scene == null )
|
||||||
|
{
|
||||||
|
log.warn( $"Cannot switch to null scene {reason}" );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info( $"Switching to {scene.ResourcePath}" );
|
||||||
|
|
||||||
|
var node = scene.Instantiate();
|
||||||
|
//node.Name = $"{log.whatFile( dbgPath )}_{log.whatFile( scene.ResourcePath )}";
|
||||||
|
|
||||||
|
var newReason = $"{reason} ({log.whatFile( scene.ResourcePath )})";
|
||||||
|
|
||||||
|
var titleDetails = $"{node.Name}";
|
||||||
|
SetTitle( titleDetails );
|
||||||
|
|
||||||
|
SwitchTo( node, reason, dbgPath, dbgLine, dbgMethod, $"LoadScene({dbgExpScene})", dbgCallFromScene: true );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SwitchTo( Node sceneNode, string reason,
|
||||||
|
[CallerFilePath] string dbgPath = "", [CallerLineNumber] int dbgLine = -1, [CallerMemberName] string dbgMethod = "", [CallerArgumentExpression( "sceneNode" )] string dbgExpSceneNode = "", bool dbgCallFromScene = false )
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if( sceneNode == null )
|
||||||
|
{
|
||||||
|
log.warn( $"Cannot switch to null scene {reason}" );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info( $"Switching to {sceneNode.Name} bcs {reason} from {log.relativePath( dbgPath )}:({dbgLine}): {dbgMethod}" );
|
||||||
|
|
||||||
|
DisplayNode?.QueueFree();
|
||||||
|
foreach( var child in Display.GetChildren() )
|
||||||
|
{
|
||||||
|
child.QueueFree();
|
||||||
|
Display.RemoveChild( child );
|
||||||
|
}
|
||||||
|
DisplayNode = sceneNode;
|
||||||
|
Display.AddChild( DisplayNode );
|
||||||
|
|
||||||
|
if( !dbgCallFromScene )
|
||||||
|
{
|
||||||
|
var titleDetails = $"{log.whatFile( dbgPath )}.{dbgMethod}";
|
||||||
|
SetTitle( titleDetails );
|
||||||
|
}
|
||||||
|
|
||||||
|
_reason = reason;
|
||||||
|
}
|
||||||
|
catch( Exception ex )
|
||||||
|
{
|
||||||
|
log.warn( $"Caught ex {ex.Message} while trying to go to {sceneNode.Name} from res://{log.relativePath( dbgPath )}:({dbgLine}): {dbgMethod}" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
private void XButton_Pressed()
|
||||||
|
{
|
||||||
|
//SwitchTo( MainMenu );
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// //
|
||||||
|
///
|
||||||
1
addons/core/core/Game.cs.uid
Normal file
1
addons/core/core/Game.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://c4g565oftdnax
|
||||||
38
addons/core/core/Game.tscn
Normal file
38
addons/core/core/Game.tscn
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
[gd_scene load_steps=2 format=3 uid="uid://b22oq70jqfh2o"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://addons/core/core/Game.cs" id="1_2r7cu"]
|
||||||
|
|
||||||
|
[node name="Game" type="CanvasLayer"]
|
||||||
|
layer = 0
|
||||||
|
script = ExtResource("1_2r7cu")
|
||||||
|
|
||||||
|
[node name="AR" type="AspectRatioContainer" parent="."]
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
offset_left = -200.0
|
||||||
|
offset_right = 200.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
|
||||||
|
[node name="Display" type="PanelContainer" parent="AR"]
|
||||||
|
custom_minimum_size = Vector2(3200, 2.08165e-12)
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="DEBUGWarning" type="Label" parent="AR/Display"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 4
|
||||||
|
text = "DEFAULT SCENE
|
||||||
|
|
||||||
|
Something Broke! This shouldnt be here"
|
||||||
|
|
||||||
|
[node name="FPSLabel" type="Label" parent="."]
|
||||||
|
offset_left = 50.0
|
||||||
|
offset_top = 39.0
|
||||||
|
offset_right = 86.0
|
||||||
|
offset_bottom = 62.0
|
||||||
|
theme_override_colors/font_color = Color(1, 1, 1, 0.254902)
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "FPS: "
|
||||||
|
|
||||||
|
[connection signal="visibility_changed" from="." to="." method="_on_visibility_changed"]
|
||||||
28
addons/core/core/MPNode.cs
Normal file
28
addons/core/core/MPNode.cs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// D E R E L I C T
|
||||||
|
//
|
||||||
|
/// // (c) 2003..2025
|
||||||
|
|
||||||
|
using Godot;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
[GlobalClass]
|
||||||
|
public partial class MPNode : Node3D
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
virtual public string GetNameFromId( long id ) => $"{id}";
|
||||||
|
|
||||||
|
// Called when the node enters the scene tree for the first time.
|
||||||
|
public override void _Ready()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||||
|
public override void _Process( double delta )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
1
addons/core/core/MPNode.cs.uid
Normal file
1
addons/core/core/MPNode.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://dykdxaolk0qpa
|
||||||
52
addons/core/core/NodeSpace.cs
Normal file
52
addons/core/core/NodeSpace.cs
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// D E R E L I C T
|
||||||
|
//
|
||||||
|
/// // (c) 2003..2025
|
||||||
|
|
||||||
|
using Godot;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
[GlobalClass]
|
||||||
|
public partial class NodeSpace : Node
|
||||||
|
{
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public string NodeClass;
|
||||||
|
|
||||||
|
|
||||||
|
public void ReplaceByScene( PackedScene scene )
|
||||||
|
{
|
||||||
|
var node = scene.Instantiate();
|
||||||
|
node.Name = Name;
|
||||||
|
|
||||||
|
log.info( $"Replace {NodeClass} with {node.GetType().Name}" );
|
||||||
|
|
||||||
|
ReplaceBy( node );
|
||||||
|
|
||||||
|
QueueFree();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReplaceByScene( string scenePath )
|
||||||
|
{
|
||||||
|
log.info( $"Replacing {Name} by {scenePath} (at {GetPath()})" );
|
||||||
|
var scene = ResourceLoader.Load<PackedScene>( scenePath );
|
||||||
|
|
||||||
|
if( scene != null )
|
||||||
|
ReplaceByScene( scene );
|
||||||
|
else
|
||||||
|
{
|
||||||
|
log.warn( $"Could not load {scenePath}" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called when the node enters the scene tree for the first time.
|
||||||
|
public override void _Ready()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||||
|
public override void _Process( double delta )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
1
addons/core/core/NodeSpace.cs.uid
Normal file
1
addons/core/core/NodeSpace.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://d3uvyevm1600t
|
||||||
86
addons/core/fsm/FSMNode.cs
Normal file
86
addons/core/fsm/FSMNode.cs
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// D E R E L I C T
|
||||||
|
//
|
||||||
|
/// // (c) 2003..2024
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Godot;
|
||||||
|
using Godot.Sharp.Extras;
|
||||||
|
|
||||||
|
namespace fsm;
|
||||||
|
|
||||||
|
[GlobalClass]
|
||||||
|
public partial class FSMNode : Node
|
||||||
|
{
|
||||||
|
|
||||||
|
[ExportCategory( "Config" )]
|
||||||
|
[Export]
|
||||||
|
public NodePath InitialNodePath = "Movement";
|
||||||
|
|
||||||
|
|
||||||
|
[ExportCategory( "Runtime" )]
|
||||||
|
[Export]
|
||||||
|
public StateNode Current = new StateNode();
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public NodePath _nextNode;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public virtual void Transition( NodePath nextNode )
|
||||||
|
{
|
||||||
|
if( nextNode == _nextNode )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if( !_nextNode.IsEmpty )
|
||||||
|
{
|
||||||
|
log.warn( $"{log.loc().Log}: Was going to {_nextNode} but instead going to {nextNode}" );
|
||||||
|
}
|
||||||
|
|
||||||
|
_nextNode = nextNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public override void _Ready()
|
||||||
|
{
|
||||||
|
base._Ready();
|
||||||
|
this.OnReady();
|
||||||
|
|
||||||
|
Current.Name = "NOPState";
|
||||||
|
|
||||||
|
_nextNode = InitialNodePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public override void _PhysicsProcess( double dt )
|
||||||
|
{
|
||||||
|
base._PhysicsProcess( dt );
|
||||||
|
|
||||||
|
//Do the transition
|
||||||
|
if( !_nextNode.IsEmpty )
|
||||||
|
{
|
||||||
|
var nextNode = GetNodeOrNull<StateNode>( _nextNode );
|
||||||
|
|
||||||
|
var oldNodePath = new NodePath( Current.Name );
|
||||||
|
|
||||||
|
Current.OnExit( _nextNode );
|
||||||
|
|
||||||
|
Current.ProcessMode = Node.ProcessModeEnum.Disabled;
|
||||||
|
|
||||||
|
Current = nextNode;
|
||||||
|
|
||||||
|
Current.OnEnter( oldNodePath );
|
||||||
|
|
||||||
|
Current.ProcessMode = Node.ProcessModeEnum.Pausable;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
1
addons/core/fsm/FSMNode.cs.uid
Normal file
1
addons/core/fsm/FSMNode.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://cwq218bv6mw6x
|
||||||
47
addons/core/fsm/StateNode.cs
Normal file
47
addons/core/fsm/StateNode.cs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// D E R E L I C T
|
||||||
|
//
|
||||||
|
/// // (c) 2003..2024
|
||||||
|
using System;
|
||||||
|
using Godot;
|
||||||
|
using Godot.Sharp.Extras;
|
||||||
|
|
||||||
|
namespace fsm;
|
||||||
|
|
||||||
|
|
||||||
|
[GlobalClass]
|
||||||
|
public partial class StateNode : Node
|
||||||
|
{
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public bool _moveAndRotate = true;
|
||||||
|
|
||||||
|
public virtual void OnEnter( NodePath oldNode )
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void OnExit( NodePath newNode )
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void _Ready()
|
||||||
|
{
|
||||||
|
base._Ready();
|
||||||
|
this.OnReady();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void _PhysicsProcess( double dt )
|
||||||
|
{
|
||||||
|
base._PhysicsProcess( dt );
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
1
addons/core/fsm/StateNode.cs.uid
Normal file
1
addons/core/fsm/StateNode.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://bmi8t3kreo03t
|
||||||
22
addons/core/math/CentEx.cs
Normal file
22
addons/core/math/CentEx.cs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static public class Vector3CmExt
|
||||||
|
{
|
||||||
|
|
||||||
|
public static Vector3Cm ToVector3Cm( this Godot.Vector3 vec ) => new Vector3Cm( vec.X.Cm, vec.Y.Cm, vec.Z.Cm );
|
||||||
|
|
||||||
|
|
||||||
|
public static Godot.Vector3 ToVector3( this Vector3Cm vecCm )
|
||||||
|
{
|
||||||
|
var xF = vecCm.x.Float;
|
||||||
|
var yF = vecCm.y.Float;
|
||||||
|
var zF = vecCm.z.Float;
|
||||||
|
|
||||||
|
return new Godot.Vector3( xF, yF, zF );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
1
addons/core/math/CentEx.cs.uid
Normal file
1
addons/core/math/CentEx.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://c1cqu7yvvsd75
|
||||||
152
addons/core/math/Geo.cs
Normal file
152
addons/core/math/Geo.cs
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
using Godot;
|
||||||
|
|
||||||
|
public static class Geo
|
||||||
|
{
|
||||||
|
|
||||||
|
public enum IntersectType
|
||||||
|
{
|
||||||
|
Before = -1, // Intersection with the infinite line occurs before p1
|
||||||
|
In = 0, // Intersection occurs between p1 and p2
|
||||||
|
After = 1, // Intersection with the infinite line occurs after p2
|
||||||
|
Short = 2 // The sphere is already past the segment's endpoint
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculates the intersection of a sphere with a line segment, always returning a point on the segment.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sphereCenter">The world position of the sphere's center.</param>
|
||||||
|
/// <param name="sphereRadius">The radius of the sphere.</param>
|
||||||
|
/// <param name="segmentP1">The starting point of the line segment.</param>
|
||||||
|
/// <param name="segmentP2">The ending point of the line segment.</param>
|
||||||
|
/// <param name="intersectionPoint">The resulting intersection point, clamped to the segment.</param>
|
||||||
|
/// <param name="intersectionType">The type of intersection that occurred.</param>
|
||||||
|
/// <returns>True if an intersection with the infinite line is mathematically possible, false otherwise.</returns>
|
||||||
|
public static bool SphereSegment(
|
||||||
|
Vector3 sphereCenter,
|
||||||
|
float sphereRadius,
|
||||||
|
Vector3 segmentP1,
|
||||||
|
Vector3 segmentP2,
|
||||||
|
out Vector3 intersectionPoint,
|
||||||
|
out IntersectType intersectionType )
|
||||||
|
{
|
||||||
|
Vector3 segmentVector = segmentP2 - segmentP1;
|
||||||
|
float segmentLengthSq = segmentVector.LengthSquared();
|
||||||
|
|
||||||
|
if( segmentLengthSq < 0.0001f )
|
||||||
|
{
|
||||||
|
intersectionPoint = segmentP2;
|
||||||
|
intersectionType = IntersectType.Short;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Project sphereCenter onto the line defined by the segment
|
||||||
|
float t = ( sphereCenter - segmentP1 ).Dot( segmentVector ) / segmentLengthSq;
|
||||||
|
Vector3 closestPointOnLine = segmentP1 + t * segmentVector;
|
||||||
|
|
||||||
|
float distSq = sphereCenter.DistanceSquaredTo( closestPointOnLine );
|
||||||
|
if( distSq > sphereRadius * sphereRadius )
|
||||||
|
{
|
||||||
|
// No intersection with the infinite line, so return the closest point on the segment.
|
||||||
|
intersectionPoint = closestPointOnLine; //.Clamp(segmentP1, segmentP2);
|
||||||
|
intersectionType = t < 0 ? IntersectType.Before : IntersectType.After;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the intersection point on the infinite line
|
||||||
|
float offset = Mathf.Sqrt( sphereRadius * sphereRadius - distSq );
|
||||||
|
Vector3 idealIntersection = closestPointOnLine + segmentVector.Normalized() * offset;
|
||||||
|
|
||||||
|
// Determine the intersection type based on the ideal point's position
|
||||||
|
float finalT = ( idealIntersection - segmentP1 ).Dot( segmentVector ) / segmentLengthSq;
|
||||||
|
|
||||||
|
if( finalT < 0 )
|
||||||
|
{
|
||||||
|
intersectionType = IntersectType.Before;
|
||||||
|
}
|
||||||
|
else if( finalT > 1.0f )
|
||||||
|
{
|
||||||
|
intersectionType = IntersectType.After;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
intersectionType = IntersectType.In;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always return a point clamped to the segment
|
||||||
|
intersectionPoint = idealIntersection; //.Clamp(segmentP1, segmentP2);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculates the intersection of a sphere and a line segment.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sphereCenter">The world position of the sphere's center.</param>
|
||||||
|
/// <param name="sphereRadius">The radius of the sphere.</param>
|
||||||
|
/// <param name="segmentP1">The starting point of the line segment.</param>
|
||||||
|
/// <param name="segmentP2">The ending point of the line segment.</param>
|
||||||
|
/// <param name="intersectionPoint">The point where the sphere first intersects the infinite line.</param>
|
||||||
|
/// <param name="intersectionType">Indicates where the intersection lies: -1 for before the segment, 0 for on the segment, 1 for after the segment.</param>
|
||||||
|
/// <returns>True if an intersection with the infinite line exists, false otherwise.</returns>
|
||||||
|
public static bool SphereSegment_old(
|
||||||
|
Vector3 sphereCenter,
|
||||||
|
float sphereRadius,
|
||||||
|
Vector3 segmentP1,
|
||||||
|
Vector3 segmentP2,
|
||||||
|
out Vector3 intersectionPoint,
|
||||||
|
out IntersectType intersectionType )
|
||||||
|
{
|
||||||
|
// Initialize out parameters
|
||||||
|
//intersectionPoint = Vector3.Zero;
|
||||||
|
intersectionType = IntersectType.Before;
|
||||||
|
|
||||||
|
Vector3 segmentVector = segmentP2 - segmentP1;
|
||||||
|
float segmentLength = segmentVector.Length();
|
||||||
|
|
||||||
|
// Handle zero-length segment case
|
||||||
|
if( segmentLength < 0.0001f )
|
||||||
|
{
|
||||||
|
intersectionType = IntersectType.Short;
|
||||||
|
intersectionPoint = segmentP2;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3 lineDir = segmentVector / segmentLength;
|
||||||
|
|
||||||
|
// 1. Find the closest point on the infinite line to the sphere's center
|
||||||
|
var toCenter = sphereCenter - segmentP1;
|
||||||
|
float t = toCenter.Dot( lineDir );
|
||||||
|
var closestPointOnLine = segmentP1 + lineDir * t;
|
||||||
|
|
||||||
|
bool isOnLine = true;
|
||||||
|
|
||||||
|
// 2. Check if an intersection is possible
|
||||||
|
float distSq = sphereCenter.DistanceSquaredTo( closestPointOnLine );
|
||||||
|
if( distSq > sphereRadius * sphereRadius )
|
||||||
|
{
|
||||||
|
isOnLine = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Calculate the intersection point
|
||||||
|
float offset = Mathf.Sqrt( sphereRadius * sphereRadius - distSq );
|
||||||
|
intersectionPoint = closestPointOnLine - lineDir * offset;
|
||||||
|
|
||||||
|
// 4. Determine where the intersection lies relative to the segment
|
||||||
|
float intersectionT = ( intersectionPoint - segmentP1 ).Dot( lineDir );
|
||||||
|
|
||||||
|
if( intersectionT < 0 )
|
||||||
|
{
|
||||||
|
intersectionType = IntersectType.Before; // Before the segment
|
||||||
|
}
|
||||||
|
else if( intersectionT > segmentLength )
|
||||||
|
{
|
||||||
|
intersectionType = IntersectType.After; // After the segment
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
intersectionType = IntersectType.In; // On the segment
|
||||||
|
}
|
||||||
|
|
||||||
|
return isOnLine;
|
||||||
|
}
|
||||||
|
}
|
||||||
1
addons/core/math/Geo.cs.uid
Normal file
1
addons/core/math/Geo.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://bsqypnus3htcr
|
||||||
67
addons/core/math/MathUtil.cs
Normal file
67
addons/core/math/MathUtil.cs
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// D E R E L I C T
|
||||||
|
//
|
||||||
|
/// // (c) 2003..2025
|
||||||
|
|
||||||
|
using Godot;
|
||||||
|
using Godot.Sharp.Extras;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static public class MathExtensions
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
static public int Range( this Random rand, int top )
|
||||||
|
{
|
||||||
|
return rand.Range( 0, top );
|
||||||
|
}
|
||||||
|
static public float Range( this Random rand, float top )
|
||||||
|
{
|
||||||
|
return rand.Range( 0.0f, top );
|
||||||
|
}
|
||||||
|
|
||||||
|
static public double Range( this Random rand, double top )
|
||||||
|
{
|
||||||
|
return rand.Range( 0.0, top );
|
||||||
|
}
|
||||||
|
|
||||||
|
static public int Range( this Random rand, int bot, int top )
|
||||||
|
{
|
||||||
|
var range = top - bot;
|
||||||
|
var r = ( rand.Next() % range ) + bot;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static public float Range( this Random rand, float bot, float top )
|
||||||
|
{
|
||||||
|
var range = top - bot;
|
||||||
|
var r = ( rand.NextSingle() * range ) + bot;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static public double Range( this Random rand, double bot, double top )
|
||||||
|
{
|
||||||
|
var range = top - bot;
|
||||||
|
var r = ( rand.NextDouble() * range ) + bot;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
//static public string ToLog(this Vector2 v) => $"{v.X:0.00}, {v.Y:0.00}";
|
||||||
|
|
||||||
|
extension( Vector2 v )
|
||||||
|
{
|
||||||
|
public string Log => $"{v.X:0.00}, {v.Y:0.00}";
|
||||||
|
}
|
||||||
|
|
||||||
|
extension( Vector3 v )
|
||||||
|
{
|
||||||
|
public string Log => $"{v.X:0.00}, {v.Y:0.00}, {v.Z:0.00}";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
1
addons/core/math/MathUtil.cs.uid
Normal file
1
addons/core/math/MathUtil.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://qigyamlcoeb6
|
||||||
32
addons/core/math/Rand.cs
Normal file
32
addons/core/math/Rand.cs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// D E R E L I C T
|
||||||
|
//
|
||||||
|
/// // (c) 2003..2025
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
//namespace client.addons.core.math;
|
||||||
|
|
||||||
|
// No namespace, just directly access rand
|
||||||
|
|
||||||
|
static public class rand
|
||||||
|
{
|
||||||
|
static ThreadLocal<Random> s_rand = new( () => new Random() );
|
||||||
|
|
||||||
|
static rand()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const int c_bitForBinary = 1 << 24;
|
||||||
|
|
||||||
|
static public double unit() => s_rand.Value.NextDouble();
|
||||||
|
|
||||||
|
static public bool binary() => ( s_rand.Value.NextInt64() & c_bitForBinary ) != 0;
|
||||||
|
|
||||||
|
}
|
||||||
1
addons/core/math/Rand.cs.uid
Normal file
1
addons/core/math/Rand.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://ddipuscm6hwr2
|
||||||
38
addons/core/math/Real.cs
Normal file
38
addons/core/math/Real.cs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// D E R E L I C T
|
||||||
|
//
|
||||||
|
/// // (c) 2003..2025
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Godot;
|
||||||
|
|
||||||
|
|
||||||
|
static public class SingleEx
|
||||||
|
{
|
||||||
|
|
||||||
|
static public float Lerp( this float start, float end, float amount ) => start + ( end - start ) * amount;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static public class DoubleEx
|
||||||
|
{
|
||||||
|
|
||||||
|
static public double Lerp( this double start, double end, double amount ) => start + ( end - start ) * amount;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static public class Vector3Ex
|
||||||
|
{
|
||||||
|
|
||||||
|
extension( Node3D n )
|
||||||
|
{
|
||||||
|
public Vector3 Forward => -n.Transform.Basis.Z;
|
||||||
|
public Vector3 Right => n.Transform.Basis.X;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
1
addons/core/math/Real.cs.uid
Normal file
1
addons/core/math/Real.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://vv5n0q4hbsbk
|
||||||
201
addons/core/misc/Helpers.cs
Normal file
201
addons/core/misc/Helpers.cs
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// D E R E L I C T
|
||||||
|
//
|
||||||
|
/// // (c) 2003..2024
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
using Godot;
|
||||||
|
|
||||||
|
|
||||||
|
using GodotDictionary = Godot.Collections.Dictionary;
|
||||||
|
|
||||||
|
public static class U
|
||||||
|
{
|
||||||
|
public static string NamePath( this Node current )
|
||||||
|
{
|
||||||
|
if( current is null )
|
||||||
|
return "";
|
||||||
|
|
||||||
|
var curName = current.Name.ToString();
|
||||||
|
|
||||||
|
return current.GetParent().NamePath() + "/" + curName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a <see cref="GodotDictionary"/> containing the necessary keys for use with an object overriding
|
||||||
|
/// <c>_GetPropertyList</c>.
|
||||||
|
/// </summary>
|
||||||
|
public static GodotDictionary MakeProperty(
|
||||||
|
Type classType,
|
||||||
|
string name,
|
||||||
|
Variant.Type type,
|
||||||
|
PropertyUsageFlags usage = PropertyUsageFlags.Default,
|
||||||
|
PropertyHint hint = PropertyHint.None,
|
||||||
|
string? hintString = "" )
|
||||||
|
{
|
||||||
|
return new GodotDictionary
|
||||||
|
{
|
||||||
|
["name"] = name,
|
||||||
|
["class_name"] = classType.Name,
|
||||||
|
["type"] = Variant.From( type ),
|
||||||
|
["usage"] = Variant.From( usage ),
|
||||||
|
["hint"] = Variant.From( hint ),
|
||||||
|
["hint_string"] = hintString ?? ""
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a hint string for the given enum type to be used when setting the <c>hint_string</c> entry of a
|
||||||
|
/// <c>_GetPropertyList</c> result.
|
||||||
|
/// </summary>
|
||||||
|
/// <seealso cref="MakeProperty"/>
|
||||||
|
public static string EnumHintString<T>()
|
||||||
|
where T : Enum
|
||||||
|
{
|
||||||
|
var s = string.Empty;
|
||||||
|
foreach( var e in Enum.GetValues( typeof( T ) ) )
|
||||||
|
{
|
||||||
|
if( !string.IsNullOrWhiteSpace( s ) )
|
||||||
|
s += ",";
|
||||||
|
s += $"{e}:{(int)e}";
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string EnumHintString( Type type )
|
||||||
|
{
|
||||||
|
var s = string.Empty;
|
||||||
|
foreach( var e in Enum.GetValues( type ) )
|
||||||
|
{
|
||||||
|
if( !string.IsNullOrWhiteSpace( s ) )
|
||||||
|
s += ",";
|
||||||
|
s += $"{e}:{(int)e}";
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly HashSet<Type> ActionTypes = new() {
|
||||||
|
typeof(Action), typeof(Action<>), typeof(Action<,>), typeof(Action<,,>), typeof(Action<,,,>),
|
||||||
|
typeof(Action<,,,,>), typeof(Action<,,,,,>), typeof(Action<,,,,,,>)
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly HashSet<Type> FunctionTypes = new() {
|
||||||
|
typeof(Func<>), typeof(Func<,>), typeof(Func<,,>), typeof(Func<,,,>),
|
||||||
|
typeof(Func<,,,,>), typeof(Func<,,,,,>), typeof(Func<,,,,,,>)
|
||||||
|
};
|
||||||
|
|
||||||
|
private static bool TypeIsAction( Type t )
|
||||||
|
=> ActionTypes.Contains( t ) || ( t.IsGenericType && ActionTypes.Contains( t.GetGenericTypeDefinition() ) );
|
||||||
|
|
||||||
|
private static bool TypeIsFunc( Type t )
|
||||||
|
=> FunctionTypes.Contains( t ) || ( t.IsGenericType && FunctionTypes.Contains( t.GetGenericTypeDefinition() ) );
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempt to retrieve a function reference to the method with the given <c>methodName</c> in the given
|
||||||
|
/// <c>targetNode</c>.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>
|
||||||
|
/// The delegate for the method, or <c>null</c> if the editor is currently running. This is to avoid throwing
|
||||||
|
/// unnecessary exceptions when using a <c>Tool</c> class in the editor. This function will not return <c>null</c>
|
||||||
|
/// during application runtime.
|
||||||
|
/// </returns>
|
||||||
|
/// <exception cref="ArgumentException">
|
||||||
|
/// The method is not found or has invalid parameters.
|
||||||
|
/// </exception>
|
||||||
|
public static T? GetMethodDelegateForNode<T>( Node targetNode, string methodName )
|
||||||
|
where T : Delegate
|
||||||
|
{
|
||||||
|
if( targetNode is null )
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException( nameof( targetNode ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !TypeIsAction( typeof( T ) ) && !TypeIsFunc( typeof( T ) ) )
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException( "The delegate being created must be of type Action or Func" );
|
||||||
|
}
|
||||||
|
|
||||||
|
var method = targetNode.GetType()
|
||||||
|
.GetMethods( BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance )
|
||||||
|
.FirstOrDefault( m => m.Name == methodName );
|
||||||
|
|
||||||
|
if( method is null )
|
||||||
|
{
|
||||||
|
// Method with given name not found
|
||||||
|
//if (Engine.EditorHint) {
|
||||||
|
// If the editor is running, we do not any information on non-Tool types. We don't want to throw
|
||||||
|
// errors when there isn't actually a problem, so let's just let the caller deal with it.
|
||||||
|
// return null;
|
||||||
|
//}
|
||||||
|
|
||||||
|
throw new ArgumentException( $"Unable to find method \"{methodName}\" in node \"{targetNode.Name}\"" );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( TypeIsAction( typeof( T ) ) )
|
||||||
|
{
|
||||||
|
var generics = typeof( T ).GetGenericArguments();
|
||||||
|
if( method.GetParameters().Count() != generics.Count()
|
||||||
|
|| method.GetParameters()
|
||||||
|
.Zip( generics, ( p, t ) => (p, t) )
|
||||||
|
.Any( pair => pair.p.ParameterType != pair.t ) )
|
||||||
|
{
|
||||||
|
// Arguments don't match
|
||||||
|
throw new ArgumentException( $"Invalid parameters for method \"{methodName}\"" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if( TypeIsFunc( typeof( T ) ) )
|
||||||
|
{
|
||||||
|
var generics = typeof( T ).GetGenericArguments();
|
||||||
|
if( generics.Any() )
|
||||||
|
{
|
||||||
|
var ret = generics[0];
|
||||||
|
var args = generics.Skip( 1 ).ToList();
|
||||||
|
|
||||||
|
if( method.ReturnType != ret )
|
||||||
|
{
|
||||||
|
throw new ArgumentException( $"Invalid return type for method \"{methodName}\"" );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( method.GetParameters()
|
||||||
|
.Zip( args, ( p, t ) => (p, t) )
|
||||||
|
.Any( pair => pair.p.ParameterType != pair.t ) )
|
||||||
|
{
|
||||||
|
throw new ArgumentException( $"Invalid parameter types for method \"{methodName}\"" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (T)Delegate.CreateDelegate( typeof( T ), targetNode, methodName );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Dummy attribute to mark exports that are already handled by an overridden <c>_GetPropertyList</c>.
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage( AttributeTargets.Field | AttributeTargets.Property )]
|
||||||
|
public class ExportFakeAttribute : Attribute
|
||||||
|
{
|
||||||
|
public ExportFakeAttribute()
|
||||||
|
{ }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class NodeExtension
|
||||||
|
{
|
||||||
|
public static IEnumerable<Node> GetChildNodes( this Node node )
|
||||||
|
=> node.GetChildren().Cast<Node>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Needed to prevent some dumb ass error.
|
||||||
|
namespace System.Runtime.CompilerServices
|
||||||
|
{
|
||||||
|
internal static class IsExternalInit { }
|
||||||
|
}
|
||||||
1
addons/core/misc/Helpers.cs.uid
Normal file
1
addons/core/misc/Helpers.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://c26dkyji1wixy
|
||||||
25
addons/core/misc/OLib.cs
Normal file
25
addons/core/misc/OLib.cs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// D E R E L I C T
|
||||||
|
//
|
||||||
|
/// // (c) 2003..2024
|
||||||
|
using Godot;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
[GlobalClass]
|
||||||
|
public partial class OLib : Node
|
||||||
|
{
|
||||||
|
// Called when the node enters the scene tree for the first time.
|
||||||
|
public override void _Ready()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||||
|
public override void _Process( double delta )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void Info( string msg ) => log.info( msg );
|
||||||
|
|
||||||
|
}
|
||||||
1
addons/core/misc/OLib.cs.uid
Normal file
1
addons/core/misc/OLib.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://cajpiw207t04y
|
||||||
300
addons/core/misc/POD.cs
Normal file
300
addons/core/misc/POD.cs
Normal file
@ -0,0 +1,300 @@
|
|||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// D E R E L I C T
|
||||||
|
//
|
||||||
|
/// // (c) 2003..2024
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
|
using Godot;
|
||||||
|
//using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
|
|
||||||
|
using Godot.Collections;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using sr = System.Reflection;
|
||||||
|
using gb = Godot.Bridge;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Collections;
|
||||||
|
|
||||||
|
public readonly struct PropertyInfo_Test
|
||||||
|
{
|
||||||
|
public Variant.Type Type { get; init; }
|
||||||
|
|
||||||
|
public StringName Name { get; init; }
|
||||||
|
|
||||||
|
public PropertyHint Hint { get; init; }
|
||||||
|
|
||||||
|
public string HintString { get; init; }
|
||||||
|
|
||||||
|
public PropertyUsageFlags Usage { get; init; }
|
||||||
|
|
||||||
|
public StringName? ClassName { get; init; }
|
||||||
|
|
||||||
|
public bool Exported { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//[GlobalClass]
|
||||||
|
public partial class POD<T> : GodotObject
|
||||||
|
where T : io.Obj, new()
|
||||||
|
{
|
||||||
|
|
||||||
|
static public gb.PropertyInfo GetPropertyInfo( Type type, string name )
|
||||||
|
{
|
||||||
|
var gt = Util.CSTypeCodeToGodotVariantType( Type.GetTypeCode( type ) );
|
||||||
|
|
||||||
|
var propHint = type.IsEnum ? PropertyHint.Enum : PropertyHint.None;
|
||||||
|
var propString = type.IsEnum ? U.EnumHintString( type ) : "";
|
||||||
|
|
||||||
|
gb.PropertyInfo info = new(
|
||||||
|
gt,
|
||||||
|
name,
|
||||||
|
propHint,
|
||||||
|
propString,
|
||||||
|
PropertyUsageFlags.Default,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if DOCUMENTATION
|
||||||
|
public MethodInfo(
|
||||||
|
StringName name,
|
||||||
|
PropertyInfo returnVal,
|
||||||
|
MethodFlags flags,
|
||||||
|
List<PropertyInfo>? arguments,
|
||||||
|
List<Variant>? defaultArguments)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
static public List<gb.MethodInfo> GetGodotMethodList()
|
||||||
|
{
|
||||||
|
return PODEnergyCom.GetGodotMethodList();
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
//*
|
||||||
|
|
||||||
|
static public List<gb.MethodInfo> GetGodotMethodList_bad()
|
||||||
|
{
|
||||||
|
List<gb.MethodInfo> methods = new();
|
||||||
|
|
||||||
|
var podType = typeof( POD<T> );
|
||||||
|
|
||||||
|
var methodsArr = podType.GetMethods( sr.BindingFlags.DeclaredOnly | sr.BindingFlags.Public | sr.BindingFlags.NonPublic );
|
||||||
|
|
||||||
|
foreach( var mi in methodsArr )
|
||||||
|
{
|
||||||
|
var retInfo = GetPropertyInfo( mi.ReturnType, "return" );
|
||||||
|
|
||||||
|
List<gb.PropertyInfo> argsInfo = new();
|
||||||
|
|
||||||
|
foreach( var pi in mi.GetParameters() )
|
||||||
|
{
|
||||||
|
//sr.ParameterInfo;
|
||||||
|
GetPropertyInfo( pi.ParameterType, pi.Name! );
|
||||||
|
}
|
||||||
|
|
||||||
|
gb.MethodInfo gmi = new()
|
||||||
|
{
|
||||||
|
Name = mi.Name,
|
||||||
|
Flags = MethodFlags.Default,
|
||||||
|
ReturnVal = retInfo,
|
||||||
|
Arguments = argsInfo
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
methods.Add( gmi );
|
||||||
|
}
|
||||||
|
|
||||||
|
//methods.AddRange( methodsArr );
|
||||||
|
|
||||||
|
return methods;
|
||||||
|
}
|
||||||
|
// */
|
||||||
|
|
||||||
|
/*
|
||||||
|
public gb.PropertyInfo ReturnVal { get; init; }
|
||||||
|
|
||||||
|
public MethodFlags Flags { get; init; }
|
||||||
|
|
||||||
|
public int Id { get; init; }
|
||||||
|
|
||||||
|
public List<gb.PropertyInfo>? Arguments { get; init; }
|
||||||
|
|
||||||
|
public List<Variant>? DefaultArguments { get; init; }
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
private T _pod;
|
||||||
|
|
||||||
|
public T Pod
|
||||||
|
{
|
||||||
|
get => _pod;
|
||||||
|
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_pod = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
public POD()
|
||||||
|
{
|
||||||
|
Pod = new T();
|
||||||
|
}
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
|
public POD( T in_pod )
|
||||||
|
{
|
||||||
|
_pod = in_pod;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public Type GetPODType()
|
||||||
|
{
|
||||||
|
return typeof( T );
|
||||||
|
}
|
||||||
|
|
||||||
|
ImmutableDictionary<StringName, FieldInfo> _properties = ImmutableDictionary<StringName, FieldInfo>.Empty;
|
||||||
|
|
||||||
|
//*
|
||||||
|
public override Variant _Get( StringName property )
|
||||||
|
{
|
||||||
|
var dbgName = property.ToString();
|
||||||
|
var prop = base._Get( property );
|
||||||
|
|
||||||
|
log.info( cat: "pod", msg: $"{GetType().Name}._Get: {dbgName} = {prop}" );
|
||||||
|
|
||||||
|
if( property == "script" && prop.VariantType == Variant.Type.Nil )
|
||||||
|
{
|
||||||
|
prop = Variant.CreateFrom( new StringName( $"{log.thisFilePath()}" ) );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//Skip this
|
||||||
|
}
|
||||||
|
|
||||||
|
if( _properties.TryGetValue( property, out var fi ) )
|
||||||
|
{
|
||||||
|
var val = fi.GetValue( _pod );
|
||||||
|
var variant = Util.Convert( val?.GetType(), val );
|
||||||
|
|
||||||
|
return variant;
|
||||||
|
}
|
||||||
|
|
||||||
|
return prop;
|
||||||
|
}
|
||||||
|
//*/
|
||||||
|
|
||||||
|
public override void _Notification( int what ) =>
|
||||||
|
base._Notification( what );
|
||||||
|
|
||||||
|
//*
|
||||||
|
public override Array<Dictionary> _GetPropertyList()
|
||||||
|
{
|
||||||
|
var other = base._GetPropertyList();
|
||||||
|
|
||||||
|
if( other == null )
|
||||||
|
{
|
||||||
|
other = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info( cat: "pod", msg: $"{GetType().Name} _GetPropertyList" );
|
||||||
|
|
||||||
|
var type = GetPODType();
|
||||||
|
|
||||||
|
var fields = refl.GetAllFields( type );
|
||||||
|
foreach( var fi in fields )
|
||||||
|
{
|
||||||
|
var tc = Type.GetTypeCode( fi.FieldType );
|
||||||
|
var gt = Util.CSTypeCodeToGodotVariantType( tc );
|
||||||
|
|
||||||
|
var isEnumerable = fi.FieldType.IsAssignableTo( typeof( IEnumerable ) );
|
||||||
|
var genericCount = fi.FieldType.GenericTypeArguments.Length;
|
||||||
|
|
||||||
|
if( isEnumerable && genericCount != 1 )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
|
||||||
|
var sn = new StringName( $"{refl.PrettyName( fi )}" );
|
||||||
|
|
||||||
|
var usage = PropertyUsageFlags.Default;
|
||||||
|
var hint = !fi.FieldType.IsEnum ? PropertyHint.None : PropertyHint.Enum;
|
||||||
|
var hintStr = !fi.FieldType.IsEnum ? "" : U.EnumHintString( fi.FieldType );
|
||||||
|
|
||||||
|
|
||||||
|
if( typeof( T ).Name == "Com" )
|
||||||
|
{
|
||||||
|
if( fi.FieldType.Name == "ComType" )
|
||||||
|
usage |= PropertyUsageFlags.ReadOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( isEnumerable )
|
||||||
|
{
|
||||||
|
hint = PropertyHint.ArrayType;
|
||||||
|
hintStr = "Object";
|
||||||
|
gt = Util.ConvertMarshalTypeToVariantType( typeof( IEnumerable ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( gt == Variant.Type.Nil )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
//usage |= PropertyUsageFlags.ReadOnly;
|
||||||
|
|
||||||
|
var dict = U.MakeProperty( this.GetType(), sn, gt,
|
||||||
|
hint: hint, hintString: hintStr );
|
||||||
|
|
||||||
|
/*
|
||||||
|
(new Godot.Collections.Dictionary()
|
||||||
|
{
|
||||||
|
{ "name", sn },
|
||||||
|
{ "type", (int)gt },
|
||||||
|
{ "usage", (int)usage },
|
||||||
|
//{ "hint", }
|
||||||
|
//{ "hint_string", $"Version {pod.Meta.Version} Reason {pod.Meta.Reason}" },
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
|
||||||
|
_properties = _properties.Add( sn, fi );
|
||||||
|
|
||||||
|
|
||||||
|
//dict[sn] = (long)gt;
|
||||||
|
|
||||||
|
log.info( cat: "pod", msg: $"{GetType().Name} PropList: Add {sn} type {gt} tc {tc}" );
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
other.Add( dict );
|
||||||
|
}
|
||||||
|
|
||||||
|
return other;
|
||||||
|
}
|
||||||
|
//*/
|
||||||
|
|
||||||
|
public override bool _PropertyCanRevert( StringName property ) =>
|
||||||
|
base._PropertyCanRevert( property );
|
||||||
|
|
||||||
|
public override Variant _PropertyGetRevert( StringName property ) =>
|
||||||
|
base._PropertyGetRevert( property );
|
||||||
|
|
||||||
|
public override bool _Set( StringName property, Variant value )
|
||||||
|
{
|
||||||
|
return base._Set( property, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void _ValidateProperty( Dictionary property ) =>
|
||||||
|
base._ValidateProperty( property );
|
||||||
|
public override bool Equals( object? obj ) =>
|
||||||
|
base.Equals( obj );
|
||||||
|
public override int GetHashCode() =>
|
||||||
|
base.GetHashCode();
|
||||||
|
public override string ToString() =>
|
||||||
|
base.ToString();
|
||||||
|
}
|
||||||
1
addons/core/misc/POD.cs.uid
Normal file
1
addons/core/misc/POD.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://bln74qohkdgbb
|
||||||
144
addons/core/misc/PODNode.cs
Normal file
144
addons/core/misc/PODNode.cs
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
using Godot;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
[Tool]
|
||||||
|
public partial class PODNode<T> : Node where T : class, new()
|
||||||
|
{
|
||||||
|
private T _data = new T();
|
||||||
|
|
||||||
|
// We can't export a generic type 'T', but we can still access it.
|
||||||
|
public T Data
|
||||||
|
{
|
||||||
|
get => _data;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_data = value ?? new T();
|
||||||
|
// If the data object is replaced, tell the editor to refresh the inspector.
|
||||||
|
if( Engine.IsEditorHint() )
|
||||||
|
{
|
||||||
|
NotifyPropertyListChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Godot.Collections.Array<Godot.Collections.Dictionary> _GetPropertyList()
|
||||||
|
{
|
||||||
|
var properties = new Godot.Collections.Array<Godot.Collections.Dictionary>();
|
||||||
|
|
||||||
|
//if (Data == null) return properties;
|
||||||
|
|
||||||
|
// Use reflection to find all writable properties in our POD.
|
||||||
|
PropertyInfo[] podProperties = typeof( T ).GetProperties( BindingFlags.Public | BindingFlags.Instance )
|
||||||
|
.Where( p => p.CanRead && p.CanWrite ).ToArray();
|
||||||
|
|
||||||
|
foreach( var prop in podProperties )
|
||||||
|
{
|
||||||
|
// Try to map the C# type to a Godot Variant type.
|
||||||
|
var (variantType, hint, hintString) = CSharpTypeToVariant( prop.PropertyType );
|
||||||
|
if( variantType == Variant.Type.Nil )
|
||||||
|
continue; // Skip unsupported types.
|
||||||
|
|
||||||
|
// Create the dictionary that Godot's Inspector understands.
|
||||||
|
properties.Add( new Godot.Collections.Dictionary
|
||||||
|
{
|
||||||
|
{ "name", $"{prop.Name}" }, // Use a category for organization.
|
||||||
|
{ "type", (int)variantType },
|
||||||
|
{ "usage", (int)PropertyUsageFlags.Default },
|
||||||
|
{ "hint", (int)hint },
|
||||||
|
{ "hint_string", hintString }
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Variant _Get( StringName property )
|
||||||
|
{
|
||||||
|
string propName = property.ToString();
|
||||||
|
if( !propName.StartsWith( "Data/" ) )
|
||||||
|
return base._Get( property );
|
||||||
|
|
||||||
|
string podPropName = propName.Remove( 0, 5 ); // Remove "Data/" prefix.
|
||||||
|
PropertyInfo propInfo = typeof( T ).GetProperty( podPropName );
|
||||||
|
|
||||||
|
if( propInfo != null )
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Ensure the property can be read.
|
||||||
|
if( !propInfo.CanRead )
|
||||||
|
{
|
||||||
|
GD.PrintErr( $"Property '{podPropName}' is not readable." );
|
||||||
|
return Variant.From<GodotObject>( null );
|
||||||
|
}
|
||||||
|
|
||||||
|
var value = Variant.From( propInfo.GetValue( Data ) );
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
catch( Exception ex )
|
||||||
|
{
|
||||||
|
GD.PrintErr( $"Failed to access property '{podPropName}': {ex.Message}" );
|
||||||
|
return Variant.From<GodotObject>( null );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return base._Get( property );
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool _Set( StringName property, Variant value )
|
||||||
|
{
|
||||||
|
string propName = property.ToString();
|
||||||
|
//if (!propName.StartsWith("Data/")) return base._Set(property, value);
|
||||||
|
|
||||||
|
string podPropName = propName.Remove( 0, 5 ); // Remove "Data/" prefix.
|
||||||
|
PropertyInfo propInfo = typeof( T ).GetProperty( podPropName );
|
||||||
|
|
||||||
|
if( propInfo != null )
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Convert Godot's Variant back to the correct C# type.
|
||||||
|
object convertedValue = value.AsType( propInfo.PropertyType );
|
||||||
|
|
||||||
|
propInfo.SetValue( Data, convertedValue );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch( Exception ex )
|
||||||
|
{
|
||||||
|
GD.PrintErr( $"Failed to set property '{podPropName}': {ex.Message}" );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return base._Set( property, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper to map C# types to Godot's Variant types and property hints.
|
||||||
|
private (Variant.Type, PropertyHint, string) CSharpTypeToVariant( Type type )
|
||||||
|
{
|
||||||
|
if( type == typeof( int ) || type == typeof( long ) )
|
||||||
|
return (Variant.Type.Int, PropertyHint.None, "");
|
||||||
|
if( type == typeof( uint ) || type == typeof( ulong ) )
|
||||||
|
return (Variant.Type.Int, PropertyHint.None, "");
|
||||||
|
if( type == typeof( float ) || type == typeof( double ) )
|
||||||
|
return (Variant.Type.Float, PropertyHint.None, "");
|
||||||
|
if( type == typeof( string ) )
|
||||||
|
return (Variant.Type.String, PropertyHint.None, "");
|
||||||
|
if( type == typeof( bool ) )
|
||||||
|
return (Variant.Type.Bool, PropertyHint.None, "");
|
||||||
|
if( type == typeof( Vector2 ) )
|
||||||
|
return (Variant.Type.Vector2, PropertyHint.None, "");
|
||||||
|
if( type == typeof( Vector3 ) )
|
||||||
|
return (Variant.Type.Vector3, PropertyHint.None, "");
|
||||||
|
if( type == typeof( Color ) )
|
||||||
|
return (Variant.Type.Color, PropertyHint.None, "");
|
||||||
|
if( type.IsEnum )
|
||||||
|
{
|
||||||
|
// Create a comma-separated string of enum names for a dropdown list.
|
||||||
|
string hintString = string.Join( ",", Enum.GetNames( type ) );
|
||||||
|
return (Variant.Type.Int, PropertyHint.Enum, hintString);
|
||||||
|
}
|
||||||
|
// Add more type mappings as needed.
|
||||||
|
return (Variant.Type.Nil, PropertyHint.None, ""); // Unsupported type.
|
||||||
|
}
|
||||||
|
}
|
||||||
1
addons/core/misc/PODNode.cs.uid
Normal file
1
addons/core/misc/PODNode.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://ddc51ql6c3qen
|
||||||
77
addons/core/misc/Samples.cs
Normal file
77
addons/core/misc/Samples.cs
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// D E R E L I C T
|
||||||
|
//
|
||||||
|
/// // (c) 2003..2024
|
||||||
|
//using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Skip for now
|
||||||
|
#if false
|
||||||
|
|
||||||
|
using cs;
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
|
||||||
|
static public class Samples
|
||||||
|
{
|
||||||
|
|
||||||
|
public static void CreateDataSamples()
|
||||||
|
{
|
||||||
|
ser.XmlCfg cfg = new()
|
||||||
|
{
|
||||||
|
POD = ser.POD.Elements,
|
||||||
|
};
|
||||||
|
|
||||||
|
//cfg.POD = lib.POD.Elements;
|
||||||
|
|
||||||
|
WriteSampleData<EntityData>( cfg );
|
||||||
|
|
||||||
|
WriteSampleData<CardData>( cfg );
|
||||||
|
|
||||||
|
//res.Ref<DeckData> deckRef = new( "samples/DeckData.xml" );
|
||||||
|
|
||||||
|
//var deck = deckRef.res;
|
||||||
|
|
||||||
|
WriteSampleData( cfg, () => new BattleData( DebugSample.Create ) );
|
||||||
|
|
||||||
|
//WriteSampleData<DeckData>( cfg );
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void WriteSampleData<TD>( ser.XmlCfg cfg, Func<TD> fn ) where TD: cs.Data
|
||||||
|
=> WriteSampleData( cfg, typeof(TD).Name, fn() );
|
||||||
|
|
||||||
|
|
||||||
|
public static void WriteSampleData<TD>( ser.XmlCfg cfg )
|
||||||
|
where TD: cs.Data, new()
|
||||||
|
=> WriteSampleData( cfg, $"samples/{typeof(TD).Name}.xml", new TD() );
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static void WriteSampleData<TD>( ser.XmlCfg cfg, string path, TD entityData )
|
||||||
|
where TD: cs.Data
|
||||||
|
{
|
||||||
|
log.high( $"WriteSampleData: Writing {typeof(TD).Name} to {path}" );
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ser.XmlSer xml = new( cfg );
|
||||||
|
using FileStream stream = new( path, FileMode.Create );
|
||||||
|
|
||||||
|
xml.Serialize( stream, entityData );
|
||||||
|
}
|
||||||
|
catch( Exception ex )
|
||||||
|
{
|
||||||
|
log.warn( $"WriteSampleData: ex {ex.Message}" );
|
||||||
|
log.info( $"Stack.\n{ex.StackTrace}" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
1
addons/core/misc/Samples.cs.uid
Normal file
1
addons/core/misc/Samples.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://cnfixc632kn4v
|
||||||
62
addons/core/misc/Test.cs
Normal file
62
addons/core/misc/Test.cs
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// D E R E L I C T
|
||||||
|
//
|
||||||
|
/// // (c) 2003..2024
|
||||||
|
/*
|
||||||
|
So, someone on the Godot.CSharp channel asked about a generic way to do covariant style (I think its covariant) code for running a different function per type
|
||||||
|
|
||||||
|
I wrote a way to do it using static lambdas on a generic class, and realized I could do a trait style system with it.
|
||||||
|
|
||||||
|
Going to have to investigate it a bit further to see if it would make some code easier.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using Godot;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace dbg;
|
||||||
|
|
||||||
|
public class GenericSetText
|
||||||
|
{
|
||||||
|
static public void SetText<T>( T v, string text ) => GenericSetText<T>.FnSetText( v, text );
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GenericSetText<T>
|
||||||
|
{
|
||||||
|
static public Action<T, string> FnSetText = ( v, s ) => GD.PrintErr( $"On {typeof( T ).Name} trying to SetText" );
|
||||||
|
static public void SetText( T v, string text ) => GenericSetText<T>.FnSetText( v, text );
|
||||||
|
}
|
||||||
|
|
||||||
|
static public class GenericTest
|
||||||
|
{
|
||||||
|
static GenericTest()
|
||||||
|
{
|
||||||
|
GenericSetText<Control>.FnSetText = ( label, s ) =>
|
||||||
|
{
|
||||||
|
var type = typeof( GenericSetText<> ).MakeGenericType( label.GetType() );
|
||||||
|
var method = type.GetMethod( "SetText", 1, new Type[1] { label.GetType() } );
|
||||||
|
method.Invoke( label, new object[1] { s } );
|
||||||
|
};
|
||||||
|
|
||||||
|
GenericSetText<Label>.FnSetText = ( label, s ) => label.Text = s;
|
||||||
|
GenericSetText<Button>.FnSetText = ( button, s ) => button.Text = s;
|
||||||
|
GenericSetText<TextEdit>.FnSetText = ( textEdit, s ) => textEdit.Text = s;
|
||||||
|
GenericSetText<RichTextLabel>.FnSetText = ( richText, s ) => richText.Text = s;
|
||||||
|
|
||||||
|
GenericSetText<string>.FnSetText = ( v, s ) => { };
|
||||||
|
}
|
||||||
|
|
||||||
|
static public void RunTests()
|
||||||
|
{
|
||||||
|
Control controlTest = new Button();
|
||||||
|
GenericSetText.SetText( controlTest, "Button text" );
|
||||||
|
Button buttonTest = new();
|
||||||
|
GenericSetText.SetText( buttonTest, "Button text" );
|
||||||
|
Label labelTest = new();
|
||||||
|
GenericSetText.SetText( labelTest, "Label Text" );
|
||||||
|
TextEdit textEditTest = new();
|
||||||
|
GenericSetText.SetText( textEditTest, "Label Text" );
|
||||||
|
int integerTest = 10;
|
||||||
|
GenericSetText.SetText( integerTest, "Integer Test" );
|
||||||
|
}
|
||||||
|
}
|
||||||
1
addons/core/misc/Test.cs.uid
Normal file
1
addons/core/misc/Test.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://clm854vcbb75s
|
||||||
550
addons/core/misc/Util.cs
Normal file
550
addons/core/misc/Util.cs
Normal file
@ -0,0 +1,550 @@
|
|||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// D E R E L I C T
|
||||||
|
//
|
||||||
|
/// // (c) 2003..2024
|
||||||
|
using Godot;
|
||||||
|
//using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
|
|
||||||
|
using Godot.Collections;
|
||||||
|
using gen = System.Collections.Generic;
|
||||||
|
using Godot.Sharp.Extras;
|
||||||
|
using System;
|
||||||
|
using IO = System.IO;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using Godot.NativeInterop;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
using gb = Godot.Bridge;
|
||||||
|
using System.Runtime.InteropServices.ObjectiveC;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Xml;
|
||||||
|
using System.Buffers;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
|
||||||
|
|
||||||
|
namespace time
|
||||||
|
{
|
||||||
|
public record struct AbsUSec
|
||||||
|
{
|
||||||
|
static public AbsUSec Now => new AbsUSec( Time.GetTicksUsec() );
|
||||||
|
|
||||||
|
ulong _usec;
|
||||||
|
|
||||||
|
public ulong Abs => _usec;
|
||||||
|
|
||||||
|
public double Seconds => (double)_usec / 1_000_000.0;
|
||||||
|
|
||||||
|
#region Cons
|
||||||
|
public AbsUSec( ulong usec )
|
||||||
|
{
|
||||||
|
_usec = usec;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AbsUSec( long usec )
|
||||||
|
{
|
||||||
|
Debug.Assert( usec > 0 );
|
||||||
|
_usec = (ulong)usec;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
static public AbsUSec operator +( AbsUSec left, RelUSec right ) => new AbsUSec( (long)left.Abs + right.Rel );
|
||||||
|
|
||||||
|
static public RelUSec operator -( AbsUSec left, AbsUSec right ) => new RelUSec( (long)left.Abs - (long)right.Abs );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public record struct RelUSec
|
||||||
|
{
|
||||||
|
long _usec;
|
||||||
|
|
||||||
|
public long Rel => _usec;
|
||||||
|
|
||||||
|
public double Seconds => (double)_usec / 1_000_000.0;
|
||||||
|
|
||||||
|
public RelUSec( long usec )
|
||||||
|
{
|
||||||
|
_usec = usec;
|
||||||
|
}
|
||||||
|
|
||||||
|
static public RelUSec operator -( RelUSec left, RelUSec right ) => new RelUSec( (long)left.Rel - (long)right.Rel );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public record struct TargetInfo
|
||||||
|
{
|
||||||
|
public readonly time.AbsUSec LastSeen = time.AbsUSec.Now;
|
||||||
|
public readonly CollisionInfo3 Info;
|
||||||
|
|
||||||
|
public float HowOldSec => (float)( ( time.AbsUSec.Now - LastSeen ).Rel ) / 1_000_000.0f;
|
||||||
|
|
||||||
|
public TargetInfo( CollisionInfo3 info )
|
||||||
|
{
|
||||||
|
Info = info;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static public class Util
|
||||||
|
{
|
||||||
|
|
||||||
|
static public ImmutableList<T> FindAll<T>( this Node root ) where T : Node
|
||||||
|
{
|
||||||
|
log.debug( $"FindAll<{typeof( T )}>: {root.GetPath()}" );
|
||||||
|
|
||||||
|
var children = root.GetNodesOfType<T>();
|
||||||
|
|
||||||
|
foreach( var ch in children )
|
||||||
|
{
|
||||||
|
if( ch == null )
|
||||||
|
{
|
||||||
|
log.warn( $"Found null in list" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var immList = children.ToImmutableList();
|
||||||
|
|
||||||
|
var allChildren = root.GetChildren();
|
||||||
|
|
||||||
|
foreach( var child in allChildren )
|
||||||
|
{
|
||||||
|
var childList = child.FindAll<T>();
|
||||||
|
|
||||||
|
immList = immList.AddRange( childList );
|
||||||
|
}
|
||||||
|
|
||||||
|
return immList;
|
||||||
|
}
|
||||||
|
|
||||||
|
static public bool VerifyNotNull( this Node node, bool logInfo = true,
|
||||||
|
[CallerMemberName] string dbgName = "",
|
||||||
|
[CallerFilePath] string dbgPath = "",
|
||||||
|
[CallerLineNumber] int dbgLine = 0,
|
||||||
|
[CallerArgumentExpression("node")]
|
||||||
|
string dbgExp_node = ""
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var nodeIsNotNull = node != null;
|
||||||
|
|
||||||
|
var loc = new SourceLoc( "", dbgName, dbgPath, dbgLine );
|
||||||
|
|
||||||
|
if( logInfo && !nodeIsNotNull )
|
||||||
|
{
|
||||||
|
log.warn( $"{loc.Log}: {dbgExp_node} IS NULL" );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
log.info( $"{loc.Log}: {dbgExp_node} is not null" );
|
||||||
|
}
|
||||||
|
|
||||||
|
return nodeIsNotNull;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#region Math
|
||||||
|
static public string Debug( this Vector3 vec )
|
||||||
|
=> $"({vec.X:0.00}, {vec.X:0.00}, {vec.X:0.00})";
|
||||||
|
static public string DebugX( this Vector3 vec )
|
||||||
|
=> $"({vec.X:0.00}, {vec.X:0.00})";
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Strings
|
||||||
|
|
||||||
|
static public string SafeOr( this string str, string def ) =>
|
||||||
|
!string.IsNullOrWhiteSpace( str ) ? str : def;
|
||||||
|
|
||||||
|
static public bool NOW( this string str ) => string.IsNullOrWhiteSpace( str );
|
||||||
|
|
||||||
|
static public bool TryGetUserArgValue( this string[] strings, string whatArg, out string value )
|
||||||
|
{
|
||||||
|
var index = 0;
|
||||||
|
foreach( var arg in strings )
|
||||||
|
{
|
||||||
|
if( arg == whatArg && index + 1 < strings.Length )
|
||||||
|
{
|
||||||
|
value = strings[index + 1];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = "";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static public bool UserArgExists( this string[] strings, string whatArg ) => strings.Contains( whatArg );
|
||||||
|
|
||||||
|
static public string RelTime( this DateTime dt )
|
||||||
|
{
|
||||||
|
var span = DateTime.Now - dt;
|
||||||
|
|
||||||
|
if( span.TotalMilliseconds < 0 )
|
||||||
|
return $"Never";
|
||||||
|
|
||||||
|
if( span.TotalMilliseconds < 1000 )
|
||||||
|
return $"{(int)span.TotalMilliseconds}ms";
|
||||||
|
|
||||||
|
if( span.TotalMilliseconds < 100 * 1000 )
|
||||||
|
return $"{(int)( span.TotalMilliseconds / 1000.0 )}sec";
|
||||||
|
|
||||||
|
//If Under a year
|
||||||
|
if( span.TotalMilliseconds < ( 52.0 * 168.0 * 60.0 * 60.0 * 1000.0 ) )
|
||||||
|
return $"{(int)( span.TotalMilliseconds / 60 * 1000.0 )}min";
|
||||||
|
|
||||||
|
return $"Always";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Res
|
||||||
|
static string ResProxySer( object obj )
|
||||||
|
{
|
||||||
|
if( obj is Resource res )
|
||||||
|
{
|
||||||
|
log.info( $"Res path from {res.ResourcePath}" );
|
||||||
|
return res.ResourcePath;
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
static object ResProxyDes( string type, string str )
|
||||||
|
{
|
||||||
|
log.info( $"Loading res {type} from {str}" );
|
||||||
|
var res = ResourceLoader.Load( str, type );
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ser.TypeProxy ResourceProxy = new( ResProxySer, ResProxyDes );
|
||||||
|
|
||||||
|
static gen.Dictionary<Type, ser.TypeProxy> TypeProxy = new() {
|
||||||
|
{ typeof(Resource), ResourceProxy },
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static ser.XmlCfg XmlCfg = new()
|
||||||
|
{
|
||||||
|
Verbose = true,
|
||||||
|
|
||||||
|
//Were not saving abitrary graphs
|
||||||
|
Structure = ser.Datastructure.Tree,
|
||||||
|
|
||||||
|
//FOr Godot types we cant otherwise save
|
||||||
|
Proxies = TypeProxy.ToImmutableDictionary(),
|
||||||
|
|
||||||
|
//For loading and saving Godot subclasses, we default to doing nothing
|
||||||
|
TypesDefault = ser.Types.None,
|
||||||
|
};
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region SaveLoad
|
||||||
|
static string s_saveDir = "save";
|
||||||
|
static string s_saveExt = "xml";
|
||||||
|
|
||||||
|
static public void SetupSaveState( string saveDir = "save", string saveExt = "xml" )
|
||||||
|
{
|
||||||
|
log.info( $"Setting up save state in {saveDir} with ext {saveExt}" );
|
||||||
|
|
||||||
|
s_saveDir = saveDir;
|
||||||
|
s_saveExt = saveExt;
|
||||||
|
|
||||||
|
lib.Util.checkAndAddDirectory( $"{s_saveDir}" );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static public void SaveState( this Control control )
|
||||||
|
{
|
||||||
|
var controlName = control.Name;
|
||||||
|
|
||||||
|
var filename = $"{s_saveDir}/{controlName}.{s_saveExt}";
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ser.XmlSer xml = new( XmlCfg );
|
||||||
|
|
||||||
|
if( IO.File.Exists( filename ) )
|
||||||
|
{
|
||||||
|
// @@ TODO Backup the existing saves
|
||||||
|
}
|
||||||
|
|
||||||
|
using var stream = new IO.FileStream( filename, IO.FileMode.Create );
|
||||||
|
|
||||||
|
xml.Serialize( stream, control );
|
||||||
|
|
||||||
|
}
|
||||||
|
catch( Exception ex )
|
||||||
|
{
|
||||||
|
log.error( $"Ex {ex.Message} trying to save control {control.Name} into {filename}" );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static public void LoadState<T>( this T control, bool verbose = false )
|
||||||
|
where T : Control
|
||||||
|
{
|
||||||
|
var controlName = control.Name;
|
||||||
|
|
||||||
|
var filename = $"{s_saveDir}/{controlName}.{s_saveExt}";
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ser.XmlSer xml = new( XmlCfg );
|
||||||
|
|
||||||
|
if( !IO.File.Exists( filename ) )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
using var stream = new IO.FileStream( filename, IO.FileMode.Open );
|
||||||
|
|
||||||
|
xml.DeserializeInto<T>( stream, control );
|
||||||
|
}
|
||||||
|
catch( Exception ex )
|
||||||
|
{
|
||||||
|
log.error( $"Ex {ex.Message} trying to save control {control.Name} into {filename}" );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
static public Godot.Variant.Type CSTypeCodeToGodotVariantType( TypeCode tc )
|
||||||
|
{
|
||||||
|
switch( tc )
|
||||||
|
{
|
||||||
|
case TypeCode.Empty:
|
||||||
|
return Variant.Type.Nil;
|
||||||
|
|
||||||
|
case TypeCode.Boolean:
|
||||||
|
return Variant.Type.Bool;
|
||||||
|
|
||||||
|
case TypeCode.DateTime:
|
||||||
|
return Variant.Type.Nil;
|
||||||
|
|
||||||
|
case TypeCode.DBNull:
|
||||||
|
return Variant.Type.Nil;
|
||||||
|
|
||||||
|
case TypeCode.Decimal:
|
||||||
|
case TypeCode.Single:
|
||||||
|
case TypeCode.Double:
|
||||||
|
return Variant.Type.Float;
|
||||||
|
|
||||||
|
case TypeCode.Char:
|
||||||
|
case TypeCode.SByte:
|
||||||
|
case TypeCode.Int16:
|
||||||
|
case TypeCode.Int32:
|
||||||
|
case TypeCode.Int64:
|
||||||
|
return Variant.Type.Int;
|
||||||
|
|
||||||
|
case TypeCode.Byte:
|
||||||
|
case TypeCode.UInt16:
|
||||||
|
case TypeCode.UInt32:
|
||||||
|
case TypeCode.UInt64:
|
||||||
|
return Variant.Type.Int;
|
||||||
|
|
||||||
|
case TypeCode.Object:
|
||||||
|
return Variant.Type.Object;
|
||||||
|
|
||||||
|
case TypeCode.String:
|
||||||
|
return Variant.Type.String;
|
||||||
|
}
|
||||||
|
return Variant.Type.Nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
static public Variant.Type ConvertMarshalTypeToVariantType( Type type )
|
||||||
|
{
|
||||||
|
if( type == typeof( Boolean ) )
|
||||||
|
return Variant.Type.Bool;
|
||||||
|
if( type == typeof( Char ) )
|
||||||
|
return Variant.Type.Int;
|
||||||
|
if( type == typeof( SByte ) )
|
||||||
|
return Variant.Type.Int;
|
||||||
|
if( type == typeof( Int16 ) )
|
||||||
|
return Variant.Type.Int;
|
||||||
|
if( type == typeof( Int32 ) )
|
||||||
|
return Variant.Type.Int;
|
||||||
|
if( type == typeof( Int64 ) )
|
||||||
|
return Variant.Type.Int;
|
||||||
|
if( type == typeof( Byte ) )
|
||||||
|
return Variant.Type.Int;
|
||||||
|
if( type == typeof( UInt16 ) )
|
||||||
|
return Variant.Type.Int;
|
||||||
|
if( type == typeof( UInt32 ) )
|
||||||
|
return Variant.Type.Int;
|
||||||
|
if( type == typeof( UInt64 ) )
|
||||||
|
return Variant.Type.Int;
|
||||||
|
if( type == typeof( Single ) )
|
||||||
|
return Variant.Type.Float;
|
||||||
|
if( type == typeof( Double ) )
|
||||||
|
return Variant.Type.Float;
|
||||||
|
if( type == typeof( String ) )
|
||||||
|
return Variant.Type.String;
|
||||||
|
if( type == typeof( Vector2 ) )
|
||||||
|
return Variant.Type.Vector2;
|
||||||
|
if( type == typeof( Vector2I ) )
|
||||||
|
return Variant.Type.Vector2I;
|
||||||
|
if( type == typeof( Rect2 ) )
|
||||||
|
return Variant.Type.Rect2;
|
||||||
|
if( type == typeof( Rect2I ) )
|
||||||
|
return Variant.Type.Rect2I;
|
||||||
|
if( type == typeof( Transform2D ) )
|
||||||
|
return Variant.Type.Transform2D;
|
||||||
|
if( type == typeof( Vector3 ) )
|
||||||
|
return Variant.Type.Vector3;
|
||||||
|
if( type == typeof( Vector3I ) )
|
||||||
|
return Variant.Type.Vector3I;
|
||||||
|
if( type == typeof( Basis ) )
|
||||||
|
return Variant.Type.Basis;
|
||||||
|
if( type == typeof( Quaternion ) )
|
||||||
|
return Variant.Type.Quaternion;
|
||||||
|
if( type == typeof( Transform3D ) )
|
||||||
|
return Variant.Type.Transform3D;
|
||||||
|
if( type == typeof( Vector4 ) )
|
||||||
|
return Variant.Type.Vector4;
|
||||||
|
if( type == typeof( Vector4I ) )
|
||||||
|
return Variant.Type.Vector4I;
|
||||||
|
if( type == typeof( Projection ) )
|
||||||
|
return Variant.Type.Projection;
|
||||||
|
if( type == typeof( Aabb ) )
|
||||||
|
return Variant.Type.Aabb;
|
||||||
|
if( type == typeof( Color ) )
|
||||||
|
return Variant.Type.Color;
|
||||||
|
if( type == typeof( Plane ) )
|
||||||
|
return Variant.Type.Plane;
|
||||||
|
if( type == typeof( Callable ) )
|
||||||
|
return Variant.Type.Callable;
|
||||||
|
if( type == typeof( Signal ) )
|
||||||
|
return Variant.Type.Signal;
|
||||||
|
if( type == typeof( Enum ) )
|
||||||
|
return Variant.Type.Int;
|
||||||
|
if( type == typeof( StringName ) )
|
||||||
|
return Variant.Type.StringName;
|
||||||
|
if( type == typeof( NodePath ) )
|
||||||
|
return Variant.Type.NodePath;
|
||||||
|
if( type == typeof( Rid ) )
|
||||||
|
return Variant.Type.Rid;
|
||||||
|
if( type == typeof( Variant ) )
|
||||||
|
return Variant.Type.Nil;
|
||||||
|
if( type == typeof( System.Collections.IEnumerable ) )
|
||||||
|
return Variant.Type.Array;
|
||||||
|
|
||||||
|
/*
|
||||||
|
if( type == typeof( GodotObjectOrDerivedArray ) ) return Variant.Type.Array;
|
||||||
|
if( type == typeof( ByteArray ) ) return Variant.Type.PackedByteArray;
|
||||||
|
if( type == typeof( Int32Array ) ) return Variant.Type.PackedInt32Array;
|
||||||
|
if( type == typeof( Int64Array ) ) return Variant.Type.PackedInt64Array;
|
||||||
|
if( type == typeof( Float32Array ) ) return Variant.Type.PackedFloat32Array;
|
||||||
|
if( type == typeof( Float64Array ) ) return Variant.Type.PackedFloat64Array;
|
||||||
|
if( type == typeof( StringArray ) ) return Variant.Type.PackedStringArray;
|
||||||
|
if( type == typeof( Vector2Array ) ) return Variant.Type.PackedVector2Array;
|
||||||
|
if( type == typeof( Vector3Array ) ) return Variant.Type.PackedVector3Array;
|
||||||
|
if( type == typeof( ColorArray ) ) return Variant.Type.PackedColorArray;
|
||||||
|
if( type == typeof( SystemArrayOfStringName ) ) return Variant.Type.Array;
|
||||||
|
if( type == typeof( SystemArrayOfNodePath ) ) return Variant.Type.Array;
|
||||||
|
if( type == typeof( SystemArrayOfRid ) ) return Variant.Type.Array;
|
||||||
|
if( type == typeof( GodotObjectOrDerived ) ) return Variant.Type.Object;
|
||||||
|
if( type == typeof( GodotDictionary ) ) return Variant.Type.Dictionary;
|
||||||
|
if( type == typeof( GodotGenericDictionary ) ) return Variant.Type.Dictionary;
|
||||||
|
if( type == typeof( GodotGenericArray ) ) return Variant.Type.Array;
|
||||||
|
*/
|
||||||
|
return Variant.Type.Nil;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static public Godot.Variant Convert( Type type, object obj )
|
||||||
|
{
|
||||||
|
var tc = Type.GetTypeCode( type );
|
||||||
|
switch( tc )
|
||||||
|
{
|
||||||
|
case TypeCode.Empty:
|
||||||
|
return new Variant();
|
||||||
|
|
||||||
|
case TypeCode.Boolean:
|
||||||
|
return Variant.From( (bool)obj );
|
||||||
|
|
||||||
|
case TypeCode.DateTime:
|
||||||
|
return new Variant();
|
||||||
|
case TypeCode.DBNull:
|
||||||
|
return new Variant();
|
||||||
|
|
||||||
|
|
||||||
|
case TypeCode.Decimal:
|
||||||
|
case TypeCode.Single:
|
||||||
|
case TypeCode.Double:
|
||||||
|
return Variant.From( (double)obj );
|
||||||
|
|
||||||
|
case TypeCode.Char:
|
||||||
|
case TypeCode.SByte:
|
||||||
|
case TypeCode.Int16:
|
||||||
|
case TypeCode.Int32:
|
||||||
|
case TypeCode.Int64:
|
||||||
|
return Variant.From( (int)obj );
|
||||||
|
|
||||||
|
case TypeCode.Byte:
|
||||||
|
case TypeCode.UInt16:
|
||||||
|
case TypeCode.UInt32:
|
||||||
|
case TypeCode.UInt64:
|
||||||
|
return Variant.From( (int)obj );
|
||||||
|
|
||||||
|
case TypeCode.String:
|
||||||
|
return Variant.From( (string)obj );
|
||||||
|
|
||||||
|
/*
|
||||||
|
case TypeCode.Object:
|
||||||
|
{
|
||||||
|
// @@@ TODO Return proxy object
|
||||||
|
//return Variant.From( obj );
|
||||||
|
//return new Variant();
|
||||||
|
if( obj is EntityId entId )
|
||||||
|
{
|
||||||
|
return Variant.From( entId.id );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
return new Variant();
|
||||||
|
}
|
||||||
|
|
||||||
|
static public Array<T> ToGDArray<[MustBeVariant] T>( this gen.IEnumerable<T> coll )
|
||||||
|
{
|
||||||
|
var arr = new Array<T>();
|
||||||
|
|
||||||
|
foreach( var t in coll )
|
||||||
|
{
|
||||||
|
//GD.Print( $"Processing {coll.GetType()}" );
|
||||||
|
|
||||||
|
if( t == null )
|
||||||
|
{
|
||||||
|
GD.PrintErr( $"ToGDArray<{typeof( T ).Name}> found a null object" );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
arr.Add( t );
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static public gen.IEnumerable<CollisionInfo3> Colliders( this ShapeCast3D sc )
|
||||||
|
{
|
||||||
|
var count = sc.GetCollisionCount();
|
||||||
|
|
||||||
|
for( int i = 0; i < count; ++i )
|
||||||
|
{
|
||||||
|
var ci = new CollisionInfo3()
|
||||||
|
{
|
||||||
|
position = sc.GetCollisionPoint( i ),
|
||||||
|
normal = sc.GetCollisionNormal( i ),
|
||||||
|
collider = sc.GetCollider( i ),
|
||||||
|
rid = sc.GetColliderRid( i ),
|
||||||
|
};
|
||||||
|
|
||||||
|
yield return ci;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
addons/core/misc/Util.cs.uid
Normal file
1
addons/core/misc/Util.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://c7og7inhkhxpc
|
||||||
60
addons/core/misc/VariantExt.cs
Normal file
60
addons/core/misc/VariantExt.cs
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
using Godot;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
public static class VariantExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a Godot Variant to a specified C# object type using a switch statement.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="variant">The variant to convert.</param>
|
||||||
|
/// <param name="targetType">The C# System.Type to convert to.</param>
|
||||||
|
/// <returns>An object of the target type, or throws an exception if conversion is not supported.</returns>
|
||||||
|
public static object AsType( this Variant variant, Type targetType )
|
||||||
|
{
|
||||||
|
// The switch statement uses type pattern matching (`when t == ...`)
|
||||||
|
// to check against the desired target type.
|
||||||
|
switch( targetType )
|
||||||
|
{
|
||||||
|
// Basic C# types
|
||||||
|
case Type t when t == typeof( bool ):
|
||||||
|
return variant.As<bool>();
|
||||||
|
|
||||||
|
case Type t when t == typeof( int ):
|
||||||
|
return variant.As<int>();
|
||||||
|
|
||||||
|
case Type t when t == typeof( long ):
|
||||||
|
return variant.As<long>();
|
||||||
|
|
||||||
|
case Type t when t == typeof( float ):
|
||||||
|
return variant.As<float>();
|
||||||
|
|
||||||
|
case Type t when t == typeof( double ):
|
||||||
|
return variant.As<double>();
|
||||||
|
|
||||||
|
case Type t when t == typeof( string ):
|
||||||
|
return variant.As<string>();
|
||||||
|
|
||||||
|
// Godot-specific structs
|
||||||
|
case Type t when t == typeof( Vector2 ):
|
||||||
|
return variant.As<Vector2>();
|
||||||
|
|
||||||
|
case Type t when t == typeof( Vector3 ):
|
||||||
|
return variant.As<Vector3>();
|
||||||
|
|
||||||
|
case Type t when t == typeof( Color ):
|
||||||
|
return variant.As<Color>();
|
||||||
|
|
||||||
|
// Handle enums (which are stored as integers in Variants)
|
||||||
|
case Type t when t.IsEnum:
|
||||||
|
return Enum.ToObject( t, variant.As<long>() );
|
||||||
|
|
||||||
|
// Handle Godot Objects (Nodes, Resources, etc.)
|
||||||
|
case Type t when t.IsSubclassOf( typeof( GodotObject ) ):
|
||||||
|
return variant.AsGodotObject(); // Use the built-in As(Type) for engine types
|
||||||
|
|
||||||
|
// Default case for unsupported types
|
||||||
|
default:
|
||||||
|
throw new ArgumentException( $"Conversion from Variant to type '{targetType.Name}' is not supported by this method." );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
addons/core/misc/VariantExt.cs.uid
Normal file
1
addons/core/misc/VariantExt.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://cw2w7f8iljtxd
|
||||||
256
addons/core/misc/ser.cs
Normal file
256
addons/core/misc/ser.cs
Normal file
@ -0,0 +1,256 @@
|
|||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// D E R E L I C T
|
||||||
|
//
|
||||||
|
/// // (c) 2003..2024
|
||||||
|
using Godot;
|
||||||
|
//using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
|
|
||||||
|
using gen = System.Collections.Generic;
|
||||||
|
using System;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
public static class SerExtensions
|
||||||
|
{
|
||||||
|
|
||||||
|
public static T GetRefl<T>( Godot.Collections.Dictionary dict )
|
||||||
|
where T : new()
|
||||||
|
{
|
||||||
|
|
||||||
|
T v = new T();
|
||||||
|
|
||||||
|
Type type = typeof( T );
|
||||||
|
|
||||||
|
Type utilType = typeof( SerExtensions );
|
||||||
|
var getReflMethod = utilType.GetMethod( "GetRefl" );
|
||||||
|
|
||||||
|
var parseArrayMethod = utilType.GetMethod( "ParseReflArray" );
|
||||||
|
|
||||||
|
//GD.Print($"GetRefl dict {dict}");
|
||||||
|
//GD.Print($"GetRefl type {type.Name}");
|
||||||
|
|
||||||
|
var props = type.GetProperties();
|
||||||
|
|
||||||
|
foreach( var prop in props )
|
||||||
|
{
|
||||||
|
var nameBld = new StringBuilder( prop.Name );
|
||||||
|
|
||||||
|
nameBld[0] = char.ToLower( nameBld[0] );
|
||||||
|
|
||||||
|
var name = nameBld.ToString();
|
||||||
|
|
||||||
|
var propType = prop.PropertyType;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if( propType.IsArray )
|
||||||
|
{
|
||||||
|
GD.Print( $"Skipping Array {name}" );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( propType.IsEnum )
|
||||||
|
{
|
||||||
|
propType = propType.GetEnumUnderlyingType();
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeCode tc = Type.GetTypeCode( propType );
|
||||||
|
|
||||||
|
//GD.Print($"{name}: {propType.Name} {tc}");
|
||||||
|
|
||||||
|
switch( tc )
|
||||||
|
{
|
||||||
|
case TypeCode.Boolean:
|
||||||
|
prop.SetValue( v, GetBool( dict, name ) );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TypeCode.Char:
|
||||||
|
prop.SetValue( v, (char)GetLong( dict, name ) );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TypeCode.DateTime:
|
||||||
|
case TypeCode.DBNull:
|
||||||
|
case TypeCode.Decimal:
|
||||||
|
case TypeCode.Empty:
|
||||||
|
{
|
||||||
|
GD.Print( $"GetRefl ERROR Unhandled type for {name}: {propType.Name}" );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TypeCode.Single:
|
||||||
|
prop.SetValue( v, (float)GetDouble( dict, name ) );
|
||||||
|
break;
|
||||||
|
case TypeCode.Double:
|
||||||
|
prop.SetValue( v, GetDouble( dict, name ) );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TypeCode.Int16:
|
||||||
|
prop.SetValue( v, (short)GetLong( dict, name ) );
|
||||||
|
break;
|
||||||
|
case TypeCode.Int32:
|
||||||
|
prop.SetValue( v, (int)GetLong( dict, name ) );
|
||||||
|
break;
|
||||||
|
case TypeCode.Int64:
|
||||||
|
prop.SetValue( v, GetLong( dict, name ) );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TypeCode.SByte:
|
||||||
|
prop.SetValue( v, (sbyte)GetULong( dict, name ) );
|
||||||
|
break;
|
||||||
|
case TypeCode.Byte:
|
||||||
|
prop.SetValue( v, (byte)GetLong( dict, name ) );
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
case TypeCode.UInt16:
|
||||||
|
prop.SetValue( v, (ushort)GetULong( dict, name ) );
|
||||||
|
break;
|
||||||
|
case TypeCode.UInt32:
|
||||||
|
prop.SetValue( v, (uint)GetULong( dict, name ) );
|
||||||
|
break;
|
||||||
|
case TypeCode.UInt64:
|
||||||
|
prop.SetValue( v, GetULong( dict, name ) );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TypeCode.Object:
|
||||||
|
{
|
||||||
|
|
||||||
|
if( propType.Name == "gen.IEnumerable`1" )
|
||||||
|
{
|
||||||
|
GD.Print( $"GetRefl Type Filling in gen.IEnumerable {name}: {propType.Name} {propType.GenericTypeArguments[0].Name}" );
|
||||||
|
|
||||||
|
var enumerableType = propType.GenericTypeArguments[0];
|
||||||
|
|
||||||
|
//*
|
||||||
|
if( enumerableType.Name == "String" )
|
||||||
|
{
|
||||||
|
enumerableType = typeof( string );
|
||||||
|
}
|
||||||
|
// */
|
||||||
|
|
||||||
|
var genericParseReflArrayMethod = parseArrayMethod.MakeGenericMethod( enumerableType );
|
||||||
|
|
||||||
|
var obj = genericParseReflArrayMethod.Invoke( null, new object[] {
|
||||||
|
dict,
|
||||||
|
name,
|
||||||
|
} );
|
||||||
|
|
||||||
|
prop.SetValue( v, obj );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var genericGetReflMethod = getReflMethod.MakeGenericMethod( propType );
|
||||||
|
|
||||||
|
var obj = genericGetReflMethod.Invoke( null, new object[] {
|
||||||
|
GetObject(dict, name)
|
||||||
|
} );
|
||||||
|
|
||||||
|
prop.SetValue( v, obj );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TypeCode.String:
|
||||||
|
prop.SetValue( v, GetString( dict, name ) );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
catch( ArgumentException ex )
|
||||||
|
{
|
||||||
|
GD.Print( $"While parsing {type.Name}: {name}" );
|
||||||
|
GD.Print( $"Caught {ex.GetType().Name} {ex.Message}" );
|
||||||
|
}
|
||||||
|
catch( Exception ex )
|
||||||
|
{
|
||||||
|
GD.Print( $"While parsing {type.Name}: {name}" );
|
||||||
|
GD.Print( $"Caught {ex.GetType().Name} {ex.Message}" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetString( Godot.Collections.Dictionary dict, string name )
|
||||||
|
{
|
||||||
|
var v = (string)dict[name];
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long GetLong( Godot.Collections.Dictionary dict, string name )
|
||||||
|
{
|
||||||
|
var val = (float)dict[name];
|
||||||
|
|
||||||
|
var v = (long)val;
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ulong GetULong( Godot.Collections.Dictionary dict, string name )
|
||||||
|
{
|
||||||
|
var val = (float)dict[name];
|
||||||
|
|
||||||
|
var v = (ulong)val;
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool GetBool( Godot.Collections.Dictionary dict, string name )
|
||||||
|
{
|
||||||
|
var v = (bool)dict[name];
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double GetDouble( Godot.Collections.Dictionary dict, string name )
|
||||||
|
{
|
||||||
|
var val = (float)dict[name];
|
||||||
|
|
||||||
|
return (double)val;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Godot.Collections.Array GetArray( Godot.Collections.Dictionary dict, string name )
|
||||||
|
{
|
||||||
|
var arr = (Godot.Collections.Array)dict[name];
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Godot.Collections.Dictionary GetObject( Godot.Collections.Dictionary dict, string name )
|
||||||
|
{
|
||||||
|
var d2 = (Godot.Collections.Dictionary)dict[name];
|
||||||
|
return d2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static gen.List<T> ParseArray<T>( Godot.Collections.Dictionary dict, string name, Func<Godot.Collections.Dictionary, T> fn )
|
||||||
|
{
|
||||||
|
var arr = new gen.List<T>();
|
||||||
|
|
||||||
|
var dictArr = GetArray( dict, name );
|
||||||
|
|
||||||
|
foreach( var arrObj in dictArr )
|
||||||
|
{
|
||||||
|
arr.Add( fn( (Godot.Collections.Dictionary)arrObj ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static gen.List<T> ParseReflArray<T>( Godot.Collections.Dictionary dict, string name )
|
||||||
|
where T : new()
|
||||||
|
{
|
||||||
|
var arr = new gen.List<T>();
|
||||||
|
|
||||||
|
var dictArr = GetArray( dict, name );
|
||||||
|
|
||||||
|
foreach( var arrObj in dictArr )
|
||||||
|
{
|
||||||
|
arr.Add( GetRefl<T>( (Godot.Collections.Dictionary)arrObj ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
1
addons/core/misc/ser.cs.uid
Normal file
1
addons/core/misc/ser.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://dyar18662and7
|
||||||
107
addons/core/mp/DirSpawner.cs
Normal file
107
addons/core/mp/DirSpawner.cs
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// D E R E L I C T
|
||||||
|
//
|
||||||
|
/// // (c) 2003..2025
|
||||||
|
|
||||||
|
using Godot;
|
||||||
|
using Godot.Sharp.Extras;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
[GlobalClass]
|
||||||
|
public partial class DirSpawner : MultiplayerSpawner
|
||||||
|
{
|
||||||
|
|
||||||
|
[Export( PropertyHint.Dir )]
|
||||||
|
public string Directory;
|
||||||
|
|
||||||
|
|
||||||
|
// Called when the node enters the scene tree for the first time.
|
||||||
|
public override void _Ready()
|
||||||
|
{
|
||||||
|
base._Ready();
|
||||||
|
|
||||||
|
this.OnReady();
|
||||||
|
|
||||||
|
|
||||||
|
log.info( $"{log.loc().Log}: Searching for spawns in {Directory}" );
|
||||||
|
|
||||||
|
AddSpawnables( Directory );
|
||||||
|
|
||||||
|
/*
|
||||||
|
var dir = gd.DirAccess.Open( Directory);
|
||||||
|
if( dir == null )
|
||||||
|
{
|
||||||
|
log.error( $"{log.loc().Log}: Unable to open directory: {Directory}" );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dir.ListDirBegin();
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
var fileName = dir.GetNext();
|
||||||
|
if (fileName == String.Empty)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (dir.CurrentIsDir())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var path = System.IO.Path.Combine(Directory, fileName);
|
||||||
|
|
||||||
|
log.info( $"{log.loc().Log}: Spawning scene from file: {path}" );
|
||||||
|
|
||||||
|
AddSpawnableScene( path );
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
var scene = ResourceLoader.Load<PackedScene>( path );
|
||||||
|
if ( scene == null )
|
||||||
|
{
|
||||||
|
GD.PrintErr( $"DirSpawner: Unable to load scene: {path}" );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var instance = scene.Instantiate();
|
||||||
|
if ( instance == null )
|
||||||
|
{
|
||||||
|
GD.PrintErr( $"DirSpawner: Unable to instantiate scene: {path}" );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
AddChild( instance );
|
||||||
|
* /
|
||||||
|
}
|
||||||
|
|
||||||
|
dir.ListDirEnd();
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddSpawnables( string dir )
|
||||||
|
{
|
||||||
|
var files = DirAccess.GetFilesAt( dir );
|
||||||
|
|
||||||
|
foreach( var file in files )
|
||||||
|
{
|
||||||
|
var path = System.IO.Path.Combine( dir, file );
|
||||||
|
|
||||||
|
log.info( $"{log.loc().Log}: Add scene: {path}" );
|
||||||
|
|
||||||
|
AddSpawnableScene( path );
|
||||||
|
}
|
||||||
|
|
||||||
|
var directories = DirAccess.GetDirectoriesAt( dir );
|
||||||
|
foreach( var subdir in directories )
|
||||||
|
{
|
||||||
|
var path = System.IO.Path.Combine( dir, subdir );
|
||||||
|
|
||||||
|
AddSpawnables( path );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||||
|
public override void _Process( double delta )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
1
addons/core/mp/DirSpawner.cs.uid
Normal file
1
addons/core/mp/DirSpawner.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://c4mhl04o8yvvl
|
||||||
26
addons/core/mp/ListSpawner.cs
Normal file
26
addons/core/mp/ListSpawner.cs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// D E R E L I C T
|
||||||
|
//
|
||||||
|
/// // (c) 2003..2025
|
||||||
|
|
||||||
|
using Godot;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
[GlobalClass]
|
||||||
|
public partial class ListSpawner : MultiplayerSpawner
|
||||||
|
{
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public Godot.Collections.Array<PackedScene> Scenes;
|
||||||
|
|
||||||
|
// Called when the node enters the scene tree for the first time.
|
||||||
|
public override void _Ready()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||||
|
public override void _Process( double delta )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
1
addons/core/mp/ListSpawner.cs.uid
Normal file
1
addons/core/mp/ListSpawner.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://q13vs3ukhvov
|
||||||
7
addons/core/plugin.cfg
Normal file
7
addons/core/plugin.cfg
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
[plugin]
|
||||||
|
|
||||||
|
name="Core"
|
||||||
|
description="A core set of classes for games in Godot"
|
||||||
|
author="marcsh"
|
||||||
|
version="0.1"
|
||||||
|
script="core.cs"
|
||||||
126
addons/core/prof/Prof.cs
Normal file
126
addons/core/prof/Prof.cs
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// D E R E L I C T
|
||||||
|
//
|
||||||
|
/// // (c) 2003..2024
|
||||||
|
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using Godot;
|
||||||
|
|
||||||
|
namespace prof;
|
||||||
|
|
||||||
|
public record struct Id( ulong id );
|
||||||
|
public interface Profiler
|
||||||
|
{
|
||||||
|
public Id Begin( string name );
|
||||||
|
public void End( Id id );
|
||||||
|
public void Text( Id id, string text );
|
||||||
|
public void Value( Id id, ulong value );
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Zone : IDisposable
|
||||||
|
{
|
||||||
|
public Zone(
|
||||||
|
[CallerFilePath] string dbgPath = "",
|
||||||
|
[CallerLineNumber] int dbgLine = 0,
|
||||||
|
[CallerMemberName] string dbgMethod = ""
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var file = log.whatFile( dbgPath );
|
||||||
|
_id = Util.GetProfiler().Begin( $"{file}::{dbgMethod}" );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Util.GetProfiler().End( _id );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Text( string text )
|
||||||
|
{
|
||||||
|
Util.GetProfiler().Text( _id, text );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Value( ulong value )
|
||||||
|
{
|
||||||
|
Util.GetProfiler().Value( _id, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
Id _id;
|
||||||
|
}
|
||||||
|
|
||||||
|
static public class Util
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static public Profiler GetProfiler()
|
||||||
|
{
|
||||||
|
//*
|
||||||
|
return new ProfilerNull();
|
||||||
|
/*/
|
||||||
|
return new ProfilerTracy();
|
||||||
|
//*/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public class ProfilerTracy : Profiler
|
||||||
|
{
|
||||||
|
public Id Begin(string name)
|
||||||
|
{
|
||||||
|
var id = GD.TracyBegin( name );
|
||||||
|
|
||||||
|
return new Id( id );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void End(Id id)
|
||||||
|
{
|
||||||
|
GD.TracyEnd( id.id );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Text(Id id, string text)
|
||||||
|
{
|
||||||
|
GD.TracyName( id.id, text );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Value(Id id, ulong value)
|
||||||
|
{
|
||||||
|
GD.TracyValue( id.id, value );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*/
|
||||||
|
|
||||||
|
public class ProfilerNull : Profiler
|
||||||
|
{
|
||||||
|
static Id s_Id = new Id( 0 );
|
||||||
|
|
||||||
|
public Id Begin( string name )
|
||||||
|
{
|
||||||
|
return s_Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void End( Id id )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Text( Id id, string text )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Value( Id id, ulong value )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//*/
|
||||||
1
addons/core/prof/Prof.cs.uid
Normal file
1
addons/core/prof/Prof.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://k3h7t2btjskm
|
||||||
19
addons/core/res/DataRes.cs
Normal file
19
addons/core/res/DataRes.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// D E R E L I C T
|
||||||
|
//
|
||||||
|
/// // (c) 2003..2025
|
||||||
|
|
||||||
|
using Godot;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
/*
|
||||||
|
[GlobalClass]
|
||||||
|
public partial class DataRes : Resource, ent.Data
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
*/
|
||||||
1
addons/core/res/DataRes.cs.uid
Normal file
1
addons/core/res/DataRes.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://c1i5d08v3p1tp
|
||||||
71
addons/core/screens/BattleScreen.cs
Normal file
71
addons/core/screens/BattleScreen.cs
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// D E R E L I C T
|
||||||
|
//
|
||||||
|
/// // (c) 2003..2024
|
||||||
|
|
||||||
|
using Godot;
|
||||||
|
using System;
|
||||||
|
using Godot.Sharp.Extras;
|
||||||
|
|
||||||
|
[GlobalClass]
|
||||||
|
public partial class BattleScreen : ScreenGeneric<BattleScreenDef>
|
||||||
|
{
|
||||||
|
|
||||||
|
static public BattleScreen _ { get; private set; }
|
||||||
|
|
||||||
|
|
||||||
|
//[ExportCategory( "Initialized" )]
|
||||||
|
//[Export]
|
||||||
|
//public Battle _battle;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public override string DebugString() => $"{GetType().Name}: {Def.ResourcePath}";
|
||||||
|
|
||||||
|
public BattleScreen()
|
||||||
|
{
|
||||||
|
_ = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void _Ready()
|
||||||
|
{
|
||||||
|
log.info( $"{GetType().Name}._Ready::Begin with Scene {Def.Scene.ResourcePath}" );
|
||||||
|
|
||||||
|
base._Ready();
|
||||||
|
|
||||||
|
this.OnReady();
|
||||||
|
|
||||||
|
Name = $"BattleScreen";
|
||||||
|
|
||||||
|
log.info( $"Running {GetType().Name} with Scene {Def.Scene.ResourcePath}" );
|
||||||
|
|
||||||
|
|
||||||
|
// @@@ PORT Battles as a concept
|
||||||
|
/*
|
||||||
|
|
||||||
|
var scene = Def.Scene.Instantiate<BattleScene>();
|
||||||
|
|
||||||
|
AddChild( scene );
|
||||||
|
|
||||||
|
scene.StartEvent();
|
||||||
|
|
||||||
|
Callable.From(
|
||||||
|
() => {
|
||||||
|
var path = Def.BattleDataPath.Replace( "res://", "" );
|
||||||
|
var battleData = new res.Ref<cs.BattleData>( path );
|
||||||
|
|
||||||
|
_battle = Battle.CreateBattle( this, scene, battleData, $"{Def.BattleDataPath}" );
|
||||||
|
} ).CallDeferred();
|
||||||
|
*/
|
||||||
|
|
||||||
|
log.info( $"{GetType().Name}._Ready::End with Scene {Def.Scene.ResourcePath}" );
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void _Process( double delta )
|
||||||
|
{
|
||||||
|
base._Process( delta );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
1
addons/core/screens/BattleScreen.cs.uid
Normal file
1
addons/core/screens/BattleScreen.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://n1qmhlxxri83
|
||||||
22
addons/core/screens/BattleScreenDef.cs
Normal file
22
addons/core/screens/BattleScreenDef.cs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// D E R E L I C T
|
||||||
|
//
|
||||||
|
/// // (c) 2003..2024
|
||||||
|
|
||||||
|
using Godot;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
|
||||||
|
[GlobalClass]
|
||||||
|
public partial class BattleScreenDef : ScreenGenericDef<BattleScreenDef, BattleScreen>
|
||||||
|
{
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public PackedScene Scene { get; set; }
|
||||||
|
|
||||||
|
[Export( PropertyHint.File )]
|
||||||
|
public string BattleDataPath;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
1
addons/core/screens/BattleScreenDef.cs.uid
Normal file
1
addons/core/screens/BattleScreenDef.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://c05ywtujy7fdp
|
||||||
29
addons/core/screens/NilScreen.cs
Normal file
29
addons/core/screens/NilScreen.cs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// D E R E L I C T
|
||||||
|
//
|
||||||
|
/// // (c) 2003..2024
|
||||||
|
|
||||||
|
using Godot;
|
||||||
|
using Godot.Sharp.Extras;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
[GlobalClass]
|
||||||
|
public partial class NilScreen : ScreenGeneric<NilScreenDef>
|
||||||
|
{
|
||||||
|
|
||||||
|
static int s_count = 1024;
|
||||||
|
|
||||||
|
public override string DebugString() => $"{GetType().Name}:{Name} {s_count}";
|
||||||
|
|
||||||
|
public override void _Ready()
|
||||||
|
{
|
||||||
|
base._Ready();
|
||||||
|
|
||||||
|
this.OnReady();
|
||||||
|
|
||||||
|
Name = $"Nil {s_count++}";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
1
addons/core/screens/NilScreen.cs.uid
Normal file
1
addons/core/screens/NilScreen.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://dry64hhed8cf3
|
||||||
15
addons/core/screens/NilScreenDef.cs
Normal file
15
addons/core/screens/NilScreenDef.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// D E R E L I C T
|
||||||
|
//
|
||||||
|
/// // (c) 2003..2024
|
||||||
|
|
||||||
|
using Godot;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
|
||||||
|
[GlobalClass]
|
||||||
|
public partial class NilScreenDef : ScreenGenericDef<NilScreenDef, NilScreen>
|
||||||
|
{
|
||||||
|
}
|
||||||
1
addons/core/screens/NilScreenDef.cs.uid
Normal file
1
addons/core/screens/NilScreenDef.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://dwj6x4da1vtnv
|
||||||
20
addons/core/screens/Screen.cs
Normal file
20
addons/core/screens/Screen.cs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// D E R E L I C T
|
||||||
|
//
|
||||||
|
/// // (c) 2003..2024
|
||||||
|
|
||||||
|
using Godot;
|
||||||
|
using System;
|
||||||
|
using System.ComponentModel;
|
||||||
|
|
||||||
|
|
||||||
|
[GlobalClass]
|
||||||
|
public partial class Screen : Node
|
||||||
|
{
|
||||||
|
|
||||||
|
public virtual ScreenDef Def { get; }
|
||||||
|
|
||||||
|
public virtual string DebugString() => $"NEED OVERRIDE {GetType().Name}: {Def.ResourcePath}";
|
||||||
|
|
||||||
|
}
|
||||||
1
addons/core/screens/Screen.cs.uid
Normal file
1
addons/core/screens/Screen.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://cpcorxlhh1sry
|
||||||
23
addons/core/screens/ScreenDef.cs
Normal file
23
addons/core/screens/ScreenDef.cs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// D E R E L I C T
|
||||||
|
//
|
||||||
|
/// // (c) 2003..2024
|
||||||
|
|
||||||
|
using Godot;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
|
||||||
|
[GlobalClass]
|
||||||
|
public partial class ScreenDef : Resource
|
||||||
|
{
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public double TransitionTime = 1.0;
|
||||||
|
|
||||||
|
public Func<Screen> FnCreate = () => new Screen();
|
||||||
|
|
||||||
|
//public Func<ScreenDef, Event<ScreenDef>> FnCreate = new Func<ScreenDef, Event<ScreenDef>>( ( def ) => { return new Event<ScreenDef>() { Def = def }; } );
|
||||||
|
|
||||||
|
}
|
||||||
1
addons/core/screens/ScreenDef.cs.uid
Normal file
1
addons/core/screens/ScreenDef.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://ckhijop6db37v
|
||||||
40
addons/core/screens/ScreenGeneric.cs
Normal file
40
addons/core/screens/ScreenGeneric.cs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// D E R E L I C T
|
||||||
|
//
|
||||||
|
/// // (c) 2003..2024
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
using Godot;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public partial class ScreenGeneric<TDEF> : Screen
|
||||||
|
where TDEF : ScreenDef
|
||||||
|
{
|
||||||
|
public ScreenGeneric()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScreenGeneric( TDEF def )
|
||||||
|
{
|
||||||
|
Def = def;
|
||||||
|
}
|
||||||
|
|
||||||
|
new public TDEF Def { get; set; }
|
||||||
|
|
||||||
|
// Called when the node enters the scene tree for the first time.
|
||||||
|
public override void _Ready()
|
||||||
|
{
|
||||||
|
base._Ready();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||||
|
public override void _Process( double delta )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
1
addons/core/screens/ScreenGeneric.cs.uid
Normal file
1
addons/core/screens/ScreenGeneric.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://b3pganeh6vkvo
|
||||||
23
addons/core/screens/ScreenGenericDef.cs
Normal file
23
addons/core/screens/ScreenGenericDef.cs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// D E R E L I C T
|
||||||
|
//
|
||||||
|
/// // (c) 2003..2024
|
||||||
|
|
||||||
|
using Godot;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public partial class ScreenGenericDef<TSUBDEF, TEVT> : ScreenDef
|
||||||
|
where TSUBDEF : ScreenDef
|
||||||
|
where TEVT : ScreenGeneric<TSUBDEF>, new()
|
||||||
|
{
|
||||||
|
public ScreenGenericDef()
|
||||||
|
{
|
||||||
|
FnCreate = () => new TEVT { Def = this as TSUBDEF };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
1
addons/core/screens/ScreenGenericDef.cs.uid
Normal file
1
addons/core/screens/ScreenGenericDef.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://duyk67wxc2dtu
|
||||||
29
addons/core/screens/ScreenScene3D.cs
Normal file
29
addons/core/screens/ScreenScene3D.cs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// D E R E L I C T
|
||||||
|
//
|
||||||
|
/// // (c) 2003..2024
|
||||||
|
|
||||||
|
using Godot;
|
||||||
|
|
||||||
|
public partial class ScreenScene3D : Node3D
|
||||||
|
{
|
||||||
|
|
||||||
|
public void FreeEvent()
|
||||||
|
{
|
||||||
|
var parent = GetParent();
|
||||||
|
|
||||||
|
parent.QueueFree();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
virtual public void StartEvent()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StopEvent()
|
||||||
|
{
|
||||||
|
ProcessMode = ProcessModeEnum.Disabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
1
addons/core/screens/ScreenScene3D.cs.uid
Normal file
1
addons/core/screens/ScreenScene3D.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://uvkoj8pnfvbn
|
||||||
30
addons/core/screens/ScreneScene.cs
Normal file
30
addons/core/screens/ScreneScene.cs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// D E R E L I C T
|
||||||
|
//
|
||||||
|
/// // (c) 2003..2024
|
||||||
|
|
||||||
|
using Godot;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
public partial class ScreenScene : Control
|
||||||
|
{
|
||||||
|
|
||||||
|
public void FreeEvent()
|
||||||
|
{
|
||||||
|
var parent = GetParent();
|
||||||
|
|
||||||
|
parent.QueueFree();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void StartEvent()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StopEvent()
|
||||||
|
{
|
||||||
|
ProcessMode = ProcessModeEnum.Disabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
1
addons/core/screens/ScreneScene.cs.uid
Normal file
1
addons/core/screens/ScreneScene.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://c60otp8j7cj41
|
||||||
34
addons/core/screens/SimpleScreen.cs
Normal file
34
addons/core/screens/SimpleScreen.cs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// D E R E L I C T
|
||||||
|
//
|
||||||
|
/// // (c) 2003..2025
|
||||||
|
|
||||||
|
using Godot;
|
||||||
|
using Godot.Sharp.Extras;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
[GlobalClass]
|
||||||
|
public partial class SimpleScreen : ScreenGeneric<SimpleScreenDef>
|
||||||
|
{
|
||||||
|
|
||||||
|
public override string DebugString() => $"{GetType().Name}(base): {Def.ResourcePath}";
|
||||||
|
|
||||||
|
public override void _Ready()
|
||||||
|
{
|
||||||
|
base._Ready();
|
||||||
|
|
||||||
|
this.OnReady();
|
||||||
|
|
||||||
|
Name = $"SimpleScreen";
|
||||||
|
|
||||||
|
log.info( $"Running SimpleScreen with Scene {Def.Scene.ResourcePath}" );
|
||||||
|
|
||||||
|
var scene = Def.Scene.Instantiate<ScreenScene>();
|
||||||
|
|
||||||
|
AddChild( scene );
|
||||||
|
|
||||||
|
scene.StartEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
1
addons/core/screens/SimpleScreen.cs.uid
Normal file
1
addons/core/screens/SimpleScreen.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://daiwvsui0wjvf
|
||||||
21
addons/core/screens/SimpleScreenDef.cs
Normal file
21
addons/core/screens/SimpleScreenDef.cs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// D E R E L I C T
|
||||||
|
//
|
||||||
|
/// // (c) 2003..2024
|
||||||
|
|
||||||
|
using Godot;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
|
||||||
|
[GlobalClass]
|
||||||
|
public partial class SimpleScreenDef : ScreenGenericDef<SimpleScreenDef, SimpleScreen>
|
||||||
|
{
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public PackedScene Scene;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
1
addons/core/screens/SimpleScreenDef.cs.uid
Normal file
1
addons/core/screens/SimpleScreenDef.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://0cfmjbu3xcoa
|
||||||
49
addons/core/screens/TraversalScreen.cs
Normal file
49
addons/core/screens/TraversalScreen.cs
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// D E R E L I C T
|
||||||
|
//
|
||||||
|
/// // (c) 2003..2024
|
||||||
|
|
||||||
|
using Godot;
|
||||||
|
using System;
|
||||||
|
using Godot.Sharp.Extras;
|
||||||
|
|
||||||
|
[GlobalClass]
|
||||||
|
public partial class TraversalScreen : ScreenGeneric<TraversalScreenDef>
|
||||||
|
{
|
||||||
|
|
||||||
|
public override string DebugString() => $"{GetType().Name}: {Def.ResourcePath}";
|
||||||
|
|
||||||
|
public override void _Ready()
|
||||||
|
{
|
||||||
|
log.info( $"{GetType().Name}._Ready::Begin with Scene {Def.Scene.ResourcePath}" );
|
||||||
|
|
||||||
|
base._Ready();
|
||||||
|
|
||||||
|
this.OnReady();
|
||||||
|
|
||||||
|
Name = $"TraversalScreen";
|
||||||
|
|
||||||
|
log.info( $"Running {GetType().Name} with Scene {Def.Scene.ResourcePath}" );
|
||||||
|
|
||||||
|
var scene = Def.Scene.Instantiate<ScreenScene3D>();
|
||||||
|
|
||||||
|
AddChild( scene );
|
||||||
|
|
||||||
|
scene.StartEvent();
|
||||||
|
|
||||||
|
log.info( $"{GetType().Name}._Ready::Begin with Scene {Def.Scene.ResourcePath}" );
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void _Process( double delta )
|
||||||
|
{
|
||||||
|
base._Process( delta );
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
1
addons/core/screens/TraversalScreen.cs.uid
Normal file
1
addons/core/screens/TraversalScreen.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://b5m5em4s2p16i
|
||||||
27
addons/core/screens/TraversalScreenDef.cs
Normal file
27
addons/core/screens/TraversalScreenDef.cs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// D E R E L I C T
|
||||||
|
//
|
||||||
|
/// // (c) 2003..2025
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// D E R E L I C T
|
||||||
|
//
|
||||||
|
/// // (c) 2003..2024
|
||||||
|
|
||||||
|
using Godot;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
|
||||||
|
[GlobalClass]
|
||||||
|
public partial class TraversalScreenDef : ScreenGenericDef<TraversalScreenDef, TraversalScreen>
|
||||||
|
{
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public PackedScene Scene;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
1
addons/core/screens/TraversalScreenDef.cs.uid
Normal file
1
addons/core/screens/TraversalScreenDef.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://biy423jxrhrcw
|
||||||
22
addons/core/ui/ActionLine.tscn
Normal file
22
addons/core/ui/ActionLine.tscn
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
[gd_scene load_steps=3 format=3 uid="uid://cwm7nrm3wcdu3"]
|
||||||
|
|
||||||
|
[ext_resource type="SystemFont" uid="uid://dy0es3e530h00" path="res://data/ui/mono_font.tres" id="1_8cvm2"]
|
||||||
|
[ext_resource type="Script" path="res://addons/core/ui/TextAnimate.cs" id="2_ugfr6"]
|
||||||
|
|
||||||
|
[node name="StatusLine" type="LineEdit"]
|
||||||
|
offset_left = 91.0
|
||||||
|
offset_top = 1135.0
|
||||||
|
offset_right = 919.0
|
||||||
|
offset_bottom = 1169.0
|
||||||
|
theme_override_fonts/font = ExtResource("1_8cvm2")
|
||||||
|
text = "//"
|
||||||
|
placeholder_text = "last_detail"
|
||||||
|
editable = false
|
||||||
|
expand_to_text_length = true
|
||||||
|
|
||||||
|
[node name="TextAnimate" type="Control" parent="."]
|
||||||
|
anchors_preset = 0
|
||||||
|
offset_right = 40.0
|
||||||
|
offset_bottom = 40.0
|
||||||
|
script = ExtResource("2_ugfr6")
|
||||||
|
Cursor = "_"
|
||||||
76
addons/core/ui/RichTextAnim.cs
Normal file
76
addons/core/ui/RichTextAnim.cs
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// D E R E L I C T
|
||||||
|
//
|
||||||
|
/// // (c) 2003..2024
|
||||||
|
using Godot;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
[GlobalClass]
|
||||||
|
public partial class RichTextAnim : RichTextLabel
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public string TextPrefix = "";
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public string TextSuffix = "";
|
||||||
|
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public double MSPerChar = 100;
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public string OriginalText = "{BAD ERROR}";
|
||||||
|
|
||||||
|
|
||||||
|
[ExportCategory( "Animation" )]
|
||||||
|
[Export]
|
||||||
|
public int StartingOffset = 0;
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public int EndingOffset = 0;
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public int CharCount = 0;
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public double TimeTillNextChar = 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Called when the node enters the scene tree for the first time.
|
||||||
|
public override void _Ready()
|
||||||
|
{
|
||||||
|
OriginalText = Text;
|
||||||
|
Text = "";
|
||||||
|
|
||||||
|
TimeTillNextChar = MSPerChar / 1000.0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||||
|
public override void _Process( double delta )
|
||||||
|
{
|
||||||
|
TimeTillNextChar -= delta;
|
||||||
|
|
||||||
|
if( TimeTillNextChar <= 0 )
|
||||||
|
{
|
||||||
|
TimeTillNextChar += ( MSPerChar / 1000.0 );
|
||||||
|
|
||||||
|
if( CharCount < OriginalText.Length )
|
||||||
|
{
|
||||||
|
Text = TextPrefix + OriginalText.Substring( 0, CharCount ) + TextSuffix;
|
||||||
|
++CharCount;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Text = TextPrefix + OriginalText + TextSuffix;
|
||||||
|
ProcessMode = ProcessModeEnum.Disabled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
addons/core/ui/RichTextAnim.cs.uid
Normal file
1
addons/core/ui/RichTextAnim.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://bfq7roxsq3xbq
|
||||||
243
addons/core/ui/TextAnimate.cs
Normal file
243
addons/core/ui/TextAnimate.cs
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// D E R E L I C T
|
||||||
|
//
|
||||||
|
/// // (c) 2003..2024
|
||||||
|
using Godot;
|
||||||
|
using Microsoft.Diagnostics.Tracing.Parsers.IIS_Trace;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
/*
|
||||||
|
T O D O:
|
||||||
|
x) Add blinking cursor
|
||||||
|
|
||||||
|
D O I N G:
|
||||||
|
|
||||||
|
|
||||||
|
D O N E:
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
[GlobalClass]
|
||||||
|
public partial class TextAnimate : Control
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
public Action<string> OnFinshed;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public NodePath TargetPath = "..";
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public int TrimStartCount = 0;
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public int TrimeEndCount = 0;
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public double InitialDelay = 0;
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public double MSPerChar = 100;
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public string OriginalText = "{ERR_BROKEN_TextAnimate}";
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public string Cursor = "";
|
||||||
|
|
||||||
|
|
||||||
|
[ExportCategory( "Runtime" )]
|
||||||
|
[Export]
|
||||||
|
public Control TargetControl = null;
|
||||||
|
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public string TextSuffix = "";
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public int CharCount = 0;
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public double TimeTillNextChar = 0;
|
||||||
|
|
||||||
|
//Dont need prefix string since thats implicit in OriginalText
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public string SuffixStr = "";
|
||||||
|
|
||||||
|
|
||||||
|
Func<String> GetNodeText;
|
||||||
|
Action<String> SetNodeText;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Called when the node enters the scene tree for the first time.
|
||||||
|
public override void _Ready()
|
||||||
|
{
|
||||||
|
log.var( this.Name );
|
||||||
|
|
||||||
|
//log.info($"{this?.Name}");
|
||||||
|
|
||||||
|
if( TargetPath == null || TargetPath.IsEmpty )
|
||||||
|
{
|
||||||
|
var parent = GetParent();
|
||||||
|
|
||||||
|
if( parent != null )
|
||||||
|
{
|
||||||
|
log.info( $"{Name}: No TargetPath set, using parent {parent.Name}" );
|
||||||
|
TargetPath = parent.GetPath();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
log.warn( $"{Name}: No TargetPath set and no parent to use" );
|
||||||
|
ProcessMode = ProcessModeEnum.Disabled;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TargetControl = GetNode<Control>( TargetPath );
|
||||||
|
|
||||||
|
if( TargetControl == null )
|
||||||
|
{
|
||||||
|
log.warn( $"{Name}({this.NamePath()}): No node at {TargetPath}" );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//*
|
||||||
|
if( TargetControl is LineEdit tcLineEdit )
|
||||||
|
{
|
||||||
|
log.info( $"{Name} Connect to {TargetControl.Name}'s TextChanged" );
|
||||||
|
tcLineEdit.TextChanged += ( text ) => Reset();
|
||||||
|
}
|
||||||
|
else if( TargetControl is RichTextLabel richTextLabel )
|
||||||
|
{
|
||||||
|
log.info( $"{Name} Connect to {TargetControl.Name}'s TextChanged" );
|
||||||
|
}
|
||||||
|
else if( TargetControl is Button button )
|
||||||
|
{
|
||||||
|
log.info( $"{Name} Connect to {TargetControl.Name}'s TextChanged" );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
//*/
|
||||||
|
{
|
||||||
|
log.high( $"{this?.Name} NOT CONNECTED {TargetControl?.Name}'s TextChanged" );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//if( TargetControl is RichTextLabel rtLabel ) rtLabel.TextChanged += ( text ) => Reset();
|
||||||
|
|
||||||
|
log.info( $"{this.Name}: TargetControl is {TargetControl?.GetType().Name}" );
|
||||||
|
|
||||||
|
var propInfo = TargetControl?.GetType().GetProperty( "Text" );
|
||||||
|
var getMethod = propInfo?.GetGetMethod();
|
||||||
|
var setMethod = propInfo?.GetSetMethod();
|
||||||
|
|
||||||
|
if( getMethod == null || setMethod == null )
|
||||||
|
{
|
||||||
|
log.warn( $"{Name}: No Text property on {TargetControl?.GetType().Name}" );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
GetNodeText = (Func<String>)Delegate.CreateDelegate( typeof( Func<String> ), TargetControl, getMethod );
|
||||||
|
SetNodeText = (Action<String>)Delegate.CreateDelegate( typeof( Action<String> ), TargetControl, setMethod );
|
||||||
|
|
||||||
|
OriginalText = GetNodeText();
|
||||||
|
|
||||||
|
SuffixStr = OriginalText.Substring( OriginalText.Length - TrimeEndCount );
|
||||||
|
|
||||||
|
Reset();
|
||||||
|
|
||||||
|
//log.debug( $"D O N E {GetType().Name}._Ready" );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset( double delayMs = -1, string origText = "" )
|
||||||
|
{
|
||||||
|
if( ProcessMode == ProcessModeEnum.Disabled )
|
||||||
|
return;
|
||||||
|
|
||||||
|
|
||||||
|
if( GetNodeText == null || SetNodeText == null )
|
||||||
|
{
|
||||||
|
log.warn( $"{Name}: No Text property on {TargetControl?.GetType().Name}" );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if( string.IsNullOrWhiteSpace( origText ) )
|
||||||
|
origText = GetNodeText();
|
||||||
|
|
||||||
|
InitialDelay = delayMs / 1000.0;
|
||||||
|
|
||||||
|
ProcessMode = ProcessModeEnum.Inherit;
|
||||||
|
|
||||||
|
OriginalText = origText;
|
||||||
|
|
||||||
|
if( delayMs < 0 )
|
||||||
|
{
|
||||||
|
delayMs = MSPerChar;
|
||||||
|
}
|
||||||
|
|
||||||
|
//log.debug( $"TA.Text Reset {OriginalText}" );
|
||||||
|
|
||||||
|
CharCount = TrimStartCount;
|
||||||
|
|
||||||
|
var text = OriginalText.Substring( 0, TrimStartCount );
|
||||||
|
TimeTillNextChar = delayMs / 1000.0;
|
||||||
|
SetNodeText( $"{text}{Cursor}{SuffixStr}" );
|
||||||
|
|
||||||
|
++CharCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||||
|
public override void _Process( double delta )
|
||||||
|
{
|
||||||
|
|
||||||
|
if( ProcessMode == ProcessModeEnum.Disabled )
|
||||||
|
return;
|
||||||
|
|
||||||
|
|
||||||
|
if( GetNodeText == null || SetNodeText == null )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//log.info($"{this.Name}");
|
||||||
|
|
||||||
|
if( InitialDelay > 0 )
|
||||||
|
{
|
||||||
|
InitialDelay -= delta;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TimeTillNextChar -= delta;
|
||||||
|
|
||||||
|
if( TimeTillNextChar <= 0 )
|
||||||
|
{
|
||||||
|
TimeTillNextChar += ( MSPerChar / 1000.0 );
|
||||||
|
|
||||||
|
if( CharCount < OriginalText.Length )
|
||||||
|
{
|
||||||
|
//SetNodeText( TextPrefix + OriginalText.Substring( 0, CharCount ) + TextSuffix );
|
||||||
|
var newText = $"{OriginalText.Substring( 0, CharCount )}{Cursor}{SuffixStr}";
|
||||||
|
SetNodeText( newText );
|
||||||
|
|
||||||
|
++CharCount;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SetNodeText( OriginalText );
|
||||||
|
OnFinshed?.Invoke( OriginalText );
|
||||||
|
//ProcessMode = ProcessModeEnum.Disabled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
addons/core/ui/TextAnimate.cs.uid
Normal file
1
addons/core/ui/TextAnimate.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://c7j5w7q4f0stt
|
||||||
Loading…
Reference in New Issue
Block a user