Initial VocationLife commit: C++ gameplay module and project foundation.

Source, config, and docs only — UE template assets stay local to save storage.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
pixachux 2026-06-26 23:52:53 +02:00
commit eae24f1c34
67 changed files with 3819 additions and 0 deletions

8
.gitattributes vendored Normal file
View File

@ -0,0 +1,8 @@
# Unreal Engine binary assets
*.uasset filter=lfs diff=lfs merge=lfs -text
*.umap filter=lfs diff=lfs merge=lfs -text
*.ubulk filter=lfs diff=lfs merge=lfs -text
*.uexp filter=lfs diff=lfs merge=lfs -text
*.ufont filter=lfs diff=lfs merge=lfs -text
*.upipelinecache filter=lfs diff=lfs merge=lfs -text
*.ushaderbytecode filter=lfs diff=lfs merge=lfs -text

30
.gitignore vendored Normal file
View File

@ -0,0 +1,30 @@
# Unreal Engine
Binaries/
DerivedDataCache/
Intermediate/
Saved/
Build/
*.sln
*.xcodeproj
*.xcworkspace
.vs/
.vscode/
*.VC.db
*.opensdf
*.sdf
*.suo
*.user
*.userosscache
# Epic template content (keep local, do not push — saves LFS/bandwidth)
Content/*
!Content/VocationLife/
Content/VocationLife/**/*.uasset
Content/VocationLife/**/*.umap
# OS
.DS_Store
Thumbs.db
# Crash reports
*.log

32
Config/DefaultEditor.ini Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,5 @@
[ContentBrowser]
ContentBrowserTab1.SelectedPaths=/Game/VocationLife
[/Script/InEditorDocumentation.InEditorDocumentationSettings]
TutorialUrl="https://dev.epicgames.com/documentation/unreal-engine/introduction-to-unreal-engine"

271
Config/DefaultEngine.ini Normal file
View File

@ -0,0 +1,271 @@
[/Script/Engine.CollisionProfile]
+Profiles=(Name="Projectile",CollisionEnabled=QueryOnly,ObjectTypeName="Projectile",CustomResponses=,HelpMessage="Preset for projectiles",bCanModify=True)
+DefaultChannelResponses=(Channel=ECC_GameTraceChannel1,Name="Projectile",DefaultResponse=ECR_Block,bTraceType=False,bStaticObject=False)
+EditProfiles=(Name="Trigger",CustomResponses=((Channel=Projectile, Response=ECR_Ignore)))
[/Script/EngineSettings.GameMapsSettings]
EditorStartupMap=/Engine/Maps/Entry.Entry
LocalMapOptions=
TransitionMap=
bUseSplitscreen=True
TwoPlayerSplitscreenLayout=Horizontal
ThreePlayerSplitscreenLayout=FavorTop
GameInstanceClass=/Script/VocationLife.VocationGameInstance
GameDefaultMap=/Engine/Maps/Entry.Entry
ServerDefaultMap=/Engine/Maps/Entry.Entry
GlobalDefaultGameMode=/Script/VocationLife.VocationGameMode
GlobalDefaultServerGameMode=/Script/VocationLife.VocationGameMode
[/Script/Engine.RendererSettings]
r.Mobile.ShadingPath=1
r.Mobile.ExtendedGBuffer=False
r.Mobile.AntiAliasing=2
r.Mobile.FloatPrecisionMode=0
r.Mobile.AllowDitheredLODTransition=False
r.DiscardUnusedQuality=False
r.AllowOcclusionQueries=True
r.MinScreenRadiusForLights=0.030000
r.MinScreenRadiusForDepthPrepass=0.030000
r.PrecomputedVisibilityWarning=False
r.TextureStreaming=True
Compat.UseDXT5NormalMaps=False
r.VirtualTextures=True
r.VirtualTexturedLightmaps=False
r.VT.TileSize=128
r.VT.TileBorderSize=4
r.VT.AnisotropicFiltering=False
r.VT.EnableAutoImport=False
bEnableVirtualTextureOpacityMask=False
r.MeshPaintVirtualTexture.Support=True
r.MeshPaintVirtualTexture.TileSize=32
r.MeshPaintVirtualTexture.TileBorderSize=2
r.MeshPaintVirtualTexture.UseCompression=True
r.StaticMesh.DefaultMeshPaintTextureSupport=True
r.MeshPaintVirtualTexture.DefaultTexelsPerVertex=4
r.MeshPaintVirtualTexture.MaxTextureSize=4096
r.vt.rvt.EnableBaseColor=True
r.vt.rvt.EnableBaseColorRoughness=True
r.vt.rvt.EnableBaseColorSpecular=True
r.vt.rvt.EnableMask4=True
r.vt.rvt.EnableWorldHeight=True
r.vt.rvt.EnableDisplacement=True
r.vt.rvt.HighQualityPerPixelHeight=True
WorkingColorSpaceChoice=sRGB
RedChromaticityCoordinate=(X=0.640000,Y=0.330000)
GreenChromaticityCoordinate=(X=0.300000,Y=0.600000)
BlueChromaticityCoordinate=(X=0.150000,Y=0.060000)
WhiteChromaticityCoordinate=(X=0.312700,Y=0.329000)
r.LegacyLuminanceFactors=False
r.ClearCoatNormal=False
r.DynamicGlobalIlluminationMethod=1
r.ReflectionMethod=1
r.ReflectionCaptureResolution=512
r.ReflectionEnvironmentLightmapMixBasedOnRoughness=True
r.Lumen.HardwareRayTracing=True
r.Lumen.HardwareRayTracing.LightingMode=0
r.Lumen.TranslucencyReflections.FrontLayer.EnableForProject=False
r.Lumen.TraceMeshSDFs=0
r.Lumen.ScreenTracingSource=0
r.Lumen.Reflections.HardwareRayTracing.Translucent.Refraction.EnableForProject=False
r.MegaLights.EnableForProject=False
r.MegaLights.FrontLayerTranslucency.EnableForProject=False
r.RayTracing.Shadows=False
r.Shadow.Virtual.Enable=1
r.RayTracing=True
r.RayTracing.RayTracingProxies.ProjectEnabled=True
r.RayTracing.UseTextureLod=False
r.PathTracing=True
r.GenerateMeshDistanceFields=True
r.DistanceFields.DefaultVoxelDensity=0.200000
r.Nanite.ProjectEnabled=True
r.AllowStaticLighting=False
r.NormalMapsForStaticLighting=False
r.ForwardShading=False
r.VertexFoggingForOpaque=True
r.SeparateTranslucency=True
r.TranslucentSortPolicy=0
TranslucentSortAxis=(X=0.000000,Y=-1.000000,Z=0.000000)
r.LocalFogVolume.ApplyOnTranslucent=False
xr.VRS.FoveationLevel=0
xr.VRS.DynamicFoveation=False
r.CustomDepth=1
r.CustomDepthTemporalAAJitter=True
r.PostProcessing.PropagateAlpha=False
r.Deferred.SupportPrimitiveAlphaHoldout=False
r.DefaultFeature.Bloom=True
r.DefaultFeature.AmbientOcclusion=True
r.DefaultFeature.AmbientOcclusionStaticFraction=True
r.DefaultFeature.AutoExposure=False
r.DefaultFeature.AutoExposure.Method=0
r.DefaultFeature.AutoExposure.Bias=1.000000
r.DefaultFeature.AutoExposure.ExtendDefaultLuminanceRange=True
r.DefaultFeature.LocalExposure.HighlightContrastScale=0.800000
r.DefaultFeature.LocalExposure.ShadowContrastScale=0.800000
r.DefaultFeature.MotionBlur=False
r.DefaultFeature.LensFlare=False
r.TemporalAA.Upsampling=True
r.AntiAliasingMethod=0
r.MSAACount=4
r.DefaultFeature.LightUnits=2
r.DefaultBackBufferPixelFormat=4
r.ScreenPercentage.Default=100.000000
r.ScreenPercentage.Default.Desktop.Mode=1
r.ScreenPercentage.Default.Mobile.Mode=0
r.ScreenPercentage.Default.VR.Mode=0
r.ScreenPercentage.Default.PathTracer.Mode=0
r.Shadow.UnbuiltPreviewInGame=True
r.StencilForLODDither=False
r.EarlyZPass=3
r.EarlyZPassOnlyMaterialMasking=True
r.Shadow.CSMCaching=False
r.DBuffer=True
r.ClearSceneMethod=1
r.VelocityOutputPass=0
r.Velocity.EnableVertexDeformation=2
r.SelectiveBasePassOutputs=False
bDefaultParticleCutouts=False
fx.GPUSimulationTextureSizeX=1024
fx.GPUSimulationTextureSizeY=1024
r.AllowGlobalClipPlane=False
r.GBufferFormat=1
r.MorphTarget.Mode=True
r.MorphTarget.MaxBlendWeight=5.000000
r.SupportSkyAtmosphere=True
r.SupportSkyAtmosphereAffectsHeightFog=True
r.SupportExpFogMatchesVolumetricFog=False
r.SupportLocalFogVolumes=True
r.SupportCloudShadowOnForwardLitTranslucent=False
r.ClusteredDeferredShading.EnableForProject=True
r.LightFunctionAtlas.Format=0
r.VolumetricFog.LightFunction=True
r.Deferred.UsesLightFunctionAtlas=True
r.SingleLayerWater.UsesLightFunctionAtlas=False
r.Translucent.UsesLightFunctionAtlas=False
r.Translucent.UsesIESProfiles=False
r.Translucent.UsesRectLights=False
r.Translucent.UsesShadowedLocalLights=False
r.GPUCrashDebugging=False
vr.InstancedStereo=False
r.MobileHDR=True
vr.MobileMultiView=False
r.Mobile.UseHWsRGBEncoding=False
vr.RoundRobinOcclusion=False
r.MeshStreaming=False
r.HeterogeneousVolumes=True
r.HeterogeneousVolumes.Shadows=False
r.Translucency.HeterogeneousVolumes=False
r.WireframeCullThreshold=5.000000
r.SupportStationarySkylight=True
r.SupportLowQualityLightmaps=True
r.SupportPointLightWholeSceneShadows=True
r.Shadow.TranslucentPerObject.ProjectEnabled=False
r.Water.SingleLayerWater.SupportCloudShadow=False
r.Substrate=False
r.Substrate.ProjectGBufferFormat=0
r.Substrate.ProjectClosuresPerPixel=4
r.Substrate.ProjectClosuresPerPixelOverride=0
r.Substrate.BytesPerPixel=80
r.Substrate.OpaqueMaterialRoughRefraction=False
r.Refraction.Blur=False
r.Substrate.Debug.AdvancedVisualizationShaders=False
r.Substrate.EnableLayerSupport=False
r.Material.RoughDiffuse=False
r.Material.EnergyConservation=False
r.Material.DefaultAutoMaterialUsage=True
r.OIT.SortedPixels=False
r.HairStrands.LODMode=True
r.VRS.Support=True
r.Mobile.EnableStaticAndCSMShadowReceivers=True
r.Mobile.EnableMovableLightCSMShaderCulling=True
r.Mobile.Forward.EnableLocalLights=1
r.Mobile.Forward.EnableClusteredReflections=False
r.Mobile.AllowDistanceFieldShadows=True
r.Mobile.EnableMovableSpotlightsShadow=False
r.GPUSkin.Limit2BoneInfluences=False
r.SupportDepthOnlyIndexBuffers=False
r.SupportReversedIndexBuffers=False
slate.MaterialShaderMode=0
r.Mobile.AmbientOcclusion=False
r.Mobile.ScreenSpaceReflections=False
r.Mobile.DBuffer=False
r.Mobile.PropagateAlpha=False
r.GPUSkin.UnlimitedBoneInfluences=False
r.GPUSkin.AlwaysUseDeformerForUnlimitedBoneInfluences=False
r.GPUSkin.UnlimitedBoneInfluencesThreshold=8
DefaultBoneInfluenceLimit=(Default=0,PerPlatform=())
r.SkinCache.CompileShaders=1
r.SkinCache.DefaultBehavior=1
r.Nanite.Foliage=False
bNaniteStreamableBulkDataOptional=(Default=False,PerPlatform=())
bStreamSkeletalMeshLODs=(Default=False,PerPlatform=())
bDiscardSkeletalMeshOptionalLODs=(Default=False,PerPlatform=())
VisualizeCalibrationColorMaterialPath=/Engine/EngineMaterials/PPM_DefaultCalibrationColor.PPM_DefaultCalibrationColor
VisualizeCalibrationCustomMaterialPath=None
VisualizeCalibrationGrayscaleMaterialPath=/Engine/EngineMaterials/PPM_DefaultCalibrationGrayscale.PPM_DefaultCalibrationGrayscale
r.PSOPrecache.ProxyCreationStrategy=1
PSOPrecacheFallbackMaterial=None
[/Script/WindowsTargetPlatform.WindowsTargetSettings]
DefaultGraphicsRHI=DefaultGraphicsRHI_DX12
DefaultGraphicsRHI=DefaultGraphicsRHI_DX12
DefaultGraphicsRHI=DefaultGraphicsRHI_DX12
-D3D12TargetedShaderFormats=PCD3D_SM5
+D3D12TargetedShaderFormats=PCD3D_SM6
-D3D11TargetedShaderFormats=PCD3D_SM5
+D3D11TargetedShaderFormats=PCD3D_SM5
[/Script/HardwareTargeting.HardwareTargetingSettings]
TargetedHardwareClass=Desktop
AppliedTargetedHardwareClass=Desktop
DefaultGraphicsPerformance=Scalable
AppliedDefaultGraphicsPerformance=Scalable
[/Script/Engine.Engine]
NearClipPlane=5.000000
+ActiveGameNameRedirects=(OldGameName="VocationLife",NewGameName="/Script/VocationLife")
+ActiveGameNameRedirects=(OldGameName="/Script/VocationLife",NewGameName="/Script/VocationLife")
[/Script/OnlineSubsystemUtils.IpNetDriver]
NetServerMaxTickRate=60
[/Script/OnlineSubsystemSteam.SteamNetDriver]
NetConnectionClassName="OnlineSubsystemSteam.SteamNetConnection"
[OnlineSubsystem]
DefaultPlatformService=Null
[OnlineSubsystemNull]
bEnabled=true
[OnlineSubsystemSteam]
bEnabled=true
SteamDevAppId=480
bInitServerOnClient=true
[/Script/Engine.GameEngine]
+NetDriverDefinitions=(DefName="GameNetDriver",DriverClassName="OnlineSubsystemUtils.IpNetDriver",DriverClassNameFallback="OnlineSubsystemUtils.IpNetDriver")
[/Script/OnlineSubsystemUtils.OnlineSession]
bUseBuildIdOverride=false
[/Script/AIModule.AISystem]
bForgetStaleActors=True
[/Script/Engine.UserInterfaceSettings]
UIScaleRule=ShortestSide
[/Script/AndroidFileServerEditor.AndroidFileServerRuntimeSettings]
bEnablePlugin=True
bAllowNetworkConnection=True
SecurityToken=55490E894C07D04A1ACF51BB99492878
bIncludeInShipping=False
bAllowExternalStartInShipping=False
bCompileAFSProject=False
bUseCompression=False
bLogFiles=False
bReportStats=False
ConnectionType=USBOnly
bUseManualIPAddress=False
ManualIPAddress=

22
Config/DefaultGame.ini Normal file
View File

@ -0,0 +1,22 @@
[/Script/EngineSettings.GeneralProjectSettings]
ProjectID=019F047E36E8731BAF0FDD0409BFCFA0
ProjectName=VocationLife
CompanyName=VocationLife Project
ProjectVersion=0.1.0
Description=Life-sim remake inspired by Fantasy Life with first-person and top-down cameras, co-op multiplayer, and dedicated server support.
SupportContact=
Homepage=
LicensingTerms=
PrivacyPolicy=
bStartInVR=False
[ConsoleVariables]
CommonUI.CheckKeyboardFocusAndParentage=1
CommonUI.DisallowUserFocusedWidgetForPendingFocusRecipient=1
CommonUI.FallbackToDesiredOnAutoRestoreFailure=1
[/Script/VocationLife.VocationEOS]
bEOSEnabled=false
ProductId=
SandboxId=
DeploymentId=

