Compare commits

...

5 Commits

Author SHA1 Message Date
e2d5098b45 Photo mode init 2024-12-20 19:33:00 +01:00
179eacb628 Git lfs update 2024-12-19 19:08:09 +01:00
2a085bbb9c Documentation update
Reviewed-on: 
Co-authored-by: Oleg Petruny <oleg.petruny@gmail.com>
Co-committed-by: Oleg Petruny <oleg.petruny@gmail.com>
2024-12-19 19:03:17 +01:00
44e9bc1369 Code & git cleanup
Reviewed-on: 
2024-12-15 14:32:51 +01:00
644083344e Voice generator module
Reviewed-on: 
Co-authored-by: Oleg Petruny <oleg.petruny@gmail.com>
Co-committed-by: Oleg Petruny <oleg.petruny@gmail.com>
2024-12-13 17:41:14 +01:00
146 changed files with 1479 additions and 1088 deletions
.gitattributes
Audio
Documentation
Fonts
Images
Models
README.md
ReleaseBuilds
UnrealProject/Lost_Edge
Config
Content
README.md
Source/Lost_Edge/Private

4
.gitattributes vendored
View File

@ -1,4 +0,0 @@
ReleaseBuilds/** filter=lfs diff=lfs merge=lfs -text
Images/** filter=lfs diff=lfs merge=lfs -text
Fonts/** filter=lfs diff=lfs merge=lfs -text
Audio/** filter=lfs diff=lfs merge=lfs -text

3
Audio/.gitattributes vendored Normal file
View File

@ -0,0 +1,3 @@
./** filter=lfs diff=lfs merge=lfs -text
.gitignore -filter
.gitattributes -filter

View File

@ -1,158 +1,131 @@
<mxfile host="app.diagrams.net" modified="2024-05-08T09:39:53.079Z" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36" etag="wAs-_Z6muctMyUQpajhH" version="24.3.1" type="device">
<mxfile host="app.diagrams.net" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36" version="25.0.3">
<diagram name="Stránka-1" id="jncRwyBWY97r-PTh6Wm3">
<mxGraphModel dx="2261" dy="746" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<mxGraphModel dx="1102" dy="527" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="OfQ8P6CXFFix212gcqCM-1" value="ContentLoader" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="340" width="120" height="40" as="geometry" />
<mxCell id="OfQ8P6CXFFix212gcqCM-12" value="Component with&amp;nbsp;&lt;span style=&quot;background-color: initial;&quot;&gt;AInteractable&lt;/span&gt;&lt;div&gt;&lt;span style=&quot;background-color: initial;&quot;&gt;actors scanning method&lt;/span&gt;&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fillColor=#CCE5FF;" parent="1" vertex="1">
<mxGeometry x="208" y="220" width="180" height="30" as="geometry" />
</mxCell>
<mxCell id="OfQ8P6CXFFix212gcqCM-2" value="GameInstance" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="340" y="60" width="120" height="40" as="geometry" />
<mxCell id="OfQ8P6CXFFix212gcqCM-7" value="On instantiation creates U&lt;span style=&quot;background-color: initial;&quot;&gt;InteractableActivator components&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: initial;&quot;&gt;stored in registers&lt;/span&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fillColor=#E6FFCC;" parent="1" vertex="1">
<mxGeometry x="558" y="120" width="261" height="41" as="geometry" />
</mxCell>
<mxCell id="OfQ8P6CXFFix212gcqCM-3" value="" style="endArrow=classic;html=1;rounded=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;strokeWidth=3;" edge="1" parent="1" source="OfQ8P6CXFFix212gcqCM-1" target="OfQ8P6CXFFix212gcqCM-2">
<mxCell id="OfQ8P6CXFFix212gcqCM-4" value="On construct call appends its class to AInteractable registers" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fillColor=#FFFFCC;" parent="1" vertex="1">
<mxGeometry x="549" y="30" width="230" height="40" as="geometry" />
</mxCell>
<mxCell id="OfQ8P6CXFFix212gcqCM-1" value="UInteractable Activator/Modificator" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#FFFFCC;" parent="1" vertex="1">
<mxGeometry x="398" width="160" height="40" as="geometry" />
</mxCell>
<mxCell id="OfQ8P6CXFFix212gcqCM-2" value="AInteractable" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#FFFFCC;" parent="1" vertex="1">
<mxGeometry x="398" y="60" width="160" height="40" as="geometry" />
</mxCell>
<mxCell id="OfQ8P6CXFFix212gcqCM-3" value="" style="endArrow=classic;html=1;rounded=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;strokeWidth=3;" parent="1" source="OfQ8P6CXFFix212gcqCM-1" target="OfQ8P6CXFFix212gcqCM-2" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="390" y="240" as="sourcePoint" />
<mxPoint x="440" y="190" as="targetPoint" />
<mxPoint x="488" y="240" as="sourcePoint" />
<mxPoint x="538" y="190" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="OfQ8P6CXFFix212gcqCM-4" value="Appends&amp;nbsp;&lt;span style=&quot;background-color: initial;&quot;&gt;instance.&lt;/span&gt;&lt;span style=&quot;background-color: initial;&quot;&gt;interactionsCollection.&lt;/span&gt;&lt;div&gt;&lt;span style=&quot;background-color: initial;&quot;&gt;(array of interactable interfaces)&lt;/span&gt;&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="460" y="5" width="230" height="30" as="geometry" />
</mxCell>
<mxCell id="OfQ8P6CXFFix212gcqCM-5" value="Player" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="340" y="120" width="120" height="40" as="geometry" />
</mxCell>
<mxCell id="OfQ8P6CXFFix212gcqCM-6" value="" style="endArrow=classic;html=1;rounded=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;strokeWidth=3;" edge="1" parent="1" source="OfQ8P6CXFFix212gcqCM-2" target="OfQ8P6CXFFix212gcqCM-5">
<mxCell id="OfQ8P6CXFFix212gcqCM-6" value="" style="endArrow=classic;html=1;rounded=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;strokeWidth=3;" parent="1" source="OfQ8P6CXFFix212gcqCM-2" target="OfQ8P6CXFFix212gcqCM-5" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="410" y="-10" as="sourcePoint" />
<mxPoint x="410" y="70" as="targetPoint" />
<mxPoint x="508" y="-10" as="sourcePoint" />
<mxPoint x="508" y="70" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="OfQ8P6CXFFix212gcqCM-7" value="On instantiation creates InteractableActivator components&lt;div&gt;from `InteractableInterface.GetActivatorsAndCallers()`.&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="460" y="125" width="320" height="30" as="geometry" />
<mxCell id="OfQ8P6CXFFix212gcqCM-8" value="UInteractableActivator" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#CCE5FF;" parent="1" vertex="1">
<mxGeometry x="218" y="180" width="160" height="40" as="geometry" />
</mxCell>
<mxCell id="OfQ8P6CXFFix212gcqCM-8" value="InteractableActivator" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="40" y="180" width="120" height="40" as="geometry" />
</mxCell>
<mxCell id="OfQ8P6CXFFix212gcqCM-9" value="" style="endArrow=classic;html=1;rounded=0;entryX=1;entryY=0;entryDx=0;entryDy=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;strokeWidth=3;" edge="1" parent="1" source="OfQ8P6CXFFix212gcqCM-5" target="OfQ8P6CXFFix212gcqCM-8">
<mxCell id="OfQ8P6CXFFix212gcqCM-9" value="" style="endArrow=classic;html=1;rounded=0;entryX=1;entryY=0;entryDx=0;entryDy=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;strokeWidth=3;" parent="1" source="OfQ8P6CXFFix212gcqCM-5" edge="1" target="OfQ8P6CXFFix212gcqCM-8">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="410" y="110" as="sourcePoint" />
<mxPoint x="410" y="130" as="targetPoint" />
<mxPoint x="508" y="111" as="sourcePoint" />
<mxPoint x="258" y="181" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="OfQ8P6CXFFix212gcqCM-10" value="" style="endArrow=classic;html=1;rounded=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;strokeWidth=3;" edge="1" parent="1" source="OfQ8P6CXFFix212gcqCM-5" target="OfQ8P6CXFFix212gcqCM-8">
<mxCell id="OfQ8P6CXFFix212gcqCM-10" value="" style="endArrow=classic;html=1;rounded=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;strokeWidth=3;" parent="1" source="OfQ8P6CXFFix212gcqCM-5" edge="1" target="OfQ8P6CXFFix212gcqCM-8">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="410" y="170" as="sourcePoint" />
<mxPoint x="380" y="200" as="targetPoint" />
<mxPoint x="508" y="171" as="sourcePoint" />
<mxPoint x="258" y="201" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="OfQ8P6CXFFix212gcqCM-11" value="" style="endArrow=classic;html=1;rounded=0;entryX=1;entryY=1;entryDx=0;entryDy=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;strokeWidth=3;" edge="1" parent="1" source="OfQ8P6CXFFix212gcqCM-5" target="OfQ8P6CXFFix212gcqCM-8">
<mxCell id="OfQ8P6CXFFix212gcqCM-11" value="" style="endArrow=classic;html=1;rounded=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;strokeWidth=3;entryX=1;entryY=1;entryDx=0;entryDy=0;" parent="1" source="OfQ8P6CXFFix212gcqCM-5" edge="1" target="OfQ8P6CXFFix212gcqCM-8">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="420" y="180" as="sourcePoint" />
<mxPoint x="390" y="210" as="targetPoint" />
<mxPoint x="518" y="181" as="sourcePoint" />
<mxPoint x="398" y="240" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="OfQ8P6CXFFix212gcqCM-12" value="Component with special&amp;nbsp;&lt;div&gt;interactable actor scanning method.&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="-160" y="185" width="200" height="30" as="geometry" />
</mxCell>
<mxCell id="OfQ8P6CXFFix212gcqCM-15" value="Player.&lt;div&gt;InteractableActivated()&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="40" y="120" width="120" height="40" as="geometry" />
</mxCell>
<mxCell id="OfQ8P6CXFFix212gcqCM-16" value="" style="endArrow=classic;html=1;rounded=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;exitX=0.5;exitY=0;exitDx=0;exitDy=0;strokeWidth=3;" edge="1" parent="1" source="OfQ8P6CXFFix212gcqCM-8" target="OfQ8P6CXFFix212gcqCM-15">
<mxCell id="OfQ8P6CXFFix212gcqCM-16" value="" style="endArrow=classic;html=1;rounded=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;exitX=0.5;exitY=0;exitDx=0;exitDy=0;strokeWidth=3;" parent="1" edge="1" target="OfQ8P6CXFFix212gcqCM-15" source="OfQ8P6CXFFix212gcqCM-8">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="250" y="170" as="sourcePoint" />
<mxPoint x="220" y="200" as="targetPoint" />
<mxPoint x="323" y="182" as="sourcePoint" />
<mxPoint x="323" y="162" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="OfQ8P6CXFFix212gcqCM-18" value="MainGameMode&lt;div&gt;.widgetsManager&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="40" width="120" height="40" as="geometry" />
</mxCell>
<mxCell id="OfQ8P6CXFFix212gcqCM-20" value="" style="endArrow=classic;html=1;rounded=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;exitX=0.5;exitY=0;exitDx=0;exitDy=0;strokeWidth=3;" edge="1" parent="1" source="OfQ8P6CXFFix212gcqCM-15" target="OfQ8P6CXFFix212gcqCM-18">
<mxCell id="OfQ8P6CXFFix212gcqCM-20" value="" style="endArrow=classic;html=1;rounded=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;exitX=0.5;exitY=0;exitDx=0;exitDy=0;strokeWidth=3;" parent="1" source="OfQ8P6CXFFix212gcqCM-15" target="OfQ8P6CXFFix212gcqCM-18" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="190" y="220" as="sourcePoint" />
<mxPoint x="110" y="170" as="targetPoint" />
<mxPoint x="548" y="220" as="sourcePoint" />
<mxPoint x="468" y="170" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="OfQ8P6CXFFix212gcqCM-21" value="&lt;div&gt;Shows interactions hint.&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="-100" y="5" width="140" height="30" as="geometry" />
<mxCell id="OfQ8P6CXFFix212gcqCM-21" value="&lt;div&gt;Show interactions hint&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fillColor=#FFCCFF;" parent="1" vertex="1">
<mxGeometry x="228" y="15" width="140" height="30" as="geometry" />
</mxCell>
<mxCell id="OfQ8P6CXFFix212gcqCM-22" value="Player.&lt;div&gt;activatedInteractable&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="340" y="240" width="120" height="40" as="geometry" />
<mxCell id="OfQ8P6CXFFix212gcqCM-28" value="Component with AInteractable modifcator&lt;span style=&quot;background-color: initial;&quot;&gt;&amp;nbsp;methods&lt;/span&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fillColor=#FFCCCC;" parent="1" vertex="1">
<mxGeometry x="8" y="220" width="180" height="30" as="geometry" />
</mxCell>
<mxCell id="OfQ8P6CXFFix212gcqCM-24" value="InteractableCaller" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="620" y="180" width="120" height="40" as="geometry" />
</mxCell>
<mxCell id="OfQ8P6CXFFix212gcqCM-25" value="" style="endArrow=classic;html=1;rounded=0;entryX=0;entryY=0;entryDx=0;entryDy=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;strokeWidth=3;" edge="1" parent="1" source="OfQ8P6CXFFix212gcqCM-5" target="OfQ8P6CXFFix212gcqCM-24">
<mxCell id="OfQ8P6CXFFix212gcqCM-32" value="" style="endArrow=none;html=1;rounded=0;strokeWidth=3;entryX=0;entryY=0.5;entryDx=0;entryDy=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" parent="1" target="OfQ8P6CXFFix212gcqCM-5" edge="1" source="OfQ8P6CXFFix212gcqCM-15">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="410" y="170" as="sourcePoint" />
<mxPoint x="330" y="240" as="targetPoint" />
<mxPoint x="358" y="150" as="sourcePoint" />
<mxPoint x="348" y="110" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="OfQ8P6CXFFix212gcqCM-26" value="" style="endArrow=classic;html=1;rounded=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;strokeWidth=3;" edge="1" parent="1" target="OfQ8P6CXFFix212gcqCM-24">
<mxCell id="OfQ8P6CXFFix212gcqCM-34" value="UInteractableModificator::[Un]Bind()&amp;nbsp;&lt;span style=&quot;background-color: initial;&quot;&gt;player&#39;s input&lt;/span&gt;" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#FFCCCC;" parent="1" vertex="1">
<mxGeometry x="18" y="120" width="160" height="40" as="geometry" />
</mxCell>
<mxCell id="OfQ8P6CXFFix212gcqCM-35" value="" style="endArrow=classic;html=1;rounded=0;exitX=0.25;exitY=0;exitDx=0;exitDy=0;strokeWidth=3;entryX=0.25;entryY=1;entryDx=0;entryDy=0;" parent="1" source="OfQ8P6CXFFix212gcqCM-24" target="OfQ8P6CXFFix212gcqCM-34" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="400" y="160" as="sourcePoint" />
<mxPoint x="490" y="200" as="targetPoint" />
<mxPoint x="-22" y="-37" as="sourcePoint" />
<mxPoint x="73" y="141.75" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="OfQ8P6CXFFix212gcqCM-27" value="" style="endArrow=classic;html=1;rounded=0;entryX=0;entryY=1;entryDx=0;entryDy=0;strokeWidth=3;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" edge="1" parent="1" source="OfQ8P6CXFFix212gcqCM-5" target="OfQ8P6CXFFix212gcqCM-24">
<mxCell id="OfQ8P6CXFFix212gcqCM-36" value="" style="endArrow=classic;html=1;rounded=0;exitX=0.5;exitY=0;exitDx=0;exitDy=0;strokeWidth=3;entryX=0.5;entryY=1;entryDx=0;entryDy=0;" parent="1" source="OfQ8P6CXFFix212gcqCM-24" target="OfQ8P6CXFFix212gcqCM-34" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="410" y="170" as="sourcePoint" />
<mxPoint x="490" y="220" as="targetPoint" />
<mxPoint x="88" y="83" as="sourcePoint" />
<mxPoint x="118" y="161.75" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="OfQ8P6CXFFix212gcqCM-28" value="Component with special&amp;nbsp;&lt;div&gt;input processing method.&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="740" y="185" width="150" height="30" as="geometry" />
</mxCell>
<mxCell id="OfQ8P6CXFFix212gcqCM-29" value="" style="endArrow=classic;html=1;rounded=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=1;exitY=1;exitDx=0;exitDy=0;strokeWidth=3;" edge="1" parent="1" source="OfQ8P6CXFFix212gcqCM-15" target="OfQ8P6CXFFix212gcqCM-22">
<mxCell id="OfQ8P6CXFFix212gcqCM-37" value="" style="endArrow=classic;html=1;rounded=0;exitX=0.75;exitY=0;exitDx=0;exitDy=0;strokeWidth=3;entryX=0.75;entryY=1;entryDx=0;entryDy=0;" parent="1" source="OfQ8P6CXFFix212gcqCM-24" target="OfQ8P6CXFFix212gcqCM-34" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="270" y="200" as="sourcePoint" />
<mxPoint x="270" y="170" as="targetPoint" />
<mxPoint x="118" y="83" as="sourcePoint" />
<mxPoint x="158" y="161.75" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="OfQ8P6CXFFix212gcqCM-32" value="" style="endArrow=classic;html=1;rounded=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;strokeWidth=3;" edge="1" parent="1" source="OfQ8P6CXFFix212gcqCM-15" target="OfQ8P6CXFFix212gcqCM-5">
<mxCell id="OfQ8P6CXFFix212gcqCM-5" value="APlayerBase" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#E6FFCC;" parent="1" vertex="1">
<mxGeometry x="398" y="120" width="160" height="40" as="geometry" />
</mxCell>
<mxCell id="OfQ8P6CXFFix212gcqCM-24" value="UInteractableModificator" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#FFCCCC;" parent="1" vertex="1">
<mxGeometry x="18" y="180" width="160" height="40" as="geometry" />
</mxCell>
<mxCell id="dr_dVIBuduT0epV415MF-4" value="" style="endArrow=classic;html=1;rounded=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;strokeWidth=3;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="OfQ8P6CXFFix212gcqCM-15" target="OfQ8P6CXFFix212gcqCM-24">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="110" y="200" as="sourcePoint" />
<mxPoint x="110" y="170" as="targetPoint" />
<mxPoint x="133" y="131" as="sourcePoint" />
<mxPoint x="133" y="89" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="OfQ8P6CXFFix212gcqCM-33" value="Player.&lt;div&gt;EnableInteractableCallers&lt;/div&gt;&lt;div&gt;ByInteractableImplemented&lt;/div&gt;&lt;div&gt;InteractionInterfaces&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="170" y="60" width="150" height="70" as="geometry" />
</mxCell>
<mxCell id="OfQ8P6CXFFix212gcqCM-34" value="InputEvents" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="620" y="240" width="120" height="40" as="geometry" />
</mxCell>
<mxCell id="OfQ8P6CXFFix212gcqCM-35" value="" style="endArrow=classic;html=1;rounded=0;entryX=0.25;entryY=0;entryDx=0;entryDy=0;exitX=0.25;exitY=1;exitDx=0;exitDy=0;strokeWidth=3;" edge="1" parent="1" source="OfQ8P6CXFFix212gcqCM-24" target="OfQ8P6CXFFix212gcqCM-34">
<mxCell id="dr_dVIBuduT0epV415MF-5" value="" style="endArrow=classic;html=1;rounded=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;strokeWidth=3;entryX=1;entryY=1;entryDx=0;entryDy=0;" edge="1" parent="1" source="OfQ8P6CXFFix212gcqCM-15" target="OfQ8P6CXFFix212gcqCM-24">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="550" y="110" as="sourcePoint" />
<mxPoint x="550" y="130" as="targetPoint" />
<mxPoint x="248" y="151" as="sourcePoint" />
<mxPoint x="168" y="151" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="OfQ8P6CXFFix212gcqCM-36" value="" style="endArrow=classic;html=1;rounded=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;strokeWidth=3;" edge="1" parent="1" source="OfQ8P6CXFFix212gcqCM-24" target="OfQ8P6CXFFix212gcqCM-34">
<mxCell id="dr_dVIBuduT0epV415MF-6" value="" style="endArrow=classic;html=1;rounded=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;strokeWidth=3;entryX=1;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="OfQ8P6CXFFix212gcqCM-15" target="OfQ8P6CXFFix212gcqCM-24">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="660" y="230" as="sourcePoint" />
<mxPoint x="660" y="250" as="targetPoint" />
<mxPoint x="248" y="151" as="sourcePoint" />
<mxPoint x="168" y="171" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="OfQ8P6CXFFix212gcqCM-37" value="" style="endArrow=classic;html=1;rounded=0;entryX=0.75;entryY=0;entryDx=0;entryDy=0;exitX=0.75;exitY=1;exitDx=0;exitDy=0;strokeWidth=3;" edge="1" parent="1" source="OfQ8P6CXFFix212gcqCM-24" target="OfQ8P6CXFFix212gcqCM-34">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="690" y="230" as="sourcePoint" />
<mxPoint x="690" y="250" as="targetPoint" />
</mxGeometry>
<mxCell id="OfQ8P6CXFFix212gcqCM-15" value="APlayerBase::&lt;div&gt;Interactable[&lt;span style=&quot;background-color: initial;&quot;&gt;De]&lt;/span&gt;&lt;span style=&quot;background-color: initial;&quot;&gt;Activated()&lt;/span&gt;&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#E6FFCC;" parent="1" vertex="1">
<mxGeometry x="218" y="120" width="160" height="40" as="geometry" />
</mxCell>
<mxCell id="OfQ8P6CXFFix212gcqCM-38" value="" style="endArrow=classic;html=1;rounded=0;entryX=1;entryY=0;entryDx=0;entryDy=0;exitX=0;exitY=0;exitDx=0;exitDy=0;strokeWidth=3;" edge="1" parent="1" source="OfQ8P6CXFFix212gcqCM-34" target="OfQ8P6CXFFix212gcqCM-22">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="520" y="230" as="sourcePoint" />
<mxPoint x="520" y="250" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="OfQ8P6CXFFix212gcqCM-40" value="" style="endArrow=classic;html=1;rounded=0;entryX=1;entryY=1;entryDx=0;entryDy=0;exitX=0;exitY=1;exitDx=0;exitDy=0;strokeWidth=3;" edge="1" parent="1" source="OfQ8P6CXFFix212gcqCM-34" target="OfQ8P6CXFFix212gcqCM-22">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="490" y="270" as="sourcePoint" />
<mxPoint x="470" y="270" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="OfQ8P6CXFFix212gcqCM-41" value="&lt;div&gt;Interactable&lt;/div&gt;.Call(InputDescription)" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="460" y="245" width="160" height="30" as="geometry" />
<mxCell id="OfQ8P6CXFFix212gcqCM-18" value="AMainGameModeBase::&lt;div&gt;widgetsManager&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#FFCCFF;" parent="1" vertex="1">
<mxGeometry x="218" y="45" width="160" height="40" as="geometry" />
</mxCell>
</root>
</mxGraphModel>

Binary file not shown.

BIN
Documentation/RAI DHF.docx Normal file

Binary file not shown.

3
Documentation/README.md Normal file
View File

@ -0,0 +1,3 @@
# Documentation
The always actual documentation is on [google drive](https://drive.google.com/drive/folders/1o40kh_8BgrMI3BzPyfNT0ZLG_5mrIjEy?usp=sharing). \
There is only a dump/backup.

Binary file not shown.

Binary file not shown.

3
Fonts/.gitattributes vendored Normal file
View File

@ -0,0 +1,3 @@
./** filter=lfs diff=lfs merge=lfs -text
.gitignore -filter
.gitattributes -filter

3
Images/.gitattributes vendored Normal file
View File

@ -0,0 +1,3 @@
./** filter=lfs diff=lfs merge=lfs -text
.gitignore -filter
.gitattributes -filter

3
Models/.gitattributes vendored Normal file
View File

@ -0,0 +1,3 @@
./** filter=lfs diff=lfs merge=lfs -text
.gitignore -filter
.gitattributes -filter

1
Models/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*.blend1

View File

@ -1,15 +1,31 @@
# Repo structure
This repository contains all sources and data. The current unreal project is located in UnrealProject/Lost_Edge/.
This repository contains all sources and data for project Lost_Edge excluding server and network deploy configs. \
Most of subdirectories represents individual modules and contains their own README.
# Documentation
Actualised documentation is on [google drive](https://drive.google.com/drive/folders/1o40kh_8BgrMI3BzPyfNT0ZLG_5mrIjEy?usp=sharing).
Lost_Edge/ \
├─ [Audio/](Audio) ---> Audio data (e.g. music, sounds, dialogues) \
├─ [Documentation/](Documentation) ---> Documentation (e.g. game and level design) \
├─ [Fonts/](Fonts) ---> Fonts used in project \
├─ [Images/](Images) ---> Image data (e.g. textures, postures, logo) \
├─ [Models/](Models) ---> Models data (e.g. characters, decorations, foliages) \
├─ [ReleaseBuilds/](ReleaseBuilds) \
│ ├─ Legacy/ ---> Legacy build created as high school project \
│ └─ vX.X/ ---> Current builds of Lost_Edge (university project) \
├─ UnrealProject/ \
│ ├─ [Lost_Edge/](UnrealProject/Lost_Edge) ---> Current unreal project of Lost_Edge \
│ └─ Lost_Edge_Legacy/ ---> Legacy unreal project created as high school project \
└─[VoiceGenerator/](VoiceGenerator) ---> AI voice generation module
# How to compile
It's recommended to work in Unreal Engine 5.4 and to install Visual Studio with the necessary packages as it described in the [official documentation](https://dev.epicgames.com/documentation/en-us/unreal-engine/setting-up-visual-studio-development-environment-for-cplusplus-projects-in-unreal-engine) (recommended settings aren't needed). \
After repo cloning, the Visual Studio project files needs to be generated. It can be done in the explorer RMB context menu of file "UnrealProject/Lost_Edge/Lost_Edge.uproject". Or by typing "%UE5PATH%\Engine\Build\BatchFiles\GenerateProjectFiles.bat" "%REPOPATH%\UnrealProject\Lost_Edge\Lost_Edge.uproject". \
After generating, "Lost_Edge.sln" can be opened and project can start building for the first time, which will fail. This is because of using OpenCV plugin that together with UnrealEngine implement their own "check()" function. This error can be used to determine that in the file "%UE5PATH%\Engine\Plugins\Runtime\OpenCV\Source\ThirdParty\OpenCV\include\opencv2\core\utility.hpp" it is necessary to comment out the warning macro on lines 52-54 and rename the function itself on line 957 to, for example, "checkcv()".
After this modification, the build should run fine and it is possible to open the project in UnrealEngine.
# Building your own project copy
Building a copy of the game requires only [UnrealProject/LostEdge/](UnrealProject/Lost_Edge) directory. \
For that purposes you can download the directory as archive or do a sparse checkout via commands below.
- git clone --no-checkout https://pixelyfier.com/git/Pixelyfier/Lost_Edge.git
- cd Lost_Edge/
- git sparse-checkout init
- git sparse-checkout set UnrealProject/Lost_Edge
- git checkout master
# Git lfs common issues
The download can be sometimes too long which make git lfs drop the connection with error "LFS: Put "https://pixelyfier.com/git/Pixelyfier/Lost_Edge.git/info/lfs/objects/HASH": read tcp IP:PORT->pixelyfier.com:443: i/o timeout" \
The download can be sometimes too long which makes git lfs drop the connection with error \
`"LFS: Put "https://pixelyfier.com/git/Pixelyfier/Lost_Edge.git/info/lfs/objects/HASH": read tcp IP:PORT->pixelyfier.com:443: i/o timeout"`
- To fix this set local/global repo config `git config lfs.activitytimeout 240`

3
ReleaseBuilds/.gitattributes vendored Normal file
View File

@ -0,0 +1,3 @@
./** filter=lfs diff=lfs merge=lfs -text
.gitignore -filter
.gitattributes -filter

View File

@ -84,7 +84,7 @@ CommandletClass=Class'/Script/UnrealEd.WorldPartitionConvertCommandlet'
+ActiveGameNameRedirects=(OldGameName="TP_Blank",NewGameName="/Script/Lost_Edge")
+ActiveGameNameRedirects=(OldGameName="/Script/TP_Blank",NewGameName="/Script/Lost_Edge")
+ActiveClassRedirects=(OldClassName="TP_BlankGameModeBase",NewClassName="Lost_EdgeGameModeBase")
GameUserSettingsClassName=/Script/Lost_Edge.CustomGameUserSettings
GameUserSettingsClassName=/Script/Lost_Edge.CustomGameSettings
LevelScriptActorClassName=/Script/Lost_Edge.LevelBase
[/Script/AndroidFileServerEditor.AndroidFileServerRuntimeSettings]

View File

@ -1,4 +1,4 @@
[/Script/Lost_Edge.CustomGameUserSettings]
[/Script/Lost_Edge.CustomGameSettings]
bUseMotionBlur=False
bShowFps=False
bMouseInverted=False

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,5 @@
# How to compile
It's recommended to work in Unreal Engine 5.4 and to install Visual Studio with the necessary packages as it described in the [official documentation](https://dev.epicgames.com/documentation/en-us/unreal-engine/setting-up-visual-studio-development-environment-for-cplusplus-projects-in-unreal-engine) (recommended settings aren't needed). \
After repo cloning, the Visual Studio project files needs to be generated. It can be done in the explorer RMB context menu of file "UnrealProject/Lost_Edge/Lost_Edge.uproject". Or by typing "%UE5PATH%\Engine\Build\BatchFiles\GenerateProjectFiles.bat" "%REPOPATH%\UnrealProject\Lost_Edge\Lost_Edge.uproject". \
After generating, "Lost_Edge.sln" can be opened and project can start building for the first time, which will fail. This is because of using OpenCV plugin that together with UnrealEngine implement their own "check()" function. This error can be used to determine that in the file "%UE5PATH%\Engine\Plugins\Runtime\OpenCV\Source\ThirdParty\OpenCV\include\opencv2\core\utility.hpp" it is necessary to comment out the warning macro on lines 52-54 and rename the function itself on line 957 to, for example, "checkcv()".
After this modification, the build should run fine and it is possible to open the project in UnrealEngine.

View File

@ -1,6 +1,5 @@
// Oleg Petruny proprietary.
#include "CameraModeBase.h"
#include "Camera/CameraComponent.h"
@ -10,12 +9,16 @@
#include "InputMappingContext.h"
#include "Kismet/GameplayStatics.h"
#include "CustomGameInstanceBase.h"
#include "CustomGameUserSettings.h"
#include "CustomGameSettings.h"
#include "CustomPlayerController.h"
#include "MainGameModeBase.h"
ACameraModeBase::ACameraModeBase()
{
static ConstructorHelpers::FObjectFinder<UInputMappingContext> asset{ TEXT("/Script/EnhancedInput.InputMappingContext'/Game/Input/IMC_CameraMode.IMC_CameraMode'") };
context = asset.Object;
ACustomPlayerController::AppendInputContext(context);
PrimaryActorTick.bCanEverTick = true;
}
@ -25,28 +28,11 @@ void ACameraModeBase::BeginPlay()
auto world = GetWorld();
//GetMovementComponent()->speed
// GetCharacterMovement()->MaxWalkSpeed = moveSpeed;
auto gameSettings = UCustomGameUserSettings::GetCustomGameUserSettings();
if(auto camera = FindComponentByClass<UCameraComponent>())
if(auto gameSettings = UCustomGameSettings::Get())
{
camera->PostProcessSettings.MotionBlurAmount = gameSettings->bUseMotionBlur ? 1.0f : 0.0f;
}
if(auto PC = Cast<APlayerController>(GetController()))
{
if(auto inputSubsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PC->GetLocalPlayer()))
if(auto camera = FindComponentByClass<UCameraComponent>())
{
if(auto GI = Cast<UCustomGameInstanceBase>(GetWorld()->GetGameInstance()))
{
inputSubsystem->ClearAllMappings();
for(auto& inputContext : GI->inputContexts)
{
inputSubsystem->AddMappingContext(inputContext.LoadSynchronous(), 0);
}
}
camera->PostProcessSettings.MotionBlurAmount = gameSettings->bUseMotionBlur ? 1.0f : 0.0f;
}
}
}
@ -66,13 +52,8 @@ void ACameraModeBase::SetupPlayerInputComponent(UInputComponent* PlayerInputComp
void ACameraModeBase::SwitchToPlayerPawn()
{
if(auto gamemode_base = UGameplayStatics::GetGameMode(GetWorld()))
{
if(auto gamemode = Cast<AMainGameModeBase>(gamemode_base))
{
gamemode->SwitchCameraMode();
}
}
if(auto gamemode = AMainGameModeBase::Get())
gamemode->SwitchCameraMode();
}
void ACameraModeBase::ElevatePawn(float value)
@ -86,4 +67,4 @@ void ACameraModeBase::SwitchRun(bool run)
// GetMovement()->MaxWalkSpeed = moveSpeed * runSpeedMultiplier;
//else
// GetCharacterMovement()->MaxWalkSpeed = moveSpeed;
}
}

View File

@ -2,11 +2,13 @@
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/SpectatorPawn.h"
#include "CameraModeBase.generated.h"
/**
* Cheap copy of PlayerBase for level fly spectating purposes.
*/
UCLASS()
class ACameraModeBase : public ASpectatorPawn
{
@ -33,4 +35,8 @@ protected:
float moveSpeed = 200;
UPROPERTY(EditDefaultsOnly)
float runSpeedMultiplier = 4;
private:
TSoftObjectPtr<class UInputMappingContext> context;
};

