454 lines
16 KiB
C++
454 lines
16 KiB
C++
// © 2025 Naked People Team. All Rights Reserved.
|
|
|
|
#include "NakedDesireCharacter.h"
|
|
#include "NakedDesire/Locations/LocationTrigger.h"
|
|
#include "NakedDesire/Clothing/ClothingManager.h"
|
|
#include "Components/CapsuleComponent.h"
|
|
#include "GameFramework/CharacterMovementComponent.h"
|
|
#include "NakedDesire/InteractionSystem/InteractionManager.h"
|
|
#include "NakedDesire/InteractionSystem/InteractionTarget.h"
|
|
#include "NakedDesire/MissionBuilder/MissionsManager.h"
|
|
#include "NakedDesire/Stats/StatsManager.h"
|
|
#include "EnhancedInputComponent.h"
|
|
#include "EnhancedInputSubsystems.h"
|
|
#include "Kismet/GameplayStatics.h"
|
|
#include "Internationalization/Text.h"
|
|
#include "NakedDesire/Clothing/ClothingItem.h"
|
|
#include "NakedDesire/Clothing/ClothingItemInstance.h"
|
|
#include "NakedDesire/Global/Constants.h"
|
|
#include "NakedDesire/Global/NakedDesireUserSettings.h"
|
|
#include "Perception/AIPerceptionStimuliSourceComponent.h"
|
|
#include "Perception/AISense_Sight.h"
|
|
|
|
ANakedDesireCharacter::ANakedDesireCharacter()
|
|
{
|
|
GetCharacterMovement()->MaxWalkSpeed = WalkSpeed;
|
|
|
|
GetCapsuleComponent()->OnComponentBeginOverlap.AddUniqueDynamic(this, &ANakedDesireCharacter::OnBeginOverlap);
|
|
GetCapsuleComponent()->OnComponentEndOverlap.AddUniqueDynamic(this, &ANakedDesireCharacter::OnEndOverlap);
|
|
|
|
bUseControllerRotationYaw = false;
|
|
|
|
GetCharacterMovement()->bOrientRotationToMovement = true;
|
|
GetCharacterMovement()->GetNavAgentPropertiesRef().bCanCrouch = true;
|
|
|
|
ClothingManager = CreateDefaultSubobject<UClothingManager>("Clothing Manager");
|
|
StatsManager = CreateDefaultSubobject<UStatsManager>("Stats Manager");
|
|
MissionsManager = CreateDefaultSubobject<UMissionsManager>("Missions Manager");
|
|
InteractionManager = CreateDefaultSubobject<UInteractionManager>("Interaction Manager");
|
|
|
|
NipplesMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>("Nipples");
|
|
NipplesMeshComponent->SetupAttachment(GetMesh());
|
|
AnalMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>("Anal");
|
|
AnalMeshComponent->SetupAttachment(GetMesh());
|
|
VaginaMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>("Vagina");
|
|
VaginaMeshComponent->SetupAttachment(GetMesh());
|
|
HeadMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>("Head");
|
|
HeadMeshComponent->SetupAttachment(GetMesh());
|
|
NeckMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>("Neck");
|
|
NeckMeshComponent->SetupAttachment(GetMesh());
|
|
FaceMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>("Face");
|
|
FaceMeshComponent->SetupAttachment(GetMesh());
|
|
EyesMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>("Eyes");
|
|
EyesMeshComponent->SetupAttachment(GetMesh());
|
|
BodyMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>("Body");
|
|
BodyMeshComponent->SetupAttachment(GetMesh());
|
|
TopMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>("Top");
|
|
TopMeshComponent->SetupAttachment(GetMesh());
|
|
BottomMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>("Bottom");
|
|
BottomMeshComponent->SetupAttachment(GetMesh());
|
|
BraMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>("Bra");
|
|
BraMeshComponent->SetupAttachment(GetMesh());
|
|
PantiesMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>("Panties");
|
|
PantiesMeshComponent->SetupAttachment(GetMesh());
|
|
SocksMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>("Socks");
|
|
SocksMeshComponent->SetupAttachment(GetMesh());
|
|
ShoesMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>("Shoes");
|
|
ShoesMeshComponent->SetupAttachment(GetMesh());
|
|
|
|
BoobLCensorship = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Boob L Censorship"));
|
|
BoobLCensorship->SetupAttachment(GetMesh(), FName(TEXT("boob_l")));
|
|
BoobRCensorship = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Boob R Censorship"));
|
|
BoobRCensorship->SetupAttachment(GetMesh(), FName(TEXT("boob_r")));
|
|
FrontBottomCensorship = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Front Bottom Censorship"));
|
|
FrontBottomCensorship->SetupAttachment(GetMesh(), FName(TEXT("pelvis")));
|
|
BackBottomCensorship = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Back Bottom Censorship"));
|
|
BackBottomCensorship->SetupAttachment(GetMesh(), FName(TEXT("pelvis")));
|
|
|
|
StimuliSourceComponent = CreateDefaultSubobject<UAIPerceptionStimuliSourceComponent>(TEXT("Stimuli Source Component"));
|
|
}
|
|
|
|
EGait ANakedDesireCharacter::GetGait() const
|
|
{
|
|
return Gait;
|
|
}
|
|
|
|
EStance ANakedDesireCharacter::GetStance() const
|
|
{
|
|
return Stance;
|
|
}
|
|
|
|
void ANakedDesireCharacter::Tick(float DeltaTime)
|
|
{
|
|
Super::Tick(DeltaTime);
|
|
|
|
if (StatsManager->Stamina == 0 && Gait == EGait::Run)
|
|
{
|
|
SetIsRunning(false);
|
|
}
|
|
|
|
if (Gait == EGait::Run && GetCharacterMovement()->Velocity.SizeSquared2D() > 100 && StatsManager->Stamina > 0)
|
|
{
|
|
GetCharacterMovement()->MaxWalkSpeed = RunSpeed;
|
|
StatsManager->DecreaseStamina(7 * DeltaTime);
|
|
}
|
|
else
|
|
{
|
|
GetCharacterMovement()->MaxWalkSpeed = WalkSpeed;
|
|
if (StatsManager->Stamina < StatsManager->MaxStamina)
|
|
{
|
|
StatsManager->IncreaseStamina(15 * DeltaTime);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ANakedDesireCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
|
|
{
|
|
Super::SetupPlayerInputComponent(PlayerInputComponent);
|
|
|
|
if (UEnhancedInputComponent* EnhancedInputComponent = Cast<UEnhancedInputComponent>(PlayerInputComponent))
|
|
{
|
|
EnhancedInputComponent->BindAction(LookAction, ETriggerEvent::Triggered, this, &ANakedDesireCharacter::OnLook);
|
|
EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &ANakedDesireCharacter::OnMove);
|
|
EnhancedInputComponent->BindAction(RunAction, ETriggerEvent::Started, this, &ANakedDesireCharacter::OnRunPress);
|
|
EnhancedInputComponent->BindAction(RunAction, ETriggerEvent::Completed, this, &ANakedDesireCharacter::OnRunRelease);
|
|
EnhancedInputComponent->BindAction(CrouchAction, ETriggerEvent::Completed, this, &ANakedDesireCharacter::OnCrouchToggle);
|
|
}
|
|
}
|
|
|
|
void ANakedDesireCharacter::NotifyControllerChanged()
|
|
{
|
|
Super::NotifyControllerChanged();
|
|
|
|
if (const APlayerController* PC = UGameplayStatics::GetPlayerController(GetWorld(), 0))
|
|
{
|
|
if (const ULocalPlayer* LocalPlayer = PC->GetLocalPlayer())
|
|
{
|
|
if (UEnhancedInputLocalPlayerSubsystem* InputSystem = LocalPlayer->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>())
|
|
{
|
|
if (MappingContext)
|
|
{
|
|
InputSystem->AddMappingContext(MappingContext, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ANakedDesireCharacter::BeginPlay()
|
|
{
|
|
Super::BeginPlay();
|
|
|
|
StimuliSourceComponent->RegisterForSense(TSubclassOf<UAISense_Sight>());
|
|
StimuliSourceComponent->RegisterWithPerceptionSystem();
|
|
|
|
// UGlobalSaveGameData* SaveGameData = UGlobalSaveGameData::LoadOrCreateSaveGame(DefaultPlayerClothing, DefaultWardrobeClothing);
|
|
|
|
// if (SaveGameData)
|
|
// {
|
|
// Money = FMath::RoundToInt(SaveGameData->Money);
|
|
// }
|
|
|
|
SetupClothingSlots();
|
|
|
|
ClothingManager->OnClothingEquip.AddUniqueDynamic(this, &ANakedDesireCharacter::OnClothingEquip);
|
|
ClothingManager->OnClothingUnequip.AddUniqueDynamic(this, &ANakedDesireCharacter::OnClothingUnequip);
|
|
|
|
UNakedDesireUserSettings::GetNakedDesireUserSettings()->OnSettingsChanged.AddUniqueDynamic(this, &ANakedDesireCharacter::OnSettingsChanged);
|
|
|
|
// ClothingManager->HydrateClothing(SaveGameData);
|
|
}
|
|
|
|
UAISense_Sight::EVisibilityResult ANakedDesireCharacter::CanBeSeenFrom(const FCanBeSeenFromContext& Context,
|
|
FVector& OutSeenLocation, int32& OutNumberOfLoSChecksPerformed, int32& OutNumberOfAsyncLosCheckRequested,
|
|
float& OutSightStrength, int32* UserData, const FOnPendingVisibilityQueryProcessedDelegate* Delegate)
|
|
{
|
|
const FVector StartLocation = Context.ObserverLocation;
|
|
const FVector BoobsBoneLocation = GetMesh()->GetBoneLocation(FName(TEXT("boobs_root")));
|
|
const FVector PelvisBoneLocation = GetMesh()->GetBoneLocation(FName(TEXT("pelvis")));
|
|
|
|
OutNumberOfLoSChecksPerformed++;
|
|
|
|
FHitResult BoobsHitResult;
|
|
const bool BoobsHit = CheckSight(StartLocation, BoobsBoneLocation, BoobsHitResult, Context.IgnoreActor);
|
|
|
|
FHitResult PelvisHitResult;
|
|
const bool PelvisHit = CheckSight(StartLocation, PelvisBoneLocation, PelvisHitResult, Context.IgnoreActor);
|
|
|
|
if ((!BoobsHit || BoobsHitResult.GetActor() == this) && ClothingManager->IsBodyTypeExposed(EPrivateBodyPartType::FrontTop))
|
|
{
|
|
UE_LOG(LogTemp, Warning, TEXT("Boobs hit"));
|
|
OutSeenLocation = BoobsBoneLocation;
|
|
OutSightStrength = 1.0f;
|
|
return UAISense_Sight::EVisibilityResult::Visible;
|
|
}
|
|
|
|
if ((!PelvisHit || PelvisHitResult.GetActor() == this) &&
|
|
(ClothingManager->IsBodyTypeExposed(EPrivateBodyPartType::BackBottom) || ClothingManager->IsBodyTypeExposed(EPrivateBodyPartType::FrontBottom)))
|
|
{
|
|
UE_LOG(LogTemp, Warning, TEXT("Pelvis hit"));
|
|
OutSeenLocation = PelvisBoneLocation;
|
|
OutSightStrength = 1.0f;
|
|
return UAISense_Sight::EVisibilityResult::Visible;
|
|
}
|
|
|
|
UE_LOG(LogTemp, Warning, TEXT("Not visoble"));
|
|
|
|
return UAISense_Sight::EVisibilityResult::NotVisible;
|
|
}
|
|
|
|
bool ANakedDesireCharacter::CheckSight(const FVector& StartLocation, const FVector& EndLocation, FHitResult& HitResult,
|
|
const AActor* IgnoreActor)
|
|
{
|
|
FCollisionQueryParams QueryParams(SCENE_QUERY_STAT(AILineOfSight), true);
|
|
QueryParams.AddIgnoredActor(IgnoreActor);
|
|
QueryParams.AddIgnoredActor(this); // ignore self
|
|
|
|
const bool bHit = GetWorld()->LineTraceSingleByChannel(
|
|
HitResult,
|
|
StartLocation,
|
|
EndLocation,
|
|
ECC_Visibility,
|
|
QueryParams
|
|
);
|
|
|
|
// DrawDebugLine(GetWorld(), StartLocation, EndLocation, bHit ? FColor::Red : FColor::Green, false, 1.0f);
|
|
|
|
return bHit;
|
|
}
|
|
|
|
void ANakedDesireCharacter::OnBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
|
|
UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
|
|
{
|
|
if (OtherActor->Implements<UInteractionTarget>())
|
|
{
|
|
InteractionManager->OnTargetEnter(OtherActor);
|
|
}
|
|
|
|
if (const ALocationTrigger* AreaTrigger = Cast<ALocationTrigger>(OtherActor))
|
|
{
|
|
CurrentArea = AreaTrigger->GetLocationData();
|
|
OnAreaEnter.Broadcast(CurrentArea);
|
|
}
|
|
}
|
|
|
|
void ANakedDesireCharacter::OnEndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
|
|
UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
|
|
{
|
|
if (OtherActor->Implements<UInteractionTarget>())
|
|
{
|
|
InteractionManager->OnTargetExit(OtherActor);
|
|
}
|
|
|
|
if (const ALocationTrigger* AreaTrigger = Cast<ALocationTrigger>(OtherActor))
|
|
{
|
|
CurrentArea = nullptr;
|
|
OnAreaExit.Broadcast(AreaTrigger->GetLocationData());
|
|
}
|
|
}
|
|
|
|
void ANakedDesireCharacter::OnClothingEquip(const UClothingItemInstance* ClothingItemInstance)
|
|
{
|
|
if (ClothingItemInstance->GetClothingItem()->CoveredBodyParts.Contains(EPrivateBodyPartType::BackBottom))
|
|
{
|
|
BackBottomCensorship->SetVisibility(false);
|
|
}
|
|
if (ClothingItemInstance->GetClothingItem()->CoveredBodyParts.Contains(EPrivateBodyPartType::FrontBottom))
|
|
{
|
|
FrontBottomCensorship->SetVisibility(false);
|
|
}
|
|
if (ClothingItemInstance->GetClothingItem()->CoveredBodyParts.Contains(EPrivateBodyPartType::FrontTop))
|
|
{
|
|
BoobLCensorship->SetVisibility(false);
|
|
BoobRCensorship->SetVisibility(false);
|
|
}
|
|
}
|
|
|
|
void ANakedDesireCharacter::OnClothingUnequip(const UClothingItemInstance* ClothingItemInstance)
|
|
{
|
|
if (!UNakedDesireUserSettings::GetNakedDesireUserSettings()->GetIsCensorshipEnabled() && !IS_DEMO)
|
|
return;
|
|
|
|
if (ClothingManager->IsBodyTypeExposed(EPrivateBodyPartType::BackBottom))
|
|
{
|
|
BackBottomCensorship->SetVisibility(true);
|
|
}
|
|
if (ClothingManager->IsBodyTypeExposed(EPrivateBodyPartType::FrontBottom))
|
|
{
|
|
FrontBottomCensorship->SetVisibility(true);
|
|
}
|
|
if (ClothingManager->IsBodyTypeExposed(EPrivateBodyPartType::FrontTop))
|
|
{
|
|
BoobLCensorship->SetVisibility(true);
|
|
BoobRCensorship->SetVisibility(true);
|
|
}
|
|
}
|
|
|
|
void ANakedDesireCharacter::OnSettingsChanged(UNakedDesireUserSettings* Settings)
|
|
{
|
|
if (Settings->GetIsCensorshipEnabled())
|
|
{
|
|
if (ClothingManager->IsBodyTypeExposed(EPrivateBodyPartType::BackBottom))
|
|
{
|
|
BackBottomCensorship->SetVisibility(true);
|
|
}
|
|
if (ClothingManager->IsBodyTypeExposed(EPrivateBodyPartType::FrontBottom))
|
|
{
|
|
FrontBottomCensorship->SetVisibility(true);
|
|
}
|
|
if (ClothingManager->IsBodyTypeExposed(EPrivateBodyPartType::FrontTop))
|
|
{
|
|
BoobLCensorship->SetVisibility(true);
|
|
BoobRCensorship->SetVisibility(true);
|
|
}
|
|
}
|
|
else if (!IS_DEMO)
|
|
{
|
|
BackBottomCensorship->SetVisibility(false);
|
|
FrontBottomCensorship->SetVisibility(false);
|
|
BoobLCensorship->SetVisibility(false);
|
|
BoobRCensorship->SetVisibility(false);
|
|
}
|
|
}
|
|
|
|
void ANakedDesireCharacter::OnLook(const FInputActionValue& Value)
|
|
{
|
|
const FVector2D MouseValue = Value.Get<FVector2D>();
|
|
|
|
AddControllerYawInput(MouseValue.X);
|
|
AddControllerPitchInput(MouseValue.Y);
|
|
}
|
|
|
|
void ANakedDesireCharacter::OnMove(const FInputActionValue& Value)
|
|
{
|
|
const FVector2D MoveInput = Value.Get<FVector2D>();
|
|
const FRotator Rotator = Controller->GetControlRotation();
|
|
const FRotationMatrix Direction = FRotationMatrix(FRotator(0, Rotator.Yaw, 0));
|
|
const FVector ForwardDirection = Direction.GetUnitAxis(EAxis::X);
|
|
const FVector RightDirection = Direction.GetUnitAxis(EAxis::Y);
|
|
|
|
AddMovementInput(ForwardDirection, MoveInput.Y);
|
|
AddMovementInput(RightDirection, MoveInput.X);
|
|
}
|
|
|
|
void ANakedDesireCharacter::OnRunPress(const FInputActionValue& Value)
|
|
{
|
|
SetIsRunning(true);
|
|
}
|
|
|
|
void ANakedDesireCharacter::OnRunRelease(const FInputActionValue& Value)
|
|
{
|
|
SetIsRunning(false);
|
|
}
|
|
|
|
void ANakedDesireCharacter::OnCrouchToggle(const FInputActionValue& Value)
|
|
{
|
|
if (GetCharacterMovement()->IsCrouching())
|
|
{
|
|
UnCrouch();
|
|
}
|
|
else
|
|
{
|
|
Crouch();
|
|
}
|
|
}
|
|
|
|
void ANakedDesireCharacter::SetupClothingSlots()
|
|
{
|
|
#define LOCTEXT_NAMESPACE "ClothingSlots"
|
|
|
|
ClothingManager->ClothingSlots.Add(
|
|
FClothingSlotData(
|
|
NipplesMeshComponent, EClothingSlotType::Nipples,
|
|
nullptr,
|
|
LOCTEXT("Nipples", "Nipples")));
|
|
ClothingManager->ClothingSlots.Add(
|
|
FClothingSlotData(
|
|
AnalMeshComponent, EClothingSlotType::Anal,
|
|
nullptr,
|
|
LOCTEXT("Anal", "Anal")));
|
|
ClothingManager->ClothingSlots.Add(
|
|
FClothingSlotData(
|
|
VaginaMeshComponent, EClothingSlotType::Vagina,
|
|
nullptr,
|
|
LOCTEXT("Vagina", "Vagina")));
|
|
ClothingManager->ClothingSlots.Add(
|
|
FClothingSlotData(
|
|
HeadMeshComponent, EClothingSlotType::Head,
|
|
nullptr,
|
|
LOCTEXT("Head", "Head")));
|
|
ClothingManager->ClothingSlots.Add(
|
|
FClothingSlotData(
|
|
NeckMeshComponent, EClothingSlotType::Neck,
|
|
nullptr,
|
|
LOCTEXT("Neck", "Neck")));
|
|
ClothingManager->ClothingSlots.Add(
|
|
FClothingSlotData(
|
|
FaceMeshComponent, EClothingSlotType::Face,
|
|
nullptr,
|
|
LOCTEXT("Face", "Face")));
|
|
ClothingManager->ClothingSlots.Add(
|
|
FClothingSlotData(
|
|
EyesMeshComponent, EClothingSlotType::Eyes,
|
|
nullptr,
|
|
LOCTEXT("Eyes", "Eyes")));
|
|
ClothingManager->ClothingSlots.Add(
|
|
FClothingSlotData(
|
|
BodyMeshComponent, EClothingSlotType::Body,
|
|
nullptr,
|
|
LOCTEXT("Body", "Body")));
|
|
ClothingManager->ClothingSlots.Add(
|
|
FClothingSlotData(
|
|
TopMeshComponent, EClothingSlotType::Top,
|
|
nullptr,
|
|
LOCTEXT("Top", "Top")));
|
|
ClothingManager->ClothingSlots.Add(
|
|
FClothingSlotData(
|
|
BottomMeshComponent, EClothingSlotType::Bottom,
|
|
nullptr,
|
|
LOCTEXT("Bottom", "Bottom")));
|
|
ClothingManager->ClothingSlots.Add(
|
|
FClothingSlotData(
|
|
BraMeshComponent, EClothingSlotType::Bra,
|
|
nullptr,
|
|
LOCTEXT("Bra", "Bra")));
|
|
ClothingManager->ClothingSlots.Add(
|
|
FClothingSlotData(
|
|
PantiesMeshComponent, EClothingSlotType::Panties,
|
|
nullptr,
|
|
LOCTEXT("Panties", "Panties")));
|
|
ClothingManager->ClothingSlots.Add(
|
|
FClothingSlotData(
|
|
SocksMeshComponent, EClothingSlotType::Socks,
|
|
nullptr,
|
|
LOCTEXT("Socks", "Socks")));
|
|
ClothingManager->ClothingSlots.Add(
|
|
FClothingSlotData(
|
|
ShoesMeshComponent, EClothingSlotType::Shoes,
|
|
nullptr,
|
|
LOCTEXT("Shoes", "Shoes")));
|
|
|
|
#undef LOCTEXT_NAMESPACE
|
|
}
|
|
|
|
void ANakedDesireCharacter::NotifyNoticed(ANPCAIController* NPCController)
|
|
{
|
|
OnNoticed.Broadcast(NPCController);
|
|
}
|
|
|
|
void ANakedDesireCharacter::SetIsRunning(const bool Value)
|
|
{
|
|
Gait = Value ? EGait::Run : EGait::Walk;
|
|
GetCharacterMovement()->MaxWalkSpeed = Gait == EGait::Run ? RunSpeed : WalkSpeed;
|
|
}
|