120
Config/DefaultInput.ini Normal file
View File

@ -0,0 +1,120 @@
[/Script/Engine.InputSettings]
-AxisConfig=(AxisKeyName="Gamepad_LeftX",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f))
-AxisConfig=(AxisKeyName="Gamepad_LeftY",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f))
-AxisConfig=(AxisKeyName="Gamepad_RightX",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f))
-AxisConfig=(AxisKeyName="Gamepad_RightY",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f))
-AxisConfig=(AxisKeyName="MouseX",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f))
-AxisConfig=(AxisKeyName="MouseY",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f))
-AxisConfig=(AxisKeyName="Mouse2D",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f))
+AxisConfig=(AxisKeyName="Mouse2D",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="Gamepad_LeftX",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="Gamepad_LeftY",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="Gamepad_RightX",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="Gamepad_RightY",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="MouseX",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="MouseY",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="MouseWheelAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="Gamepad_LeftTriggerAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="Gamepad_RightTriggerAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="Gamepad_Special_Left_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="Gamepad_Special_Left_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="Vive_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="Vive_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="Vive_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="Vive_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="Vive_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="Vive_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="MixedReality_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="MixedReality_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="MixedReality_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="MixedReality_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="MixedReality_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="MixedReality_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="MixedReality_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="MixedReality_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="MixedReality_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="MixedReality_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="OculusTouch_Left_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="OculusTouch_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="OculusTouch_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="OculusTouch_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="OculusTouch_Right_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="OculusTouch_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="OculusTouch_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="OculusTouch_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="ValveIndex_Left_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="ValveIndex_Left_Grip_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="ValveIndex_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="ValveIndex_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="ValveIndex_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Touch",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="ValveIndex_Right_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="ValveIndex_Right_Grip_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="ValveIndex_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="ValveIndex_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="ValveIndex_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="OculusTouch_Left_Thumbstick",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="OculusTouch_Left_FaceButton1",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="OculusTouch_Left_Trigger",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="OculusTouch_Left_FaceButton2",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="OculusTouch_Left_IndexPointing",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="OculusTouch_Left_ThumbUp",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="OculusTouch_Left_ThumbRest",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="OculusTouch_Right_Thumbstick",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="OculusTouch_Right_FaceButton1",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="OculusTouch_Right_Trigger",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="OculusTouch_Right_FaceButton2",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="OculusTouch_Right_IndexPointing",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="OculusTouch_Right_ThumbUp",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="OculusTouch_Right_ThumbRest",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="OculusHand_Left_ThumbPinchStrength",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="OculusHand_Left_IndexPinchStrength",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="OculusHand_Left_MiddlePinchStrength",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="OculusHand_Left_RingPinchStrength",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="OculusHand_Left_PinkPinchStrength",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="OculusHand_Right_ThumbPinchStrength",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="OculusHand_Right_IndexPinchStrength",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="OculusHand_Right_MiddlePinchStrength",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="OculusHand_Right_RingPinchStrength",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="OculusHand_Right_PinkPinchStrength",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="Cosmos_Left_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="Cosmos_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="Cosmos_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="Cosmos_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="Cosmos_Right_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="Cosmos_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="Cosmos_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="Cosmos_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
bAltEnterTogglesFullscreen=True
bF11TogglesFullscreen=True
bUseMouseForTouch=False
bEnableMouseSmoothing=True
bEnableFOVScaling=True
bCaptureMouseOnLaunch=True
bEnableLegacyInputScales=True
bEnableMotionControls=True
bFilterInputByPlatformUser=False
bShouldFlushPressedKeysOnViewportFocusLost=True
bAlwaysShowTouchInterface=False
bShowConsoleOnFourFingerTap=True
bEnableGestureRecognizer=False
bUseAutocorrect=False
DefaultViewportMouseCaptureMode=CapturePermanently
DefaultViewportMouseLockMode=LockAlways
FOVScale=0.011110
DoubleClickTime=0.200000
DefaultPlayerInputClass=/Script/EnhancedInput.EnhancedPlayerInput
DefaultInputComponentClass=/Script/EnhancedInput.EnhancedInputComponent
DefaultTouchInterface=None
-ConsoleKeys=Tilde
+ConsoleKeys=Tilde
[EnhancedInputPlatformSettings_Windows EnhancedInputPlatformSettings]
bShouldLogMappingContextRedirects=False

View File

@ -0,0 +1,31 @@
; THESE ARE GENERATED FILES, DO NOT EDIT DIRECTLY!
; USE THE LOCALIZATION DASHBOARD IN THE UNREAL EDITOR TO EDIT THE CONFIGURATION
[CommonSettings]
SourcePath=Content/Localization/Game
DestinationPath=Content/Localization/Game
ManifestName=Game.manifest
ArchiveName=Game.archive
ResourceName=Game.locres
bSkipSourceCheck=false
bValidateFormatPatterns=true
bValidateSafeWhitespace=false
bValidateRichTextTags=false
NativeCulture=en
CulturesToGenerate=en
CulturesToGenerate=ar
CulturesToGenerate=ko
CulturesToGenerate=ja
CulturesToGenerate=zh-Hans
CulturesToGenerate=fr
CulturesToGenerate=de
CulturesToGenerate=it
CulturesToGenerate=es
CulturesToGenerate=es-419
CulturesToGenerate=pl
CulturesToGenerate=pt-BR
CulturesToGenerate=ru
CulturesToGenerate=tr
[GatherTextStep0]
CommandletClass=GenerateTextLocalizationResource

View File

@ -0,0 +1,32 @@
; THESE ARE GENERATED FILES, DO NOT EDIT DIRECTLY!
; USE THE LOCALIZATION DASHBOARD IN THE UNREAL EDITOR TO EDIT THE CONFIGURATION
[CommonSettings]
SourcePath=Content/Localization/Game
DestinationPath=Content/Localization/Game
NativeCulture=en
CulturesToGenerate=en
CulturesToGenerate=ar
CulturesToGenerate=ko
CulturesToGenerate=ja
CulturesToGenerate=zh-Hans
CulturesToGenerate=fr
CulturesToGenerate=de
CulturesToGenerate=it
CulturesToGenerate=es
CulturesToGenerate=es-419
CulturesToGenerate=pl
CulturesToGenerate=pt-BR
CulturesToGenerate=ru
CulturesToGenerate=tr
ManifestName=Game.manifest
ArchiveName=Game.archive
PortableObjectName=Game.po
[GatherTextStep0]
CommandletClass=InternationalizationExport
bExportLoc=true
LocalizedTextCollapseMode=ELocalizedTextCollapseMode::IdenticalTextIdAndSource
POFormat=EPortableObjectFormat::Unreal
ShouldPersistCommentsOnExport=false
ShouldAddSourceLocationsAsComments=true

View File

@ -0,0 +1,27 @@
; THESE ARE GENERATED FILES, DO NOT EDIT DIRECTLY!
; USE THE LOCALIZATION DASHBOARD IN THE UNREAL EDITOR TO EDIT THE CONFIGURATION
[CommonSettings]
SourcePath=Content/Localization/Game
DestinationPath=Content/Localization/Game
NativeCulture=en
CulturesToGenerate=en
CulturesToGenerate=ar
CulturesToGenerate=ko
CulturesToGenerate=ja
CulturesToGenerate=zh-Hans
CulturesToGenerate=fr
CulturesToGenerate=de
CulturesToGenerate=it
CulturesToGenerate=es
CulturesToGenerate=es-419
CulturesToGenerate=pl
CulturesToGenerate=pt-BR
CulturesToGenerate=ru
CulturesToGenerate=tr
ManifestName=Game.manifest
ArchiveName=Game.archive
DialogueScriptName=GameDialogue.csv
[GatherTextStep0]
CommandletClass=ExportDialogueScript

View File

@ -0,0 +1,48 @@
; THESE ARE GENERATED FILES, DO NOT EDIT DIRECTLY!
; USE THE LOCALIZATION DASHBOARD IN THE UNREAL EDITOR TO EDIT THE CONFIGURATION
[CommonSettings]
ManifestDependencies=../../Engine/Content/Localization/Engine/Engine.manifest
ManifestDependencies=../../Engine/Content/Localization/Editor/Editor.manifest
SourcePath=Content/Localization/Game
DestinationPath=Content/Localization/Game
ManifestName=Game.manifest
ArchiveName=Game.archive
NativeCulture=en
CulturesToGenerate=en
CulturesToGenerate=ar
CulturesToGenerate=ko
CulturesToGenerate=ja
CulturesToGenerate=zh-Hans
CulturesToGenerate=fr
CulturesToGenerate=de
CulturesToGenerate=it
CulturesToGenerate=es
CulturesToGenerate=es-419
CulturesToGenerate=pl
CulturesToGenerate=pt-BR
CulturesToGenerate=ru
CulturesToGenerate=tr
[GatherTextStep0]
CommandletClass=GatherTextFromAssets
IncludePathFilters=Content/DemoTemplate/*
ExcludePathFilters=Content/Localization/*
PackageFileNameFilters=*.umap
PackageFileNameFilters=*.uasset
ShouldExcludeDerivedClasses=false
ShouldGatherFromEditorOnlyData=false
SkipGatherCache=false
[GatherTextStep1]
CommandletClass=GenerateGatherManifest
[GatherTextStep2]
CommandletClass=GenerateGatherArchive
[GatherTextStep3]
CommandletClass=GenerateTextLocalizationReport
bWordCountReport=true
WordCountReportName=Game.csv
bConflictReport=true
ConflictReportName=Game_Conflicts.txt

View File

@ -0,0 +1,27 @@
; THESE ARE GENERATED FILES, DO NOT EDIT DIRECTLY!
; USE THE LOCALIZATION DASHBOARD IN THE UNREAL EDITOR TO EDIT THE CONFIGURATION
[CommonSettings]
SourcePath=Content/Localization/Game
DestinationPath=Content/Localization/Game
ManifestName=Game.manifest
ArchiveName=Game.archive
CulturesToGenerate=en
CulturesToGenerate=ar
CulturesToGenerate=ko
CulturesToGenerate=ja
CulturesToGenerate=zh-Hans
CulturesToGenerate=fr
CulturesToGenerate=de
CulturesToGenerate=it
CulturesToGenerate=es
CulturesToGenerate=es-419
CulturesToGenerate=pl
CulturesToGenerate=pt-BR
CulturesToGenerate=ru
CulturesToGenerate=tr
[GatherTextStep0]
CommandletClass=GenerateTextLocalizationReport
bWordCountReport=true
WordCountReportName=Game.csv

View File

@ -0,0 +1,30 @@
; THESE ARE GENERATED FILES, DO NOT EDIT DIRECTLY!
; USE THE LOCALIZATION DASHBOARD IN THE UNREAL EDITOR TO EDIT THE CONFIGURATION
[CommonSettings]
SourcePath=Content/Localization/Game
DestinationPath=Content/Localization/Game
NativeCulture=en
CulturesToGenerate=en
CulturesToGenerate=ar
CulturesToGenerate=ko
CulturesToGenerate=ja
CulturesToGenerate=zh-Hans
CulturesToGenerate=fr
CulturesToGenerate=de
CulturesToGenerate=it
CulturesToGenerate=es
CulturesToGenerate=es-419
CulturesToGenerate=pl
CulturesToGenerate=pt-BR
CulturesToGenerate=ru
CulturesToGenerate=tr
ManifestName=Game.manifest
ArchiveName=Game.archive
PortableObjectName=Game.po
[GatherTextStep0]
CommandletClass=InternationalizationExport
bImportLoc=true
LocalizedTextCollapseMode=ELocalizedTextCollapseMode::IdenticalTextIdAndSource
POFormat=EPortableObjectFormat::Unreal

View File

@ -0,0 +1,28 @@
; THESE ARE GENERATED FILES, DO NOT EDIT DIRECTLY!
; USE THE LOCALIZATION DASHBOARD IN THE UNREAL EDITOR TO EDIT THE CONFIGURATION
[CommonSettings]
SourcePath=Content/Localization/Game
ManifestName=Game.manifest
ArchiveName=Game.archive
NativeCulture=en
CulturesToGenerate=en
CulturesToGenerate=ar
CulturesToGenerate=ko
CulturesToGenerate=ja
CulturesToGenerate=zh-Hans
CulturesToGenerate=fr
CulturesToGenerate=de
CulturesToGenerate=it
CulturesToGenerate=es
CulturesToGenerate=es-419
CulturesToGenerate=pl
CulturesToGenerate=pt-BR
CulturesToGenerate=ru
CulturesToGenerate=tr
[GatherTextStep0]
CommandletClass=ImportLocalizedDialogue
RawAudioPath=
ImportedDialogueFolder=ImportedDialogue
bImportNativeAsSource=false

View File

@ -0,0 +1,27 @@
; THESE ARE GENERATED FILES, DO NOT EDIT DIRECTLY!
; USE THE LOCALIZATION DASHBOARD IN THE UNREAL EDITOR TO EDIT THE CONFIGURATION
[CommonSettings]
SourcePath=Content/Localization/Game
DestinationPath=Content/Localization/Game
NativeCulture=en
CulturesToGenerate=en
CulturesToGenerate=ar
CulturesToGenerate=ko
CulturesToGenerate=ja
CulturesToGenerate=zh-Hans
CulturesToGenerate=fr
CulturesToGenerate=de
CulturesToGenerate=it
CulturesToGenerate=es
CulturesToGenerate=es-419
CulturesToGenerate=pl
CulturesToGenerate=pt-BR
CulturesToGenerate=ru
CulturesToGenerate=tr
ManifestName=Game.manifest
ArchiveName=Game.archive
DialogueScriptName=GameDialogue.csv
[GatherTextStep0]
CommandletClass=ImportDialogueScript

5
Config/ServerConfig.ini Normal file
View File

@ -0,0 +1,5 @@
[/Script/VocationLife.VocationServerSettings]
ServerName=VocationLife Dedicated Server
MaxPlayers=4
Port=7777
bLANOnly=true

View File

View File

View File

View File

View File

View File

View File

View File

30
Docs/CrossplayEOS.md Normal file
View File

@ -0,0 +1,30 @@
# Crossplay (Epic Online Services)
VocationLife ships with an EOS integration layer that is **disabled by default**.
## Enable EOS
1. Open `VocationLife.uproject` and enable the **OnlineSubsystemEOS** plugin.
2. Set credentials in [`Config/DefaultGame.ini`](../Config/DefaultGame.ini):
```ini
[/Script/VocationLife.VocationEOS]
bEOSEnabled=true
ProductId=YOUR_PRODUCT_ID
SandboxId=YOUR_SANDBOX_ID
DeploymentId=YOUR_DEPLOYMENT_ID
```
3. Configure the EOS plugin settings in `Config/DefaultEngine.ini` per Epic documentation.
4. Call `InitializeEOS()` on `UVocationEOSSubsystem` from your menu flow once UI is ready.
## Console ports
Console builds require platform SDKs and certification. Recommended order:
1. Stabilize PC multiplayer (listen + dedicated)
2. Enable EOS for PC crossplay
3. Add PlayStation / Xbox targets with platform-specific Online Subsystem modules
4. Use separate scalability profiles (no hardware RT on consoles)
The `UVocationSessionSubsystem` can be extended to route session creation through EOS when `UVocationEOSSubsystem::IsEOSEnabled()` returns true.

42
Docs/DedicatedServer.md Normal file
View File

@ -0,0 +1,42 @@
# Dedicated Server Hosting
VocationLife includes a `VocationLifeServer` build target for headless dedicated hosting.
## Build
```bash
Engine/Build/BatchFiles/RunUBT.sh VocationLifeServer Linux Development \
-project="/path/to/VocationLife/VocationLife.uproject"
```
> **Note:** Some UE Linux preview distributions do not ship server targets. Use a full engine build or Windows `VocationLifeServer` target if Linux server compilation is unavailable.
## Configuration
Edit [`Config/ServerConfig.ini`](../Config/ServerConfig.ini):
```ini
[/Script/VocationLife.VocationServerSettings]
ServerName=VocationLife Dedicated Server
MaxPlayers=4
Port=7777
bLANOnly=true
```
## Run
```bash
./VocationLife/Binaries/Linux/VocationLifeServer \
/Game/Engine/Maps/Entry \
-log -port=7777 -MaxPlayers=4
```
## Connect
From a client console (`~`):
```
open 127.0.0.1:7777
```
Or use **H** to host and **J** to join from the in-game pause overlay (LAN).

99
README.md Normal file
View File

@ -0,0 +1,99 @@
# VocationLife
A life-simulation action RPG remake inspired by Fantasy Life, built in **Unreal Engine 5.8**.
## Vision
- First-person gameplay (primary) with classic top-down camera toggle
- Mouse, keyboard, and gamepad support via Enhanced Input
- Co-op multiplayer (listen server first, dedicated server hosting)
- Optional hardware ray tracing and scalability presets
- Future crossplay via Epic Online Services (EOS)
## Requirements
- Unreal Engine 5.8
- Windows (DX12), Linux (Vulkan), or macOS
- GPU with ray tracing support for the RT preset (optional)
## Getting Started
1. Open `VocationLife.uproject` in Unreal Engine 5.8.
2. Allow the editor to compile the `VocationLife` C++ module on first launch.
3. Press **Play** — a test world (floor, mining nodes, enemy) is spawned automatically.
## Controls
| Action | Keyboard | Gamepad |
|--------|----------|---------|
| Move | WASD | Left stick |
| Look | Mouse | Right stick |
| Jump | Space | A / Cross |
| Interact / Craft | E | X / Square |
| Attack | LMB | RT |
| Toggle camera (FP / Top-Down) | V | Menu |
| Quick save | I | Y / Triangle |
| Pause | Esc | Start |
### Pause menu shortcuts
While paused:
- **F1F5** — Graphics presets (Low → Ray Tracing)
- **H** — Host co-op session
- **J** — Find and join LAN session
- **S** — Save game
## Project Structure
```
Content/VocationLife/ Blueprint extensions and art (create in editor)
Source/VocationLife/ Core gameplay C++ (camera, input, inventory, crafting, multiplayer)
Config/ Engine and server configuration
```
## Dedicated Server Hosting
Build the server target:
```bash
/path/to/UnrealEngine/Engine/Build/BatchFiles/Linux/Build.sh \
VocationLifeServer Linux Development \
-project="/path/to/VocationLife/VocationLife.uproject"
```
Run headless:
```bash
./VocationLife/Binaries/Linux/VocationLifeServer \
-log -port=7777 -MaxPlayers=4
```
Edit `Config/ServerConfig.ini` for server name, port, and player limit.
Clients connect with:
```bash
open 127.0.0.1:7777
```
## Multiplayer
- **LAN / Null subsystem** — works out of the box for local testing.
- **Steam** — set `SteamDevAppId` in `Config/DefaultEngine.ini` (default: 480 for Spacewar test app).
- **EOS Crossplay** — enable `OnlineSubsystemEOS` plugin and fill credentials in `Config/DefaultGame.ini` under `[/Script/VocationLife.VocationEOS]`.
## Graphics Presets
Presets are applied at runtime via `UVocationGraphicsLibrary`:
- Low / Medium / High / Ultra — scalability levels
- Ray Tracing — enables `r.RayTracing`, Lumen HW-RT, and path tracing
## Legal
This is a fan project. It is not affiliated with Level-5 or Nintendo. Use original art and naming for any public release.
## Development Roadmap
See the project plan for phased delivery: foundation → vertical slice → multiplayer → dedicated servers → crossplay.

View File

@ -0,0 +1,15 @@
// Copyright VocationLife Project. All Rights Reserved.
using UnrealBuildTool;
using System.Collections.Generic;
public class VocationLifeTarget : TargetRules
{
public VocationLifeTarget(TargetInfo Target) : base(Target)
{
Type = TargetType.Game;
DefaultBuildSettings = BuildSettingsVersion.V7;
IncludeOrderVersion = EngineIncludeOrderVersion.Unreal5_8;
ExtraModuleNames.Add("VocationLife");
}
}

View File

@ -0,0 +1,46 @@
// Copyright VocationLife Project. All Rights Reserved.
#include "CombatComponent.h"
#include "Net/UnrealNetwork.h"
UCombatComponent::UCombatComponent()
{
SetIsReplicatedByDefault(true);
PrimaryComponentTick.bCanEverTick = false;
}
void UCombatComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(UCombatComponent, Health);
}
void UCombatComponent::ApplyDamage(float DamageAmount, AActor* DamageCauser)
{
if (!GetOwner() || !GetOwner()->HasAuthority() || DamageAmount <= 0.f || !IsAlive())
{
return;
}
Health = FMath::Clamp(Health - DamageAmount, 0.f, MaxHealth);
OnHealthChanged.Broadcast(Health, MaxHealth);
if (!IsAlive())
{
OnDeath.Broadcast();
}
}
void UCombatComponent::ServerApplyDamage_Implementation(float DamageAmount, AActor* DamageCauser)
{
ApplyDamage(DamageAmount, DamageCauser);
}
void UCombatComponent::OnRep_Health()
{
OnHealthChanged.Broadcast(Health, MaxHealth);
if (!IsAlive())
{
OnDeath.Broadcast();
}
}

View File

@ -0,0 +1,124 @@
// Copyright VocationLife Project. All Rights Reserved.
#include "CraftingComponent.h"
#include "InventoryComponent.h"
#include "VocationGameInstance.h"
#include "VocationPlayerState.h"
#include "GameFramework/Pawn.h"
#include "GameFramework/PlayerState.h"
UCraftingComponent::UCraftingComponent()
{
PrimaryComponentTick.bCanEverTick = false;
}
bool UCraftingComponent::CanCraftRecipe(const FRecipeDefinition& Recipe) const
{
const UInventoryComponent* Inventory = GetInventory();
if (!Inventory)
{
return false;
}
if (const APawn* OwnerPawn = Cast<APawn>(GetOwner()))
{
if (const AVocationPlayerState* PS = OwnerPawn->GetPlayerState<AVocationPlayerState>())
{
if (Recipe.RequiredVocation != EVocationLifeClass::None &&
PS->GetActiveVocation() != Recipe.RequiredVocation)
{
return false;
}
}
}
for (const FRecipeIngredient& Ingredient : Recipe.Ingredients)
{
if (!Inventory->HasItem(Ingredient.ItemId, Ingredient.Quantity))
{
return false;
}
}
return true;
}
bool UCraftingComponent::CraftRecipe(const FRecipeDefinition& Recipe)
{
if (!GetOwner() || !GetOwner()->HasAuthority())
{
return false;
}
if (!CanCraftRecipe(Recipe))
{
return false;
}
UInventoryComponent* Inventory = GetInventory();
if (!Inventory)
{
return false;
}
for (const FRecipeIngredient& Ingredient : Recipe.Ingredients)
{
Inventory->RemoveItem(Ingredient.ItemId, Ingredient.Quantity);
}
Inventory->AddItem(Recipe.OutputItemId, Recipe.OutputQuantity);
OnCraftCompleted.Broadcast(Recipe.RecipeId, Recipe.OutputItemId);
return true;
}
void UCraftingComponent::ServerCraftRecipe_Implementation(FName RecipeId)
{
FRecipeDefinition Recipe;
if (FindRecipe(RecipeId, Recipe))
{
CraftRecipe(Recipe);
}
}
TArray<FRecipeDefinition> UCraftingComponent::GetAvailableRecipes(EVocationLifeClass Vocation) const
{
TArray<FRecipeDefinition> Result;
if (const UWorld* World = GetWorld())
{
if (const UVocationGameInstance* GI = World->GetGameInstance<UVocationGameInstance>())
{
for (const FRecipeDefinition& Recipe : GI->GetRecipeDefinitions())
{
if (Recipe.RequiredVocation == EVocationLifeClass::None || Recipe.RequiredVocation == Vocation)
{
Result.Add(Recipe);
}
}
}
}
return Result;
}
UInventoryComponent* UCraftingComponent::GetInventory() const
{
return GetOwner() ? GetOwner()->FindComponentByClass<UInventoryComponent>() : nullptr;
}
bool UCraftingComponent::FindRecipe(FName RecipeId, FRecipeDefinition& OutRecipe) const
{
if (const UWorld* World = GetWorld())
{
if (const UVocationGameInstance* GI = World->GetGameInstance<UVocationGameInstance>())
{
for (const FRecipeDefinition& Recipe : GI->GetRecipeDefinitions())
{
if (Recipe.RecipeId == RecipeId)
{
OutRecipe = Recipe;
return true;
}
}
}
}
return false;
}

View File

@ -0,0 +1,158 @@
// Copyright VocationLife Project. All Rights Reserved.
#include "InventoryComponent.h"
#include "VocationGameInstance.h"
#include "Net/UnrealNetwork.h"
#include "Engine/World.h"
UInventoryComponent::UInventoryComponent()
{
SetIsReplicatedByDefault(true);
}
void UInventoryComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(UInventoryComponent, Slots);
}
bool UInventoryComponent::AddItem(FName ItemId, int32 Quantity)
{
if (!GetOwner() || !GetOwner()->HasAuthority() || ItemId.IsNone() || Quantity <= 0)
{
return false;
}
const int32 MaxStack = GetMaxStack(ItemId);
int32 Remaining = Quantity;
while (Remaining > 0)
{
const int32 SlotIndex = FindSlotIndex(ItemId);
if (SlotIndex != INDEX_NONE)
{
FInventorySlot& Slot = Slots[SlotIndex];
const int32 Space = MaxStack - Slot.Quantity;
const int32 ToAdd = FMath::Min(Space, Remaining);
Slot.Quantity += ToAdd;
Remaining -= ToAdd;
if (Remaining <= 0)
{
break;
}
}
FInventorySlot NewSlot;
NewSlot.ItemId = ItemId;
NewSlot.Quantity = FMath::Min(Remaining, MaxStack);
Slots.Add(NewSlot);
Remaining -= NewSlot.Quantity;
}
OnInventoryChanged.Broadcast();
return true;
}
bool UInventoryComponent::RemoveItem(FName ItemId, int32 Quantity)
{
if (!GetOwner() || !GetOwner()->HasAuthority() || ItemId.IsNone() || Quantity <= 0)
{
return false;
}
if (!HasItem(ItemId, Quantity))
{
return false;
}
int32 Remaining = Quantity;
for (int32 Index = Slots.Num() - 1; Index >= 0 && Remaining > 0; --Index)
{
if (Slots[Index].ItemId != ItemId)
{
continue;
}
const int32 ToRemove = FMath::Min(Slots[Index].Quantity, Remaining);
Slots[Index].Quantity -= ToRemove;
Remaining -= ToRemove;
if (Slots[Index].Quantity <= 0)
{
Slots.RemoveAt(Index);
}
}
OnInventoryChanged.Broadcast();
return true;
}
bool UInventoryComponent::HasItem(FName ItemId, int32 Quantity) const
{
return GetItemCount(ItemId) >= Quantity;
}
int32 UInventoryComponent::GetItemCount(FName ItemId) const
{
int32 Total = 0;
for (const FInventorySlot& Slot : Slots)
{
if (Slot.ItemId == ItemId)
{
Total += Slot.Quantity;
}
}
return Total;
}
void UInventoryComponent::SetSlots(const TArray<FInventorySlot>& NewSlots)
{
if (GetOwner() && GetOwner()->HasAuthority())
{
Slots = NewSlots;
OnRep_Slots();
}
}
void UInventoryComponent::ServerAddItem_Implementation(FName ItemId, int32 Quantity)
{
AddItem(ItemId, Quantity);
}
void UInventoryComponent::ServerRemoveItem_Implementation(FName ItemId, int32 Quantity)
{
RemoveItem(ItemId, Quantity);
}
void UInventoryComponent::OnRep_Slots()
{
OnInventoryChanged.Broadcast();
}
int32 UInventoryComponent::FindSlotIndex(FName ItemId) const
{
for (int32 Index = 0; Index < Slots.Num(); ++Index)
{
if (Slots[Index].ItemId == ItemId && Slots[Index].Quantity < GetMaxStack(ItemId))
{
return Index;
}
}
return INDEX_NONE;
}
int32 UInventoryComponent::GetMaxStack(FName ItemId) const
{
if (const UWorld* World = GetWorld())
{
if (const UVocationGameInstance* GI = World->GetGameInstance<UVocationGameInstance>())
{
FVocationItemDefinition Def;
if (GI->FindItemDefinition(ItemId, Def))
{
return Def.MaxStack;
}
}
}
return 99;
}

View File

@ -0,0 +1,78 @@
// Copyright VocationLife Project. All Rights Reserved.
#include "MiningNode.h"
#include "VocationCharacter.h"
#include "InventoryComponent.h"
#include "Components/StaticMeshComponent.h"
#include "Engine/StaticMesh.h"
#include "Net/UnrealNetwork.h"
#include "TimerManager.h"
#include "UObject/ConstructorHelpers.h"
AMiningNode::AMiningNode()
{
bReplicates = true;
MeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("MeshComponent"));
RootComponent = MeshComponent;
static ConstructorHelpers::FObjectFinder<UStaticMesh> CubeMesh(TEXT("/Engine/BasicShapes/Cube.Cube"));
if (CubeMesh.Succeeded())
{
MeshComponent->SetStaticMesh(CubeMesh.Object);
MeshComponent->SetWorldScale3D(FVector(1.5f, 1.5f, 1.5f));
}
}
void AMiningNode::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(AMiningNode, bIsDepleted);
}
FText AMiningNode::GetInteractionPrompt_Implementation() const
{
return bIsDepleted
? FText::FromString(TEXT("Ore depleted"))
: FText::FromString(TEXT("Mine [E]"));
}
bool AMiningNode::CanInteract_Implementation(APawn* InteractingPawn) const
{
return !bIsDepleted && InteractingPawn != nullptr;
}
void AMiningNode::Interact_Implementation(APawn* InteractingPawn)
{
if (!HasAuthority() || bIsDepleted || !InteractingPawn)
{
return;
}
if (AVocationCharacter* Character = Cast<AVocationCharacter>(InteractingPawn))
{
if (UInventoryComponent* Inventory = Character->GetInventoryComponent())
{
Inventory->AddItem(OreItemId, OreYield);
}
}
bIsDepleted = true;
OnRep_Depleted();
GetWorldTimerManager().SetTimer(RespawnTimerHandle, this, &AMiningNode::HandleRespawn, RespawnTimeSeconds, false);
}
void AMiningNode::OnRep_Depleted()
{
if (MeshComponent)
{
MeshComponent->SetVisibility(!bIsDepleted);
MeshComponent->SetCollisionEnabled(bIsDepleted ? ECollisionEnabled::NoCollision : ECollisionEnabled::QueryAndPhysics);
}
}
void AMiningNode::HandleRespawn()
{
bIsDepleted = false;
OnRep_Depleted();
}

View File

@ -0,0 +1,339 @@
// Copyright VocationLife Project. All Rights Reserved.
#include "VocationCharacter.h"
#include "VocationPlayerController.h"
#include "VocationPlayerState.h"
#include "VocationGameInstance.h"
#include "InventoryComponent.h"
#include "CraftingComponent.h"
#include "CombatComponent.h"
#include "Interactable.h"
#include "VocationEnemy.h"
#include "Camera/CameraComponent.h"
#include "Components/CapsuleComponent.h"
#include "Components/SkeletalMeshComponent.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "GameFramework/SpringArmComponent.h"
#include "EnhancedInputComponent.h"
#include "InputActionValue.h"
#include "Net/UnrealNetwork.h"
#include "Engine/World.h"
#include "DrawDebugHelpers.h"
#include "Kismet/GameplayStatics.h"
AVocationCharacter::AVocationCharacter()
{
PrimaryActorTick.bCanEverTick = true;
bReplicates = true;
GetCapsuleComponent()->InitCapsuleSize(42.f, 96.f);
FirstPersonCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FirstPersonCamera"));
FirstPersonCamera->SetupAttachment(GetCapsuleComponent());
FirstPersonCamera->SetRelativeLocation(FVector(0.f, 0.f, 64.f));
FirstPersonCamera->bUsePawnControlRotation = true;
TopDownSpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("TopDownSpringArm"));
TopDownSpringArm->SetupAttachment(RootComponent);
TopDownSpringArm->TargetArmLength = TopDownArmLength;
TopDownSpringArm->SetRelativeRotation(FRotator(TopDownPitch, 0.f, 0.f));
TopDownSpringArm->bDoCollisionTest = true;
TopDownSpringArm->bUsePawnControlRotation = false;
TopDownSpringArm->bInheritPitch = false;
TopDownSpringArm->bInheritRoll = false;
TopDownSpringArm->bInheritYaw = false;
TopDownSpringArm->SetActive(false);
TopDownCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("TopDownCamera"));
TopDownCamera->SetupAttachment(TopDownSpringArm, USpringArmComponent::SocketName);
TopDownCamera->bUsePawnControlRotation = false;
InventoryComponent = CreateDefaultSubobject<UInventoryComponent>(TEXT("InventoryComponent"));
CraftingComponent = CreateDefaultSubobject<UCraftingComponent>(TEXT("CraftingComponent"));
CombatComponent = CreateDefaultSubobject<UCombatComponent>(TEXT("CombatComponent"));
if (USkeletalMeshComponent* MeshComp = GetMesh())
{
MeshComp->SetOnlyOwnerSee(true);
MeshComp->SetupAttachment(FirstPersonCamera);
MeshComp->SetRelativeLocation(FVector(25.f, 0.f, -90.f));
}
if (UCharacterMovementComponent* Movement = GetCharacterMovement())
{
Movement->bOrientRotationToMovement = false;
Movement->RotationRate = FRotator(0.f, 540.f, 0.f);
Movement->JumpZVelocity = 500.f;
Movement->AirControl = 0.35f;
Movement->MaxWalkSpeed = 500.f;
}
bUseControllerRotationPitch = true;
bUseControllerRotationYaw = true;
bUseControllerRotationRoll = false;
}
void AVocationCharacter::BeginPlay()
{
Super::BeginPlay();
ApplyCameraModeVisuals();
if (HasAuthority())
{
if (UInventoryComponent* Inventory = GetInventoryComponent())
{
Inventory->AddItem(TEXT("Coal"), 5);
}
}
}
void AVocationCharacter::Tick(float DeltaSeconds)
{
Super::Tick(DeltaSeconds);
UpdateCameraMode(DeltaSeconds);
UpdateInteractionPrompt();
}
void AVocationCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
}
void AVocationCharacter::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(AVocationCharacter, CurrentCameraMode);
DOREPLIFETIME(AVocationCharacter, TargetCameraMode);
}
void AVocationCharacter::ToggleCameraMode()
{
const EVocationCameraMode NewMode = CurrentCameraMode == EVocationCameraMode::FirstPerson
? EVocationCameraMode::TopDown
: EVocationCameraMode::FirstPerson;
SetCameraMode(NewMode, false);
}
void AVocationCharacter::SetCameraMode(EVocationCameraMode NewMode, bool bInstant)
{
if (NewMode == TargetCameraMode && !bInstant)
{
return;
}
if (!HasAuthority())
{
ServerSetCameraMode(NewMode);
return;
}
TargetCameraMode = NewMode;
if (bInstant)
{
CurrentCameraMode = NewMode;
CameraBlendAlpha = 1.f;
bIsBlendingCamera = false;
ApplyCameraModeVisuals();
OnCameraModeChanged.Broadcast(CurrentCameraMode);
}
else
{
bIsBlendingCamera = true;
CameraBlendAlpha = 0.f;
}
if (AVocationPlayerController* PC = Cast<AVocationPlayerController>(GetController()))
{
PC->ApplyInputMappingForCameraMode(NewMode);
}
}
void AVocationCharacter::HandleMove(const FInputActionValue& Value)
{
const FVector2D MovementVector = Value.Get<FVector2D>();
if (MovementVector.IsNearlyZero())
{
return;
}
if (CurrentCameraMode == EVocationCameraMode::TopDown)
{
const FRotator YawRotation(0.f, GetControlRotation().Yaw, 0.f);
const FVector Forward = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
const FVector Right = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
AddMovementInput(Forward, MovementVector.Y);
AddMovementInput(Right, MovementVector.X);
}
else
{
AddMovementInput(GetActorForwardVector(), MovementVector.Y);
AddMovementInput(GetActorRightVector(), MovementVector.X);
}
}
void AVocationCharacter::HandleLook(const FInputActionValue& Value)
{
if (CurrentCameraMode != EVocationCameraMode::FirstPerson)
{
const FVector2D LookVector = Value.Get<FVector2D>();
AddControllerYawInput(LookVector.X);
return;
}
const FVector2D LookVector = Value.Get<FVector2D>();
AddControllerYawInput(LookVector.X);
AddControllerPitchInput(LookVector.Y);
}
void AVocationCharacter::HandleJumpStarted()
{
Jump();
}
void AVocationCharacter::HandleInteract()
{
if (AActor* Target = FindInteractableTarget())
{
if (Target->GetClass()->ImplementsInterface(UInteractable::StaticClass()))
{
IInteractable::Execute_Interact(Target, this);
}
}
else if (UCraftingComponent* Crafting = GetCraftingComponent())
{
if (const AVocationPlayerState* PS = GetPlayerState<AVocationPlayerState>())
{
const TArray<FRecipeDefinition> Recipes = Crafting->GetAvailableRecipes(PS->GetActiveVocation());
for (const FRecipeDefinition& Recipe : Recipes)
{
if (Crafting->CanCraftRecipe(Recipe))
{
Crafting->CraftRecipe(Recipe);
break;
}
}
}
}
}
void AVocationCharacter::HandleAttack()
{
if (!CombatComponent)
{
return;
}
const FVector Start = GetActorLocation() + FVector(0.f, 0.f, 50.f);
const FVector End = Start + GetActorForwardVector() * CombatComponent->AttackRange;
FCollisionQueryParams Params(SCENE_QUERY_STAT(VocationAttack), false, this);
FHitResult Hit;
if (GetWorld()->LineTraceSingleByChannel(Hit, Start, End, ECC_Pawn, Params))
{
if (AVocationEnemy* Enemy = Cast<AVocationEnemy>(Hit.GetActor()))
{
if (UCombatComponent* EnemyCombat = Enemy->GetCombatComponent())
{
EnemyCombat->ServerApplyDamage(CombatComponent->AttackDamage, this);
}
}
}
}
void AVocationCharacter::HandleToggleCamera()
{
ToggleCameraMode();
}
void AVocationCharacter::HandleInventory()
{
if (UVocationGameInstance* GI = GetWorld()->GetGameInstance<UVocationGameInstance>())
{
GI->SaveGame();
}
}
AActor* AVocationCharacter::FindInteractableTarget() const
{
const FVector Start = FirstPersonCamera ? FirstPersonCamera->GetComponentLocation() : GetActorLocation();
const FVector Forward = GetControlRotation().Vector();
const FVector End = Start + Forward * InteractionRange;
FCollisionQueryParams Params(SCENE_QUERY_STAT(VocationInteract), false, this);
FHitResult Hit;
if (GetWorld()->LineTraceSingleByChannel(Hit, Start, End, ECC_Visibility, Params))
{
if (Hit.GetActor() && Hit.GetActor()->GetClass()->ImplementsInterface(UInteractable::StaticClass()))
{
if (IInteractable::Execute_CanInteract(Hit.GetActor(), const_cast<AVocationCharacter*>(this)))
{
return Hit.GetActor();
}
}
}
return nullptr;
}
void AVocationCharacter::UpdateCameraMode(float DeltaSeconds)
{
if (!bIsBlendingCamera)
{
return;
}
CameraBlendAlpha = FMath::Clamp(CameraBlendAlpha + (DeltaSeconds / FMath::Max(CameraBlendTime, 0.01f)), 0.f, 1.f);
if (CameraBlendAlpha >= 1.f)
{
CurrentCameraMode = TargetCameraMode;
bIsBlendingCamera = false;
ApplyCameraModeVisuals();
OnCameraModeChanged.Broadcast(CurrentCameraMode);
}
}
void AVocationCharacter::ApplyCameraModeVisuals()
{
const bool bFirstPerson = CurrentCameraMode == EVocationCameraMode::FirstPerson;
if (APlayerController* PC = Cast<APlayerController>(GetController()))
{
PC->SetViewTargetWithBlend(this, bIsBlendingCamera ? CameraBlendTime : 0.f);
}
FirstPersonCamera->SetActive(bFirstPerson);
TopDownSpringArm->SetActive(!bFirstPerson);
TopDownCamera->SetActive(!bFirstPerson);
bUseControllerRotationPitch = bFirstPerson;
if (UCharacterMovementComponent* Movement = GetCharacterMovement())
{
Movement->bOrientRotationToMovement = !bFirstPerson;
}
}
void AVocationCharacter::UpdateInteractionPrompt()
{
if (AVocationPlayerController* PC = Cast<AVocationPlayerController>(GetController()))
{
if (AActor* Target = FindInteractableTarget())
{
const FText Prompt = IInteractable::Execute_GetInteractionPrompt(Target);
PC->ShowInteractionPrompt(Prompt);
}
else
{
PC->ClearInteractionPrompt();
}
}
}
void AVocationCharacter::OnRep_CameraMode()
{
ApplyCameraModeVisuals();
OnCameraModeChanged.Broadcast(CurrentCameraMode);
}
void AVocationCharacter::ServerSetCameraMode_Implementation(EVocationCameraMode NewMode)
{
SetCameraMode(NewMode, false);
}

View File

@ -0,0 +1,36 @@
// Copyright VocationLife Project. All Rights Reserved.
#include "VocationEOSSubsystem.h"
#include "Misc/ConfigCacheIni.h"
void UVocationEOSSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
Super::Initialize(Collection);
GConfig->GetBool(TEXT("/Script/VocationLife.VocationEOS"), TEXT("bEOSEnabled"), bEOSEnabled, GGameIni);
GConfig->GetString(TEXT("/Script/VocationLife.VocationEOS"), TEXT("ProductId"), ProductId, GGameIni);
GConfig->GetString(TEXT("/Script/VocationLife.VocationEOS"), TEXT("SandboxId"), SandboxId, GGameIni);
GConfig->GetString(TEXT("/Script/VocationLife.VocationEOS"), TEXT("DeploymentId"), DeploymentId, GGameIni);
if (!bEOSEnabled)
{
SubsystemState = EEOSSubsystemState::Disabled;
}
}
void UVocationEOSSubsystem::InitializeEOS()
{
if (!bEOSEnabled)
{
SubsystemState = EEOSSubsystemState::Disabled;
return;
}
if (ProductId.IsEmpty() || SandboxId.IsEmpty() || DeploymentId.IsEmpty())
{
SubsystemState = EEOSSubsystemState::Error;
return;
}
SubsystemState = EEOSSubsystemState::Ready;
}

View File

@ -0,0 +1,24 @@
// Copyright VocationLife Project. All Rights Reserved.
#include "VocationEnemy.h"
#include "CombatComponent.h"
AVocationEnemy::AVocationEnemy()
{
CombatComponent = CreateDefaultSubobject<UCombatComponent>(TEXT("CombatComponent"));
AutoPossessAI = EAutoPossessAI::PlacedInWorldOrSpawned;
}
void AVocationEnemy::BeginPlay()
{
Super::BeginPlay();
if (CombatComponent)
{
CombatComponent->OnDeath.AddDynamic(this, &AVocationEnemy::HandleDeath);
}
}
void AVocationEnemy::HandleDeath()
{
Destroy();
}

View File

@ -0,0 +1,177 @@
// Copyright VocationLife Project. All Rights Reserved.
#include "VocationGameInstance.h"
#include "VocationSaveGame.h"
#include "VocationGraphicsLibrary.h"
#include "VocationCharacter.h"
#include "VocationPlayerState.h"
#include "InventoryComponent.h"
#include "CombatComponent.h"
#include "GameFramework/PlayerState.h"
#include "Kismet/GameplayStatics.h"
UVocationGameInstance::UVocationGameInstance()
{
}
void UVocationGameInstance::Init()
{
Super::Init();
InitializeDefaultData();
OnDataReady.Broadcast();
}
void UVocationGameInstance::InitializeDefaultData()
{
ItemDefinitions.Empty();
RecipeDefinitions.Empty();
auto AddItem = [this](FName Id, const FString& Name, int32 MaxStack, bool bMaterial = true)
{
FVocationItemDefinition Def;
Def.ItemId = Id;
Def.DisplayName = FText::FromString(Name);
Def.MaxStack = MaxStack;
Def.bIsCraftingMaterial = bMaterial;
ItemDefinitions.Add(Id, Def);
};
AddItem(TEXT("IronOre"), TEXT("Iron Ore"), 99);
AddItem(TEXT("Coal"), TEXT("Coal"), 99);
AddItem(TEXT("IronIngot"), TEXT("Iron Ingot"), 50, false);
AddItem(TEXT("IronSword"), TEXT("Iron Sword"), 1, false);
FRecipeDefinition SmeltIron;
SmeltIron.RecipeId = TEXT("SmeltIron");
SmeltIron.DisplayName = FText::FromString(TEXT("Smelt Iron Ingot"));
SmeltIron.RequiredVocation = EVocationLifeClass::Blacksmith;
SmeltIron.Ingredients = {
{TEXT("IronOre"), 2},
{TEXT("Coal"), 1}
};
SmeltIron.OutputItemId = TEXT("IronIngot");
SmeltIron.OutputQuantity = 1;
RecipeDefinitions.Add(SmeltIron);
FRecipeDefinition ForgeSword;
ForgeSword.RecipeId = TEXT("ForgeIronSword");
ForgeSword.DisplayName = FText::FromString(TEXT("Forge Iron Sword"));
ForgeSword.RequiredVocation = EVocationLifeClass::Blacksmith;
ForgeSword.Ingredients = {
{TEXT("IronIngot"), 2},
{TEXT("Coal"), 1}
};
ForgeSword.OutputItemId = TEXT("IronSword");
ForgeSword.OutputQuantity = 1;
RecipeDefinitions.Add(ForgeSword);
}
bool UVocationGameInstance::FindItemDefinition(FName ItemId, FVocationItemDefinition& OutDefinition) const
{
if (const FVocationItemDefinition* Found = ItemDefinitions.Find(ItemId))
{
OutDefinition = *Found;
return true;
}
return false;
}
bool UVocationGameInstance::SaveGame(const FString& SlotName)
{
UVocationSaveGame* SaveObject = Cast<UVocationSaveGame>(
UGameplayStatics::CreateSaveGameObject(UVocationSaveGame::StaticClass()));
if (!SaveObject)
{
return false;
}
if (APawn* Pawn = UGameplayStatics::GetPlayerPawn(this, 0))
{
SaveObject->PlayerLocation = Pawn->GetActorLocation();
SaveObject->PlayerRotation = Pawn->GetActorRotation();
if (const AVocationCharacter* Character = Cast<AVocationCharacter>(Pawn))
{
if (UCombatComponent* Combat = Character->GetCombatComponent())
{
SaveObject->PlayerHealth = Combat->GetHealth();
}
if (UInventoryComponent* Inventory = Character->GetInventoryComponent())
{
SaveObject->Inventory = Inventory->GetSlots();
}
}
}
if (APlayerState* PlayerState = UGameplayStatics::GetPlayerState(this, 0))
{
if (const AVocationPlayerState* PS = Cast<AVocationPlayerState>(PlayerState))
{
SaveObject->ActiveVocation = PS->GetActiveVocation();
}
}
SaveObject->GraphicsPreset = CurrentGraphicsPreset;
SaveObject->bRayTracingEnabled = bRayTracingEnabled;
const bool bSuccess = UGameplayStatics::SaveGameToSlot(SaveObject, SlotName, 0);
if (bSuccess)
{
CurrentSave = SaveObject;
}
return bSuccess;
}
bool UVocationGameInstance::LoadGame(const FString& SlotName)
{
if (!UGameplayStatics::DoesSaveGameExist(SlotName, 0))
{
return false;
}
USaveGame* Loaded = UGameplayStatics::LoadGameFromSlot(SlotName, 0);
UVocationSaveGame* SaveObject = Cast<UVocationSaveGame>(Loaded);
if (!SaveObject)
{
return false;
}
CurrentSave = SaveObject;
SetGraphicsPreset(SaveObject->GraphicsPreset, SaveObject->bRayTracingEnabled);
if (APawn* Pawn = UGameplayStatics::GetPlayerPawn(this, 0))
{
Pawn->SetActorLocation(SaveObject->PlayerLocation);
Pawn->SetActorRotation(SaveObject->PlayerRotation);
if (AVocationCharacter* Character = Cast<AVocationCharacter>(Pawn))
{
if (UInventoryComponent* Inventory = Character->GetInventoryComponent())
{
Inventory->SetSlots(SaveObject->Inventory);
}
}
}
if (APlayerState* PlayerState = UGameplayStatics::GetPlayerState(this, 0))
{
if (AVocationPlayerState* PS = Cast<AVocationPlayerState>(PlayerState))
{
PS->SetActiveVocation(SaveObject->ActiveVocation);
}
}
return true;
}
bool UVocationGameInstance::HasSaveGame(const FString& SlotName) const
{
return UGameplayStatics::DoesSaveGameExist(SlotName, 0);
}
void UVocationGameInstance::SetGraphicsPreset(EVocationGraphicsPreset Preset, bool bEnableRayTracing)
{
CurrentGraphicsPreset = Preset;
bRayTracingEnabled = bEnableRayTracing;
UVocationGraphicsLibrary::ApplyGraphicsPreset(Preset, bEnableRayTracing);
}

View File

@ -0,0 +1,64 @@
// Copyright VocationLife Project. All Rights Reserved.
#include "VocationGameMode.h"
#include "VocationCharacter.h"
#include "VocationPlayerController.h"
#include "VocationPlayerState.h"
#include "VocationHUD.h"
#include "VocationTestWorldBuilder.h"
#include "Engine/World.h"
#include "GameFramework/PlayerController.h"
#include "Kismet/GameplayStatics.h"
#include "Misc/ConfigCacheIni.h"
#include "Misc/Paths.h"
AVocationGameMode::AVocationGameMode()
{
DefaultPawnClass = AVocationCharacter::StaticClass();
PlayerControllerClass = AVocationPlayerController::StaticClass();
PlayerStateClass = AVocationPlayerState::StaticClass();
HUDClass = AVocationHUD::StaticClass();
bUseSeamlessTravel = true;
}
void AVocationGameMode::InitGame(const FString& MapName, const FString& Options, FString& ErrorMessage)
{
Super::InitGame(MapName, Options, ErrorMessage);
int32 ConfigMaxPlayers = MaxCoopPlayers;
const FString ServerConfigPath = FPaths::ProjectConfigDir() / TEXT("ServerConfig.ini");
GConfig->GetInt(TEXT("/Script/VocationLife.VocationServerSettings"), TEXT("MaxPlayers"), ConfigMaxPlayers, ServerConfigPath);
MaxCoopPlayers = ConfigMaxPlayers;
}
void AVocationGameMode::PostLogin(APlayerController* NewPlayer)
{
Super::PostLogin(NewPlayer);
if (HasAuthority() && GetWorld())
{
TArray<AActor*> ExistingBuilders;
UGameplayStatics::GetAllActorsOfClass(GetWorld(), AVocationTestWorldBuilder::StaticClass(), ExistingBuilders);
if (ExistingBuilders.Num() == 0)
{
GetWorld()->SpawnActor<AVocationTestWorldBuilder>();
}
}
}
FString AVocationGameMode::InitNewPlayer(APlayerController* NewPlayerController, const FUniqueNetIdRepl& UniqueId,
const FString& Options, const FString& Portal)
{
FString ErrorMessage = Super::InitNewPlayer(NewPlayerController, UniqueId, Options, Portal);
if (!ErrorMessage.IsEmpty())
{
return ErrorMessage;
}
if (GetNumPlayers() >= MaxCoopPlayers)
{
return FString::Printf(TEXT("Server is full (%d/%d players)."), MaxCoopPlayers, MaxCoopPlayers);
}
return ErrorMessage;
}

View File

@ -0,0 +1,70 @@
// Copyright VocationLife Project. All Rights Reserved.
#include "VocationGraphicsLibrary.h"
#include "Scalability.h"
#include "Engine/Engine.h"
#include "GameFramework/GameUserSettings.h"
void UVocationGraphicsLibrary::ApplyGraphicsPreset(EVocationGraphicsPreset Preset, bool bEnableRayTracing)
{
UGameUserSettings* Settings = GEngine ? GEngine->GetGameUserSettings() : nullptr;
if (!Settings)
{
return;
}
int32 QualityLevel = 2;
switch (Preset)
{
case EVocationGraphicsPreset::Low:
QualityLevel = 0;
break;
case EVocationGraphicsPreset::Medium:
QualityLevel = 1;
break;
case EVocationGraphicsPreset::High:
QualityLevel = 2;
break;
case EVocationGraphicsPreset::Ultra:
case EVocationGraphicsPreset::RayTracing:
QualityLevel = 3;
break;
default:
break;
}
Scalability::FQualityLevels QualityLevels;
QualityLevels.SetFromSingleQualityLevel(QualityLevel);
Scalability::SetQualityLevels(QualityLevels);
Settings->SetOverallScalabilityLevel(QualityLevel);
const bool bUseRayTracing = bEnableRayTracing || Preset == EVocationGraphicsPreset::RayTracing;
if (GEngine)
{
GEngine->Exec(nullptr, *FString::Printf(TEXT("r.RayTracing %d"), bUseRayTracing ? 1 : 0));
GEngine->Exec(nullptr, *FString::Printf(TEXT("r.Lumen.HardwareRayTracing %d"), bUseRayTracing ? 1 : 0));
GEngine->Exec(nullptr, *FString::Printf(TEXT("r.PathTracing %d"), bUseRayTracing ? 1 : 0));
}
Settings->ApplySettings(false);
Settings->SaveSettings();
}
FText UVocationGraphicsLibrary::GetGraphicsPresetDisplayName(EVocationGraphicsPreset Preset)
{
switch (Preset)
{
case EVocationGraphicsPreset::Low:
return FText::FromString(TEXT("Low"));
case EVocationGraphicsPreset::Medium:
return FText::FromString(TEXT("Medium"));
case EVocationGraphicsPreset::High:
return FText::FromString(TEXT("High"));
case EVocationGraphicsPreset::Ultra:
return FText::FromString(TEXT("Ultra"));
case EVocationGraphicsPreset::RayTracing:
return FText::FromString(TEXT("Ray Tracing"));
default:
return FText::FromString(TEXT("Custom"));
}
}

View File

@ -0,0 +1,83 @@
// Copyright VocationLife Project. All Rights Reserved.
#include "VocationHUD.h"
#include "VocationGraphicsLibrary.h"
#include "Engine/Canvas.h"
#include "Engine/Engine.h"
void AVocationHUD::BeginPlay()
{
Super::BeginPlay();
}
void AVocationHUD::SetInteractionPrompt(const FText& Prompt)
{
CurrentInteractionPrompt = Prompt;
}
void AVocationHUD::ClearInteractionPrompt()
{
CurrentInteractionPrompt = FText::GetEmpty();
}
void AVocationHUD::SetPauseVisible(bool bVisible)
{
bShowPauseMenu = bVisible;
}
void AVocationHUD::ShowDebugStatus(const FString& Status)
{
DebugStatusLine = Status;
}
void AVocationHUD::ApplyGraphicsPresetFromHUD(EVocationGraphicsPreset Preset, bool bRayTracing)
{
UVocationGraphicsLibrary::ApplyGraphicsPreset(Preset, bRayTracing);
}
void AVocationHUD::DrawHUD()
{
Super::DrawHUD();
DrawMainHUD();
}
void AVocationHUD::DrawMainHUD()
{
if (!Canvas)
{
return;
}
const float Scale = Canvas->ClipX / 1920.f;
const FLinearColor TextColor(1.f, 1.f, 1.f, 1.f);
if (!CurrentInteractionPrompt.IsEmpty())
{
const FString PromptString = CurrentInteractionPrompt.ToString();
float TextWidth = 0.f;
float TextHeight = 0.f;
Canvas->StrLen(GEngine->GetMediumFont(), PromptString, TextWidth, TextHeight);
Canvas->DrawColor = FColor::White;
Canvas->DrawText(GEngine->GetMediumFont(), PromptString,
(Canvas->ClipX - TextWidth) * 0.5f, Canvas->ClipY * 0.85f, 1.2f * Scale, 1.2f * Scale);
}
if (!DebugStatusLine.IsEmpty())
{
Canvas->DrawColor = FColor::Cyan;
Canvas->DrawText(GEngine->GetSmallFont(), DebugStatusLine, 40.f * Scale, 40.f * Scale, Scale, Scale);
}
if (bShowPauseMenu)
{
const FString PauseText = TEXT("PAUSED\nF1 Low | F2 Medium | F3 High | F4 Ultra | F5 Ray Tracing\nH Host | J Join | S Save");
Canvas->DrawColor = FColor::Yellow;
Canvas->DrawText(GEngine->GetMediumFont(), PauseText, Canvas->ClipX * 0.35f, Canvas->ClipY * 0.35f, Scale, Scale);
}
else
{
const FString Controls = TEXT("WASD Move | Mouse Look | V Camera | E Interact/Craft | LMB Attack | I Save | Esc Pause");
Canvas->DrawColor = FColor(200, 200, 200);
Canvas->DrawText(GEngine->GetSmallFont(), Controls, 40.f * Scale, Canvas->ClipY - 60.f * Scale, Scale, Scale);
}
}

View File

@ -0,0 +1,281 @@
// Copyright VocationLife Project. All Rights Reserved.
#include "VocationPlayerController.h"
#include "VocationCharacter.h"
#include "VocationHUD.h"
#include "VocationSessionSubsystem.h"
#include "VocationGameInstance.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
#include "InputAction.h"
#include "InputMappingContext.h"
#include "InputCoreTypes.h"
#include "Blueprint/UserWidget.h"
#include "Kismet/GameplayStatics.h"
AVocationPlayerController::AVocationPlayerController()
{
}
void AVocationPlayerController::BeginPlay()
{
Super::BeginPlay();
CreateRuntimeInputAssets();
if (ULocalPlayer* LocalPlayer = GetLocalPlayer())
{
if (UEnhancedInputLocalPlayerSubsystem* Subsystem =
LocalPlayer->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>())
{
Subsystem->AddMappingContext(SharedMappingContext, SharedMappingPriority);
Subsystem->AddMappingContext(FirstPersonMappingContext, ModeMappingPriority);
}
}
if (AVocationHUD* HUD = Cast<AVocationHUD>(GetHUD()))
{
VocationHUD = HUD;
}
if (UVocationGameInstance* GI = GetGameInstance<UVocationGameInstance>())
{
GI->SetGraphicsPreset(GI->GetGraphicsPreset(), GI->IsRayTracingEnabled());
}
}
void AVocationPlayerController::SetupInputComponent()
{
Super::SetupInputComponent();
if (UEnhancedInputComponent* EnhancedInput = Cast<UEnhancedInputComponent>(InputComponent))
{
if (AVocationCharacter* Character = Cast<AVocationCharacter>(GetPawn()))
{
EnhancedInput->BindAction(MoveAction, ETriggerEvent::Triggered, Character, &AVocationCharacter::HandleMove);
EnhancedInput->BindAction(LookAction, ETriggerEvent::Triggered, Character, &AVocationCharacter::HandleLook);
EnhancedInput->BindAction(JumpAction, ETriggerEvent::Started, Character, &AVocationCharacter::HandleJumpStarted);
EnhancedInput->BindAction(InteractAction, ETriggerEvent::Started, Character, &AVocationCharacter::HandleInteract);
EnhancedInput->BindAction(AttackAction, ETriggerEvent::Started, Character, &AVocationCharacter::HandleAttack);
EnhancedInput->BindAction(ToggleCameraAction, ETriggerEvent::Started, Character, &AVocationCharacter::HandleToggleCamera);
EnhancedInput->BindAction(InventoryAction, ETriggerEvent::Started, Character, &AVocationCharacter::HandleInventory);
EnhancedInput->BindAction(PauseAction, ETriggerEvent::Started, this, &AVocationPlayerController::TogglePauseMenu);
}
}
InputComponent->BindKey(EKeys::F1, IE_Pressed, this, &AVocationPlayerController::ApplyLowGraphics).bConsumeInput = false;
InputComponent->BindKey(EKeys::F2, IE_Pressed, this, &AVocationPlayerController::ApplyMediumGraphics).bConsumeInput = false;
InputComponent->BindKey(EKeys::F3, IE_Pressed, this, &AVocationPlayerController::ApplyHighGraphics).bConsumeInput = false;
InputComponent->BindKey(EKeys::F4, IE_Pressed, this, &AVocationPlayerController::ApplyUltraGraphics).bConsumeInput = false;
InputComponent->BindKey(EKeys::F5, IE_Pressed, this, &AVocationPlayerController::ApplyRayTracingGraphics).bConsumeInput = false;
InputComponent->BindKey(EKeys::H, IE_Pressed, this, &AVocationPlayerController::HostCoopGame).bConsumeInput = false;
InputComponent->BindKey(EKeys::J, IE_Pressed, this, &AVocationPlayerController::JoinFirstFoundSession).bConsumeInput = false;
InputComponent->BindKey(EKeys::S, IE_Pressed, this, &AVocationPlayerController::QuickSave).bConsumeInput = false;
}
void AVocationPlayerController::OnPossess(APawn* InPawn)
{
Super::OnPossess(InPawn);
SetupInputComponent();
if (AVocationCharacter* Character = Cast<AVocationCharacter>(InPawn))
{
ApplyInputMappingForCameraMode(Character->GetCameraMode());
}
}
void AVocationPlayerController::TogglePauseMenu()
{
bPauseMenuOpen = !bPauseMenuOpen;
SetPause(bPauseMenuOpen);
if (VocationHUD)
{
VocationHUD->SetPauseVisible(bPauseMenuOpen);
}
}
void AVocationPlayerController::ShowInteractionPrompt(const FText& Prompt)
{
if (VocationHUD)
{
VocationHUD->SetInteractionPrompt(Prompt);
}
}
void AVocationPlayerController::ClearInteractionPrompt()
{
if (VocationHUD)
{
VocationHUD->ClearInteractionPrompt();
}
}
void AVocationPlayerController::HostCoopGame()
{
if (UVocationSessionSubsystem* Sessions = GetGameInstance()->GetSubsystem<UVocationSessionSubsystem>())
{
Sessions->HostSession(4, TEXT("VocationLife"));
}
}
void AVocationPlayerController::FindCoopGames()
{
if (UVocationSessionSubsystem* Sessions = GetGameInstance()->GetSubsystem<UVocationSessionSubsystem>())
{
Sessions->FindSessions();
}
}
void AVocationPlayerController::JoinCoopGame(int32 SessionIndex)
{
if (UVocationSessionSubsystem* Sessions = GetGameInstance()->GetSubsystem<UVocationSessionSubsystem>())
{
Sessions->JoinSessionByIndex(SessionIndex);
}
}
void AVocationPlayerController::ApplyInputMappingForCameraMode(EVocationCameraMode CameraMode)
{
if (ULocalPlayer* LocalPlayer = GetLocalPlayer())
{
if (UEnhancedInputLocalPlayerSubsystem* Subsystem =
LocalPlayer->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>())
{
Subsystem->RemoveMappingContext(FirstPersonMappingContext);
Subsystem->RemoveMappingContext(TopDownMappingContext);
if (CameraMode == EVocationCameraMode::FirstPerson)
{
Subsystem->AddMappingContext(FirstPersonMappingContext, ModeMappingPriority);
}
else
{
Subsystem->AddMappingContext(TopDownMappingContext, ModeMappingPriority);
}
}
}
}
void AVocationPlayerController::CreateRuntimeInputAssets()
{
MoveAction = NewObject<UInputAction>(this, TEXT("IA_Move"));
MoveAction->ValueType = EInputActionValueType::Axis2D;
LookAction = NewObject<UInputAction>(this, TEXT("IA_Look"));
LookAction->ValueType = EInputActionValueType::Axis2D;
JumpAction = NewObject<UInputAction>(this, TEXT("IA_Jump"));
InteractAction = NewObject<UInputAction>(this, TEXT("IA_Interact"));
AttackAction = NewObject<UInputAction>(this, TEXT("IA_Attack"));
ToggleCameraAction = NewObject<UInputAction>(this, TEXT("IA_ToggleCamera"));
InventoryAction = NewObject<UInputAction>(this, TEXT("IA_Inventory"));
PauseAction = NewObject<UInputAction>(this, TEXT("IA_Pause"));
SharedMappingContext = NewObject<UInputMappingContext>(this, TEXT("IMC_Vocation_Shared"));
SharedMappingContext->MapKey(JumpAction, EKeys::SpaceBar);
SharedMappingContext->MapKey(InteractAction, EKeys::E);
SharedMappingContext->MapKey(AttackAction, EKeys::LeftMouseButton);
SharedMappingContext->MapKey(ToggleCameraAction, EKeys::V);
SharedMappingContext->MapKey(InventoryAction, EKeys::I);
SharedMappingContext->MapKey(PauseAction, EKeys::Escape);
SharedMappingContext->MapKey(JumpAction, EKeys::Gamepad_FaceButton_Bottom);
SharedMappingContext->MapKey(InteractAction, EKeys::Gamepad_FaceButton_Left);
SharedMappingContext->MapKey(AttackAction, EKeys::Gamepad_RightTrigger);
SharedMappingContext->MapKey(ToggleCameraAction, EKeys::Gamepad_Special_Right);
SharedMappingContext->MapKey(InventoryAction, EKeys::Gamepad_FaceButton_Top);
SharedMappingContext->MapKey(PauseAction, EKeys::Gamepad_Special_Left);
FirstPersonMappingContext = NewObject<UInputMappingContext>(this, TEXT("IMC_Vocation_FP"));
FirstPersonMappingContext->MapKey(MoveAction, EKeys::W);
FirstPersonMappingContext->MapKey(MoveAction, EKeys::S);
FirstPersonMappingContext->MapKey(MoveAction, EKeys::A);
FirstPersonMappingContext->MapKey(MoveAction, EKeys::D);
FirstPersonMappingContext->MapKey(LookAction, EKeys::Mouse2D);
FirstPersonMappingContext->MapKey(MoveAction, EKeys::Gamepad_LeftX);
FirstPersonMappingContext->MapKey(MoveAction, EKeys::Gamepad_LeftY);
FirstPersonMappingContext->MapKey(LookAction, EKeys::Gamepad_RightX);
FirstPersonMappingContext->MapKey(LookAction, EKeys::Gamepad_RightY);
TopDownMappingContext = NewObject<UInputMappingContext>(this, TEXT("IMC_Vocation_TP"));
TopDownMappingContext->MapKey(MoveAction, EKeys::W);
TopDownMappingContext->MapKey(MoveAction, EKeys::S);
TopDownMappingContext->MapKey(MoveAction, EKeys::A);
TopDownMappingContext->MapKey(MoveAction, EKeys::D);
TopDownMappingContext->MapKey(LookAction, EKeys::Mouse2D);
TopDownMappingContext->MapKey(MoveAction, EKeys::Gamepad_LeftX);
TopDownMappingContext->MapKey(MoveAction, EKeys::Gamepad_LeftY);
TopDownMappingContext->MapKey(LookAction, EKeys::Gamepad_RightX);
}
void AVocationPlayerController::ApplyLowGraphics()
{
if (UVocationGameInstance* GI = GetGameInstance<UVocationGameInstance>())
{
GI->SetGraphicsPreset(EVocationGraphicsPreset::Low, false);
}
}
void AVocationPlayerController::ApplyMediumGraphics()
{
if (UVocationGameInstance* GI = GetGameInstance<UVocationGameInstance>())
{
GI->SetGraphicsPreset(EVocationGraphicsPreset::Medium, false);
}
}
void AVocationPlayerController::ApplyHighGraphics()
{
if (UVocationGameInstance* GI = GetGameInstance<UVocationGameInstance>())
{
GI->SetGraphicsPreset(EVocationGraphicsPreset::High, false);
}
}
void AVocationPlayerController::ApplyUltraGraphics()
{
if (UVocationGameInstance* GI = GetGameInstance<UVocationGameInstance>())
{
GI->SetGraphicsPreset(EVocationGraphicsPreset::Ultra, false);
}
}
void AVocationPlayerController::ApplyRayTracingGraphics()
{
if (UVocationGameInstance* GI = GetGameInstance<UVocationGameInstance>())
{
GI->SetGraphicsPreset(EVocationGraphicsPreset::RayTracing, true);
}
}
void AVocationPlayerController::JoinFirstFoundSession()
{
if (UVocationSessionSubsystem* Sessions = GetGameInstance()->GetSubsystem<UVocationSessionSubsystem>())
{
Sessions->OnSessionSearchComplete.AddDynamic(this, &AVocationPlayerController::HandleSessionSearchForJoin);
Sessions->FindSessions();
}
}
void AVocationPlayerController::HandleSessionSearchForJoin(const TArray<FString>& SessionNames)
{
if (UVocationSessionSubsystem* Sessions = GetGameInstance()->GetSubsystem<UVocationSessionSubsystem>())
{
Sessions->OnSessionSearchComplete.RemoveDynamic(this, &AVocationPlayerController::HandleSessionSearchForJoin);
if (SessionNames.Num() > 0)
{
Sessions->JoinSessionByIndex(0);
}
}
}
void AVocationPlayerController::QuickSave()
{
if (UVocationGameInstance* GI = GetGameInstance<UVocationGameInstance>())
{
GI->SaveGame();
if (VocationHUD)
{
VocationHUD->ShowDebugStatus(TEXT("Game saved."));
}
}
}

View File

@ -0,0 +1,30 @@
// Copyright VocationLife Project. All Rights Reserved.
#include "VocationPlayerState.h"
#include "Net/UnrealNetwork.h"
AVocationPlayerState::AVocationPlayerState()
{
bReplicates = true;
}
void AVocationPlayerState::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(AVocationPlayerState, ActiveVocation);
DOREPLIFETIME(AVocationPlayerState, Health);
DOREPLIFETIME(AVocationPlayerState, MaxHealth);
}
void AVocationPlayerState::SetActiveVocation(EVocationLifeClass NewVocation)
{
if (HasAuthority())
{
ActiveVocation = NewVocation;
OnRep_ActiveVocation();
}
}
void AVocationPlayerState::OnRep_ActiveVocation()
{
}

View File

@ -0,0 +1,166 @@
// Copyright VocationLife Project. All Rights Reserved.
#include "VocationSessionSubsystem.h"
#include "OnlineSubsystem.h"
#include "OnlineSessionSettings.h"
#include "Interfaces/OnlineSessionInterface.h"
static const FName VOCATION_SESSION_NAME = TEXT("VocationLifeSession");
void UVocationSessionSubsystem::HostSession(int32 MaxPlayers, const FString& SessionName)
{
IOnlineSubsystem* OnlineSubsystem = IOnlineSubsystem::Get();
if (!OnlineSubsystem)
{
OnSessionJoined.Broadcast(false);
return;
}
IOnlineSessionPtr Sessions = OnlineSubsystem->GetSessionInterface();
if (!Sessions.IsValid())
{
OnSessionJoined.Broadcast(false);
return;
}
CreateSessionDelegateHandle = Sessions->AddOnCreateSessionCompleteDelegate_Handle(
FOnCreateSessionCompleteDelegate::CreateUObject(this, &UVocationSessionSubsystem::OnCreateSessionComplete));
TSharedRef<FOnlineSessionSettings> SessionSettings = MakeShared<FOnlineSessionSettings>();
SessionSettings->bIsLANMatch = true;
SessionSettings->NumPublicConnections = MaxPlayers;
SessionSettings->bShouldAdvertise = true;
SessionSettings->bUsesPresence = true;
SessionSettings->Set(FName(TEXT("SESSION_NAME")), SessionName, EOnlineDataAdvertisementType::ViaOnlineServiceAndPing);
const ULocalPlayer* LocalPlayer = GetWorld() && GetWorld()->GetFirstLocalPlayerFromController()
? GetWorld()->GetFirstLocalPlayerFromController()
: nullptr;
if (!Sessions->CreateSession(0, VOCATION_SESSION_NAME, *SessionSettings))
{
Sessions->ClearOnCreateSessionCompleteDelegate_Handle(CreateSessionDelegateHandle);
OnSessionJoined.Broadcast(false);
}
}
void UVocationSessionSubsystem::FindSessions()
{
IOnlineSubsystem* OnlineSubsystem = IOnlineSubsystem::Get();
if (!OnlineSubsystem)
{
OnSessionSearchComplete.Broadcast({});
return;
}
IOnlineSessionPtr Sessions = OnlineSubsystem->GetSessionInterface();
if (!Sessions.IsValid())
{
OnSessionSearchComplete.Broadcast({});
return;
}
FindSessionsDelegateHandle = Sessions->AddOnFindSessionsCompleteDelegate_Handle(
FOnFindSessionsCompleteDelegate::CreateUObject(this, &UVocationSessionSubsystem::OnFindSessionsComplete));
SessionSearch = MakeShared<FOnlineSessionSearch>();
SessionSearch->bIsLanQuery = true;
SessionSearch->MaxSearchResults = 20;
SessionSearch->QuerySettings.Set(FName(TEXT("PRESENCESEARCH")), true, EOnlineComparisonOp::Equals);
if (!Sessions->FindSessions(0, SessionSearch.ToSharedRef()))
{
Sessions->ClearOnFindSessionsCompleteDelegate_Handle(FindSessionsDelegateHandle);
OnSessionSearchComplete.Broadcast({});
}
}
void UVocationSessionSubsystem::JoinSessionByIndex(int32 SessionIndex)
{
IOnlineSubsystem* OnlineSubsystem = IOnlineSubsystem::Get();
if (!OnlineSubsystem || !SessionSearch.IsValid())
{
OnSessionJoined.Broadcast(false);
return;
}
IOnlineSessionPtr Sessions = OnlineSubsystem->GetSessionInterface();
if (!Sessions.IsValid() || !SessionSearch->SearchResults.IsValidIndex(SessionIndex))
{
OnSessionJoined.Broadcast(false);
return;
}
JoinSessionDelegateHandle = Sessions->AddOnJoinSessionCompleteDelegate_Handle(
FOnJoinSessionCompleteDelegate::CreateUObject(this, &UVocationSessionSubsystem::OnJoinSessionComplete));
if (!Sessions->JoinSession(0, VOCATION_SESSION_NAME, SessionSearch->SearchResults[SessionIndex]))
{
Sessions->ClearOnJoinSessionCompleteDelegate_Handle(JoinSessionDelegateHandle);
OnSessionJoined.Broadcast(false);
}
}
void UVocationSessionSubsystem::DestroySession()
{
if (IOnlineSubsystem* OnlineSubsystem = IOnlineSubsystem::Get())
{
if (IOnlineSessionPtr Sessions = OnlineSubsystem->GetSessionInterface())
{
Sessions->DestroySession(VOCATION_SESSION_NAME);
}
}
bIsHosting = false;
}
void UVocationSessionSubsystem::OnCreateSessionComplete(FName SessionName, bool bWasSuccessful)
{
if (IOnlineSubsystem* OnlineSubsystem = IOnlineSubsystem::Get())
{
if (IOnlineSessionPtr Sessions = OnlineSubsystem->GetSessionInterface())
{
Sessions->ClearOnCreateSessionCompleteDelegate_Handle(CreateSessionDelegateHandle);
}
}
bIsHosting = bWasSuccessful;
OnSessionJoined.Broadcast(bWasSuccessful);
}
void UVocationSessionSubsystem::OnFindSessionsComplete(bool bWasSuccessful)
{
TArray<FString> SessionNames;
if (bWasSuccessful && SessionSearch.IsValid())
{
for (const FOnlineSessionSearchResult& Result : SessionSearch->SearchResults)
{
FString Name;
Result.Session.SessionSettings.Get(FName(TEXT("SESSION_NAME")), Name);
SessionNames.Add(Name.IsEmpty() ? TEXT("VocationLife Server") : Name);
}
}
if (IOnlineSubsystem* OnlineSubsystem = IOnlineSubsystem::Get())
{
if (IOnlineSessionPtr Sessions = OnlineSubsystem->GetSessionInterface())
{
Sessions->ClearOnFindSessionsCompleteDelegate_Handle(FindSessionsDelegateHandle);
}
}
OnSessionSearchComplete.Broadcast(SessionNames);
}
void UVocationSessionSubsystem::OnJoinSessionComplete(FName SessionName, EOnJoinSessionCompleteResult::Type Result)
{
if (IOnlineSubsystem* OnlineSubsystem = IOnlineSubsystem::Get())
{
if (IOnlineSessionPtr Sessions = OnlineSubsystem->GetSessionInterface())
{
Sessions->ClearOnJoinSessionCompleteDelegate_Handle(JoinSessionDelegateHandle);
}
}
const bool bSuccess = Result == EOnJoinSessionCompleteResult::Success;
OnSessionJoined.Broadcast(bSuccess);
}

View File

@ -0,0 +1,84 @@
// Copyright VocationLife Project. All Rights Reserved.
#include "VocationTestWorldBuilder.h"
#include "MiningNode.h"
#include "VocationEnemy.h"
#include "Engine/StaticMeshActor.h"
#include "Components/StaticMeshComponent.h"
#include "Engine/StaticMesh.h"
#include "Engine/DirectionalLight.h"
#include "Engine/SkyLight.h"
#include "Components/DirectionalLightComponent.h"
#include "Components/SkyLightComponent.h"
#include "EngineUtils.h"
#include "Kismet/GameplayStatics.h"
#include "UObject/ConstructorHelpers.h"
AVocationTestWorldBuilder::AVocationTestWorldBuilder()
{
PrimaryActorTick.bCanEverTick = false;
}
void AVocationTestWorldBuilder::BeginPlay()
{
Super::BeginPlay();
if (bAutoBuildOnBeginPlay && GetWorld() && HasAuthority())
{
BuildTestWorld();
}
}
void AVocationTestWorldBuilder::BuildTestWorld()
{
UWorld* World = GetWorld();
if (!World)
{
return;
}
static ConstructorHelpers::FObjectFinder<UStaticMesh> PlaneMesh(TEXT("/Engine/BasicShapes/Plane.Plane"));
if (PlaneMesh.Succeeded())
{
FActorSpawnParameters SpawnParams;
SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
if (AStaticMeshActor* Floor = World->SpawnActor<AStaticMeshActor>(FVector::ZeroVector, FRotator::ZeroRotator, SpawnParams))
{
Floor->GetStaticMeshComponent()->SetStaticMesh(PlaneMesh.Object);
Floor->SetActorScale3D(FVector(20.f, 20.f, 1.f));
}
}
FActorSpawnParameters SpawnParams;
SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
World->SpawnActor<AMiningNode>(FVector(400.f, 0.f, 50.f), FRotator::ZeroRotator, SpawnParams);
World->SpawnActor<AMiningNode>(FVector(-350.f, 250.f, 50.f), FRotator::ZeroRotator, SpawnParams);
World->SpawnActor<AVocationEnemy>(FVector(800.f, 0.f, 100.f), FRotator(180.f, 0.f, 0.f), SpawnParams);
TArray<AActor*> FoundLights;
UGameplayStatics::GetAllActorsOfClass(World, ADirectionalLight::StaticClass(), FoundLights);
if (FoundLights.Num() == 0)
{
ADirectionalLight* Sun = World->SpawnActor<ADirectionalLight>();
if (Sun)
{
Sun->SetActorRotation(FRotator(-45.f, 45.f, 0.f));
if (UDirectionalLightComponent* Light = Sun->GetComponent())
{
Light->SetIntensity(6.f);
}
}
}
TArray<AActor*> FoundSkies;
UGameplayStatics::GetAllActorsOfClass(World, ASkyLight::StaticClass(), FoundSkies);
if (FoundSkies.Num() == 0)
{
ASkyLight* Sky = World->SpawnActor<ASkyLight>();
if (Sky && Sky->GetLightComponent())
{
Sky->GetLightComponent()->SetIntensity(1.f);
}
}
}

View File

@ -0,0 +1,58 @@
// Copyright VocationLife Project. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "CombatComponent.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnHealthChanged, float, NewHealth, float, MaxHealth);
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnDeath);
UCLASS(ClassGroup = (VocationLife), meta = (BlueprintSpawnableComponent))
class VOCATIONLIFE_API UCombatComponent : public UActorComponent
{
GENERATED_BODY()
public:
UCombatComponent();
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
UFUNCTION(BlueprintCallable, Category = "VocationLife|Combat")
void ApplyDamage(float DamageAmount, AActor* DamageCauser);
UFUNCTION(Server, Reliable, BlueprintCallable, Category = "VocationLife|Combat")
void ServerApplyDamage(float DamageAmount, AActor* DamageCauser);
UFUNCTION(BlueprintCallable, Category = "VocationLife|Combat")
bool IsAlive() const { return Health > 0.f; }
UFUNCTION(BlueprintCallable, Category = "VocationLife|Combat")
float GetHealth() const { return Health; }
UFUNCTION(BlueprintCallable, Category = "VocationLife|Combat")
float GetMaxHealth() const { return MaxHealth; }
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VocationLife|Combat")
float AttackDamage = 15.f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VocationLife|Combat")
float AttackRange = 200.f;
UPROPERTY(BlueprintAssignable, Category = "VocationLife|Combat")
FOnHealthChanged OnHealthChanged;
UPROPERTY(BlueprintAssignable, Category = "VocationLife|Combat")
FOnDeath OnDeath;
protected:
UPROPERTY(ReplicatedUsing = OnRep_Health, EditAnywhere, BlueprintReadWrite, Category = "VocationLife|Combat")
float Health = 100.f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VocationLife|Combat")
float MaxHealth = 100.f;
UFUNCTION()
void OnRep_Health();
};

View File

@ -0,0 +1,40 @@
// Copyright VocationLife Project. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "VocationTypes.h"
#include "CraftingComponent.generated.h"
class UInventoryComponent;
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnCraftCompleted, FName, RecipeId, FName, OutputItemId);
UCLASS(ClassGroup = (VocationLife), meta = (BlueprintSpawnableComponent))
class VOCATIONLIFE_API UCraftingComponent : public UActorComponent
{
GENERATED_BODY()
public:
UCraftingComponent();
UFUNCTION(BlueprintCallable, Category = "VocationLife|Crafting")
bool CanCraftRecipe(const FRecipeDefinition& Recipe) const;
UFUNCTION(BlueprintCallable, Category = "VocationLife|Crafting")
bool CraftRecipe(const FRecipeDefinition& Recipe);
UFUNCTION(Server, Reliable, BlueprintCallable, Category = "VocationLife|Crafting")
void ServerCraftRecipe(FName RecipeId);
UFUNCTION(BlueprintCallable, Category = "VocationLife|Crafting")
TArray<FRecipeDefinition> GetAvailableRecipes(EVocationLifeClass Vocation) const;
UPROPERTY(BlueprintAssignable, Category = "VocationLife|Crafting")
FOnCraftCompleted OnCraftCompleted;
protected:
UInventoryComponent* GetInventory() const;
bool FindRecipe(FName RecipeId, FRecipeDefinition& OutRecipe) const;
};

View File

@ -0,0 +1,28 @@
// Copyright VocationLife Project. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "Interactable.generated.h"
UINTERFACE(MinimalAPI, BlueprintType)
class UInteractable : public UInterface
{
GENERATED_BODY()
};
class VOCATIONLIFE_API IInteractable
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Interaction")
FText GetInteractionPrompt() const;
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Interaction")
bool CanInteract(APawn* InteractingPawn) const;
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Interaction")
void Interact(APawn* InteractingPawn);
};

View File

@ -0,0 +1,58 @@
// Copyright VocationLife Project. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "VocationTypes.h"
#include "InventoryComponent.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnInventoryChanged);
UCLASS(ClassGroup = (VocationLife), meta = (BlueprintSpawnableComponent))
class VOCATIONLIFE_API UInventoryComponent : public UActorComponent
{
GENERATED_BODY()
public:
UInventoryComponent();
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
UFUNCTION(BlueprintCallable, Category = "VocationLife|Inventory")
bool AddItem(FName ItemId, int32 Quantity);
UFUNCTION(BlueprintCallable, Category = "VocationLife|Inventory")
bool RemoveItem(FName ItemId, int32 Quantity);
UFUNCTION(BlueprintCallable, Category = "VocationLife|Inventory")
bool HasItem(FName ItemId, int32 Quantity = 1) const;
UFUNCTION(BlueprintCallable, Category = "VocationLife|Inventory")
int32 GetItemCount(FName ItemId) const;
UFUNCTION(BlueprintCallable, Category = "VocationLife|Inventory")
const TArray<FInventorySlot>& GetSlots() const { return Slots; }
UFUNCTION(BlueprintCallable, Category = "VocationLife|Inventory")
void SetSlots(const TArray<FInventorySlot>& NewSlots);
UFUNCTION(Server, Reliable, BlueprintCallable, Category = "VocationLife|Inventory")
void ServerAddItem(FName ItemId, int32 Quantity);
UFUNCTION(Server, Reliable, BlueprintCallable, Category = "VocationLife|Inventory")
void ServerRemoveItem(FName ItemId, int32 Quantity);
UPROPERTY(BlueprintAssignable, Category = "VocationLife|Inventory")
FOnInventoryChanged OnInventoryChanged;
protected:
UPROPERTY(ReplicatedUsing = OnRep_Slots, BlueprintReadOnly, Category = "VocationLife|Inventory")
TArray<FInventorySlot> Slots;
UFUNCTION()
void OnRep_Slots();
int32 FindSlotIndex(FName ItemId) const;
int32 GetMaxStack(FName ItemId) const;
};

View File

@ -0,0 +1,52 @@
// Copyright VocationLife Project. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Interactable.h"
#include "MiningNode.generated.h"
class UStaticMeshComponent;
UCLASS()
class VOCATIONLIFE_API AMiningNode : public AActor, public IInteractable
{
GENERATED_BODY()
public:
AMiningNode();
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
virtual FText GetInteractionPrompt_Implementation() const override;
virtual bool CanInteract_Implementation(APawn* InteractingPawn) const override;
virtual void Interact_Implementation(APawn* InteractingPawn) override;
UFUNCTION(BlueprintCallable, Category = "VocationLife|Mining")
bool IsDepleted() const { return bIsDepleted; }
protected:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "VocationLife|Mining")
TObjectPtr<UStaticMeshComponent> MeshComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "VocationLife|Mining")
FName OreItemId = TEXT("IronOre");
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "VocationLife|Mining")
int32 OreYield = 3;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "VocationLife|Mining")
float RespawnTimeSeconds = 30.f;
UPROPERTY(ReplicatedUsing = OnRep_Depleted, BlueprintReadOnly, Category = "VocationLife|Mining")
bool bIsDepleted = false;
UFUNCTION()
void OnRep_Depleted();
UFUNCTION()
void HandleRespawn();
FTimerHandle RespawnTimerHandle;
};

View File

@ -0,0 +1,129 @@
// Copyright VocationLife Project. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "VocationTypes.h"
#include "VocationCharacter.generated.h"
class UCameraComponent;
class USpringArmComponent;
class UInventoryComponent;
class UCraftingComponent;
class UCombatComponent;
class UInputAction;
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnCameraModeChanged, EVocationCameraMode, NewMode);
UCLASS(Blueprintable)
class VOCATIONLIFE_API AVocationCharacter : public ACharacter
{
GENERATED_BODY()
public:
AVocationCharacter();
virtual void BeginPlay() override;
virtual void Tick(float DeltaSeconds) override;
virtual void SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) override;
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
UFUNCTION(BlueprintCallable, Category = "VocationLife|Camera")
void ToggleCameraMode();
UFUNCTION(BlueprintCallable, Category = "VocationLife|Camera")
void SetCameraMode(EVocationCameraMode NewMode, bool bInstant = false);
UFUNCTION(BlueprintCallable, Category = "VocationLife|Camera")
EVocationCameraMode GetCameraMode() const { return CurrentCameraMode; }
UFUNCTION(BlueprintCallable, Category = "VocationLife|Input")
void HandleMove(const struct FInputActionValue& Value);
UFUNCTION(BlueprintCallable, Category = "VocationLife|Input")
void HandleLook(const struct FInputActionValue& Value);
UFUNCTION(BlueprintCallable, Category = "VocationLife|Input")
void HandleJumpStarted();
UFUNCTION(BlueprintCallable, Category = "VocationLife|Input")
void HandleInteract();
UFUNCTION(BlueprintCallable, Category = "VocationLife|Input")
void HandleAttack();
UFUNCTION(BlueprintCallable, Category = "VocationLife|Input")
void HandleToggleCamera();
UFUNCTION(BlueprintCallable, Category = "VocationLife|Input")
void HandleInventory();
UFUNCTION(BlueprintCallable, Category = "VocationLife|Interaction")
AActor* FindInteractableTarget() const;
UFUNCTION(BlueprintCallable, Category = "VocationLife")
UInventoryComponent* GetInventoryComponent() const { return InventoryComponent; }
UFUNCTION(BlueprintCallable, Category = "VocationLife")
UCraftingComponent* GetCraftingComponent() const { return CraftingComponent; }
UFUNCTION(BlueprintCallable, Category = "VocationLife")
UCombatComponent* GetCombatComponent() const { return CombatComponent; }
UPROPERTY(BlueprintAssignable, Category = "VocationLife|Camera")
FOnCameraModeChanged OnCameraModeChanged;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "VocationLife|Camera")
TObjectPtr<UCameraComponent> FirstPersonCamera;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "VocationLife|Camera")
TObjectPtr<USpringArmComponent> TopDownSpringArm;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "VocationLife|Camera")
TObjectPtr<UCameraComponent> TopDownCamera;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "VocationLife")
TObjectPtr<UInventoryComponent> InventoryComponent;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "VocationLife")
TObjectPtr<UCraftingComponent> CraftingComponent;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "VocationLife")
TObjectPtr<UCombatComponent> CombatComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "VocationLife|Camera")
float CameraBlendTime = 0.35f;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "VocationLife|Camera")
float TopDownArmLength = 900.f;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "VocationLife|Camera")
float TopDownPitch = -45.f;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "VocationLife|Interaction")
float InteractionRange = 300.f;
protected:
void UpdateCameraMode(float DeltaSeconds);
void ApplyCameraModeVisuals();
void UpdateInteractionPrompt();
UPROPERTY(ReplicatedUsing = OnRep_CameraMode, BlueprintReadOnly, Category = "VocationLife|Camera")
EVocationCameraMode CurrentCameraMode = EVocationCameraMode::FirstPerson;
UPROPERTY(ReplicatedUsing = OnRep_CameraMode)
EVocationCameraMode TargetCameraMode = EVocationCameraMode::FirstPerson;
UPROPERTY()
float CameraBlendAlpha = 1.f;
UPROPERTY()
bool bIsBlendingCamera = false;
UFUNCTION()
void OnRep_CameraMode();
UFUNCTION(Server, Reliable)
void ServerSetCameraMode(EVocationCameraMode NewMode);
};

View File

@ -0,0 +1,57 @@
// Copyright VocationLife Project. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Subsystems/GameInstanceSubsystem.h"
#include "VocationEOSSubsystem.generated.h"
UENUM(BlueprintType)
enum class EEOSSubsystemState : uint8
{
Disabled,
Initializing,
Ready,
Error
};
/**
* Epic Online Services integration layer for future crossplay.
* Configured via Config/DefaultVocationLife.ini disabled until EOS credentials are set.
*/
UCLASS()
class VOCATIONLIFE_API UVocationEOSSubsystem : public UGameInstanceSubsystem
{
GENERATED_BODY()
public:
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
UFUNCTION(BlueprintCallable, Category = "VocationLife|EOS")
bool IsEOSEnabled() const { return bEOSEnabled; }
UFUNCTION(BlueprintCallable, Category = "VocationLife|EOS")
EEOSSubsystemState GetState() const { return SubsystemState; }
UFUNCTION(BlueprintCallable, Category = "VocationLife|EOS")
void InitializeEOS();
UFUNCTION(BlueprintCallable, Category = "VocationLife|EOS")
FString GetProductId() const { return ProductId; }
protected:
UPROPERTY()
bool bEOSEnabled = false;
UPROPERTY()
EEOSSubsystemState SubsystemState = EEOSSubsystemState::Disabled;
UPROPERTY()
FString ProductId;
UPROPERTY()
FString SandboxId;
UPROPERTY()
FString DeploymentId;
};

View File

@ -0,0 +1,30 @@
// Copyright VocationLife Project. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "VocationEnemy.generated.h"
class UCombatComponent;
UCLASS()
class VOCATIONLIFE_API AVocationEnemy : public ACharacter
{
GENERATED_BODY()
public:
AVocationEnemy();
virtual void BeginPlay() override;
UFUNCTION(BlueprintCallable, Category = "VocationLife|Enemy")
UCombatComponent* GetCombatComponent() const { return CombatComponent; }
protected:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "VocationLife|Enemy")
TObjectPtr<UCombatComponent> CombatComponent;
UFUNCTION()
void HandleDeath();
};