View File

@ -7,7 +7,7 @@
#include "Kismet/GameplayStatics.h"
#include "UObject/Object.h"
#include "CustomGameInstanceBase.h"
#include "CustomGameInstance.h"
#include "Levels/LevelBase.h"
#include "PlayerBase.h"
@ -53,25 +53,6 @@ TArray<int32> UCommonFunctions::GetRandomIntArray(int32 size, int32 min, int32 m
ALevelBase* UCommonFunctions::GetCurrentLevelScript(UObject* obj)
{
if(auto world = obj->GetWorld())
if(auto level = world->GetCurrentLevel())
if(auto script = level->GetLevelScriptActor())
return Cast<ALevelBase>(script);
return nullptr;
}
APlayerBase* UCommonFunctions::GetPlayer(UObject* obj)
{
if(auto pc = UGameplayStatics::GetPlayerController(obj->GetWorld(), 0))
if(auto pawn = pc->GetPawn())
return Cast<APlayerBase>(pawn);
return nullptr;
}
namespace SlowMotion
{
FTimerHandle timer;
@ -86,7 +67,7 @@ void UCommonFunctions::EnterSlowMotion(float duration)
SlowMotion::targetDilation = SlowMotion::slowDilation;
auto GI = UCustomGameInstanceBase::GetGameInstance();
auto GI = UCustomGameInstance::Get();
if(!GI)
return;
@ -101,7 +82,7 @@ void UCommonFunctions::ExitSlowMotion(float duration)
SlowMotion::targetDilation = SlowMotion::normalDilation;
auto GI = UCustomGameInstanceBase::GetGameInstance();
auto GI = UCustomGameInstance::Get();
if(!GI)
return;
@ -116,7 +97,7 @@ FWorldDilationChangedDelegate& UCommonFunctions::GetWorldDilationChangedDelegate
void UCommonFunctions::SlowMotionTick()
{
const UWorld* world = UCustomGameInstanceBase::GetGameInstance()->GetWorld();
const UWorld* world = UCustomGameInstance::Get()->GetWorld();
const float currentDilation = UGameplayStatics::GetGlobalTimeDilation(world);
float newDilation = FMath::FInterpTo(currentDilation, SlowMotion::targetDilation, 1, SlowMotion::interpolationSpeed);

View File

@ -2,34 +2,33 @@
#pragma once
#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "CommonFunctions.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FWorldDilationChangedDelegate, float, newDilation);
/**
* Collection of common/universal/without own scope/specific functions.
*/
UCLASS()
class UCommonFunctions : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
/** Returns true if the object is UE class template (used for copy/archetype/meta system but not on the level) */
UFUNCTION(BlueprintPure)
static bool IsNonGameObject(class UObject* object);
/** "Overload" of the built-in keys translator (to not build own engine copy) */
UFUNCTION(BlueprintPure)
static FText GetKeyDisplayName(struct FKey key);
/** Recursively destroy actor and all its childs (the default Destroy doesn't have consistent behavior) */
UFUNCTION(BlueprintCallable, Category = Actor)
static void DestroyActorRecursively(class AActor* actor);
UFUNCTION(BlueprintPure)
static TArray<int32> GetRandomIntArray(int32 size = 16, int32 min = 0, int32 max = 16);
UFUNCTION(BlueprintCallable, Category = Level)
static class ALevelBase* GetCurrentLevelScript(class UObject* obj);
UFUNCTION(BlueprintCallable, Category = Player)
static class APlayerBase* GetPlayer(class UObject* obj);
UFUNCTION(BlueprintCallable, Category = World)
@ -41,6 +40,10 @@ public:
static FWorldDilationChangedDelegate& GetWorldDilationChangedDelegate();
UFUNCTION(BlueprintPure)
static TArray<int32> GetRandomIntArray(int32 size = 16, int32 min = 0, int32 max = 16);
template<typename T>
static TArray<T> ArrayDiff(const TArray<T>& a, const TArray<T>& b);
template<typename T>

View File

@ -1,12 +1,12 @@
// Oleg Petruny proprietary.
#include "ContentLoader.h"
#include "CommonFunctions.h"
#include "IPlatformFilePak.h"
#include "Misc/FileHelper.h"
#include "CommonFunctions.h"
#include <regex>
#include <string>
#include <vector>

View File

@ -10,13 +10,16 @@
UENUM(BlueprintType)
enum class EContentDownloadMethod : uint8
{
ClearRandom = 0,
NonRepeatRandom,
RatingOftenRandom
ClearRandom = 0, //!< Always random from 0 to n
NonRepeatRandom, //!< Download the one that isn't downloaded in current runtime (Reset on full set)
RatingOftenRandom //!< NonRepeatRandom combined with asset rating [WIP]
};
DECLARE_DYNAMIC_DELEGATE_OneParam(FContentDownloadedCallback, FString, pakFilePath);
/*
* High language wrapper for Paks(Assets) download and loading.
*/
UCLASS(BlueprintType)
class UContentLoader : public UObject
{
@ -35,12 +38,18 @@ public:
protected:
virtual void BeginDestroy() override;
/** Sends http get request */
void HttpGet(const FString& url, FHttpRequestCompleteDelegate requestCompleteCallback);
/** Parses assets html/json index */
TArray<FString> ParseDirectoryIndex(const TArray<uint8>& content, const FString& contentType);
/** Selects item by desired method */
FString SelectContentByMethod(const TArray<FString>& content, const EContentDownloadMethod method);
/** Reads Pak content */
FString GetPakMountContent(const FString& pakFilePath, TArray<FString>* content = nullptr);
/** Returns mount path to desired content item */
UClass* GetPakClass(const FString& pakContentPath);
/** Cache of already downloaded content at runtime (used by EContentDownloadMethod::NonRepeatRandom)*/
TArray<FString> downloadedContent;
};

View File

@ -0,0 +1,74 @@
// Oleg Petruny proprietary.
#include "CustomGameInstance.h"
#include "Kismet/GameplayStatics.h"
#include "Kismet/KismetSystemLibrary.h"
#include "ContentLoader.h"
#include "Levels/LevelBase.h"
#include "PlayerBase.h"
#include "SaveData.h"
UCustomGameInstance* UCustomGameInstance::instance = nullptr;
void UCustomGameInstance::Init()
{
UGameInstance::Init();
instance = this;
contentLoader = NewObject<UContentLoader>(this);
saveData = Cast<USaveData>(UGameplayStatics::CreateSaveGameObject(USaveData::StaticClass()));
}
UCustomGameInstance* UCustomGameInstance::Get()
{
return instance;
}
UContentLoader* UCustomGameInstance::GetContentLoader()
{
if(auto GI = Get())
return GI->contentLoader;
return nullptr;
}
void UCustomGameInstance::SaveGame(FName checkpointName)
{
auto levelScript = ALevelBase::Get();
if(!levelScript)
return;
auto player = APlayerBase::Get();
if(!player)
return;
saveData->level = GetWorld()->GetFName();
saveData->state = levelScript->GetState();
saveData->checkpoint = checkpointName;
if(player->leftPocketItem)
saveData->playerLeftPocketItem = player->leftPocketItem->GetFName();
else
saveData->playerLeftPocketItem = FName(TEXT(""));
if(player->rightPocketItem)
saveData->playerRightPocketItem = player->rightPocketItem->GetFName();
else
saveData->playerRightPocketItem = FName(TEXT(""));
UGameplayStatics::SaveGameToSlot(saveData, TEXT("Save"), 0);
}
void UCustomGameInstance::LoadGame()
{
saveData = Cast<USaveData>(UGameplayStatics::LoadGameFromSlot(TEXT("Save"), 0));
if(!saveData)
return;
UGameplayStatics::OpenLevel(this, saveData->level);
}
void UCustomGameInstance::ExitGame()
{
UKismetSystemLibrary::QuitGame(GetWorld(), nullptr, EQuitPreference::Quit, true);
}

View File

@ -4,29 +4,29 @@
#include "Engine/GameInstance.h"
#include "CustomGameInstanceBase.generated.h"
#include "CustomGameInstance.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FLevelBeginnedDelegate, FName, levelName);
/**
* Expands basic UE game instance.
* Manages saves, handles content loader and can shutdown the game.
*/
UCLASS()
class UCustomGameInstanceBase : public UGameInstance
class UCustomGameInstance : public UGameInstance
{
GENERATED_BODY()
public:
/** Instantiates content loader, dummy save data and applies settings */
virtual void Init() override;
UFUNCTION(BlueprintPure)
static UCustomGameInstanceBase* GetGameInstance();
UFUNCTION(BlueprintPure, meta = (DisplayName = "Get Custom Game Instance"))
static UCustomGameInstance* Get();
UFUNCTION(BlueprintPure)
static class UContentLoader* GetContentLoader();
UFUNCTION(BlueprintCallable, Category = Settings)
void ApplyMouseSettings();
void AppendInteractableModificatorClass(TSubclassOf<class UInteractableModificator> modificator);
UFUNCTION(BlueprintCallable, Category = Save)
void SaveGame(FName checkpointName);
UFUNCTION(BlueprintCallable, Category = Save)
@ -34,17 +34,11 @@ public:
UFUNCTION(BlueprintCallable)
void ExitGame();
static UCustomGameInstanceBase* instance;
static UCustomGameInstance* instance;
/** Public delegate called by ALevelBase instance on instantiation */
FLevelBeginnedDelegate OnLevelBeginned;
UPROPERTY(EditDefaultsOnly)
TSet<TSubclassOf<class UInteractableActivator>> interactionsActivators;
TSet<TSubclassOf<class UInteractableModificator>> interactionsModificators;
UPROPERTY(EditDefaultsOnly)
TSet<TSoftObjectPtr<class UInputMappingContext>> inputContexts;
UPROPERTY(VisibleAnywhere)
class USaveData* saveData = nullptr;

View File

@ -1,149 +0,0 @@
// Oleg Petruny proprietary.
#include "CustomGameInstanceBase.h"
#include "EnhancedInputLibrary.h"
#include "EnhancedInputSubsystems.h"
#include "InputMappingContext.h"
#include "Kismet/GameplayStatics.h"
#include "Kismet/KismetSystemLibrary.h"
#include "CommonFunctions.h"
#include "ContentLoader.h"
#include "CustomGameUserSettings.h"
#include "Interactable/Activators/InCameraInteractableActivator.h"
#include "Interactable/Activators/RaycastInteractableActivator.h"
#include "Interactable/Modificators/ActivateInteractableModificator.h"
#include "Interactable/Modificators/SawInteractableModificator.h"
#include "Levels/LevelBase.h"
#include "PlayerBase.h"
#include "SaveData.h"
UCustomGameInstanceBase* UCustomGameInstanceBase::instance = nullptr;
void UCustomGameInstanceBase::Init()
{
UGameInstance::Init();
instance = this;
contentLoader = NewObject<UContentLoader>(this);
interactionsActivators.Add(URaycastInteractableActivator::StaticClass());
interactionsActivators.Add(UInCameraInteractableActivator::StaticClass());
for(auto& modificator : interactionsModificators)
{
if(modificator.GetDefaultObject()->GetMappingContext())
{
inputContexts.Add(modificator.GetDefaultObject()->GetMappingContext());
}
}
ApplyMouseSettings();
saveData = Cast<USaveData>(UGameplayStatics::CreateSaveGameObject(USaveData::StaticClass()));
}
UCustomGameInstanceBase* UCustomGameInstanceBase::GetGameInstance()
{
return instance;
}
UContentLoader* UCustomGameInstanceBase::GetContentLoader()
{
if(auto GI = GetGameInstance())
return GI->contentLoader;
return nullptr;
}
void UCustomGameInstanceBase::ApplyMouseSettings()
{
if(auto gameSettings = UCustomGameUserSettings::GetCustomGameUserSettings())
{
for(auto& context : inputContexts)
{
if(!context.LoadSynchronous())
continue;
for(auto& mapping : context.LoadSynchronous()->GetMappings())
{
if(mapping.Key == EKeys::Mouse2D)
{
for(auto& modifier : mapping.Modifiers)
{
if(auto negate_modifier = Cast<UInputModifierNegate>(modifier))
{
negate_modifier->bY = !gameSettings->bMouseInverted;
}
if(auto scalar_modifier = Cast<UInputModifierScalar>(modifier))
{
scalar_modifier->Scalar = FVector{ gameSettings->GetMouseSensetivity() * 0.5 };
}
}
}
}
UEnhancedInputLibrary::RequestRebuildControlMappingsUsingContext(context.LoadSynchronous());
}
}
}
void UCustomGameInstanceBase::AppendInteractableModificatorClass(TSubclassOf<class UInteractableModificator> modificator)
{
bool alreadyPresent = false;
interactionsModificators.Add(modificator, &alreadyPresent);
if(!alreadyPresent)
{
if(auto IC = modificator.GetDefaultObject()->GetMappingContext())
{
inputContexts.Add(IC);
if(auto PC = UGameplayStatics::GetPlayerController(GetWorld(), 0))
{
if(auto inputSubsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PC->GetLocalPlayer()))
{
inputSubsystem->AddMappingContext(IC, 0);
}
}
}
}
}
void UCustomGameInstanceBase::SaveGame(FName checkpointName)
{
auto levelScript = UCommonFunctions::GetCurrentLevelScript(this);
if(!levelScript)
return;
auto player = UCommonFunctions::GetPlayer(this);
if(!player)
return;
saveData->level = GetWorld()->GetFName();
saveData->state = levelScript->GetState();
saveData->checkpoint = checkpointName;
if(player->leftPocketItem)
saveData->playerLeftPocketItem = player->leftPocketItem->GetFName();
else
saveData->playerLeftPocketItem = FName(TEXT(""));
if(player->rightPocketItem)
saveData->playerRightPocketItem = player->rightPocketItem->GetFName();
else
saveData->playerRightPocketItem = FName(TEXT(""));
UGameplayStatics::SaveGameToSlot(saveData, TEXT("Save"), 0);
}
void UCustomGameInstanceBase::LoadGame()
{
saveData = Cast<USaveData>(UGameplayStatics::LoadGameFromSlot(TEXT("Save"), 0));
if(!saveData)
return;
UGameplayStatics::OpenLevel(this, saveData->level);
}
void UCustomGameInstanceBase::ExitGame()
{
UKismetSystemLibrary::QuitGame(GetWorld(), nullptr, EQuitPreference::Quit, true);
}

View File

@ -0,0 +1,28 @@
// Oleg Petruny proprietary.
#include "CustomGameSettings.h"
#include "Kismet/GameplayStatics.h"
UCustomGameSettings::UCustomGameSettings(const FObjectInitializer& ObjectInitializer) :Super(ObjectInitializer)
{
bUseMotionBlur = false;
bShowFps = false;
bMouseInverted = false;
fMouseSensetivity = 1.0f;
}
UCustomGameSettings* UCustomGameSettings::Get()
{
return Cast<UCustomGameSettings>(UGameUserSettings::GetGameUserSettings());
}
void UCustomGameSettings::SetMouseSensetivity(float value)
{
fMouseSensetivity = FMath::Clamp(value, 0.1f, 2.0f);
}
float UCustomGameSettings::GetMouseSensetivity() const
{
return fMouseSensetivity;
}

View File

@ -0,0 +1,49 @@
// Oleg Petruny proprietary.
#pragma once
#include "GameFramework/GameUserSettings.h"
#include "CustomGameSettings.generated.h"
/**
* Manages custom game settings.
* Mouse sensetivity and inversion, motion blur usage, fps show.
*/
UCLASS(Config = Game, defaultconfig)
class UCustomGameSettings : public UGameUserSettings
{
GENERATED_UCLASS_BODY()
public:
// Is auto defined by UE but implementation is in cpp
//UCustomGameSettings(const FObjectInitializer& ObjectInitializer);
UFUNCTION(BlueprintPure, Category = Settings, meta = (DisplayName = "Get Custom Game Settings"))
static UCustomGameSettings* Get();
/**
* Sets mouse sensetivity multiplier
* @param value [0.1 - 2.0]
*/
UFUNCTION(BlueprintCallable, Category = Settings)
void SetMouseSensetivity(float value);
/** Returns mouse sensetivity multiplier in [0.1 - 2.0] */
UFUNCTION(BlueprintCallable, Category = Settings)
float GetMouseSensetivity() const;
UPROPERTY(Config, BlueprintReadWrite)
bool bUseMotionBlur;
UPROPERTY(Config, BlueprintReadWrite)
bool bShowFps;
UPROPERTY(Config, BlueprintReadWrite)
bool bMouseInverted;
protected:
UPROPERTY(Config)
float fMouseSensetivity;
};

View File

@ -1,29 +0,0 @@
// Oleg Petruny proprietary.
#include "CustomGameUserSettings.h"
#include "Kismet/GameplayStatics.h"
UCustomGameUserSettings::UCustomGameUserSettings(const FObjectInitializer& ObjectInitializer) :Super(ObjectInitializer)
{
bUseMotionBlur = false;
bShowFps = false;
bMouseInverted = false;
fMouseSensetivity = 1.0f;
}
UCustomGameUserSettings* UCustomGameUserSettings::GetCustomGameUserSettings()
{
return Cast<UCustomGameUserSettings>(UGameUserSettings::GetGameUserSettings());
}
void UCustomGameUserSettings::SetMouseSensetivity(float value)
{
fMouseSensetivity = FMath::Clamp(value, 0.1f, 2.0f);
}
float UCustomGameUserSettings::GetMouseSensetivity() const
{
return fMouseSensetivity;
}

View File

@ -1,41 +0,0 @@
// Oleg Petruny proprietary.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/GameUserSettings.h"
#include "CustomGameUserSettings.generated.h"
UCLASS()
class UCustomGameUserSettings : public UGameUserSettings
{
GENERATED_UCLASS_BODY()
public:
UFUNCTION(BlueprintCallable, Category = Settings)
static UCustomGameUserSettings* GetCustomGameUserSettings();
// Sets mouse sensetivity multiplier
// @param value [0.1 - 2.0]
UFUNCTION(BlueprintCallable, Category = Settings)
void SetMouseSensetivity(float value);
// Returns mouse sensetivity multiplier in [0.1 - 2.0]
UFUNCTION(BlueprintCallable, Category = Settings)
float GetMouseSensetivity() const;
UPROPERTY(Config, BlueprintReadWrite)
bool bUseMotionBlur;
UPROPERTY(Config, BlueprintReadWrite)
bool bShowFps;
UPROPERTY(Config, BlueprintReadWrite)
bool bMouseInverted;
protected:
UPROPERTY(Config)
float fMouseSensetivity;
};

View File

@ -0,0 +1,116 @@
// Oleg Petruny proprietary.
#include "CustomPlayerController.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputLibrary.h"
#include "EnhancedInputSubsystems.h"
#include "InputMappingContext.h"
#include "CustomGameInstance.h"
#include "CustomGameSettings.h"
ACustomPlayerController* ACustomPlayerController::instance = nullptr;
UEnhancedInputComponent* ACustomPlayerController::input = nullptr;
UEnhancedInputLocalPlayerSubsystem* ACustomPlayerController::subsystem = nullptr;
TSet<TSoftObjectPtr<UInputMappingContext>> ACustomPlayerController::contexts = {};
TSet<TSoftObjectPtr<UInputMappingContext>> ACustomPlayerController::contextsBeforeInit = {};
void ACustomPlayerController::AppendInputContext(TSoftObjectPtr<class UInputMappingContext> context)
{
if(!context.IsValid())
return;
if(!UCustomGameInstance::Get()) //game settings not initialized yet
{
contextsBeforeInit.Add(context);
return;
}
ApplyMouseSettings(context);
contexts.Add(context);
if(subsystem)
subsystem->AddMappingContext(context.LoadSynchronous(), 0);
}
void ACustomPlayerController::BeginPlay()
{
Super::BeginPlay();
instance = this;
for(auto& context : contextsBeforeInit)
{
ApplyMouseSettings(context);
contexts.Add(context);
}
contextsBeforeInit.Empty();
subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(GetLocalPlayer());
if(subsystem)
{
subsystem->ClearAllMappings();
for(auto& inputContext : contexts)
subsystem->AddMappingContext(inputContext.LoadSynchronous(), 0);
}
input = Cast<UEnhancedInputComponent>(InputComponent);
if(input)
{
((UInputComponent*)input)->BindAction(TEXT("AnyKey"), IE_Pressed, this, &ACustomPlayerController::OnAnyKeyPressed);
((UInputComponent*)input)->BindAction(TEXT("AnyKey"), IE_Released, this, &ACustomPlayerController::OnAnyKeyReleased);
}
}
void ACustomPlayerController::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
instance = nullptr;
input = nullptr;
subsystem = nullptr;
Super::EndPlay(EndPlayReason);
}
void ACustomPlayerController::ApplyMouseSettings(TSoftObjectPtr<class UInputMappingContext>& context)
{
auto gameSettings = UCustomGameSettings::Get();
if(!gameSettings)
return;
if(!context.LoadSynchronous())
return;
for(auto& mapping : context.LoadSynchronous()->GetMappings())
{
if(mapping.Key != EKeys::Mouse2D)
continue;
for(auto& modifier : mapping.Modifiers)
{
if(gameSettings->bMouseInverted)
{
if(auto negate_modifier = Cast<UInputModifierNegate>(modifier))
{
negate_modifier->bY = !negate_modifier->bY;
continue;
}
}
if(auto scalar_modifier = Cast<UInputModifierScalar>(modifier))
scalar_modifier->Scalar = FVector{ gameSettings->GetMouseSensetivity() * 0.5 };
}
}
UEnhancedInputLibrary::RequestRebuildControlMappingsUsingContext(context.LoadSynchronous());
}
void ACustomPlayerController::OnAnyKeyPressed(FKey key)
{
if(onAnyKeyPressed.IsBound())
onAnyKeyPressed.Broadcast(key);
}
void ACustomPlayerController::OnAnyKeyReleased(FKey key)
{
if(onAnyKeyReleased.IsBound())
onAnyKeyReleased.Broadcast(key);
}

View File

@ -0,0 +1,52 @@
// Oleg Petruny proprietary.
#pragma once
#include "GameFramework/PlayerController.h"
#include "CustomPlayerController.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FPlayerAnyKeyPressedDelegate, FKey, key);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FPlayerAnyKeyReleasedDelegate, FKey, key);
/**
* De-facto wrapper/interface of all usefull shortcuts and automatization around input system.
* Append new input context here for auto init on BeginPlay and apply user input settings.
* Also, has delegates onAnyKeyPressed/Released.
*/
UCLASS()
class ACustomPlayerController : public APlayerController
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintPure, meta = (DisplayName = "Get Custom Player Controller"))
static ACustomPlayerController* Get() { return instance; }
UFUNCTION(BlueprintPure)
static class UEnhancedInputComponent* GetInput() { return input; }
UFUNCTION(BlueprintPure)
static class UEnhancedInputLocalPlayerSubsystem* GetInputSubsystem() { return subsystem; }
UFUNCTION(BlueprintCallable)
static void AppendInputContext(TSoftObjectPtr<class UInputMappingContext> context);
FPlayerAnyKeyPressedDelegate onAnyKeyPressed;
FPlayerAnyKeyReleasedDelegate onAnyKeyReleased;
protected:
virtual void BeginPlay() override;
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
private:
static void ApplyMouseSettings(TSoftObjectPtr<class UInputMappingContext>& context);
void OnAnyKeyPressed(FKey key);
void OnAnyKeyReleased(FKey key);
static ACustomPlayerController* instance;
static class UEnhancedInputLocalPlayerSubsystem* subsystem;
static class UEnhancedInputComponent* input;
static TSet<TSoftObjectPtr<class UInputMappingContext>> contexts;
/** Contexts added before game instance fully initialized cannot apply game settings because not fully initialized game */
static TSet<TSoftObjectPtr<class UInputMappingContext>> contextsBeforeInit;
};

