NPC Behaviors: movement/fight classes, Smoother Quaternion fix, ThirdPersonCamera lock blend split
- New Behavior/Movements: MoveOnPath, GoToPointBehavior, FleeBehavior, HidingSpot/HideAtSpotBehavior, NavigationBehaviorMovement; BehaviorMovement gained Flee/GetGlobalPosition, CharacterBodyBehaviorMovement moved from raw duration floats to Smoothing-resource exports. - New Behavior/Fight: FightManager/FightSlot/JoinFight/LeaveFight/IsEngagedInFight/HasAttackPermission/IsAllyAttacking/FightState, mirroring the CameraManager/CameraSlot singleton+slot pattern. - New Conditions/IsValueBelowThreshold (generic against NumberValue). - Fixed Smoother.SmoothWithCoefficient(Quaternion...) not renormalizing after Slerp (crashed live after sustained use) and a MathX.SmoothingCoefficient argument-order bug in all three SmoothForDuration overloads. - ThirdPersonCamera: lock engage/release now blends yaw/pitch (fixing a hard isLocked snap), fixed a yaw-staleness regression from that change, then split the single lock blend into independent focus (lockEngageSmoothing/lockReleaseSmoothing) and orientation (lockOrientationEngageSmoothing/lockOrientationReleaseSmoothing) blends. - Includes carried-over uncommitted state from the earlier NPC/AI/Behaviours task (Behavior/StateMachine, Receptor/ReceptorBase/ReceptorControl, related Icons) that hadn't been committed yet. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
ba487e326c
commit
211613e06b
|
|
@ -0,0 +1,59 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
version="1.1"
|
||||
id="svg4"
|
||||
sodipodi:docname="OnStateActive.svg"
|
||||
inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs8" />
|
||||
<sodipodi:namedview
|
||||
id="namedview6"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050"
|
||||
showgrid="false"
|
||||
inkscape:zoom="10.363534"
|
||||
inkscape:cx="17.079116"
|
||||
inkscape:cy="24.701998"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1017"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg4" />
|
||||
<circle
|
||||
style="fill:#f37fe1;fill-opacity:1;stroke:none;stroke-width:0.486852;stroke-linecap:square;stroke-linejoin:round;stroke-opacity:1;paint-order:stroke fill markers"
|
||||
id="path127342"
|
||||
cx="8"
|
||||
cy="8"
|
||||
r="5.5349336" />
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="fill:#740b63;fill-opacity:1;stroke:none;stroke-width:5;stroke-linecap:square;stroke-linejoin:round;stroke-opacity:1;paint-order:stroke fill markers"
|
||||
id="path127396"
|
||||
inkscape:flatsided="true"
|
||||
sodipodi:sides="3"
|
||||
sodipodi:cx="-593.57117"
|
||||
sodipodi:cy="268.06439"
|
||||
sodipodi:r1="28.148277"
|
||||
sodipodi:r2="14.074139"
|
||||
sodipodi:arg1="0"
|
||||
sodipodi:arg2="1.0471976"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="m -565.42289,268.06439 -42.22242,24.37713 v -48.75425 z"
|
||||
inkscape:transform-center-x="-0.79173686"
|
||||
transform="matrix(0.1125095,0,0,0.1125095,74.782389,-22.15979)"
|
||||
inkscape:transform-center-y="1.1649022e-06" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
|
|
@ -0,0 +1,43 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://pu48jird8x7e"
|
||||
path="res://.godot/imported/OnStateActive.svg-44af1d9740b53d2319633872a1d84635.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/rokojori_action_library/Icons/OnStateActive.svg"
|
||||
dest_files=["res://.godot/imported/OnStateActive.svg-44af1d9740b53d2319633872a1d84635.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
version="1.1"
|
||||
id="svg4"
|
||||
sodipodi:docname="OnStateEntered.svg"
|
||||
inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs8">
|
||||
<linearGradient
|
||||
inkscape:collect="never"
|
||||
id="linearGradient167856">
|
||||
<stop
|
||||
style="stop-color:#f37fe1;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop167852" />
|
||||
<stop
|
||||
style="stop-color:#ee53d6;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop167854" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient3749"
|
||||
id="radialGradient3751"
|
||||
cx="5.4636054"
|
||||
cy="4.3360767"
|
||||
fx="5.4636054"
|
||||
fy="4.3360767"
|
||||
r="7.0478635"
|
||||
gradientTransform="matrix(1.2601288,0,0,1.1040295,-1.5805922,-0.17591381)"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
id="linearGradient3749">
|
||||
<stop
|
||||
style="stop-color:#ffc401;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3745" />
|
||||
<stop
|
||||
style="stop-color:#e68500;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop3747" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="never"
|
||||
xlink:href="#linearGradient167856"
|
||||
id="linearGradient167858"
|
||||
x1="-453.08392"
|
||||
y1="102.53325"
|
||||
x2="-486.03638"
|
||||
y2="186.16046"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(-0.0733327,-0.05446238,-0.04233865,0.0943316,-20.033234,-29.855994)" />
|
||||
<linearGradient
|
||||
inkscape:collect="never"
|
||||
xlink:href="#linearGradient167856"
|
||||
id="linearGradient168595"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="-441.29767"
|
||||
y1="72.449623"
|
||||
x2="-510.28955"
|
||||
y2="168.54135"
|
||||
gradientTransform="matrix(0.0733327,-0.05446238,0.04233865,0.0943316,32.324914,-29.907668)" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="namedview6"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050"
|
||||
showgrid="false"
|
||||
inkscape:zoom="14.65625"
|
||||
inkscape:cx="19.513859"
|
||||
inkscape:cy="11.872068"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1017"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg4" />
|
||||
<path
|
||||
id="path127114"
|
||||
style="fill:url(#linearGradient167858);fill-opacity:1;stroke:none;stroke-width:0.480194;stroke-linecap:square;stroke-linejoin:round;stroke-opacity:1;paint-order:stroke fill markers"
|
||||
d="M 9.1054688 1.9101562 C 8.6140756 1.9101562 8.2187497 2.3054819 8.21875 2.796875 L 8.21875 2.96875 C 8.2187496 3.4601427 8.6140755 3.8554725 9.1054688 3.8554688 L 13.011719 3.8554688 L 13.011719 12.46875 L 9.0546875 12.46875 C 8.591645 12.468749 8.2187493 12.841644 8.21875 13.304688 L 8.21875 13.423828 C 8.2187499 13.886872 8.5916438 14.259766 9.0546875 14.259766 L 13.898438 14.259766 C 14.361481 14.259765 14.734375 13.886872 14.734375 13.423828 L 14.734375 13.304688 C 14.734376 13.300768 14.734432 13.296869 14.734375 13.292969 C 14.734418 13.288949 14.734377 13.28525 14.734375 13.28125 L 14.734375 3.2480469 C 14.734375 3.1978914 14.730909 3.1496485 14.724609 3.1015625 C 14.731209 3.0577763 14.734375 3.014433 14.734375 2.96875 L 14.734375 2.796875 C 14.734374 2.3054818 14.339049 1.9101559 13.847656 1.9101562 L 9.1054688 1.9101562 z M 6.6132812 4.5761719 L 6.6132812 5.8671875 C 6.4989359 5.8492975 6.3831942 5.8398437 6.2636719 5.8398438 L 3.1777344 5.8398438 C 1.9391981 5.8398438 0.94140565 6.8376361 0.94140625 8.0761719 C 0.94140615 9.3147076 1.9391981 10.310547 3.1777344 10.310547 L 6.2636719 10.310547 C 6.3831942 10.310547 6.4989357 10.301043 6.6132812 10.283203 L 6.6132812 11.748047 L 11.441406 8.1621094 L 6.6132812 4.5761719 z " />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.1 KiB |
|
|
@ -0,0 +1,43 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://di0gwxfn5v3jm"
|
||||
path="res://.godot/imported/OnStateEntered.svg-cc2c47cc15c447c0d4066848fb108e6f.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/rokojori_action_library/Icons/OnStateEntered.svg"
|
||||
dest_files=["res://.godot/imported/OnStateEntered.svg-cc2c47cc15c447c0d4066848fb108e6f.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
version="1.1"
|
||||
id="svg4"
|
||||
sodipodi:docname="OnStateExited.svg"
|
||||
inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs8">
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient167856"
|
||||
id="linearGradient167858"
|
||||
x1="-441.29767"
|
||||
y1="72.449623"
|
||||
x2="-510.28955"
|
||||
y2="168.54135"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.0733327,-0.05446238,0.04233865,0.0943316,35.822756,-29.855994)" />
|
||||
<linearGradient
|
||||
id="linearGradient167856">
|
||||
<stop
|
||||
style="stop-color:#f37fe1;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop167852" />
|
||||
<stop
|
||||
style="stop-color:#ee53d6;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop167854" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="namedview6"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050"
|
||||
showgrid="false"
|
||||
inkscape:zoom="14.65625"
|
||||
inkscape:cx="3.684435"
|
||||
inkscape:cy="7.4371002"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1017"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg4" />
|
||||
<path
|
||||
id="path127114"
|
||||
style="fill:url(#linearGradient167858);fill-opacity:1;stroke:none;stroke-width:0.480194;stroke-linecap:square;stroke-linejoin:round;stroke-opacity:1;paint-order:stroke fill markers"
|
||||
d="m 1.941406,1.9101565 c -0.4913928,-4e-7 -0.8867185,0.395325 -0.8867188,0.8867187 l 1e-7,0.1718749 c 0,0.045683 0.00317,0.089026 0.00977,0.1328122 -0.00634,0.048086 -0.00977,0.096329 -0.00977,0.1464845 l -1e-7,10.0332032 c -2.9e-6,0.004 -4.26e-5,0.0077 7e-7,0.01172 -5.69e-5,0.0039 -3e-7,0.0078 6e-7,0.01172 l 4e-7,0.11914 c 0,0.463044 0.3728941,0.835937 0.8359376,0.835938 h 4.84375 c 0.4630442,0 0.8359376,-0.372894 0.8359375,-0.835938 l -4e-7,-0.11914 c 7e-7,-0.463044 -0.3728941,-0.835938 -0.8359371,-0.835937 l -3.9570312,-10e-7 -2e-7,-8.6132809 3.90625,-10e-8 c 0.4913936,-2e-7 0.8867192,-0.3953255 0.8867188,-0.8867187 l -2e-7,-0.171875 c 4e-7,-0.4913936 -0.3953247,-0.8867191 -0.8867183,-0.886719 z m 8.169922,2.7167963 V 5.9179692 C 9.9969825,5.9000768 9.881241,5.8906244 9.7617186,5.8906247 l -3.0859376,3e-7 c -1.2385375,0 -2.2363281,0.9977911 -2.2363275,2.2363281 -1e-7,1.2385375 0.9977934,2.2343719 2.2363273,2.2343749 l 3.085938,10e-7 c 0.1195224,-10e-7 0.2352635,-0.0095 0.3496092,-0.02734 v 1.464844 l 4.828111,-3.5859704 z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
|
|
@ -0,0 +1,43 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dcs52ui6ku110"
|
||||
path="res://.godot/imported/OnStateExited.svg-5e9a8abbedeb843cc99c78ab473f3106.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/rokojori_action_library/Icons/OnStateExited.svg"
|
||||
dest_files=["res://.godot/imported/OnStateExited.svg-5e9a8abbedeb843cc99c78ab473f3106.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
version="1.1"
|
||||
id="svg4"
|
||||
sodipodi:docname="State.svg"
|
||||
inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs8" />
|
||||
<sodipodi:namedview
|
||||
id="namedview6"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050"
|
||||
showgrid="false"
|
||||
inkscape:zoom="5.1817669"
|
||||
inkscape:cx="-10.228171"
|
||||
inkscape:cy="-6.3684841"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1017"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g132985" />
|
||||
<g
|
||||
id="g132985"
|
||||
transform="matrix(0.19767936,0,0,0.19767936,12.531667,12.007634)">
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="fill:#740b63;fill-opacity:1;stroke:#f37fe1;stroke-width:10.1174;stroke-linecap:square;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
|
||||
id="path124828"
|
||||
inkscape:flatsided="true"
|
||||
sodipodi:sides="6"
|
||||
sodipodi:cx="-421.24405"
|
||||
sodipodi:cy="160.35995"
|
||||
sodipodi:r1="38.013397"
|
||||
sodipodi:r2="32.92057"
|
||||
sodipodi:arg1="0.52359878"
|
||||
sodipodi:arg2="1.0471976"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="m -388.32348,179.36665 -32.92057,19.0067 -32.92057,-19.0067 0,-38.01339 32.92057,-19.0067 32.92057,19.0067 z"
|
||||
transform="matrix(0.9206707,0,0,0.9206707,364.90272,-167.91212)" />
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:8.1746352;stroke-linecap:square;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
|
||||
id="path159155"
|
||||
inkscape:flatsided="true"
|
||||
sodipodi:sides="6"
|
||||
sodipodi:cx="-421.24405"
|
||||
sodipodi:cy="160.35995"
|
||||
sodipodi:r1="38.013397"
|
||||
sodipodi:r2="32.92057"
|
||||
sodipodi:arg1="0.52359878"
|
||||
sodipodi:arg2="1.0471976"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="m -388.32348,179.36665 -32.92057,19.0067 -32.92057,-19.0067 0,-38.01339 32.92057,-19.0067 32.92057,19.0067 z"
|
||||
transform="matrix(0.61882848,0,0,0.61882848,237.75348,-119.50872)" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
|
|
@ -0,0 +1,43 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://cyii8eavle6jq"
|
||||
path="res://.godot/imported/State.svg-152f831ddb60ea6a98f9b0cc3a8104b5.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/rokojori_action_library/Icons/State.svg"
|
||||
dest_files=["res://.godot/imported/State.svg-152f831ddb60ea6a98f9b0cc3a8104b5.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
version="1.1"
|
||||
id="svg4"
|
||||
sodipodi:docname="StateMachine.svg"
|
||||
inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs8" />
|
||||
<sodipodi:namedview
|
||||
id="namedview6"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050"
|
||||
showgrid="false"
|
||||
inkscape:zoom="5.181767"
|
||||
inkscape:cx="27.210795"
|
||||
inkscape:cy="38.403888"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1017"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g142048" />
|
||||
<g
|
||||
id="g142056"
|
||||
transform="matrix(0.66105636,0,0,0.66105636,5.7899013,7.6782027)"
|
||||
style="display:inline;stroke:#000000;stroke-width:5.4496;stroke-dasharray:none;stroke-opacity:1">
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="fill:#f37fe1;fill-opacity:1;stroke:#000000;stroke-width:6.90539;stroke-linecap:square;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
|
||||
id="path142050"
|
||||
inkscape:flatsided="true"
|
||||
sodipodi:sides="6"
|
||||
sodipodi:cx="3.3432834"
|
||||
sodipodi:cy="-4.0255861"
|
||||
sodipodi:r1="5.9458289"
|
||||
sodipodi:r2="5.1492391"
|
||||
sodipodi:arg1="-1.5707963"
|
||||
sodipodi:arg2="-1.0471976"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="m 3.3432836,-9.971415 5.1492388,2.9729146 -2e-7,5.9458289 -5.1492389,2.9729143 -5.1492389,-2.9729146 2e-7,-5.9458289 z"
|
||||
transform="matrix(0.78918119,0,0,0.78918119,0.70482703,-0.84866927)" />
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="fill:#f37fe1;fill-opacity:1;stroke:#000000;stroke-width:6.90539;stroke-linecap:square;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
|
||||
id="path142052"
|
||||
inkscape:flatsided="true"
|
||||
sodipodi:sides="6"
|
||||
sodipodi:cx="3.3432834"
|
||||
sodipodi:cy="-4.0255861"
|
||||
sodipodi:r1="5.9458289"
|
||||
sodipodi:r2="5.1492391"
|
||||
sodipodi:arg1="-1.5707963"
|
||||
sodipodi:arg2="-1.0471976"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="m 3.3432836,-9.971415 5.1492388,2.9729146 -2e-7,5.9458289 -5.1492389,2.9729143 -5.1492389,-2.9729146 2e-7,-5.9458289 z"
|
||||
transform="matrix(0.78918119,0,0,0.78918119,-4.4124436,8.0212665)" />
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="fill:#f37fe1;fill-opacity:1;stroke:#000000;stroke-width:6.90539;stroke-linecap:square;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
|
||||
id="path142054"
|
||||
inkscape:flatsided="true"
|
||||
sodipodi:sides="6"
|
||||
sodipodi:cx="3.3432834"
|
||||
sodipodi:cy="-4.0255861"
|
||||
sodipodi:r1="5.9458289"
|
||||
sodipodi:r2="5.1492391"
|
||||
sodipodi:arg1="-1.5707963"
|
||||
sodipodi:arg2="-1.0471976"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="m 3.3432836,-9.971415 5.1492388,2.9729146 -2e-7,5.9458289 -5.1492389,2.9729143 -5.1492389,-2.9729146 2e-7,-5.9458289 z"
|
||||
transform="matrix(0.78918119,0,0,0.78918119,5.8220977,8.0212665)" />
|
||||
</g>
|
||||
<g
|
||||
id="g142048"
|
||||
transform="matrix(0.66105636,0,0,0.66105636,5.7899012,7.6782027)"
|
||||
style="display:inline;stroke-width:1.43833;stroke-dasharray:none">
|
||||
<path
|
||||
id="path141676"
|
||||
style="fill:#f37fe1;fill-opacity:1;stroke:#f2f2f2;stroke-width:3.45269;stroke-linecap:square;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
|
||||
d="m 3.3432835,-9.9697838 -5.1477509,2.9725922 v 5.9451843 L 3.3432835,1.9205849 8.4910344,-1.0520073 V -6.9971916 Z M -3.1410108,1.2691604 -8.2887617,4.2417525 v 5.9451845 l 5.1477509,2.972592 5.147751,-2.972592 V 4.2417525 Z m 12.9685885,0 -5.1477509,2.9725921 v 5.9451845 l 5.1477509,2.972592 5.1477513,-2.972592 V 4.2417525 Z"
|
||||
transform="matrix(0.78918119,0,0,0.78918119,0.70482703,-0.84866927)" />
|
||||
<path
|
||||
style="fill:#f37fe1;fill-opacity:1;stroke:none;stroke-width:6.05092;stroke-linecap:square;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
|
||||
d="M -0.62091858,-1.7623014 -1.879881,0.26298577 1.3131395,3.4560062 2.225431,7.1781552 4.4331766,7.1964018 5.6921388,3.0728437 8.5019962,0.17175663 7.3890018,-1.7440556 3.3749183,-0.75878065 Z"
|
||||
id="path148777" />
|
||||
</g>
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="fill:#740b63;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
|
||||
id="path143575"
|
||||
inkscape:flatsided="true"
|
||||
sodipodi:sides="6"
|
||||
sodipodi:cx="-7.1982942"
|
||||
sodipodi:cy="0.95522386"
|
||||
sodipodi:r1="2.3844016"
|
||||
sodipodi:r2="2.0649524"
|
||||
sodipodi:arg1="1.0471976"
|
||||
sodipodi:arg2="1.5707963"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="m -6.0060935,3.0201762 -2.3844015,-10e-8 -1.1922007,-2.06495236 1.1922009,-2.06495224 2.3844015,10e-8 1.1922007,2.06495237 z"
|
||||
transform="matrix(0.59025285,0.34078264,-0.34078264,0.59025285,9.2099604,12.879823)" />
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="fill:#740b63;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
|
||||
id="path145051"
|
||||
inkscape:flatsided="true"
|
||||
sodipodi:sides="6"
|
||||
sodipodi:cx="-7.1982942"
|
||||
sodipodi:cy="0.95522386"
|
||||
sodipodi:r1="2.3844016"
|
||||
sodipodi:r2="2.0649524"
|
||||
sodipodi:arg1="1.0471976"
|
||||
sodipodi:arg2="1.5707963"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="m -6.0060935,3.0201762 -2.3844015,-10e-8 -1.1922007,-2.06495236 1.1922009,-2.06495224 2.3844015,10e-8 1.1922007,2.06495237 z"
|
||||
transform="matrix(0.59025285,0.34078264,-0.34078264,0.59025285,12.52825,6.8270172)" />
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="fill:#740b63;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
|
||||
id="path145053"
|
||||
inkscape:flatsided="true"
|
||||
sodipodi:sides="6"
|
||||
sodipodi:cx="-7.1982942"
|
||||
sodipodi:cy="0.95522386"
|
||||
sodipodi:r1="2.3844016"
|
||||
sodipodi:r2="2.0649524"
|
||||
sodipodi:arg1="1.0471976"
|
||||
sodipodi:arg2="1.5707963"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="m -6.0060935,3.0201762 -2.3844015,-10e-8 -1.1922007,-2.06495236 1.1922009,-2.06495224 2.3844015,10e-8 1.1922007,2.06495237 z"
|
||||
transform="matrix(0.59025285,0.34078264,-0.34078264,0.59025285,16.000164,12.910548)" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.9 KiB |
|
|
@ -0,0 +1,43 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://bc0rlxsml3vwi"
|
||||
path="res://.godot/imported/StateMachine.svg-310f195fb3534c1788d58d28c8ba9b86.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/rokojori_action_library/Icons/StateMachine.svg"
|
||||
dest_files=["res://.godot/imported/StateMachine.svg-310f195fb3534c1788d58d28c8ba9b86.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
version="1.1"
|
||||
id="svg4"
|
||||
sodipodi:docname="StateTransition.svg"
|
||||
inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs8" />
|
||||
<sodipodi:namedview
|
||||
id="namedview6"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050"
|
||||
showgrid="false"
|
||||
inkscape:zoom="14.65625"
|
||||
inkscape:cx="14.533049"
|
||||
inkscape:cy="6.4818762"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1017"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg4" />
|
||||
<g
|
||||
id="g138178"
|
||||
transform="matrix(0.50901285,0,0,0.50901285,0.91506103,-3.9818794)">
|
||||
<circle
|
||||
style="fill:#f37fe1;fill-opacity:1;stroke:none;stroke-width:0.894194;stroke-linecap:square;stroke-linejoin:round;stroke-opacity:1;paint-order:stroke fill markers"
|
||||
id="circle129058"
|
||||
cx="13.918979"
|
||||
cy="23.539444"
|
||||
r="10.165928" />
|
||||
<g
|
||||
id="g129070"
|
||||
transform="matrix(0.31604434,0,0,0.31604434,131.07854,-159.12113)"
|
||||
style="fill:#ffffff;fill-opacity:1">
|
||||
<rect
|
||||
style="opacity:1;fill:#740b63;fill-opacity:1;stroke:none;stroke-width:5;stroke-linecap:square;stroke-linejoin:round;stroke-opacity:1;paint-order:stroke fill markers"
|
||||
id="rect129062"
|
||||
width="13.094925"
|
||||
height="30.040344"
|
||||
x="-387.35083"
|
||||
y="562.93854"
|
||||
ry="3.1069341"
|
||||
rx="3.1069341" />
|
||||
<rect
|
||||
style="opacity:1;fill:#740b63;fill-opacity:1;stroke:none;stroke-width:5;stroke-linecap:square;stroke-linejoin:round;stroke-opacity:1;paint-order:stroke fill markers"
|
||||
id="rect129066"
|
||||
width="13.094925"
|
||||
height="30.040344"
|
||||
x="-367.15625"
|
||||
y="562.93854"
|
||||
ry="3.1069341"
|
||||
rx="3.1069341" />
|
||||
</g>
|
||||
</g>
|
||||
<path
|
||||
id="rect401"
|
||||
style="color:#000000;fill:#1de3ab;fill-opacity:1;stroke-linecap:round;stroke-linejoin:round;-inkscape-stroke:none;paint-order:markers stroke fill"
|
||||
d="m 1.319336,0.0888674 c -0.674094,0 -1.248047,0.571999 -1.248047,1.246093 v 2.511719 h 2 V 2.0888674 H 3.81543 v -2 z m 10.84375,0 v 2 h 1.765625 v 1.757812 h 2 v -2.511719 c 0,-0.674094 -0.573952,-1.246093 -1.248047,-1.246093 z M 0.071289,12.194336 v 2.46875 c 0,0.674094 0.573953,1.248047 1.248047,1.248047 H 3.81543 v -2 H 2.071289 v -1.716797 z m 13.857422,0 v 1.716797 h -1.765625 v 2 h 2.517578 c 0.674095,0 1.248047,-0.573953 1.248047,-1.248047 v -2.46875 z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
|
|
@ -0,0 +1,43 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://bkubxrl2eugj6"
|
||||
path="res://.godot/imported/StateTransition.svg-9e667c2d4f17c05ec9915b7e754fb653.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/rokojori_action_library/Icons/StateTransition.svg"
|
||||
dest_files=["res://.godot/imported/StateTransition.svg-9e667c2d4f17c05ec9915b7e754fb653.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
using Godot;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using Rokojori.Extensions;
|
||||
namespace Rokojori;
|
||||
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class FightManager:Node
|
||||
{
|
||||
[Export]
|
||||
public int maxEngaged = 3;
|
||||
|
||||
[Export]
|
||||
public int maxAttacking = 1;
|
||||
|
||||
List<FightSlot> _slots = new List<FightSlot>();
|
||||
Dictionary<FightSlot,double> _lastAttackTime = new Dictionary<FightSlot,double>();
|
||||
|
||||
ulong _cachedFrame = ulong.MaxValue;
|
||||
List<FightSlot> _cachedEngaged = new List<FightSlot>();
|
||||
List<FightSlot> _cachedAttackers = new List<FightSlot>();
|
||||
|
||||
public static FightManager Get()
|
||||
{
|
||||
return Unique<FightManager>.Get();
|
||||
}
|
||||
|
||||
public int registeredCount => _slots.Count;
|
||||
public int engagedCount => EngagedSlots().Count;
|
||||
public int attackingCount => AttackingSlots().Count;
|
||||
|
||||
public void Register( FightSlot slot )
|
||||
{
|
||||
if ( slot == null || _slots.Contains( slot ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_slots.Add( slot );
|
||||
}
|
||||
|
||||
public void Unregister( FightSlot slot )
|
||||
{
|
||||
_slots.Remove( slot );
|
||||
_lastAttackTime.Remove( slot );
|
||||
}
|
||||
|
||||
List<FightSlot> EngagedSlots()
|
||||
{
|
||||
RefreshFrameCache();
|
||||
return _cachedEngaged;
|
||||
}
|
||||
|
||||
List<FightSlot> AttackingSlots()
|
||||
{
|
||||
RefreshFrameCache();
|
||||
return _cachedAttackers;
|
||||
}
|
||||
|
||||
void RefreshFrameCache()
|
||||
{
|
||||
var frame = Engine.GetPhysicsFrames();
|
||||
|
||||
if ( frame == _cachedFrame )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_cachedFrame = frame;
|
||||
|
||||
_cachedEngaged = _slots
|
||||
.OrderByDescending( s => s.priority )
|
||||
.Take( Mathf.Max( 0, maxEngaged ) )
|
||||
.ToList();
|
||||
|
||||
var now = Time.GetTicksMsec() / 1000.0;
|
||||
|
||||
_cachedAttackers = _cachedEngaged
|
||||
.OrderByDescending( s => s.priority )
|
||||
.ThenBy( s => _lastAttackTime.TryGetValue( s, out var t ) ? t : -1 )
|
||||
.Take( Mathf.Max( 0, maxAttacking ) )
|
||||
.ToList();
|
||||
|
||||
_cachedAttackers.ForEach( s => _lastAttackTime[ s ] = now );
|
||||
}
|
||||
|
||||
public bool IsEngaged( FightSlot slot )
|
||||
{
|
||||
return slot != null && EngagedSlots().Contains( slot );
|
||||
}
|
||||
|
||||
public bool HasAttackPermission( FightSlot slot )
|
||||
{
|
||||
return slot != null && AttackingSlots().Contains( slot );
|
||||
}
|
||||
|
||||
public bool IsAnyOtherAttacking( FightSlot self )
|
||||
{
|
||||
return AttackingSlots().Any( s => s != self );
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://cpu8c7kvpg8cf
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
using Godot;
|
||||
|
||||
using Rokojori.Extensions;
|
||||
namespace Rokojori;
|
||||
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class FightSlot:Node
|
||||
{
|
||||
[Export]
|
||||
public float priority = 0;
|
||||
|
||||
bool _joined = false;
|
||||
|
||||
public void Join()
|
||||
{
|
||||
if ( _joined )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var manager = FightManager.Get();
|
||||
|
||||
if ( manager == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
manager.Register( this );
|
||||
_joined = true;
|
||||
}
|
||||
|
||||
public void Leave()
|
||||
{
|
||||
if ( ! _joined )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FightManager.Get()?.Unregister( this );
|
||||
_joined = false;
|
||||
}
|
||||
|
||||
public override void _ExitTree()
|
||||
{
|
||||
Leave();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://bvfaxecr50ode
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
using Godot;
|
||||
|
||||
using Rokojori.Extensions;
|
||||
namespace Rokojori;
|
||||
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class FightState:Node
|
||||
{
|
||||
public bool isInCombat => ( FightManager.Get()?.registeredCount ?? 0 ) > 0;
|
||||
|
||||
public int attackerCount => FightManager.Get()?.attackingCount ?? 0;
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://b7ygkdwfomf1c
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
using Godot;
|
||||
|
||||
using Rokojori.Extensions;
|
||||
namespace Rokojori;
|
||||
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class HasAttackPermission:SceneCondition
|
||||
{
|
||||
[Export]
|
||||
public FightSlot slot;
|
||||
|
||||
public override bool Evaluate()
|
||||
{
|
||||
return FightManager.Get()?.HasAttackPermission( slot ) ?? false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://f5sb5yvwtbem
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
using Godot;
|
||||
|
||||
using Rokojori.Extensions;
|
||||
namespace Rokojori;
|
||||
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class IsAllyAttacking:SceneCondition
|
||||
{
|
||||
[Export]
|
||||
public FightSlot slot;
|
||||
|
||||
public override bool Evaluate()
|
||||
{
|
||||
return FightManager.Get()?.IsAnyOtherAttacking( slot ) ?? false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://b5gql6d11xntt
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
using Godot;
|
||||
|
||||
using Rokojori.Extensions;
|
||||
namespace Rokojori;
|
||||
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class IsEngagedInFight:SceneCondition
|
||||
{
|
||||
[Export]
|
||||
public FightSlot slot;
|
||||
|
||||
public override bool Evaluate()
|
||||
{
|
||||
return FightManager.Get()?.IsEngaged( slot ) ?? false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://k8fi0cfr0wl4
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
using Godot;
|
||||
|
||||
using Rokojori.Extensions;
|
||||
namespace Rokojori;
|
||||
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class JoinFight:Action
|
||||
{
|
||||
[Export]
|
||||
public FightSlot slot;
|
||||
|
||||
protected override void _OnTrigger()
|
||||
{
|
||||
slot?.Join();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://4jvpwjxix4ce
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
using Godot;
|
||||
|
||||
using Rokojori.Extensions;
|
||||
namespace Rokojori;
|
||||
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class LeaveFight:Action
|
||||
{
|
||||
[Export]
|
||||
public FightSlot slot;
|
||||
|
||||
protected override void _OnTrigger()
|
||||
{
|
||||
slot?.Leave();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://d08f82rnn7kkr
|
||||
|
|
@ -9,8 +9,12 @@ public abstract partial class BehaviorMovement:Node
|
|||
{
|
||||
public abstract void GoToPosition( Vector3 worldPosition );
|
||||
public abstract void LookAtPosition( Vector3 worldPosition );
|
||||
public abstract void FleeFromPosition( Vector3 worldPosition );
|
||||
|
||||
public abstract void GoTo( Node3D node );
|
||||
public abstract void Follow( Node3D node );
|
||||
public abstract void LookAt( Node3D node );
|
||||
public abstract void Flee( Node3D node );
|
||||
|
||||
public abstract Vector3 GetGlobalPosition();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,12 +16,33 @@ public partial class CharacterBodyBehaviorMovement:BehaviorMovement
|
|||
[Export]
|
||||
public float arrivalDistance = 0.1f;
|
||||
|
||||
[Export]
|
||||
public float fleeDistance = 10f;
|
||||
|
||||
[ExportGroup("Smoothing")]
|
||||
[Export]
|
||||
public Smoothing rotationSmoothing;
|
||||
|
||||
[Export]
|
||||
public Smoothing velocitySmoothing;
|
||||
|
||||
[Export]
|
||||
public Smoothing accelerationSmoothing;
|
||||
|
||||
Node3D _followTarget;
|
||||
Vector3? _targetPosition;
|
||||
Vector3? _fleeFromPosition;
|
||||
bool _fleeing;
|
||||
|
||||
public override Vector3 GetGlobalPosition()
|
||||
{
|
||||
return body?.GlobalPosition ?? Vector3.Zero;
|
||||
}
|
||||
|
||||
public override void GoToPosition( Vector3 worldPosition )
|
||||
{
|
||||
_followTarget = null;
|
||||
_fleeing = false;
|
||||
_targetPosition = worldPosition;
|
||||
}
|
||||
|
||||
|
|
@ -40,21 +61,45 @@ public partial class CharacterBodyBehaviorMovement:BehaviorMovement
|
|||
return;
|
||||
}
|
||||
|
||||
body.GlobalTransform = new Transform3D( Basis.LookingAt( direction, Vector3.Up ), body.GlobalPosition );
|
||||
var targetRotation = Basis.LookingAt( direction, Vector3.Up ).GetRotationQuaternion();
|
||||
var delta = (float) GetPhysicsProcessDeltaTime();
|
||||
var smoothedRotation = Smoothing.Apply( rotationSmoothing, targetRotation, delta );
|
||||
|
||||
body.GlobalTransform = new Transform3D( new Basis( smoothedRotation ), body.GlobalPosition );
|
||||
}
|
||||
|
||||
public override void FleeFromPosition( Vector3 worldPosition )
|
||||
{
|
||||
_followTarget = null;
|
||||
_targetPosition = null;
|
||||
_fleeing = true;
|
||||
_fleeFromPosition = worldPosition;
|
||||
}
|
||||
|
||||
public override void GoTo( Node3D node )
|
||||
{
|
||||
_followTarget = null;
|
||||
_fleeing = false;
|
||||
_targetPosition = node == null ? null : node.GlobalPosition;
|
||||
}
|
||||
|
||||
public override void Follow( Node3D node )
|
||||
{
|
||||
_followTarget = node;
|
||||
_fleeing = false;
|
||||
_targetPosition = null;
|
||||
}
|
||||
|
||||
public override void Flee( Node3D node )
|
||||
{
|
||||
if ( node == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FleeFromPosition( node.GlobalPosition );
|
||||
}
|
||||
|
||||
public override void LookAt( Node3D node )
|
||||
{
|
||||
if ( node == null )
|
||||
|
|
@ -72,6 +117,19 @@ public partial class CharacterBodyBehaviorMovement:BehaviorMovement
|
|||
return;
|
||||
}
|
||||
|
||||
Vector3 direction;
|
||||
bool arrived;
|
||||
|
||||
if ( _fleeing && _fleeFromPosition != null )
|
||||
{
|
||||
var away = body.GlobalPosition - _fleeFromPosition.Value;
|
||||
away.Y = 0;
|
||||
|
||||
arrived = away.Length() >= fleeDistance;
|
||||
direction = away.LengthSquared() < 0.0001f ? Vector3.Forward : away.Normalized();
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector3? target = _followTarget != null ? _followTarget.GlobalPosition : _targetPosition;
|
||||
|
||||
if ( target == null )
|
||||
|
|
@ -82,18 +140,21 @@ public partial class CharacterBodyBehaviorMovement:BehaviorMovement
|
|||
var toTarget = target.Value - body.GlobalPosition;
|
||||
toTarget.Y = 0;
|
||||
|
||||
if ( toTarget.Length() <= arrivalDistance )
|
||||
arrived = toTarget.Length() <= arrivalDistance;
|
||||
direction = arrived ? Vector3.Zero : toTarget.Normalized();
|
||||
}
|
||||
|
||||
var desiredVelocity = arrived ? Vector3.Zero : direction * moveSpeed;
|
||||
|
||||
var velocityStage = Smoothing.Apply( velocitySmoothing, desiredVelocity, (float) delta );
|
||||
var accelerationStage = Smoothing.Apply( accelerationSmoothing, velocityStage, (float) delta );
|
||||
|
||||
body.Velocity = accelerationStage;
|
||||
body.MoveAndSlide();
|
||||
|
||||
if ( ! arrived && accelerationStage.LengthSquared() > 0.0001f )
|
||||
{
|
||||
body.Velocity = Vector3.Zero;
|
||||
body.MoveAndSlide();
|
||||
return;
|
||||
}
|
||||
|
||||
var direction = toTarget.Normalized();
|
||||
|
||||
body.Velocity = direction * moveSpeed;
|
||||
body.MoveAndSlide();
|
||||
|
||||
LookAtPosition( body.GlobalPosition + direction );
|
||||
LookAtPosition( body.GlobalPosition + accelerationStage.Normalized() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
using Godot;
|
||||
|
||||
using Rokojori.Extensions;
|
||||
namespace Rokojori;
|
||||
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class FleeBehavior:Action
|
||||
{
|
||||
[Export]
|
||||
public BehaviorMovement movement;
|
||||
|
||||
[Export]
|
||||
public Node3D target;
|
||||
|
||||
protected override void _OnTrigger()
|
||||
{
|
||||
if ( movement == null || target == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
movement.Flee( target );
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://vvewrqnq4msf
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
using Godot;
|
||||
|
||||
using Rokojori.Extensions;
|
||||
namespace Rokojori;
|
||||
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class GoToPointBehavior:SequenceAction
|
||||
{
|
||||
[Export]
|
||||
public BehaviorMovement movement;
|
||||
|
||||
[Export]
|
||||
public Node3D target;
|
||||
|
||||
[Export]
|
||||
public float arrivalDistance = 0.2f;
|
||||
|
||||
int _activeId = -1;
|
||||
|
||||
protected override void _OnTrigger()
|
||||
{
|
||||
if ( movement == null || target == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
movement.GoTo( target );
|
||||
_activeId = DispatchStart();
|
||||
}
|
||||
|
||||
public override void _PhysicsProcess( double delta )
|
||||
{
|
||||
if ( _activeId == -1 )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( target == null )
|
||||
{
|
||||
var cancelledId = _activeId;
|
||||
_activeId = -1;
|
||||
DispatchCancelled( cancelledId );
|
||||
return;
|
||||
}
|
||||
|
||||
var distance = ( movement.GetGlobalPosition() - target.GlobalPosition ).Length();
|
||||
|
||||
if ( distance <= arrivalDistance )
|
||||
{
|
||||
var id = _activeId;
|
||||
_activeId = -1;
|
||||
DispatchEnd( id );
|
||||
}
|
||||
}
|
||||
|
||||
public override void CancelAction( int id )
|
||||
{
|
||||
if ( id == _activeId )
|
||||
{
|
||||
_activeId = -1;
|
||||
DispatchCancelled( id );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://c8t8djdwj7sah
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
using System.Linq;
|
||||
using Godot;
|
||||
|
||||
using Rokojori.Extensions;
|
||||
namespace Rokojori;
|
||||
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class HideAtSpotBehavior:SequenceAction
|
||||
{
|
||||
[Export]
|
||||
public BehaviorMovement movement;
|
||||
|
||||
[Export]
|
||||
public Node searchRoot;
|
||||
|
||||
[Export]
|
||||
public SelectorFlag[] requiredFlags = [];
|
||||
|
||||
[Export]
|
||||
public float arrivalDistance = 0.2f;
|
||||
|
||||
[ExportGroup("Read Only")]
|
||||
[Export]
|
||||
public HidingSpot chosenSpot;
|
||||
|
||||
int _activeId = -1;
|
||||
|
||||
protected override void _OnTrigger()
|
||||
{
|
||||
if ( movement == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var root = searchRoot ?? GetTree()?.CurrentScene;
|
||||
|
||||
if ( root == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var spots = root.GetAll<HidingSpot>( Matches );
|
||||
|
||||
if ( spots.Count == 0 )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var position = movement.GetGlobalPosition();
|
||||
|
||||
chosenSpot = spots.OrderBy( s => s.GlobalPosition.DistanceSquaredTo( position ) ).First();
|
||||
|
||||
movement.GoTo( chosenSpot );
|
||||
_activeId = DispatchStart();
|
||||
}
|
||||
|
||||
bool Matches( HidingSpot spot )
|
||||
{
|
||||
if ( requiredFlags == null || requiredFlags.Length == 0 )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return requiredFlags.All( f => spot.flags.Contains( f ) );
|
||||
}
|
||||
|
||||
public override void _PhysicsProcess( double delta )
|
||||
{
|
||||
if ( _activeId == -1 || chosenSpot == null || movement == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var distance = ( movement.GetGlobalPosition() - chosenSpot.GlobalPosition ).Length();
|
||||
|
||||
if ( distance <= arrivalDistance )
|
||||
{
|
||||
var id = _activeId;
|
||||
_activeId = -1;
|
||||
DispatchEnd( id );
|
||||
}
|
||||
}
|
||||
|
||||
public override void CancelAction( int id )
|
||||
{
|
||||
if ( id == _activeId )
|
||||
{
|
||||
_activeId = -1;
|
||||
DispatchCancelled( id );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://cys665jxtmxi3
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
using Godot;
|
||||
|
||||
using Rokojori.Extensions;
|
||||
namespace Rokojori;
|
||||
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class HidingSpot:Node3D
|
||||
{
|
||||
[Export]
|
||||
public SelectorFlag[] flags = [];
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://dx60nkvsjsikr
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
using Godot;
|
||||
|
||||
using Rokojori.Extensions;
|
||||
namespace Rokojori;
|
||||
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class MoveOnPath:SequenceAction
|
||||
{
|
||||
[Export]
|
||||
public Node3D target;
|
||||
|
||||
[Export]
|
||||
public Spline spline;
|
||||
|
||||
[Export]
|
||||
public float duration = 1f;
|
||||
|
||||
double _elapsed;
|
||||
int _activeId = -1;
|
||||
|
||||
protected override void _OnTrigger()
|
||||
{
|
||||
if ( target == null || spline == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_elapsed = 0;
|
||||
_activeId = DispatchStart();
|
||||
}
|
||||
|
||||
public override void _PhysicsProcess( double delta )
|
||||
{
|
||||
if ( _activeId == -1 )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_elapsed += delta;
|
||||
|
||||
var t = duration <= 0 ? 1f : Mathf.Clamp( (float)( _elapsed / duration ), 0f, 1f );
|
||||
|
||||
target.GlobalPosition = spline.GetCurve().PositionAt( t );
|
||||
|
||||
if ( t >= 1f )
|
||||
{
|
||||
var id = _activeId;
|
||||
_activeId = -1;
|
||||
DispatchEnd( id );
|
||||
}
|
||||
}
|
||||
|
||||
public override void CancelAction( int id )
|
||||
{
|
||||
if ( id == _activeId )
|
||||
{
|
||||
_activeId = -1;
|
||||
DispatchCancelled( id );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://cvi582q7lkeod
|
||||
|
|
@ -0,0 +1,189 @@
|
|||
using Godot;
|
||||
|
||||
using Rokojori.Extensions;
|
||||
namespace Rokojori;
|
||||
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class NavigationBehaviorMovement:BehaviorMovement
|
||||
{
|
||||
[Export]
|
||||
public CharacterBody3D body;
|
||||
|
||||
[Export]
|
||||
public NavigationAgent3D agent;
|
||||
|
||||
[Export]
|
||||
public float moveSpeed = 5f;
|
||||
|
||||
[Export]
|
||||
public float fleeDistance = 10f;
|
||||
|
||||
[ExportGroup("Smoothing")]
|
||||
[Export]
|
||||
public Smoothing rotationSmoothing;
|
||||
|
||||
[Export]
|
||||
public Smoothing velocitySmoothing;
|
||||
|
||||
Node3D _followTarget;
|
||||
Vector3? _fleeFromPosition;
|
||||
bool _fleeing;
|
||||
|
||||
public override Vector3 GetGlobalPosition()
|
||||
{
|
||||
return body?.GlobalPosition ?? Vector3.Zero;
|
||||
}
|
||||
|
||||
public override void GoToPosition( Vector3 worldPosition )
|
||||
{
|
||||
_followTarget = null;
|
||||
_fleeing = false;
|
||||
agent?.SetTargetPosition( worldPosition );
|
||||
}
|
||||
|
||||
public override void GoTo( Node3D node )
|
||||
{
|
||||
_followTarget = null;
|
||||
_fleeing = false;
|
||||
|
||||
if ( node == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
agent?.SetTargetPosition( node.GlobalPosition );
|
||||
}
|
||||
|
||||
public override void Follow( Node3D node )
|
||||
{
|
||||
_followTarget = node;
|
||||
_fleeing = false;
|
||||
}
|
||||
|
||||
public override void LookAtPosition( Vector3 worldPosition )
|
||||
{
|
||||
if ( body == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var direction = worldPosition - body.GlobalPosition;
|
||||
direction.Y = 0;
|
||||
|
||||
if ( direction.LengthSquared() < 0.0001f )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var targetRotation = Basis.LookingAt( direction, Vector3.Up ).GetRotationQuaternion();
|
||||
var delta = (float) GetPhysicsProcessDeltaTime();
|
||||
var smoothedRotation = Smoothing.Apply( rotationSmoothing, targetRotation, delta );
|
||||
|
||||
body.GlobalTransform = new Transform3D( new Basis( smoothedRotation ), body.GlobalPosition );
|
||||
}
|
||||
|
||||
public override void LookAt( Node3D node )
|
||||
{
|
||||
if ( node == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LookAtPosition( node.GlobalPosition );
|
||||
}
|
||||
|
||||
public override void FleeFromPosition( Vector3 worldPosition )
|
||||
{
|
||||
_followTarget = null;
|
||||
_fleeing = true;
|
||||
_fleeFromPosition = worldPosition;
|
||||
}
|
||||
|
||||
public override void Flee( Node3D node )
|
||||
{
|
||||
if ( node == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FleeFromPosition( node.GlobalPosition );
|
||||
}
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
if ( agent != null && agent.AvoidanceEnabled )
|
||||
{
|
||||
agent.VelocityComputed += OnVelocityComputed;
|
||||
}
|
||||
}
|
||||
|
||||
void OnVelocityComputed( Vector3 safeVelocity )
|
||||
{
|
||||
if ( body == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
body.Velocity = safeVelocity;
|
||||
body.MoveAndSlide();
|
||||
}
|
||||
|
||||
public override void _PhysicsProcess( double delta )
|
||||
{
|
||||
if ( body == null || agent == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( _fleeing && _fleeFromPosition != null )
|
||||
{
|
||||
var away = body.GlobalPosition - _fleeFromPosition.Value;
|
||||
away.Y = 0;
|
||||
|
||||
if ( away.Length() >= fleeDistance )
|
||||
{
|
||||
body.Velocity = Vector3.Zero;
|
||||
body.MoveAndSlide();
|
||||
return;
|
||||
}
|
||||
|
||||
var fleeDirection = away.LengthSquared() < 0.0001f ? Vector3.Forward : away.Normalized();
|
||||
agent.SetTargetPosition( body.GlobalPosition + fleeDirection * fleeDistance );
|
||||
}
|
||||
else if ( _followTarget != null )
|
||||
{
|
||||
agent.SetTargetPosition( _followTarget.GlobalPosition );
|
||||
}
|
||||
|
||||
if ( agent.IsNavigationFinished() )
|
||||
{
|
||||
body.Velocity = Vector3.Zero;
|
||||
body.MoveAndSlide();
|
||||
return;
|
||||
}
|
||||
|
||||
var nextPosition = agent.GetNextPathPosition();
|
||||
var toNext = nextPosition - body.GlobalPosition;
|
||||
toNext.Y = 0;
|
||||
|
||||
var desiredVelocity = toNext.LengthSquared() < 0.0001f ? Vector3.Zero : toNext.Normalized() * moveSpeed;
|
||||
|
||||
var finalVelocity = Smoothing.Apply( velocitySmoothing, desiredVelocity, (float) delta );
|
||||
|
||||
if ( agent.AvoidanceEnabled )
|
||||
{
|
||||
agent.Velocity = finalVelocity;
|
||||
}
|
||||
else
|
||||
{
|
||||
body.Velocity = finalVelocity;
|
||||
body.MoveAndSlide();
|
||||
}
|
||||
|
||||
if ( finalVelocity.LengthSquared() > 0.0001f )
|
||||
{
|
||||
LookAtPosition( body.GlobalPosition + finalVelocity.Normalized() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://b15wtairu8jfd
|
||||
|
|
@ -5,101 +5,15 @@ namespace Rokojori;
|
|||
|
||||
[Tool]
|
||||
[GlobalClass, Icon("res://addons/rokojori_action_library/Icons/Receptor.svg")]
|
||||
public partial class Receptor:Action
|
||||
public partial class Receptor:ReceptorBase
|
||||
{
|
||||
|
||||
[Export]
|
||||
public SceneCondition sceneCondition;
|
||||
|
||||
[Export( PropertyHint.Range, "0.1,100")]
|
||||
public float receptionTriggerCoverage = 75f;
|
||||
|
||||
[Export( PropertyHint.Range, "10,2000")]
|
||||
public float receptionWindowDurationMS = 500f;
|
||||
|
||||
[Export( PropertyHint.Range, "10,1000")]
|
||||
public float tickRateMS = 100f;
|
||||
|
||||
[Export]
|
||||
public Timeline timeline;
|
||||
|
||||
[ExportGroup("Debugging")]
|
||||
[Export]
|
||||
public float fillState = 0;
|
||||
|
||||
int resultSize => Mathf.CeilToInt( receptionWindowDurationMS / tickRateMS );
|
||||
|
||||
RingBuffer<bool> _resultsBuffer;
|
||||
|
||||
bool _activated = false;
|
||||
|
||||
public bool activated => _activated;
|
||||
|
||||
bool _active = false;
|
||||
|
||||
public bool active => _active;
|
||||
|
||||
int _eventID = -1;
|
||||
|
||||
public void Start()
|
||||
protected override bool _Sample()
|
||||
{
|
||||
if ( _active )
|
||||
{
|
||||
return;
|
||||
return sceneCondition?.Evaluate() ?? false;
|
||||
}
|
||||
|
||||
_active = true;
|
||||
|
||||
var duration = tickRateMS / 1000f;
|
||||
var tle = TimelineManager.ScheduleLoopEvent( timeline, duration, 0, tle => Sample() );
|
||||
_eventID = tle.id;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
if ( ! _active )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_active = false;
|
||||
|
||||
TimelineManager.RemoveEvent( timeline, _eventID );
|
||||
_eventID = -1;
|
||||
}
|
||||
|
||||
protected override void _OnTrigger()
|
||||
{
|
||||
Sample();
|
||||
}
|
||||
|
||||
void Sample()
|
||||
{
|
||||
// this.LogInfo( "Receptor Update" );
|
||||
|
||||
if ( sceneCondition == null )
|
||||
{
|
||||
// this.LogInfo( "No condition" );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( _resultsBuffer == null || _resultsBuffer.capacity != resultSize )
|
||||
{
|
||||
_resultsBuffer = new RingBuffer<bool>( resultSize );
|
||||
}
|
||||
|
||||
_resultsBuffer.Add( sceneCondition.Evaluate() );
|
||||
|
||||
if ( _resultsBuffer.count != resultSize )
|
||||
{
|
||||
// this.LogInfo( "No activation" );
|
||||
_activated = false;
|
||||
return;
|
||||
}
|
||||
|
||||
var numActive = _resultsBuffer.CountItems( b => b );
|
||||
fillState = numActive / (float) resultSize;
|
||||
|
||||
_activated = fillState >= receptionTriggerCoverage / 100f;
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,109 @@
|
|||
using Godot;
|
||||
using System.Collections.Generic;
|
||||
using Rokojori.Extensions;
|
||||
namespace Rokojori;
|
||||
|
||||
[Tool]
|
||||
[GlobalClass, Icon("res://addons/rokojori_action_library/Icons/Receptor.svg")]
|
||||
public abstract partial class ReceptorBase:Action
|
||||
{
|
||||
|
||||
[Export( PropertyHint.Range, "0.1,100")]
|
||||
public float receptionTriggerCoverage = 75f;
|
||||
|
||||
[Export( PropertyHint.Range, "10,2000")]
|
||||
public float receptionWindowDurationMS = 500f;
|
||||
|
||||
[Export( PropertyHint.Range, "10,1000")]
|
||||
public float tickRateMS = 100f;
|
||||
|
||||
[Export]
|
||||
public Timeline timeline;
|
||||
|
||||
[ExportGroup("Debugging")]
|
||||
[Export]
|
||||
public float fillState = 0;
|
||||
|
||||
[Export]
|
||||
public bool printUpdates = false;
|
||||
|
||||
int resultSize => Mathf.CeilToInt( receptionWindowDurationMS / tickRateMS );
|
||||
|
||||
RingBuffer<bool> _resultsBuffer;
|
||||
|
||||
bool _activated = false;
|
||||
|
||||
public bool activated => _activated;
|
||||
|
||||
bool _active = false;
|
||||
|
||||
public bool active => _active;
|
||||
|
||||
int _eventID = -1;
|
||||
|
||||
public void Start()
|
||||
{
|
||||
if ( _active )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_active = true;
|
||||
|
||||
var duration = tickRateMS / 1000f;
|
||||
var tle = TimelineManager.ScheduleLoopEvent( timeline, duration, 0, tle => ProcessSample() );
|
||||
_eventID = tle.id;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
if ( ! _active )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_active = false;
|
||||
|
||||
TimelineManager.RemoveEvent( timeline, _eventID );
|
||||
_eventID = -1;
|
||||
}
|
||||
|
||||
protected override void _OnTrigger()
|
||||
{
|
||||
ProcessSample();
|
||||
}
|
||||
|
||||
protected abstract bool _Sample();
|
||||
|
||||
void ProcessSample()
|
||||
{
|
||||
var sample = _Sample();
|
||||
|
||||
|
||||
|
||||
if ( _resultsBuffer == null || _resultsBuffer.capacity != resultSize )
|
||||
{
|
||||
_resultsBuffer = new RingBuffer<bool>( resultSize );
|
||||
}
|
||||
|
||||
_resultsBuffer.Add( sample );
|
||||
|
||||
if ( _resultsBuffer.count != resultSize )
|
||||
{
|
||||
// this.LogInfo( "No activation" );
|
||||
_activated = false;
|
||||
return;
|
||||
}
|
||||
|
||||
var numActive = _resultsBuffer.CountItems( b => b );
|
||||
fillState = numActive / (float) resultSize;
|
||||
|
||||
|
||||
_activated = fillState >= receptionTriggerCoverage / 100f;
|
||||
|
||||
if ( printUpdates )
|
||||
{
|
||||
this.LogInfo( fillState, _activated );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://dj5ffwbp0v4ee
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
using Godot;
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
|
||||
using Rokojori.Extensions;
|
||||
namespace Rokojori;
|
||||
|
||||
[Tool]
|
||||
[GlobalClass, Icon("res://addons/rokojori_action_library/Icons/ReceptorControl.svg")]
|
||||
public partial class ReceptorControl:Action
|
||||
{
|
||||
public enum Mode
|
||||
{
|
||||
Start,
|
||||
Stop
|
||||
}
|
||||
|
||||
[Export]
|
||||
public Receptor[] receptors;
|
||||
|
||||
[Export]
|
||||
public Node[] containers;
|
||||
|
||||
|
||||
|
||||
[Export]
|
||||
public Mode mode;
|
||||
|
||||
protected override void _OnTrigger()
|
||||
{
|
||||
switch ( mode )
|
||||
{
|
||||
case Mode.Start:
|
||||
{
|
||||
ForEachReceptor( r => { r.LogInfo( "Starting" ); r.Start(); } );
|
||||
}
|
||||
break;
|
||||
|
||||
case Mode.Stop:
|
||||
{
|
||||
ForEachReceptor( r => r.Stop() );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ForEachReceptor( System.Action<Receptor> callback )
|
||||
{
|
||||
receptors?.ForEachNonNull( r => callback( r ) );
|
||||
containers?.ForEachNonNull( c => c.ForEach<Receptor>( r => callback( r ) ) );
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://l66q2ciopfmj
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
using Godot;
|
||||
|
||||
using Rokojori.Extensions;
|
||||
namespace Rokojori;
|
||||
|
||||
[Tool]
|
||||
[GlobalClass, Icon("res://addons/rokojori_action_library/Icons/OnStateActive.svg")]
|
||||
public partial class OnStateActive:Sequence
|
||||
{
|
||||
|
||||
[Export]
|
||||
public bool isExclusive = true;
|
||||
|
||||
[Export]
|
||||
public bool reinitialize = false;
|
||||
|
||||
public enum LoopMode
|
||||
{
|
||||
No_Loop,
|
||||
Next_Frame,
|
||||
Direct_Continuation
|
||||
}
|
||||
|
||||
[Export]
|
||||
public LoopMode loopMode = LoopMode.No_Loop;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
base._Ready();
|
||||
|
||||
onSequenceDone.AddAction( OnDone );
|
||||
}
|
||||
|
||||
protected override void _OnTrigger()
|
||||
{
|
||||
// this.LogInfo( "Triggering" );
|
||||
base._OnTrigger();
|
||||
}
|
||||
|
||||
async void OnDone( SequenceActionFinishedEvent ev )
|
||||
{
|
||||
if ( LoopMode.No_Loop == loopMode )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( LoopMode.Next_Frame == loopMode )
|
||||
{
|
||||
await this.RequestNextFrame();
|
||||
}
|
||||
|
||||
|
||||
// this.LogInfo( "Loop Re-Triggering" );
|
||||
Trigger();
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://dr8k0q7q3760
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
using Godot;
|
||||
|
||||
using Rokojori.Extensions;
|
||||
namespace Rokojori;
|
||||
|
||||
[Tool]
|
||||
[GlobalClass, Icon("res://addons/rokojori_action_library/Icons/OnStateEntered.svg")]
|
||||
public partial class OnStateEntered:StateEnterExitAction
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://duny4jcvytcjs
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
using Godot;
|
||||
|
||||
using Rokojori.Extensions;
|
||||
namespace Rokojori;
|
||||
|
||||
[Tool]
|
||||
[GlobalClass, Icon("res://addons/rokojori_action_library/Icons/OnStateExited.svg")]
|
||||
public partial class OnStateExited:StateEnterExitAction
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://cxv11h345udfg
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
using Godot;
|
||||
|
||||
using Rokojori.Extensions;
|
||||
namespace Rokojori;
|
||||
|
||||
[Tool]
|
||||
[GlobalClass, Icon("res://addons/rokojori_action_library/Icons/State.svg")]
|
||||
public partial class State:Node
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://bf32kfq4h4ws3
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
using Godot;
|
||||
|
||||
using Rokojori.Extensions;
|
||||
namespace Rokojori;
|
||||
|
||||
[Tool]
|
||||
[GlobalClass, Icon("res://addons/rokojori_action_library/Icons/OnEvent.svg")]
|
||||
public abstract partial class StateEnterExitAction:Sequence
|
||||
{
|
||||
[Export]
|
||||
public SceneCondition sceneCondition;
|
||||
|
||||
[Export]
|
||||
public Condition condition;
|
||||
|
||||
[Export]
|
||||
public State state;
|
||||
|
||||
public bool Matches( State other )
|
||||
{
|
||||
if ( state != null && state != other )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( sceneCondition != null && ! sceneCondition.Evaluate() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( condition != null && ! condition.Evaluate() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://cs64njpww5p5v
|
||||
|
|
@ -0,0 +1,232 @@
|
|||
using Godot;
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Rokojori.Extensions;
|
||||
namespace Rokojori;
|
||||
|
||||
[Tool]
|
||||
[GlobalClass, Icon("res://addons/rokojori_action_library/Icons/StateMachine.svg")]
|
||||
public partial class StateMachine:SequenceAction
|
||||
{
|
||||
[ExportGroup( "Debugging")]
|
||||
[Export]
|
||||
public State currentState;
|
||||
|
||||
[Export]
|
||||
public bool printStateChanges = false;
|
||||
|
||||
[Export]
|
||||
public bool printTransitionChanges = false;
|
||||
|
||||
int _runID = -1;
|
||||
|
||||
List<Receptor> _receptors = new List<Receptor>();
|
||||
|
||||
|
||||
protected override void _OnTrigger()
|
||||
{
|
||||
if ( currentState != null )
|
||||
{
|
||||
// Already running.
|
||||
return;
|
||||
}
|
||||
|
||||
var states = this.GetDirectChildren<State>();
|
||||
|
||||
if ( states.Count == 0 )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_runID = DispatchStart();
|
||||
|
||||
StartReceptors( states );
|
||||
|
||||
EnterState( states[ 0 ], null );
|
||||
}
|
||||
|
||||
public override void _Process( double delta )
|
||||
{
|
||||
if ( Engine.IsEditorHint() || currentState == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CheckTransitions();
|
||||
}
|
||||
|
||||
Dictionary<StateTransition,bool> _transitions = new Dictionary<StateTransition, bool>();
|
||||
|
||||
void CheckTransitions()
|
||||
{
|
||||
|
||||
var transitions = currentState.GetDirectChildren<StateTransition>();
|
||||
|
||||
for ( int i = 0; i < transitions.Count; i++ )
|
||||
{
|
||||
var transition = transitions[ i ];
|
||||
var isTriggered = transition.IsTriggered();
|
||||
|
||||
var before = ! _transitions.ContainsKey( transition ) ? false : _transitions[ transition ];
|
||||
|
||||
if ( before != isTriggered )
|
||||
{
|
||||
_transitions[ transition ] = isTriggered;
|
||||
RJLog.Log( HierarchyName.Of( transition ), isTriggered );
|
||||
}
|
||||
|
||||
|
||||
if ( isTriggered )
|
||||
{
|
||||
DoTransition( transition.targetState );
|
||||
transition.onTransition?.Trigger();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DoTransition( State target )
|
||||
{
|
||||
var previous = currentState;
|
||||
|
||||
if ( target == previous )
|
||||
{
|
||||
ReinitializeState( previous );
|
||||
return;
|
||||
}
|
||||
|
||||
ExitState( previous, target );
|
||||
|
||||
if ( target == null )
|
||||
{
|
||||
EndStateMachine();
|
||||
return;
|
||||
}
|
||||
|
||||
EnterState( target, previous );
|
||||
}
|
||||
|
||||
void EnterState( State state, State previous )
|
||||
{
|
||||
currentState = state;
|
||||
|
||||
if ( printStateChanges )
|
||||
{
|
||||
this.LogInfo( "Entering State:", HierarchyName.Of( state ) );
|
||||
}
|
||||
|
||||
state.ForEachDirectChild<OnStateEntered>( oe =>
|
||||
{
|
||||
if ( oe.Matches( previous ) )
|
||||
{
|
||||
oe.Trigger();
|
||||
}
|
||||
} );
|
||||
|
||||
state.ForEachDirectChild<OnStateActive>( oa => oa.Trigger() );
|
||||
}
|
||||
|
||||
void ExitState( State state, State next )
|
||||
{
|
||||
if ( printStateChanges )
|
||||
{
|
||||
this.LogInfo( "Exiting State:", HierarchyName.Of( state ) );
|
||||
}
|
||||
|
||||
state.ForEachDirectChild<OnStateExited>( oe =>
|
||||
{
|
||||
if ( oe.Matches( next ) )
|
||||
{
|
||||
oe.Trigger();
|
||||
}
|
||||
} );
|
||||
|
||||
state.ForEachDirectChild<OnStateActive>( oa => oa.CancelAllRunning() );
|
||||
}
|
||||
|
||||
void ReinitializeState( State state )
|
||||
{
|
||||
state.ForEachDirectChild<OnStateExited>( oe =>
|
||||
{
|
||||
if ( oe.Matches( state ) )
|
||||
{
|
||||
oe.Trigger();
|
||||
}
|
||||
} );
|
||||
|
||||
state.ForEachDirectChild<OnStateEntered>( oe =>
|
||||
{
|
||||
if ( oe.Matches( state ) )
|
||||
{
|
||||
oe.Trigger();
|
||||
}
|
||||
} );
|
||||
|
||||
state.ForEachDirectChild<OnStateActive>( oa =>
|
||||
{
|
||||
if ( ! oa.reinitialize )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( oa.isExclusive && oa.isRunning )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
oa.CancelAllRunning();
|
||||
oa.Trigger();
|
||||
} );
|
||||
}
|
||||
|
||||
void StartReceptors( List<State> states )
|
||||
{
|
||||
_receptors.Clear();
|
||||
|
||||
states.ForEach( s =>
|
||||
s.ForEachDirectChild<StateTransition>( t =>
|
||||
{
|
||||
if ( t.receptor != null )
|
||||
{
|
||||
_receptors.Add( t.receptor );
|
||||
}
|
||||
} )
|
||||
);
|
||||
|
||||
_receptors.ForEach( r => r.Start() );
|
||||
}
|
||||
|
||||
void StopReceptors()
|
||||
{
|
||||
_receptors.ForEach( r => r.Stop() );
|
||||
_receptors.Clear();
|
||||
}
|
||||
|
||||
void EndStateMachine()
|
||||
{
|
||||
currentState = null;
|
||||
|
||||
SetProcess( false );
|
||||
|
||||
StopReceptors();
|
||||
|
||||
DispatchEnd( _runID );
|
||||
}
|
||||
|
||||
public override void CancelAction( int id )
|
||||
{
|
||||
if ( id != _runID || currentState == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
currentState = null;
|
||||
|
||||
SetProcess( false );
|
||||
|
||||
StopReceptors();
|
||||
|
||||
DispatchCancelled( id );
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://cnv4f0rjl6l0f
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
using Godot;
|
||||
|
||||
using Rokojori.Extensions;
|
||||
namespace Rokojori;
|
||||
|
||||
[Tool]
|
||||
[GlobalClass, Icon("res://addons/rokojori_action_library/Icons/StateTransition.svg")]
|
||||
public partial class StateTransition:Node
|
||||
{
|
||||
[Export]
|
||||
public SceneCondition sceneCondition;
|
||||
|
||||
[Export]
|
||||
public Condition condition;
|
||||
|
||||
[Export]
|
||||
public Receptor receptor;
|
||||
|
||||
// Transitioning to a null targetState ends the owning StateMachine.
|
||||
[Export]
|
||||
public State targetState;
|
||||
|
||||
|
||||
[Export]
|
||||
public Action onTransition;
|
||||
|
||||
|
||||
[ExportToolButton( "Set Reference Name" )]
|
||||
public Callable setReferencedNameButton => Callable.From(
|
||||
()=>
|
||||
{
|
||||
if ( targetState == null )
|
||||
{
|
||||
this.Name = "▸ (nothing)";
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Name = "▸ " + targetState.Name;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
public bool IsTriggered()
|
||||
{
|
||||
if ( sceneCondition?.Evaluate() ?? false )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( condition?.Evaluate() ?? false )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( receptor?.activated ?? false )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://dok5gagvhoo6r
|
||||
|
|
@ -39,6 +39,8 @@ namespace Rokojori
|
|||
public bool canProcess => processInEditor || ! Engine.IsEditorHint();
|
||||
|
||||
Vector3 _lastLockTargetPosition;
|
||||
float _lockBlend;
|
||||
float _orientationBlend;
|
||||
|
||||
public override void _Process( double delta )
|
||||
{
|
||||
|
|
@ -88,10 +90,23 @@ namespace Rokojori
|
|||
// lockBlend fades 0->1 on engage and 1->0 on release instead of snapping, so the focus
|
||||
// point, yaw override, pitch clamp and bounding-distance below all ease in/out together -
|
||||
// lockTargetPosition keeps the last known value through the fade-out tail even after
|
||||
// lockTarget itself has gone null.
|
||||
var lockBlend = Smoothing.Apply( settings.lockBlendSmoothing, hasLockTarget ? 1f : 0f, delta );
|
||||
// lockTarget itself has gone null. Engage and release each get their own Smoothing (and
|
||||
// therefore their own duration), driven off the same persistent _lockBlend value via
|
||||
// ApplyWith so switching which one is active each frame can't desync from the other's
|
||||
// internal state.
|
||||
var lockBlendSmoothing = hasLockTarget ? settings.lockEngageSmoothing : settings.lockReleaseSmoothing;
|
||||
_lockBlend = Smoothing.ApplyWith( lockBlendSmoothing, _lockBlend, hasLockTarget ? 1f : 0f, delta );
|
||||
var lockBlend = _lockBlend;
|
||||
var isLocked = lockBlend > 0.001f;
|
||||
|
||||
// Separate blend for yaw/pitch: the lock target's position needs easing since it keeps
|
||||
// moving, but once released there's nothing left to track - yaw/pitch should return to
|
||||
// "the normal camera" on their own (fast) terms instead of riding lockBlend's slower,
|
||||
// framing-tuned duration back down.
|
||||
var orientationBlendSmoothing = hasLockTarget ? settings.lockOrientationEngageSmoothing : settings.lockOrientationReleaseSmoothing;
|
||||
_orientationBlend = Smoothing.ApplyWith( orientationBlendSmoothing, _orientationBlend, hasLockTarget ? 1f : 0f, delta );
|
||||
var orientationBlend = _orientationBlend;
|
||||
|
||||
var focusPosition = targetPosition.Lerp( ( targetPosition + lockTargetPosition ) / 2f, lockBlend );
|
||||
|
||||
var yawAxis = 0f;
|
||||
|
|
@ -104,21 +119,37 @@ namespace Rokojori
|
|||
}
|
||||
|
||||
// this.LogInfo( "YAW:", yawAxis, "PITCH:", pitchAxis );
|
||||
if ( isLocked )
|
||||
|
||||
// Free-look yaw keeps integrating, just scaled down as orientationBlend rises, so it
|
||||
// doesn't freeze the instant a lock starts and resumes smoothly from wherever it actually
|
||||
// is once released - rather than hard-pausing on the isLocked boolean.
|
||||
yaw += yawAxis * settings.yawSpeed * data.yawSpeed * delta * ( 1f - orientationBlend );
|
||||
|
||||
var lockYawTarget = yaw;
|
||||
|
||||
if ( hasLockTarget )
|
||||
{
|
||||
var lockDirection = lockTargetPosition - targetPosition;
|
||||
lockDirection.Y = 0;
|
||||
|
||||
if ( lockDirection.LengthSquared() > 0.0001f )
|
||||
{
|
||||
yaw = Math3D.GlobalYawDegrees( lockDirection );
|
||||
lockYawTarget = Math3D.GlobalYawDegrees( lockDirection );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
yaw += yawAxis * settings.yawSpeed * data.yawSpeed * delta;
|
||||
}
|
||||
|
||||
// Blended by orientationBlend instead of hard-switching on isLocked, so engage eases over
|
||||
// lockOrientationEngageSmoothing's duration while release can return to free-look fast via
|
||||
// lockOrientationReleaseSmoothing, independent of how slow lockBlend (focus/distance) is
|
||||
// still easing out. The normal (faster) yawSmoothing below only adds its usual per-frame
|
||||
// responsiveness on top of that already-eased target.
|
||||
var appliedYaw = MathX.LerpAngleDegrees( yaw, lockYawTarget, orientationBlend );
|
||||
|
||||
// Keep the free-look accumulator synced to whatever is actually being displayed, so it
|
||||
// never goes stale while locked (it barely integrates then) - otherwise releasing the lock
|
||||
// makes lockYawTarget's "no target" fallback snap back to a long-stale pre-lock value
|
||||
// instead of the direction the camera was just actually facing.
|
||||
yaw = appliedYaw;
|
||||
|
||||
if ( data.pitchIsRelative )
|
||||
{
|
||||
|
|
@ -130,12 +161,11 @@ namespace Rokojori
|
|||
pitch = Mathf.Remap( pitchAxis, -1, 1, settings.minPitch, settings.maxPitch );
|
||||
}
|
||||
|
||||
if ( isLocked )
|
||||
{
|
||||
pitch = Mathf.Clamp( pitch, settings.lockMinPitch, settings.lockMaxPitch );
|
||||
}
|
||||
|
||||
var appliedYaw = yaw;
|
||||
// Same orientationBlend for the pitch clamp range - avoids a hard clamp snap on
|
||||
// engage/release, and returns to the normal range just as quickly as yaw does on release.
|
||||
var blendedMinPitch = Mathf.Lerp( settings.minPitch, settings.lockMinPitch, orientationBlend );
|
||||
var blendedMaxPitch = Mathf.Lerp( settings.maxPitch, settings.lockMaxPitch, orientationBlend );
|
||||
pitch = Mathf.Clamp( pitch, blendedMinPitch, blendedMaxPitch );
|
||||
|
||||
var smoothedYaw = Smoothing.ApplyDegrees( settings.yawSmoothing, appliedYaw, delta );
|
||||
|
||||
|
|
|
|||
|
|
@ -79,7 +79,20 @@ namespace Rokojori
|
|||
public Smoothing lockTargetFollowSmoothing = new FrameSmoothing();
|
||||
|
||||
[Export]
|
||||
public Smoothing lockBlendSmoothing = new FrameSmoothing();
|
||||
public Smoothing lockEngageSmoothing = new FrameSmoothing();
|
||||
|
||||
[Export]
|
||||
public Smoothing lockReleaseSmoothing = new FrameSmoothing();
|
||||
|
||||
// Separate from the pair above: those blend the focus position and bounding distance (can
|
||||
// stay slow/cinematic on release). These blend yaw and the pitch clamp range - on release,
|
||||
// orientation should snap back to "the normal camera" fast, independent of how slow the
|
||||
// framing is still easing out.
|
||||
[Export]
|
||||
public Smoothing lockOrientationEngageSmoothing = new FrameSmoothing();
|
||||
|
||||
[Export]
|
||||
public Smoothing lockOrientationReleaseSmoothing = new FrameSmoothing();
|
||||
|
||||
[Export]
|
||||
public float lockMinPitch = -10;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
using Godot;
|
||||
|
||||
using Rokojori.Extensions;
|
||||
namespace Rokojori;
|
||||
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class IsValueBelowThreshold:SceneCondition
|
||||
{
|
||||
[Export]
|
||||
public NumberValue numberValue;
|
||||
|
||||
[Export]
|
||||
public double threshold = -1;
|
||||
|
||||
public override bool Evaluate()
|
||||
{
|
||||
if ( numberValue == null )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var t = threshold < 0 ? numberValue.lowTreshold : threshold;
|
||||
|
||||
return numberValue.value < t;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://dj8npbgn3g3wm
|
||||
|
|
@ -42,6 +42,19 @@ namespace Rokojori
|
|||
return box.center;
|
||||
}
|
||||
|
||||
public static bool CanSee( this Node3D self, Node3D other, float angle = 45f )
|
||||
{
|
||||
var forward = self.GlobalForward();
|
||||
var dir = self.DirectionTowards( other ).Normalized();
|
||||
|
||||
var dot = forward.Dot( dir );
|
||||
|
||||
var threshold = Mathf.Cos( Mathf.DegToRad( angle / 2f ) );
|
||||
|
||||
return dot > threshold;
|
||||
}
|
||||
|
||||
|
||||
public static Vector3 GetGlobalCenterOfChildren( this Node3D self, bool bounds = false, bool onlyVisible = true )
|
||||
{
|
||||
var center = Vector3.Zero;
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ namespace Rokojori
|
|||
|
||||
public float SmoothForDuration( float value, float nextValue, float duration, float delta, float processDelta = MathX.fps120Delta )
|
||||
{
|
||||
var coefficient = MathX.SmoothingCoefficient( duration * 1000f, processDelta );
|
||||
var coefficient = MathX.SmoothingCoefficient( duration * 1000f, 0.1f, processDelta );
|
||||
return SmoothWithCoefficient( value, nextValue, coefficient, delta, processDelta );
|
||||
}
|
||||
|
||||
|
|
@ -50,7 +50,7 @@ namespace Rokojori
|
|||
|
||||
public Quaternion SmoothForDuration( Quaternion value, Quaternion nextValue, float duration, float delta, float processDelta = MathX.fps120Delta )
|
||||
{
|
||||
var coefficient = MathX.SmoothingCoefficient( duration * 1000f, processDelta );
|
||||
var coefficient = MathX.SmoothingCoefficient( duration * 1000f, 0.1f, processDelta );
|
||||
return SmoothWithCoefficient( value, nextValue, coefficient, delta, processDelta );
|
||||
}
|
||||
|
||||
|
|
@ -60,7 +60,7 @@ namespace Rokojori
|
|||
|
||||
while ( overflowDelta > processDelta )
|
||||
{
|
||||
value = value.Slerp( nextValue, coefficient );
|
||||
value = value.Slerp( nextValue, coefficient ).Normalized();
|
||||
overflowDelta -= processDelta;
|
||||
}
|
||||
|
||||
|
|
@ -69,7 +69,7 @@ namespace Rokojori
|
|||
|
||||
public Vector3 SmoothForDuration( Vector3 value, Vector3 nextValue, float duration, float delta, float processDelta = MathX.fps120Delta )
|
||||
{
|
||||
var coefficient = MathX.SmoothingCoefficient( duration * 1000f, processDelta );
|
||||
var coefficient = MathX.SmoothingCoefficient( duration * 1000f, 0.1f, processDelta );
|
||||
return SmoothWithCoefficient( value, nextValue, coefficient, delta, processDelta );
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ namespace Rokojori
|
|||
{
|
||||
public class PhysicsRayCast
|
||||
{
|
||||
public static CollisionData GetFirst( CharacterBody3D body, Vector3 from, Vector3 to )
|
||||
public static CollisionData GetFirst( PhysicsBody3D body, Vector3 from, Vector3 to )
|
||||
{
|
||||
var world = body.GetWorld3D();
|
||||
var rayParameters = new PhysicsRayQueryParameters3D
|
||||
|
|
@ -25,7 +25,7 @@ namespace Rokojori
|
|||
return hits.Count == 0 ? null : hits[ 0 ];
|
||||
}
|
||||
|
||||
public static CollisionData GetFirst( CharacterBody3D body, Vector3 direction )
|
||||
public static CollisionData GetFirst( PhysicsBody3D body, Vector3 direction )
|
||||
{
|
||||
var from = body.GlobalPosition;
|
||||
var to = from + direction;
|
||||
|
|
@ -33,7 +33,7 @@ namespace Rokojori
|
|||
return GetFirst( body, from, to );
|
||||
}
|
||||
|
||||
public static int GetAll( List<CollisionData> outputCollisionData, CharacterBody3D body, Vector3 from, Vector3 to )
|
||||
public static int GetAll( List<CollisionData> outputCollisionData, PhysicsBody3D body, Vector3 from, Vector3 to )
|
||||
{
|
||||
var world = body.GetWorld3D();
|
||||
var rayParameters = new PhysicsRayQueryParameters3D
|
||||
|
|
@ -49,7 +49,7 @@ namespace Rokojori
|
|||
return CollisionData.MultiRayCast( world, rayParameters, outputCollisionData );
|
||||
}
|
||||
|
||||
public static int GetAll( List<CollisionData> outputCollisionData, CharacterBody3D body, Vector3 direction )
|
||||
public static int GetAll( List<CollisionData> outputCollisionData, PhysicsBody3D body, Vector3 direction )
|
||||
{
|
||||
var from = body.GlobalPosition;
|
||||
var to = from + direction;
|
||||
|
|
|
|||
Loading…
Reference in New Issue