View File

@ -0,0 +1,71 @@
// Copyright VocationLife Project. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Engine/GameInstance.h"
#include "VocationTypes.h"
#include "VocationGameInstance.generated.h"
class UVocationSaveGame;
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnVocationDataReady);
UCLASS(Blueprintable)
class VOCATIONLIFE_API UVocationGameInstance : public UGameInstance
{
GENERATED_BODY()
public:
UVocationGameInstance();
virtual void Init() override;
UFUNCTION(BlueprintCallable, Category = "VocationLife|Save")
bool SaveGame(const FString& SlotName = TEXT("VocationSave"));
UFUNCTION(BlueprintCallable, Category = "VocationLife|Save")
bool LoadGame(const FString& SlotName = TEXT("VocationSave"));
UFUNCTION(BlueprintCallable, Category = "VocationLife|Save")
bool HasSaveGame(const FString& SlotName = TEXT("VocationSave")) const;
UFUNCTION(BlueprintCallable, Category = "VocationLife|Data")
const TMap<FName, FVocationItemDefinition>& GetItemDefinitions() const { return ItemDefinitions; }
UFUNCTION(BlueprintCallable, Category = "VocationLife|Data")
const TArray<FRecipeDefinition>& GetRecipeDefinitions() const { return RecipeDefinitions; }
UFUNCTION(BlueprintCallable, Category = "VocationLife|Data")
bool FindItemDefinition(FName ItemId, FVocationItemDefinition& OutDefinition) const;
UFUNCTION(BlueprintCallable, Category = "VocationLife|Graphics")
void SetGraphicsPreset(EVocationGraphicsPreset Preset, bool bEnableRayTracing);
UFUNCTION(BlueprintCallable, Category = "VocationLife|Graphics")
EVocationGraphicsPreset GetGraphicsPreset() const { return CurrentGraphicsPreset; }
UFUNCTION(BlueprintCallable, Category = "VocationLife|Graphics")
bool IsRayTracingEnabled() const { return bRayTracingEnabled; }
UPROPERTY(BlueprintAssignable, Category = "VocationLife")
FOnVocationDataReady OnDataReady;
UPROPERTY(BlueprintReadOnly, Category = "VocationLife|Save")
TObjectPtr<UVocationSaveGame> CurrentSave;
protected:
void InitializeDefaultData();
UPROPERTY()
TMap<FName, FVocationItemDefinition> ItemDefinitions;
UPROPERTY()
TArray<FRecipeDefinition> RecipeDefinitions;
UPROPERTY()
EVocationGraphicsPreset CurrentGraphicsPreset = EVocationGraphicsPreset::High;
UPROPERTY()
bool bRayTracingEnabled = false;
};