View File

@ -9,7 +9,7 @@
#include "LevelSequence.h"
#include "LevelSequencePlayer.h"
#include "CustomGameInstanceBase.h"
#include "CustomPlayerController.h"
#include "MainGameModeBase.h"
#include "PlayerBase.h"
#include "Widgets/CutsceneSkipWidget.h"
@ -19,14 +19,8 @@
UCutsceneManager::UCutsceneManager()
{
static ConstructorHelpers::FObjectFinder<UInputMappingContext> asset{ TEXT("/Script/EnhancedInput.InputMappingContext'/Game/Input/IMC_Cutscene.IMC_Cutscene'") };
_inputContext = asset.Object;
if(auto world = GetWorld())
{
if(auto GI = Cast<UCustomGameInstanceBase>(world->GetGameInstance()))
{
GI->inputContexts.Add(_inputContext);
}
}
context = asset.Object;
ACustomPlayerController::AppendInputContext(context);
}
void UCutsceneManager::EnqueueSequence(ULevelSequence* sequence, FCutsceneEndCallback endCallback)
@ -34,106 +28,82 @@ void UCutsceneManager::EnqueueSequence(ULevelSequence* sequence, FCutsceneEndCal
if(!sequence)
return;
FScopeLock lock1(&_sequencesLock);
FScopeLock lock2(&_callbacksLock);
FScopeLock lock1(&sequencesLock);
FScopeLock lock2(&callbacksLock);
if(_endCallbacks.IsEmpty()) // most first sequence, so widgets and binds don't exist
{
if(auto PC = UGameplayStatics::GetPlayerController(GetWorld(), 0))
{
_lastPlayer = Cast<APlayerBase>(PC->GetPawn());
if(_lastPlayer)
{
_lastPlayer->LockPlayer({ .walk = true, .jump = true, .run = true, .interaction = true, .camera = true });
OnFirstCutsceneInit();
auto& mapping = _inputContext.LoadSynchronous()->GetMapping(0);
int32 handler1 = _lastPlayer->inputComponent->BindAction(mapping.Action, ETriggerEvent::Started, this, &UCutsceneManager::OnInputHold).GetHandle();
int32 handler2 = _lastPlayer->inputComponent->BindAction(mapping.Action, ETriggerEvent::Completed, this, &UCutsceneManager::OnInputUnhold).GetHandle();
_handlers = { handler1, handler2 };
if(auto WM = AMainGameModeBase::GetWidgetsManager())
{
WM->HideWidgets();
static FSkipCutsceneDelegate skipCutsceneDelegate;
if(!skipCutsceneDelegate.IsBound())
skipCutsceneDelegate.BindDynamic(this, &UCutsceneManager::SkipSequence);
WM->EnableCutsceneWidget(skipCutsceneDelegate);
}
}
}
}
_nextSequences.Enqueue(sequence);
_endCallbacks.Enqueue(endCallback);
nextSequences.Enqueue(sequence);
endCallbacks.Enqueue(endCallback);
PlayNextSequence();
}
void UCutsceneManager::PlayNextSequence()
{
if(_sequencePlayer)
if(sequencePlayer)
{
if(_sequencePlayer->IsPlaying())
if(sequencePlayer->IsPlaying())
return;
else
_sequencePlayer->MarkAsGarbage();
sequencePlayer->MarkAsGarbage();
}
FScopeLock lock(&_sequencesLock);
FScopeLock lock(&sequencesLock);
ULevelSequence* sequence;
_nextSequences.Dequeue(sequence);
_sequencePlayer = ULevelSequencePlayer::CreateLevelSequencePlayer(GetWorld(), sequence, FMovieSceneSequencePlaybackSettings{}, _sequencePlayerActor);
_sequencePlayer->OnStop.AddDynamic(this, &UCutsceneManager::OnSequenceEnd);
_sequencePlayer->Play();
nextSequences.Dequeue(sequence);
sequencePlayer = ULevelSequencePlayer::CreateLevelSequencePlayer(GetWorld(), sequence, FMovieSceneSequencePlaybackSettings{}, sequencePlayerActor);
sequencePlayer->OnStop.AddDynamic(this, &UCutsceneManager::OnSequenceEnd);
sequencePlayer->Play();
}
void UCutsceneManager::SkipSequence()
{
if(!_sequencePlayer || !_sequencePlayer->IsPlaying())
if(!sequencePlayer || !sequencePlayer->IsPlaying())
return;
_lastlySkipped = true;
_sequencePlayer->GoToEndAndStop();
lastlySkipped = true;
sequencePlayer->GoToEndAndStop();
}
void UCutsceneManager::ClearQueue()
{
FScopeLock lock1(&_sequencesLock);
FScopeLock lock2(&_callbacksLock);
if(!_nextSequences.IsEmpty())
_nextSequences.Empty();
if(!_endCallbacks.IsEmpty())
_endCallbacks.Empty();
FScopeLock lock1(&sequencesLock);
FScopeLock lock2(&callbacksLock);
if(!nextSequences.IsEmpty())
nextSequences.Empty();
if(!endCallbacks.IsEmpty())
endCallbacks.Empty();
}
void UCutsceneManager::LockCallback(bool lock)
{
_lockCallback = lock;
lockCallback = lock;
}
void UCutsceneManager::OnSequenceEnd()
{
if(_lockCallback)
if(lockCallback)
return;
_sequencePlayer->MarkAsGarbage();
_sequencePlayer = nullptr;
sequencePlayer->MarkAsGarbage();
sequencePlayer = nullptr;
FScopeLock lock(&_callbacksLock);
FScopeLock lock(&callbacksLock);
FCutsceneEndCallback callback;
_endCallbacks.Dequeue(callback);
endCallbacks.Dequeue(callback);
if(callback.IsBound())
callback.Execute();
if(!_nextSequences.IsEmpty())
if(!nextSequences.IsEmpty())
{
PlayNextSequence();
if(_lastlySkipped)
if(lastlySkipped)
{
_lastlySkipped = false;
if(_holding)
lastlySkipped = false;
if(holding)
{
OnInputHold();
}
@ -147,36 +117,63 @@ void UCutsceneManager::OnSequenceEnd()
WM->ShowWidgets();
}
if(_lastPlayer)
if(lastPlayer)
{
_lastPlayer->UnlockPlayer({ .walk = true, .jump = true, .run = true, .interaction = true, .camera = true });
lastPlayer->UnlockPlayer({ .walk = true, .jump = true, .run = true, .interaction = true, .camera = true });
_lastPlayer->inputComponent->RemoveBindingByHandle(_handlers.Key);
_lastPlayer->inputComponent->RemoveBindingByHandle(_handlers.Value);
auto input = ACustomPlayerController::GetInput();
input->RemoveBindingByHandle(handlers.Key);
input->RemoveBindingByHandle(handlers.Value);
_lastPlayer = nullptr;
lastPlayer = nullptr;
}
_lastlySkipped = false;
_holding = false;
lastlySkipped = false;
holding = false;
}
void UCutsceneManager::OnFirstCutsceneInit() // most first sequence, so widgets and binds don't exist
{
if(!endCallbacks.IsEmpty())
return;
if(auto PC = UGameplayStatics::GetPlayerController(GetWorld(), 0))
{
lastPlayer = Cast<APlayerBase>(PC->GetPawn());
if(lastPlayer)
{
lastPlayer->LockPlayer({ .walk = true, .jump = true, .run = true, .interaction = true, .camera = true });
auto& mapping = context.LoadSynchronous()->GetMapping(0);
auto input = ACustomPlayerController::GetInput();
int32 handler1 = input->BindAction(mapping.Action, ETriggerEvent::Started, this, &UCutsceneManager::OnInputHold).GetHandle();
int32 handler2 = input->BindAction(mapping.Action, ETriggerEvent::Completed, this, &UCutsceneManager::OnInputUnhold).GetHandle();
handlers = { handler1, handler2 };
if(auto WM = AMainGameModeBase::GetWidgetsManager())
{
WM->HideWidgets();
static FSkipCutsceneDelegate skipCutsceneDelegate;
if(!skipCutsceneDelegate.IsBound())
skipCutsceneDelegate.BindDynamic(this, &UCutsceneManager::SkipSequence);
WM->EnableCutsceneWidget(skipCutsceneDelegate);
}
}
}
}
void UCutsceneManager::OnInputHold()
{
_holding = true;
holding = true;
if(auto WM = AMainGameModeBase::GetWidgetsManager())
{
WM->AnimateCutsceneWidget(EInputAnimatedWidgetAnimation::Hold);
}
}
void UCutsceneManager::OnInputUnhold()
{
_holding = false;
holding = false;
if(auto WM = AMainGameModeBase::GetWidgetsManager())
{
WM->AnimateCutsceneWidget(EInputAnimatedWidgetAnimation::Unhold);
}
}

View File

@ -32,22 +32,23 @@ private:
void PlayNextSequence();
UFUNCTION()
void OnSequenceEnd();
void OnFirstCutsceneInit();
void OnInputHold();
void OnInputUnhold();
class ULevelSequencePlayer* _sequencePlayer = nullptr;
class ALevelSequenceActor* _sequencePlayerActor = nullptr;
TQueue<class ULevelSequence*> _nextSequences;
FCriticalSection _sequencesLock;
TQueue<FCutsceneEndCallback> _endCallbacks;
FCriticalSection _callbacksLock;
class ULevelSequencePlayer* sequencePlayer = nullptr;
class ALevelSequenceActor* sequencePlayerActor = nullptr;
TQueue<class ULevelSequence*> nextSequences;
FCriticalSection sequencesLock;
TQueue<FCutsceneEndCallback> endCallbacks;
FCriticalSection callbacksLock;
class APlayerBase* _lastPlayer = nullptr;
TSoftObjectPtr<class UInputMappingContext> _inputContext;
TPair<int32, int32> _handlers;
class APlayerBase* lastPlayer = nullptr;
TSoftObjectPtr<class UInputMappingContext> context;
TPair<int32, int32> handlers;
bool _lockCallback = false;
bool _lastlySkipped = false;
bool _holding = false;
bool lockCallback = false;
bool lastlySkipped = false;
bool holding = false;
};