View File

@ -0,0 +1,29 @@
// Copyright VocationLife Project. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "VocationGameMode.generated.h"
UCLASS(Blueprintable)
class VOCATIONLIFE_API AVocationGameMode : public AGameModeBase
{
GENERATED_BODY()
public:
AVocationGameMode();
virtual void InitGame(const FString& MapName, const FString& Options, FString& ErrorMessage) override;
virtual void PostLogin(APlayerController* NewPlayer) override;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "VocationLife|Multiplayer")
int32 MaxCoopPlayers = 4;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "VocationLife|Server")
FString ServerWelcomeMessage = TEXT("Welcome to VocationLife");
protected:
virtual FString InitNewPlayer(APlayerController* NewPlayerController, const FUniqueNetIdRepl& UniqueId,
const FString& Options, const FString& Portal) override;
};

View File

@ -0,0 +1,21 @@
// Copyright VocationLife Project. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "VocationTypes.h"
#include "VocationGraphicsLibrary.generated.h"
UCLASS()
class VOCATIONLIFE_API UVocationGraphicsLibrary : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, Category = "VocationLife|Graphics")
static void ApplyGraphicsPreset(EVocationGraphicsPreset Preset, bool bEnableRayTracing);
UFUNCTION(BlueprintCallable, Category = "VocationLife|Graphics")
static FText GetGraphicsPresetDisplayName(EVocationGraphicsPreset Preset);
};

View File

@ -0,0 +1,48 @@
// Copyright VocationLife Project. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/HUD.h"
#include "VocationTypes.h"
#include "VocationHUD.generated.h"
class UUserWidget;
UCLASS()
class VOCATIONLIFE_API AVocationHUD : public AHUD
{
GENERATED_BODY()
public:
virtual void BeginPlay() override;
UFUNCTION(BlueprintCallable, Category = "VocationLife|UI")
void SetInteractionPrompt(const FText& Prompt);
UFUNCTION(BlueprintCallable, Category = "VocationLife|UI")
void ClearInteractionPrompt();
UFUNCTION(BlueprintCallable, Category = "VocationLife|UI")
void SetPauseVisible(bool bVisible);
UFUNCTION(BlueprintCallable, Category = "VocationLife|UI")
void ShowDebugStatus(const FString& Status);
UFUNCTION(BlueprintCallable, Category = "VocationLife|UI")
void ApplyGraphicsPresetFromHUD(EVocationGraphicsPreset Preset, bool bRayTracing);
protected:
void DrawMainHUD();
virtual void DrawHUD() override;
UPROPERTY(EditDefaultsOnly, Category = "VocationLife|UI")
TSubclassOf<UUserWidget> PauseMenuWidgetClass;
UPROPERTY()
TObjectPtr<UUserWidget> PauseMenuWidget;
FText CurrentInteractionPrompt;
FString DebugStatusLine;
bool bShowPauseMenu = false;
};