View File

@ -1,6 +1,5 @@
// Oleg Petruny proprietary.
#include "DialogueManager.h"
#include "Components/AudioComponent.h"
@ -11,7 +10,7 @@
#include "Kismet/KismetMathLibrary.h"
#include "Sound/SoundWave.h"
#include "CustomGameInstanceBase.h"
#include "CustomPlayerController.h"
#include "MainGameModeBase.h"
#include "PlayerBase.h"
#include "Widgets/DialogueSkipWidget.h"
@ -21,14 +20,8 @@
UDialogueManager::UDialogueManager()
{
static ConstructorHelpers::FObjectFinder<UInputMappingContext> asset{ TEXT("/Script/EnhancedInput.InputMappingContext'/Game/Input/IMC_Dialogue.IMC_Dialogue'") };
_inputContext = asset.Object;
if(auto world = GetWorld())
{
if(auto GI = Cast<UCustomGameInstanceBase>(world->GetGameInstance()))
{
GI->inputContexts.Add(_inputContext);
}
}
context = asset.Object;
ACustomPlayerController::AppendInputContext(context);
}
void UDialogueManager::PlayDialogue(FDialogueEnqueProperties properties, FDialogueEndCallback endCallback)
@ -49,32 +42,28 @@ void UDialogueManager::PlayDialogue(FDialogueEnqueProperties properties, FDialog
FTimerHandle timer;
int32 timerId;
_timersLock.Lock();
timerId = _timers.Num();
_timers.Add(timer);
_timersLock.Unlock();
timersLock.Lock();
timerId = timers.Num();
timers.Add(timer);
timersLock.Unlock();
UGameplayStatics::PlaySound2D(this, row->wave.LoadSynchronous());
if(auto WM = AMainGameModeBase::GetWidgetsManager())
{
WM->ShowDialogueWidget(*row);
}
auto func = [properties = properties,
timerId = timerId,
_timersLock = &_timersLock,
_timers = &_timers,
endCallback = endCallback]()
auto func = [properties,
timerId,
timersLock = &timersLock,
timers = &timers,
endCallback]()
{
FDialogueRow* row = reinterpret_cast<FDialogueRow*>(properties.dialogue.LoadSynchronous()->FindRowUnchecked(properties.rowName));
if(auto WM = AMainGameModeBase::GetWidgetsManager())
{
WM->HideDialogueWidget(*row);
}
_timersLock->Lock();
_timers->RemoveAt(timerId);
_timersLock->Unlock();
timersLock->Lock();
timers->RemoveAt(timerId);
timersLock->Unlock();
endCallback.Execute();
};
@ -88,51 +77,30 @@ void UDialogueManager::EnqueDialogue(FDialogueEnqueProperties properties, FDialo
if(!properties.dialogue.LoadSynchronous())
return;
FScopeLock lock1(&_dialoguesLock);
FScopeLock lock2(&_callbacksLock);
FScopeLock lock1(&dialoguesLock);
FScopeLock lock2(&callbacksLock);
if(_endCallbacks.IsEmpty()) // most first dialogue, so widgets and binds don't exist
{
if(auto PC = UGameplayStatics::GetPlayerController(GetWorld(), 0))
{
_lastPlayer = Cast<APlayerBase>(PC->GetPawn());
if(_lastPlayer)
{
auto& mapping = _inputContext.LoadSynchronous()->GetMapping(0);
_inputHandler = _lastPlayer->inputComponent->BindAction(mapping.Action, ETriggerEvent::Triggered, this, &UDialogueManager::OnInputPress).GetHandle();
OnFirstDialogueInit();
if(auto WM = AMainGameModeBase::GetWidgetsManager())
{
static FDialogueSkipDelegate skipDialogueDelegate;
if(!skipDialogueDelegate.IsBound())
{
skipDialogueDelegate.BindDynamic(this, &UDialogueManager::SkipDialogue);
WM->SetInputDialogueWidget(mapping.Key, mapping.Action->ActionDescription, skipDialogueDelegate);
}
}
}
}
}
_nextDialogues.Enqueue(properties);
_endCallbacks.Enqueue(endCallback);
nextDialogues.Enqueue(properties);
endCallbacks.Enqueue(endCallback);
PlayNextDialogue();
}
void UDialogueManager::PlayNextDialogue()
{
_dialoguesLock.Lock();
dialoguesLock.Lock();
auto properties = _nextDialogues.Peek();
auto properties = nextDialogues.Peek();
TArray<FName> rows = properties->dialogue.LoadSynchronous()->GetRowNames();
if(rows.Num() == 0)
{
FDialogueEndCallback callback;
if(_endCallbacks.Dequeue(callback))
if(endCallbacks.Dequeue(callback))
callback.Execute();
_nextDialogues.Pop();
nextDialogues.Pop();
return;
}
@ -146,22 +114,20 @@ void UDialogueManager::PlayNextDialogue()
if(!row)
{
FDialogueEndCallback callback;
_endCallbacks.Dequeue(callback);
endCallbacks.Dequeue(callback);
callback.ExecuteIfBound();
_nextDialogues.Pop();
nextDialogues.Pop();
return;
}
if(properties->playMode == EDialoguePlayMode::Sequential)
if(properties->playMode == EDialoguePlayMode::Sequential
&& !properties->rowName.ToString().IsNumeric())
{
if(!properties->rowName.ToString().IsNumeric())
{
_nextDialogues.Pop();
FDialogueEndCallback callback;
if(_endCallbacks.Dequeue(callback))
callback.Execute();
return;
}
nextDialogues.Pop();
FDialogueEndCallback callback;
if(endCallbacks.Dequeue(callback))
callback.Execute();
return;
}
if(row->wave.LoadSynchronous())
@ -170,9 +136,7 @@ void UDialogueManager::PlayNextDialogue()
leadDialogueAudio = nullptr;
if(auto WM = AMainGameModeBase::GetWidgetsManager())
{
WM->ShowDialogueWidget(*row);
}
FTimerHandle timer;
int32 timerId;
@ -180,27 +144,53 @@ void UDialogueManager::PlayNextDialogue()
const float duration = row->wave ? row->wave->GetDuration() : row->duration;
GetWorld()->GetTimerManager().SetTimer(timer, [&]() { OnDialogueEnd(); }, duration, false);
_timersLock.Lock();
timerId = _timers.Num();
_timers.Add(timer);
_timersLock.Unlock();
timersLock.Lock();
timerId = timers.Num();
timers.Add(timer);
timersLock.Unlock();
leadDialogueTimerId = timerId;
leadDialogueProperties = properties;
_dialoguesLock.Unlock();
dialoguesLock.Unlock();
}
void UDialogueManager::OnFirstDialogueInit() // most first dialogue, so widgets and binds don't exist
{
if(!endCallbacks.IsEmpty())
return;
if(auto PC = UGameplayStatics::GetPlayerController(GetWorld(), 0))
{
lastPlayer = Cast<APlayerBase>(PC->GetPawn());
if(lastPlayer)
{
auto& mapping = context.LoadSynchronous()->GetMapping(0);
inputHandler = ACustomPlayerController::GetInput()->BindAction(mapping.Action, ETriggerEvent::Triggered, this, &UDialogueManager::OnInputPress).GetHandle();
if(auto WM = AMainGameModeBase::GetWidgetsManager())
{
static FDialogueSkipDelegate skipDialogueDelegate;
if(!skipDialogueDelegate.IsBound())
{
skipDialogueDelegate.BindDynamic(this, &UDialogueManager::SkipDialogue);
WM->SetInputDialogueWidget(mapping.Key, mapping.Action->ActionDescription, skipDialogueDelegate);
}
}
}
}
}
void UDialogueManager::SkipDialogue()
{
if(_timers.Num() == 0 || leadDialogueTimerId < 0)
if(timers.Num() == 0 || leadDialogueTimerId < 0)
return;
_timersLock.Lock();
GetWorld()->GetTimerManager().ClearTimer(_timers[leadDialogueTimerId]);
_timers.RemoveAt(leadDialogueTimerId);
timersLock.Lock();
GetWorld()->GetTimerManager().ClearTimer(timers[leadDialogueTimerId]);
timers.RemoveAt(leadDialogueTimerId);
leadDialogueTimerId = -1;
_timersLock.Unlock();
timersLock.Unlock();
if(leadDialogueAudio)
leadDialogueAudio->Stop();
@ -210,25 +200,25 @@ void UDialogueManager::SkipDialogue()
void UDialogueManager::ClearQueue()
{
if(!_nextDialogues.IsEmpty())
_nextDialogues.Empty();
if(!_endCallbacks.IsEmpty())
_endCallbacks.Empty();
if(!nextDialogues.IsEmpty())
nextDialogues.Empty();
if(!endCallbacks.IsEmpty())
endCallbacks.Empty();
}
void UDialogueManager::LockCallback(bool lock)
{
_lockCallback = lock;
lockCallback = lock;
}
void UDialogueManager::BeginDestroy()
{
if(auto world = GetWorld())
{
_timersLock.Lock();
for(auto& timer : _timers)
timersLock.Lock();
for(auto& timer : timers)
world->GetTimerManager().ClearTimer(timer);
_timersLock.Unlock();
timersLock.Unlock();
}
UObject::BeginDestroy();
@ -236,41 +226,35 @@ void UDialogueManager::BeginDestroy()
void UDialogueManager::OnDialogueEnd()
{
_dialoguesLock.Lock();
dialoguesLock.Lock();
FDialogueRow* row = reinterpret_cast<FDialogueRow*>(leadDialogueProperties->dialogue.LoadSynchronous()->FindRowUnchecked(leadDialogueProperties->rowName));
if(auto WM = AMainGameModeBase::GetWidgetsManager())
{
WM->HideDialogueWidget(*row);
}
if(leadDialogueProperties->playMode == EDialoguePlayMode::Sequential)
if(leadDialogueProperties->playMode == EDialoguePlayMode::Sequential
&& leadDialogueProperties->rowName.ToString().IsNumeric())
{
if(leadDialogueProperties->rowName.ToString().IsNumeric())
{
leadDialogueProperties->rowName = FName(FString::FromInt(FCString::Atoi(*(leadDialogueProperties->rowName.ToString())) + 1));
}
leadDialogueProperties->rowName = FName(FString::FromInt(FCString::Atoi(*(leadDialogueProperties->rowName.ToString())) + 1));
}
_dialoguesLock.Unlock();
dialoguesLock.Unlock();
if(!_endCallbacks.IsEmpty())
if(!endCallbacks.IsEmpty())
PlayNextDialogue();
_dialoguesLock.Lock();
if(_endCallbacks.IsEmpty() && _lastPlayer)
dialoguesLock.Lock();
if(endCallbacks.IsEmpty() && lastPlayer)
{
_lastPlayer->inputComponent->RemoveBindingByHandle(_inputHandler);
_lastPlayer = nullptr;
ACustomPlayerController::GetInput()->RemoveBindingByHandle(inputHandler);
lastPlayer = nullptr;
}
_dialoguesLock.Unlock();
dialoguesLock.Unlock();
}
void UDialogueManager::OnInputPress()
{
if(auto WM = AMainGameModeBase::GetWidgetsManager())
{
WM->AnimateDialogueWidget(EInputAnimatedWidgetAnimation::Click);
}
}

View File

@ -68,7 +68,7 @@ class UDialogueManager : public UObject
public:
UDialogueManager();
// Ignores play mode and force pushing dialogue
/** Ignores play mode and force pushing dialogue */
UFUNCTION(BlueprintCallable)
void PlayDialogue(FDialogueEnqueProperties properties, FDialogueEndCallback endCallback);
@ -89,26 +89,28 @@ protected:
private:
void PlayNextDialogue();
void OnFirstDialogueInit();
UFUNCTION()
void OnDialogueEnd();
void OnInputPress();
TQueue<FDialogueEnqueProperties> _nextDialogues;
FCriticalSection _dialoguesLock;
TQueue<FDialogueEndCallback> _endCallbacks;
FCriticalSection _callbacksLock;
TQueue<FDialogueEnqueProperties> nextDialogues;
FCriticalSection dialoguesLock;
TQueue<FDialogueEndCallback> endCallbacks;
FCriticalSection callbacksLock;
TArray<FTimerHandle> _timers;
FCriticalSection _timersLock;
TArray<FTimerHandle> timers;
FCriticalSection timersLock;
int32 leadDialogueTimerId = -1;
FDialogueEnqueProperties* leadDialogueProperties;
class UAudioComponent* leadDialogueAudio;
class APlayerBase* _lastPlayer = nullptr;
TSoftObjectPtr<class UInputMappingContext> _inputContext;
int32 _inputHandler;
class APlayerBase* lastPlayer = nullptr;
TSoftObjectPtr<class UInputMappingContext> context;
int32 inputHandler;
bool _lockCallback = false;
bool lockCallback = false;
};

View File

@ -1,9 +1,9 @@
// Oleg Petruny proprietary.
#include "InCameraInteractableActivator.h"
#include "CommonFunctions.h"
#include "Interactable/Interactable.h"
#include "InteractableScreenCapturer.h"
UInCameraInteractableActivator::UInCameraInteractableActivator(const FObjectInitializer& ObjectInitializer)
@ -20,10 +20,10 @@ UInCameraInteractableActivator::UInCameraInteractableActivator(const FObjectInit
activatorType = EActivatorType::Saw;
scanDistance = 7000;
_capturer = CreateDefaultSubobject<UInteractableScreenCapturer>(TEXT("UInCameraInteractableActivator_UInteractableScreenCapturer"));
_capturer->interactableInScreenDelegate.BindUObject(this, &UInCameraInteractableActivator::NewSeenInteractable_Implementation);
_capturer->SetupAttachment(this);
_capturer->scanDistance = scanDistance;
capturer = CreateDefaultSubobject<UInteractableScreenCapturer>(TEXT("UInCameraInteractableActivator_UInteractableScreenCapturer"));
capturer->interactableInScreenDelegate.BindUObject(this, &UInCameraInteractableActivator::NewSeenInteractable);
capturer->SetupAttachment(this);
capturer->scanDistance = scanDistance;
PrimaryComponentTick.bCanEverTick = true;
PrimaryComponentTick.bStartWithTickEnabled = false;
@ -31,27 +31,28 @@ UInCameraInteractableActivator::UInCameraInteractableActivator(const FObjectInit
void UInCameraInteractableActivator::OnRegister()
{
UInteractableActivator::OnRegister();
_capturer->RegisterComponent();
_capturer->Activate();
Super::OnRegister();
capturer->RegisterComponent();
capturer->Activate();
}
void UInCameraInteractableActivator::NewSeenInteractable_Implementation(AInteractable* interactable)
void UInCameraInteractableActivator::NewSeenInteractable(AInteractable* interactable)
{
_interactablesToActivate.Enqueue(interactable);
interactablesToActivate.Enqueue(interactable);
SetComponentTickEnabled(true);
}
void UInCameraInteractableActivator::Scan_Implementation()
{
SetComponentTickEnabled(false);
while(!_interactablesToActivate.IsEmpty())
while(!interactablesToActivate.IsEmpty())
{
AInteractable* interactable;
_interactablesToActivate.Dequeue(interactable);
interactablesToActivate.Dequeue(interactable);
if(interactableActivatedDelegate.IsBound())
{
interactableActivatedDelegate.Execute(interactable, activatorType);
OnNewSeenInteractable(interactable);
}
}
}

View File

@ -2,13 +2,14 @@
#pragma once
#include "CoreMinimal.h"
#include "InteractableActivator.h"
#include "InCameraInteractableActivator.generated.h"
UCLASS(Blueprintable, BlueprintType)
/**
* Activates interactable only once if is in a camera view
*/
UCLASS(Blueprintable, BlueprintType, ClassGroup = InteractableActivator, meta = (BlueprintSpawnableComponent, ShortTooltip = "Activates interactable only once if is in a camera view"), MinimalAPI)
class UInCameraInteractableActivator : public UInteractableActivator
{
GENERATED_BODY()
@ -19,14 +20,30 @@ public:
protected:
virtual void OnRegister() override;
virtual bool AutoInstantiateInPlayer() override { return true; }
/**
* Scan is performed independently by _capturer member object.
* This implementation just activates interactables in the game thread.
*/
virtual void Scan_Implementation() override;
UFUNCTION(BlueprintNativeEvent, BlueprintCallable)
LOST_EDGE_API void NewSeenInteractable(class AInteractable* interactable);
virtual void NewSeenInteractable_Implementation(class AInteractable* interactable);
/**
* Thread safe enques interactable for activation in the next game tick
* @param interactable .. interactable to activate
*/
UFUNCTION(BlueprintCallable)
void NewSeenInteractable(class AInteractable* interactable);
/**
* Called after interactable activation
* @param interactable .. interactable activated
*/
UFUNCTION(BlueprintImplementableEvent)
void OnNewSeenInteractable(class AInteractable* interactable);
private:
UPROPERTY()
class UInteractableScreenCapturer* _capturer;
TQueue<class AInteractable*> _interactablesToActivate;
class UInteractableScreenCapturer* capturer;
TQueue<class AInteractable*> interactablesToActivate;
};

View File

@ -1,19 +1,23 @@
// Oleg Petruny proprietary.
#include "InteractableActivator.h"
#include "Engine/CollisionProfile.h"
#include "Interactable/Interactable.h"
#include "PlayerBase.h"
UInteractableActivator::UInteractableActivator(const FObjectInitializer& ObjectInitializer)
: USceneComponent(ObjectInitializer)
{
if(HasAnyFlags(RF_ClassDefaultObject | RF_ArchetypeObject))
if(GetClass() != UInteractableActivator::StaticClass())
{
return;
AInteractable::AppendActivatorClass(GetClass());
}
if(HasAnyFlags(RF_ClassDefaultObject | RF_ArchetypeObject))
return;
world = GetWorld();
player = Cast<APlayerBase>(GetOwner());

View File

@ -3,7 +3,6 @@
#pragma once
#include "Components/SceneComponent.h"
#include "CoreMinimal.h"
#include "Interactable/Interactable.h"
@ -12,15 +11,22 @@
DECLARE_DELEGATE_TwoParams(FInteractableActivated, AInteractable*, EActivatorType);
DECLARE_DELEGATE_TwoParams(FInteractableDeactivated, AInteractable*, EActivatorType);
UCLASS(Abstract, Blueprintable, BlueprintType)
/**
* Activates interactable based on type
*/
UCLASS(Abstract, Blueprintable, BlueprintType, ClassGroup = InteractableActivator, meta = (ShortTooltip = "Activates interactable based on type"), MinimalAPI)
class UInteractableActivator : public USceneComponent
{
GENERATED_BODY()
public:
/** Append itself to CustomGameInstance modificators registry */
UInteractableActivator(const FObjectInitializer& ObjectInitializer);
virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
virtual bool AutoInstantiateInPlayer() { return false; }
/** Resets activator state forcing to rescan */
UFUNCTION(BlueprintNativeEvent, BlueprintCallable)
LOST_EDGE_API void Rescan();
virtual void Rescan_Implementation() {}
@ -29,6 +35,7 @@ public:
FInteractableActivated interactableDeactivatedDelegate;
protected:
/** Activator scan function in game thread tick */
UFUNCTION(BlueprintNativeEvent, BlueprintCallable)
LOST_EDGE_API void Scan();
virtual void Scan_Implementation() PURE_VIRTUAL(UInteractableActivator::Scan_Implementation, );

View File

@ -1,6 +1,5 @@
// Oleg Petruny proprietary.
#include "InteractableScreenCapturer.h"
#include "Engine/Texture.h"
@ -16,9 +15,12 @@
#include "Interactable/Interactable.h"
#include "InteractableScreenCapturerBitMapCS.h"
constexpr float tickInterval = 1.0f / 5;
constexpr float textureWidth = 1280 / 2;
constexpr float textureHeight = 720 / 2;
namespace
{
constexpr float tickInterval = 1.0f / 5;
constexpr float textureWidth = 1280 / 2;
constexpr float textureHeight = 720 / 2;
}
UInteractableScreenCapturer::UInteractableScreenCapturer(const FObjectInitializer& ObjectInitializer)
: USceneCaptureComponent2D(ObjectInitializer)
@ -85,7 +87,7 @@ void UInteractableScreenCapturer::TickComponent(float DeltaTime, enum ELevelTick
{
USceneCaptureComponent2D::TickComponent(DeltaTime, TickType, ThisTickFunction);
CaptureScene();
GetCameraView(DeltaTime, _view);
GetCameraView(DeltaTime, view);
Process();
}
break;
@ -104,7 +106,7 @@ void UInteractableScreenCapturer::Process()
[
capture = TextureTarget->GetResource()->TextureRHI,
world = GetWorld(),
view = _view,
view = view,
//output = _output->GetResource()->TextureRHI,
this
]
@ -262,6 +264,7 @@ void UInteractableScreenCapturer::Process()
AsyncTask(ENamedThreads::GameThread, [=, this]()
{
interactableInScreenDelegate.Execute(interactable);
OnInteractableInScreen(interactable);
});
}
}

View File

@ -9,7 +9,10 @@
DECLARE_DELEGATE_OneParam(FInteractableInScreen, class AInteractable*);
UCLASS(hidecategories = (Collision, Object, Physics, SceneComponent), ClassGroup = Rendering, editinlinenew, meta = (BlueprintSpawnableComponent), MinimalAPI)
/**
* Notifies only once about interactable is in a camera view
*/
UCLASS(BlueprintType, hidecategories = (Collision, Object, Physics, SceneComponent), ClassGroup = Rendering, editinlinenew, meta = (BlueprintSpawnableComponent, ShortTooltip = "Notifies only once about interactable is in a camera view"), MinimalAPI)
class UInteractableScreenCapturer : public USceneCaptureComponent2D
{
GENERATED_BODY()
@ -19,15 +22,18 @@ public:
virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
FInteractableInScreen interactableInScreenDelegate;
UFUNCTION(BlueprintImplementableEvent)
void OnInteractableInScreen(class AInteractable* interactable);
UPROPERTY(EditAnywhere)
float scanDistance = 7000;
protected:
/** Enques render thread task to find obect on screen */
void Process();
private:
FMinimalViewInfo _view;
FMinimalViewInfo view; //!< Camera view cache
TSet<class AInteractable*> _sawInteractables;
//class UTextureRenderTarget2D* _output;
};

View File

@ -1,6 +1,5 @@
// Oleg Petruny proprietary.
#include "RaycastInteractableActivator.h"
#include "DrawDebugHelpers.h"
@ -14,12 +13,12 @@ URaycastInteractableActivator::URaycastInteractableActivator(const FObjectInitia
void URaycastInteractableActivator::Rescan_Implementation()
{
_last = nullptr;
lastInteractable = nullptr;
}
void URaycastInteractableActivator::Scan_Implementation()
{
FHitResult result{};
FHitResult result;
FVector startLocation = GetComponentLocation();
FVector endLocation = startLocation + (GetComponentRotation().Vector() * scanDistance);
@ -32,17 +31,17 @@ void URaycastInteractableActivator::Scan_Implementation()
if(result.bBlockingHit)
{
if(_last != result.GetActor())
if(lastInteractable != result.GetActor())
{
if(_last)
if(lastInteractable)
{
interactableDeactivatedDelegate.Execute(_last, activatorType);
_last = nullptr;
interactableDeactivatedDelegate.Execute(lastInteractable, activatorType);
lastInteractable = nullptr;
}
_activated = true;
activated = true;
if(auto interactable = Cast<AInteractable>(result.GetActor()))
{
_last = interactable;
lastInteractable = interactable;
if(interactableActivatedDelegate.IsBound())
{
interactableActivatedDelegate.Execute(interactable, activatorType);
@ -53,21 +52,21 @@ void URaycastInteractableActivator::Scan_Implementation()
#ifdef INTERACTABLE_ACTIVATOR_DEBUG
DrawDebugLine(GetWorld(), startLocation, endLocation, FColor::Green, false, PrimaryComponentTick.TickInterval, 0, 0.1f);
#endif // INTERACTABLE_ACTIVATOR_DEBUG
}
}
else
{
if(_activated)
if(activated)
{
if(interactableDeactivatedDelegate.IsBound())
{
interactableDeactivatedDelegate.Execute(_last, activatorType);
interactableDeactivatedDelegate.Execute(lastInteractable, activatorType);
}
_activated = false;
_last = nullptr;
activated = false;
lastInteractable = nullptr;
}
#ifdef INTERACTABLE_ACTIVATOR_DEBUG
DrawDebugLine(GetWorld(), startLocation, endLocation, FColor::Red, false, PrimaryComponentTick.TickInterval, 10, 0.1f);
#endif // INTERACTABLE_ACTIVATOR_DEBUG
}
}
}
}

View File

@ -2,12 +2,14 @@
#pragma once
#include "CoreMinimal.h"
#include "InteractableActivator.h"
#include "RaycastInteractableActivator.generated.h"
UCLASS(Blueprintable, BlueprintType)
/**
* Activates interactable with a single raycast from a camera center
*/
UCLASS(Blueprintable, BlueprintType, ClassGroup = InteractableActivator, meta = (BlueprintSpawnableComponent, ShortTooltip = "Activates interactable with a single raycast from a camera center"), MinimalAPI)
class URaycastInteractableActivator : public UInteractableActivator
{
GENERATED_BODY()
@ -15,12 +17,14 @@ class URaycastInteractableActivator : public UInteractableActivator
public:
URaycastInteractableActivator(const FObjectInitializer& ObjectInitializer);
virtual bool AutoInstantiateInPlayer() override { return true; }
virtual void Rescan_Implementation() override;
protected:
virtual void Scan_Implementation() override;
private:
class AInteractable* _last = nullptr;
bool _activated = false;
class AInteractable* lastInteractable = nullptr;
bool activated = false;
};

View File

@ -1,15 +1,38 @@
// Oleg Petruny proprietary.
#include "Interactable.h"
#include "Kismet/GameplayStatics.h"
#include "Activators/InteractableActivator.h"
#include "CustomPlayerController.h"
#include "MainGameModeBase.h"
#include "Modificators/InteractableModificator.h"
#include "PlayerBase.h"
#include "Widgets/WidgetsManager.h"
TSet<TSubclassOf<class UInteractableActivator>> AInteractable::interactionActivators = {};
TSet<TSubclassOf<class UInteractableModificator>> AInteractable::interactionModificators = {};
void AInteractable::AppendActivatorClass(TSubclassOf<class UInteractableActivator> activator)
{
if(interactionActivators.Contains(activator))
return;
interactionActivators.Add(activator);
}
void AInteractable::AppendModificatorClass(TSubclassOf<class UInteractableModificator> modificator)
{
if(interactionModificators.Contains(modificator))
return;
interactionModificators.Add(modificator);
auto IC = modificator.GetDefaultObject()->GetMappingContext();
ACustomPlayerController::AppendInputContext(IC);
}
int32 AInteractable::GetActivatedFlags()
{
return activated;
@ -86,13 +109,13 @@ void AInteractable::EndPlay(const EEndPlayReason::Type EndPlayReason)
if(activated)
{
activationLockers.Empty();
_Deactivate(static_cast<EActivatorType>(activated));
Deactivate(static_cast<EActivatorType>(activated));
}
Super::EndPlay(EndPlayReason);
}
void AInteractable::_Activate(EActivatorType type)
void AInteractable::Activate(EActivatorType type)
{
#ifdef INTERACTABLE_DEBUG
GEngine->AddOnScreenDebugMessage(30 + (int)type, 5.0f, FColor::Cyan, TEXT("Player activate: ") + this->GetName()
@ -113,7 +136,7 @@ void AInteractable::_Activate(EActivatorType type)
if(static_cast<uint8>(modificator.Value->GetActivatorTypes()) & static_cast<uint8>(type))
{
WM->ShowInteractionHints(modificator.Value);
modificator.Value->Bind(player->inputComponent);
modificator.Value->Bind(ACustomPlayerController::GetInput());
}
}
}
@ -124,10 +147,10 @@ void AInteractable::_Activate(EActivatorType type)
collision->SetCustomDepthStencilValue(132);
}
Activate(type);
OnActivate(type);
}
void AInteractable::_Deactivate(EActivatorType type)
void AInteractable::Deactivate(EActivatorType type)
{
#ifdef INTERACTABLE_DEBUG
GEngine->AddOnScreenDebugMessage(30 + (int)type, 5.0f, FColor::Magenta, TEXT("Player deactivate: ") + this->GetName()
@ -159,5 +182,5 @@ void AInteractable::_Deactivate(EActivatorType type)
collision->SetCustomDepthStencilValue(0);
}
Deactivate(type);
OnDeactivate(type);
}

View File

@ -2,15 +2,18 @@
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Interactable.generated.h"
/** Turns on debug of interactable objects */
//#define INTERACTABLE_DEBUG
/** Turns on debug of interactable activator components */
//#define INTERACTABLE_ACTIVATOR_DEBUG
/** Turns on debug of interactable modificator components */
//#define INTERACTABLE_MODIFICATOR_DEBUG
/** Defines activation types */
UENUM(BlueprintType, meta = (Bitflags, UseEnumValuesAsMaskValuesInEditor = "true"))
enum class EActivatorType : uint8
{
@ -27,38 +30,61 @@ enum class EActivatorType : uint8
};
ENUM_CLASS_FLAGS(EActivatorType);
/**
* Object capable of reacting to activators and execute modificators.
* Sets all needed settings as collision layers on begin play.
*/
UCLASS(Blueprintable, BlueprintType, MinimalAPI)
class AInteractable : public AActor
{
GENERATED_BODY()
public:
static void AppendActivatorClass(TSubclassOf<class UInteractableActivator> activator);
static void AppendModificatorClass(TSubclassOf<class UInteractableModificator> modificator);
/** Interactables shared cache */
static TSet<TSubclassOf<class UInteractableActivator>> interactionActivators;
static TSet<TSubclassOf<class UInteractableModificator>> interactionModificators;
public:
/** Returns flags mask of activated types */
int32 GetActivatedFlags();
void _Activate(EActivatorType type);
UFUNCTION(BlueprintNativeEvent, BlueprintCallable)
/** Receives activate signal from activator of specific type and activates all modificators of that type */
UFUNCTION(BlueprintCallable)
void Activate(EActivatorType type);
virtual void Activate_Implementation(EActivatorType type) {}
void _Deactivate(EActivatorType type);
UFUNCTION(BlueprintNativeEvent, BlueprintCallable)
/** Receives deactivate signal from activator of specific type and deactivates all modificators of that type */
UFUNCTION(BlueprintCallable)
void Deactivate(EActivatorType type);
virtual void Deactivate_Implementation(EActivatorType type) {}
/**
* All modificators that requires (de)activation lock for current interactable.
* Used manually by modificators to handle operations which can be continued after physical deactivation.
* Eg. MoveModificator movement while mouse buttons are down even if player don't activating interactable anymore.
*/
TSet<class UInteractableModificator*> activationLockers;
protected:
virtual void BeginPlay() override;
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
UFUNCTION(BlueprintImplementableEvent)
void OnActivate(EActivatorType type);
UFUNCTION(BlueprintImplementableEvent)
void OnDeactivate(EActivatorType type);
/** Mask of active activator types */
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (Bitmask, BitmaskEnum = "EActivatorType"))
int32 activated = 0;
/** Map of modificators to activator types initialized on BeginPlay */
UPROPERTY()
TMap<EActivatorType, class UInteractableModificator*> modificators;
class APlayerBase* player = nullptr;
TArray<UPrimitiveComponent*> collisions;
class APlayerBase* player = nullptr;
};

View File

@ -1,6 +1,5 @@
// Oleg Petruny proprietary.
#include "ActivateInteractableModificator.h"
#include "EnhancedInputComponent.h"

View File

@ -2,14 +2,16 @@
#pragma once
#include "CoreMinimal.h"
#include "InteractableModificator.h"
#include "ActivateInteractableModificator.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FActivateInteractableModificatorActivatedDelegate);
UCLASS(ClassGroup = InteractableModificator, meta = (BlueprintSpawnableComponent), Blueprintable, BlueprintType)
/**
* Basic modificator for type Use
*/
UCLASS(Blueprintable, BlueprintType, ClassGroup = InteractableModificator, meta = (BlueprintSpawnableComponent, ShortTooltip = "Basic modificator for type Use"), MinimalAPI)
class UActivateInteractableModificator : public UInteractableModificator
{
GENERATED_BODY()

View File

@ -1,14 +1,11 @@
// Oleg Petruny proprietary.
#include "EditInteractableModificator.h"
#include "Interactable/Interactable.h"
UEditInteractableModificator::UEditInteractableModificator(const FObjectInitializer& ObjectInitializer)
: UInteractableModificator(ObjectInitializer)
{
activatorTypes |= static_cast<uint8>(EActivatorType::Collide);
}
//UEditInteractableModificator::UEditInteractableModificator(const FObjectInitializer& ObjectInitializer)
// : UInteractableModificator(ObjectInitializer)
//{
// activatorTypes |= static_cast<uint8>(EActivatorType::Collide);
//}

View File

@ -2,17 +2,20 @@
#pragma once
#include "CoreMinimal.h"
#include "InteractableModificator.h"
#include "EditInteractableModificator.generated.h"
//#include "EditInteractableModificator.generated.h"
UCLASS(ClassGroup = InteractableModificator, meta = (BlueprintSpawnableComponent), Blueprintable, BlueprintType)
class UEditInteractableModificator : public UInteractableModificator
{
GENERATED_BODY()
public:
UEditInteractableModificator(const FObjectInitializer& ObjectInitializer);
};
/**
* <WIP>
* Edits object topology on collision
*/
//UCLASS(Blueprintable, BlueprintType, ClassGroup = InteractableModificator, meta = (BlueprintSpawnableComponent, ShortTooltip = "Edits object topology on collision"), MinimalAPI)
//class UEditInteractableModificator : public UInteractableModificator
//{
// GENERATED_BODY()
//
//public:
// UEditInteractableModificator(const FObjectInitializer& ObjectInitializer);
//
//};

View File

@ -1,29 +1,20 @@
// Oleg Petruny proprietary.
#include "InteractableModificator.h"
#include "InputMappingContext.h"
#include "CustomGameInstanceBase.h"
#include "Interactable/Interactable.h"
#include "Widgets/InteractableHintWidget.h"
void UInteractableModificator::OnRegister()
{
UActorComponent::OnRegister();
if(auto world = GetWorld())
{
if(auto GI = Cast<UCustomGameInstanceBase>(world->GetGameInstance()))
{
GI->AppendInteractableModificatorClass(this->GetClass());
}
}
AInteractable::AppendModificatorClass(GetClass());
}
const UInputMappingContext* UInteractableModificator::GetMappingContext() const
const TSoftObjectPtr<class UInputMappingContext> UInteractableModificator::GetMappingContext() const
{
return inputMapping.LoadSynchronous();
return inputMapping;
}
EActivatorType UInteractableModificator::GetActivatorTypes() const

View File

@ -3,29 +3,37 @@
#pragma once
#include "Components/ActorComponent.h"
#include "CoreMinimal.h"
#include "InteractableModificator.generated.h"
enum class EActivatorType : uint8;
UCLASS(Abstract, Blueprintable, BlueprintType)
/**
* Do something on activation by specified activator types
*/
UCLASS(Abstract, Blueprintable, BlueprintType, ClassGroup = InteractableModificator, meta = (BlueprintSpawnableComponent, ShortTooltip = "Do something on activation by specified activator types"), MinimalAPI)
class UInteractableModificator : public UActorComponent
{
GENERATED_BODY()
public:
void OnRegister() override;
/** Append itself to CustomGameInstance modificators registry */
virtual void OnRegister() override;
/** Returns input mappings assigned in constructor */
UFUNCTION(BlueprintCallable)
const class UInputMappingContext* GetMappingContext() const;
const TSoftObjectPtr<class UInputMappingContext> GetMappingContext() const;
/** Filters activation type in interractable */
UFUNCTION(BlueprintCallable)
EActivatorType GetActivatorTypes() const;
/** Called from interactable on activation (mostly used to bind input context to internal modificator functions) */
UFUNCTION(BlueprintNativeEvent, BlueprintCallable)
void Bind(class UEnhancedInputComponent* input);
virtual void Bind_Implementation(class UEnhancedInputComponent* input) {}
/** Called from interactable on deactivation (mostly used to unbind input context from internal functions) */
UFUNCTION(BlueprintNativeEvent, BlueprintCallable)
void Unbind();
virtual void Unbind_Implementation() {}

View File

@ -1,6 +1,5 @@
// Oleg Petruny proprietary.
#include "InventoryInteractableModificator.h"
#include "EnhancedInputComponent.h"

View File

@ -2,12 +2,14 @@
#pragma once
#include "CoreMinimal.h"
#include "InteractableModificator.h"
#include "InventoryInteractableModificator.generated.h"
UCLASS(ClassGroup = InteractableModificator, meta = (BlueprintSpawnableComponent), Blueprintable, BlueprintType)
/**
* Modificator of type Use for storing items in a APlayerBase
*/
UCLASS(Blueprintable, BlueprintType, ClassGroup = InteractableModificator, meta = (BlueprintSpawnableComponent, ShortTooltip = "Modificator of type Use for storing items in a APlayerBase"), MinimalAPI)
class UInventoryInteractableModificator : public UInteractableModificator
{
GENERATED_BODY()

View File

@ -1,6 +1,5 @@
// Oleg Petruny proprietary.
#include "MoveInteractableModificator.h"
#include "EnhancedInputComponent.h"
@ -90,7 +89,7 @@ void UMoveInteractableModificator::Unbind_Implementation()
bindindingHandlers.Empty();
SetComponentTickEnabled(false);
actor->_Deactivate(GetActivatorTypes());
actor->Deactivate(GetActivatorTypes());
OnMoveDeactivated.Broadcast();
}

View File

@ -2,7 +2,6 @@
#pragma once
#include "CoreMinimal.h"
#include "InputActionValue.h"
#include "InteractableModificator.h"
@ -13,7 +12,10 @@ DECLARE_DYNAMIC_MULTICAST_DELEGATE(FDeactivateMoveInteractableModificatorActivat
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FHoldingMoveInteractableModificatorActivatedDelegate);
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FRotatingMoveInteractableModificatorActivatedDelegate);
UCLASS(ClassGroup = InteractableModificator, meta = (BlueprintSpawnableComponent), Blueprintable, BlueprintType)
/**
* Basic modificator for Move type activator
*/
UCLASS(Blueprintable, BlueprintType, ClassGroup = InteractableModificator, meta = (BlueprintSpawnableComponent, ShortTooltip = "Basic modificator for Move type activator"), MinimalAPI)
class UMoveInteractableModificator : public UInteractableModificator
{
GENERATED_BODY()

View File

@ -1,6 +1,5 @@
// Oleg Petruny proprietary.
#include "SawInteractableModificator.h"
#include "Interactable/Interactable.h"

View File

@ -2,14 +2,16 @@
#pragma once
#include "CoreMinimal.h"
#include "InteractableModificator.h"
#include "SawInteractableModificator.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FSawInteractableModificatorActivatedDelegate);
UCLASS(ClassGroup = InteractableModificator, meta = (BlueprintSpawnableComponent), Blueprintable, BlueprintType)
/**
* Basic modificator for Saw type activator
*/
UCLASS(Blueprintable, BlueprintType, ClassGroup = InteractableModificator, meta = (BlueprintSpawnableComponent, ShortTooltip = "Basic modificator for Saw type activator"), MinimalAPI)
class USawInteractableModificator : public UInteractableModificator
{
GENERATED_BODY()

View File

@ -1,12 +1,11 @@
// Oleg Petruny proprietary.
#include "Checkpoint.h"
#include "CustomGameInstanceBase.h"
#include "CustomGameInstance.h"
void ACheckpoint::SaveGame()
{
if(auto GI = Cast<UCustomGameInstanceBase>(GetWorld()->GetGameInstance()))
if(auto GI = UCustomGameInstance::Get())
GI->SaveGame(GetFName());
}

View File

@ -6,6 +6,7 @@
#include "Checkpoint.generated.h"
/** Simple actor for game save */
UCLASS(Blueprintable, BlueprintType, MinimalAPI)
class ACheckpoint : public AActor
{

View File

@ -1,6 +1,5 @@
// Oleg Petruny proprietary.
#include "Level1.h"
#include "Atmosphere/AtmosphericFog.h"

View File

@ -1,6 +1,5 @@
// Oleg Petruny proprietary.
#include "Level2.h"
void ALevel2::BeginPlay()

View File

@ -1,6 +1,5 @@
// Oleg Petruny proprietary.
#include "Level3.h"
#include "Atmosphere/AtmosphericFog.h"

View File

@ -1,6 +1,5 @@
// Oleg Petruny proprietary.
#include "Level4.h"
#include "Atmosphere/AtmosphericFog.h"

View File

@ -1,6 +1,5 @@
// Oleg Petruny proprietary.
#include "Level5.h"
#include "Atmosphere/AtmosphericFog.h"

View File

@ -1,6 +1,5 @@
// Oleg Petruny proprietary.
#include "LevelBase.h"
#include "Engine/StaticMesh.h"
@ -10,8 +9,8 @@
#include "Kismet/GameplayStatics.h"
#include "LevelSequencePlayer.h"
#include "CommonFunctions.h"
#include "CustomGameInstanceBase.h"
#include "CustomGameInstance.h"
#include "CustomPlayerController.h"
#include "Interactable/Interactable.h"
#include "Levels/Checkpoint.h"
#include "MainGameModeBase.h"
@ -19,21 +18,17 @@
#include "PlayerBase.h"
#include "SaveData.h"
ALevelBase* ALevelBase::Get()
{
if(auto GM = AMainGameModeBase::Get())
return GM->leadLevel.Get();
return nullptr;
}
void ALevelBase::BeginPlay()
{
AMainGameModeBase::leadLevel = TStrongObjectPtr<ALevelBase>{ this };
if(auto world = GetWorld())
{
if(auto GI = Cast<UCustomGameInstanceBase>(world->GetGameInstance()))
{
for(TActorIterator<AMinigame> it(GetWorld()); it; ++it)
{
GI->inputContexts.Add(it->GetInputMappings());
}
}
}
ALevelScriptActor::BeginPlay();
BroadcastNewLevelBeginPlay();
@ -62,7 +57,7 @@ void ALevelBase::IterateToState(int32 to)
void ALevelBase::BroadcastNewLevelBeginPlay()
{
if(auto GI = Cast<UCustomGameInstanceBase>(GetWorld()->GetGameInstance()))
if(auto GI = UCustomGameInstance::Get())
GI->OnLevelBeginned.Broadcast(GetFName());
}
@ -83,13 +78,13 @@ void ALevelBase::StartLevelAnimations()
void ALevelBase::ApplySaveData()
{
auto GI = UCustomGameInstanceBase::GetGameInstance();
auto GI = UCustomGameInstance::Get();
if(!GI || !GI->saveData || GI->saveData->level != GetWorld()->GetFName())
return;
IterateToState(GI->saveData->state);
auto player = UCommonFunctions::GetPlayer(this);
auto player = APlayerBase::Get();
if(!player)
return;

Some files were not shown because too many files have changed in this diff Show More