View File

@ -0,0 +1,123 @@
// Copyright VocationLife Project. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/PlayerController.h"
#include "VocationTypes.h"
#include "VocationPlayerController.generated.h"
class UInputMappingContext;
class UInputAction;
class AVocationHUD;
UCLASS()
class VOCATIONLIFE_API AVocationPlayerController : public APlayerController
{
GENERATED_BODY()
public:
AVocationPlayerController();
virtual void BeginPlay() override;
virtual void SetupInputComponent() override;
virtual void OnPossess(APawn* InPawn) override;
UFUNCTION(BlueprintCallable, Category = "VocationLife|UI")
void TogglePauseMenu();
UFUNCTION(BlueprintCallable, Category = "VocationLife|UI")
void ShowInteractionPrompt(const FText& Prompt);
UFUNCTION(BlueprintCallable, Category = "VocationLife|UI")
void ClearInteractionPrompt();
UFUNCTION(BlueprintCallable, Category = "VocationLife|Session")
void HostCoopGame();
UFUNCTION(BlueprintCallable, Category = "VocationLife|Session")
void FindCoopGames();
UFUNCTION(BlueprintCallable, Category = "VocationLife|Session")
void JoinCoopGame(int32 SessionIndex);
UFUNCTION(BlueprintCallable, Category = "VocationLife|Input")
void ApplyInputMappingForCameraMode(EVocationCameraMode CameraMode);
UFUNCTION()
void ApplyLowGraphics();
UFUNCTION()
void ApplyMediumGraphics();
UFUNCTION()
void ApplyHighGraphics();
UFUNCTION()
void ApplyUltraGraphics();
UFUNCTION()
void ApplyRayTracingGraphics();
UFUNCTION()
void JoinFirstFoundSession();
UFUNCTION()
void QuickSave();
UFUNCTION()
void HandleSessionSearchForJoin(const TArray<FString>& SessionNames);
protected:
void CreateRuntimeInputAssets();
UPROPERTY()
TObjectPtr<UInputMappingContext> SharedMappingContext;
UPROPERTY()
TObjectPtr<UInputMappingContext> FirstPersonMappingContext;
UPROPERTY()
TObjectPtr<UInputMappingContext> TopDownMappingContext;
UPROPERTY()
TObjectPtr<UInputAction> MoveAction;
UPROPERTY()
TObjectPtr<UInputAction> LookAction;
UPROPERTY()
TObjectPtr<UInputAction> JumpAction;
UPROPERTY()
TObjectPtr<UInputAction> InteractAction;
UPROPERTY()
TObjectPtr<UInputAction> AttackAction;
UPROPERTY()
TObjectPtr<UInputAction> ToggleCameraAction;
UPROPERTY()
TObjectPtr<UInputAction> InventoryAction;
UPROPERTY()
TObjectPtr<UInputAction> PauseAction;
UPROPERTY()
TObjectPtr<AVocationHUD> VocationHUD;
UPROPERTY(EditDefaultsOnly, Category = "VocationLife|Input")
int32 SharedMappingPriority = 0;
UPROPERTY(EditDefaultsOnly, Category = "VocationLife|Input")
int32 ModeMappingPriority = 1;
UPROPERTY(EditDefaultsOnly, Category = "VocationLife|Input")
float MouseSensitivity = 1.f;
UPROPERTY(EditDefaultsOnly, Category = "VocationLife|Input")
float GamepadLookSensitivity = 1.f;
bool bPauseMenuOpen = false;
};

View File

@ -0,0 +1,38 @@
// Copyright VocationLife Project. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/PlayerState.h"
#include "VocationTypes.h"
#include "VocationPlayerState.generated.h"
UCLASS()
class VOCATIONLIFE_API AVocationPlayerState : public APlayerState
{
GENERATED_BODY()
public:
AVocationPlayerState();
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
UFUNCTION(BlueprintCallable, Category = "VocationLife")
EVocationLifeClass GetActiveVocation() const { return ActiveVocation; }
UFUNCTION(BlueprintCallable, Category = "VocationLife")
void SetActiveVocation(EVocationLifeClass NewVocation);
UPROPERTY(ReplicatedUsing = OnRep_ActiveVocation, BlueprintReadOnly, Category = "VocationLife")
EVocationLifeClass ActiveVocation = EVocationLifeClass::Miner;
UPROPERTY(Replicated, BlueprintReadOnly, Category = "VocationLife")
float Health = 100.f;
UPROPERTY(Replicated, BlueprintReadOnly, Category = "VocationLife")
float MaxHealth = 100.f;
protected:
UFUNCTION()
void OnRep_ActiveVocation();
};

View File

@ -0,0 +1,36 @@
// Copyright VocationLife Project. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/SaveGame.h"
#include "VocationTypes.h"
#include "VocationSaveGame.generated.h"
UCLASS()
class VOCATIONLIFE_API UVocationSaveGame : public USaveGame
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintReadWrite, Category = "Save")
FVector PlayerLocation = FVector::ZeroVector;
UPROPERTY(BlueprintReadWrite, Category = "Save")
FRotator PlayerRotation = FRotator::ZeroRotator;
UPROPERTY(BlueprintReadWrite, Category = "Save")
TArray<FInventorySlot> Inventory;
UPROPERTY(BlueprintReadWrite, Category = "Save")
EVocationLifeClass ActiveVocation = EVocationLifeClass::Miner;
UPROPERTY(BlueprintReadWrite, Category = "Save")
float PlayerHealth = 100.f;
UPROPERTY(BlueprintReadWrite, Category = "Save")
EVocationGraphicsPreset GraphicsPreset = EVocationGraphicsPreset::High;
UPROPERTY(BlueprintReadWrite, Category = "Save")
bool bRayTracingEnabled = false;
};

View File

@ -0,0 +1,51 @@
// Copyright VocationLife Project. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Subsystems/GameInstanceSubsystem.h"
#include "Interfaces/OnlineSessionInterface.h"
#include "VocationSessionSubsystem.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnSessionSearchComplete, const TArray<FString>&, SessionNames);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnSessionJoined, bool, bSuccess);
UCLASS()
class VOCATIONLIFE_API UVocationSessionSubsystem : public UGameInstanceSubsystem
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, Category = "VocationLife|Session")
void HostSession(int32 MaxPlayers = 4, const FString& SessionName = TEXT("VocationLife"));
UFUNCTION(BlueprintCallable, Category = "VocationLife|Session")
void FindSessions();
UFUNCTION(BlueprintCallable, Category = "VocationLife|Session")
void JoinSessionByIndex(int32 SessionIndex);
UFUNCTION(BlueprintCallable, Category = "VocationLife|Session")
void DestroySession();
UFUNCTION(BlueprintCallable, Category = "VocationLife|Session")
bool IsHosting() const { return bIsHosting; }
UPROPERTY(BlueprintAssignable, Category = "VocationLife|Session")
FOnSessionSearchComplete OnSessionSearchComplete;
UPROPERTY(BlueprintAssignable, Category = "VocationLife|Session")
FOnSessionJoined OnSessionJoined;
protected:
void OnCreateSessionComplete(FName SessionName, bool bWasSuccessful);
void OnFindSessionsComplete(bool bWasSuccessful);
void OnJoinSessionComplete(FName SessionName, EOnJoinSessionCompleteResult::Type Result);
FDelegateHandle CreateSessionDelegateHandle;
FDelegateHandle FindSessionsDelegateHandle;
FDelegateHandle JoinSessionDelegateHandle;
TSharedPtr<class FOnlineSessionSearch> SessionSearch;
bool bIsHosting = false;
};

View File

@ -0,0 +1,28 @@
// Copyright VocationLife Project. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "VocationTestWorldBuilder.generated.h"
/**
* Spawns a minimal playable test layout when Lvl_VocationTest has no placed actors.
* Place one instance in the test map or let GameMode spawn it automatically.
*/
UCLASS()
class VOCATIONLIFE_API AVocationTestWorldBuilder : public AActor
{
GENERATED_BODY()
public:
AVocationTestWorldBuilder();
virtual void BeginPlay() override;
protected:
void BuildTestWorld();
UPROPERTY(EditDefaultsOnly, Category = "VocationLife|World")
bool bAutoBuildOnBeginPlay = true;
};

View File

@ -0,0 +1,107 @@
// Copyright VocationLife Project. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "VocationTypes.generated.h"
UENUM(BlueprintType)
enum class EVocationCameraMode : uint8
{
FirstPerson UMETA(DisplayName = "First Person"),
TopDown UMETA(DisplayName = "Top Down")
};
UENUM(BlueprintType)
enum class EVocationGraphicsPreset : uint8
{
Low UMETA(DisplayName = "Low"),
Medium UMETA(DisplayName = "Medium"),
High UMETA(DisplayName = "High"),
Ultra UMETA(DisplayName = "Ultra"),
RayTracing UMETA(DisplayName = "Ray Tracing")
};
UENUM(BlueprintType)
enum class EVocationLifeClass : uint8
{
None,
Miner,
Blacksmith,
Paladin,
Mercenary,
Hunter,
Wizard,
Woodcutter,
Angler,
Cook,
Carpenter,
Tailor,
Alchemist
};
USTRUCT(BlueprintType)
struct FVocationItemDefinition
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item")
FName ItemId = NAME_None;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item")
FText DisplayName;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item")
int32 MaxStack = 99;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item")
bool bIsCraftingMaterial = true;
};
USTRUCT(BlueprintType)
struct FInventorySlot
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventory")
FName ItemId = NAME_None;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventory")
int32 Quantity = 0;
};
USTRUCT(BlueprintType)
struct FRecipeIngredient
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Recipe")
FName ItemId = NAME_None;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Recipe")
int32 Quantity = 1;
};
USTRUCT(BlueprintType)
struct FRecipeDefinition
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Recipe")
FName RecipeId = NAME_None;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Recipe")
FText DisplayName;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Recipe")
EVocationLifeClass RequiredVocation = EVocationLifeClass::None;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Recipe")
TArray<FRecipeIngredient> Ingredients;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Recipe")
FName OutputItemId = NAME_None;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Recipe")
int32 OutputQuantity = 1;
};

View File

@ -0,0 +1,34 @@
// Copyright VocationLife Project. All Rights Reserved.
using UnrealBuildTool;
public class VocationLife : ModuleRules
{
public VocationLife(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(new string[]
{
"Core",
"CoreUObject",
"Engine",
"InputCore",
"EnhancedInput",
"UMG",
"Slate",
"SlateCore",
"OnlineSubsystem",
"OnlineSubsystemUtils",
"Json",
"JsonUtilities"
});
if (Target.Platform == UnrealTargetPlatform.Win64 ||
Target.Platform == UnrealTargetPlatform.Linux ||
Target.Platform == UnrealTargetPlatform.Mac)
{
PrivateDependencyModuleNames.Add("OnlineSubsystemSteam");
}
}
}

View File

@ -0,0 +1,6 @@
// Copyright VocationLife Project. All Rights Reserved.
#include "VocationLife.h"
#include "Modules/ModuleManager.h"
IMPLEMENT_PRIMARY_GAME_MODULE(FDefaultGameModuleImpl, VocationLife, "VocationLife");

View File

@ -0,0 +1,5 @@
// Copyright VocationLife Project. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"

View File

@ -0,0 +1,15 @@
// Copyright VocationLife Project. All Rights Reserved.
using UnrealBuildTool;
using System.Collections.Generic;
public class VocationLifeEditorTarget : TargetRules
{
public VocationLifeEditorTarget(TargetInfo Target) : base(Target)
{
Type = TargetType.Editor;
DefaultBuildSettings = BuildSettingsVersion.V7;
IncludeOrderVersion = EngineIncludeOrderVersion.Unreal5_8;
ExtraModuleNames.Add("VocationLife");
}
}

View File

@ -0,0 +1,16 @@
// Copyright VocationLife Project. All Rights Reserved.
using UnrealBuildTool;
using System.Collections.Generic;
public class VocationLifeServerTarget : TargetRules
{
public VocationLifeServerTarget(TargetInfo Target) : base(Target)
{
Type = TargetType.Server;
DefaultBuildSettings = BuildSettingsVersion.V7;
IncludeOrderVersion = EngineIncludeOrderVersion.Unreal5_8;
ExtraModuleNames.Add("VocationLife");
bUseChecksInShipping = true;
}
}

50
VocationLife.uproject Normal file
View File

@ -0,0 +1,50 @@
{
"FileVersion": 3,
"EngineAssociation": "5.8",
"Category": "",
"Description": "Fantasy Life inspired life-sim remake with dual camera, co-op multiplayer, and optional ray tracing.",
"Modules": [
{
"Name": "VocationLife",
"Type": "Runtime",
"LoadingPhase": "Default"
}
],
"Plugins": [
{
"Name": "ModelingToolsEditorMode",
"Enabled": true,
"TargetAllowList": [
"Editor"
]
},
{
"Name": "Landmass",
"Enabled": true
},
{
"Name": "InEditorDocumentation",
"Enabled": true,
"TargetAllowList": [
"Editor"
],
"PlatformAllowList": [
"Win64",
"Mac",
"Linux"
]
},
{
"Name": "AIAssistant",
"Enabled": true
},
{
"Name": "OnlineSubsystemSteam",
"Enabled": true
},
{
"Name": "OnlineSubsystemEOS",
"Enabled": false
}
]
}