Files
Naked-Desire/Source/NakedDesire/Player/NakedDesireCharacter.cpp
T
2026-06-03 15:16:27 +03:00

407 lines
15 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/Clothing/ClothingSlotsData.h"
#include "NakedDesire/Global/Constants.h"
#include "NakedDesire/Global/NakedDesireUserSettings.h"
#include "NakedDesire/UI/RadialMenu/RadialMenuController.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");
RadialMenuController = CreateDefaultSubobject<URadialMenuController>(TEXT("Radial Menu Controller"));
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());
BodySuitMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>("Body Suit");
BodySuitMeshComponent->SetupAttachment(GetMesh());
TopMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>("Top");
TopMeshComponent->SetupAttachment(GetMesh());
BottomMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>("Bottom");
BottomMeshComponent->SetupAttachment(GetMesh());
UnderwearTopMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>("Underwear Top");
UnderwearTopMeshComponent->SetupAttachment(GetMesh());
UnderwearBottomMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>("Underwear Bottom");
UnderwearBottomMeshComponent->SetupAttachment(GetMesh());
SocksMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>("Socks");
SocksMeshComponent->SetupAttachment(GetMesh());
FootwearMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>("Footwear");
FootwearMeshComponent->SetupAttachment(GetMesh());
OuterwearMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>("Outerwear");
OuterwearMeshComponent->SetupAttachment(GetMesh());
WristRestraintMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>("Wrist Restraint");
WristRestraintMeshComponent->SetupAttachment(GetMesh());
AnkleRestraintMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>("Ankle Restraint");
AnkleRestraintMeshComponent->SetupAttachment(GetMesh());
NeckRestraintMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>("Neck Restraint");
NeckRestraintMeshComponent->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")));
VaginaCensorship = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Vagina Censorship"));
VaginaCensorship->SetupAttachment(GetMesh(), FName(TEXT("pelvis")));
AnalCensorship = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Anal Censorship"));
AnalCensorship->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);
EnhancedInputComponent->BindAction(EquipmentAction, ETriggerEvent::Started, this, &ANakedDesireCharacter::OnEquipmentPress);
}
}
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();
ClothingManager->OnClothingEquip.AddUniqueDynamic(this, &ANakedDesireCharacter::OnClothingEquip);
ClothingManager->OnClothingUnequip.AddUniqueDynamic(this, &ANakedDesireCharacter::OnClothingUnequip);
UNakedDesireUserSettings::GetNakedDesireUserSettings()->OnSettingsChanged.AddUniqueDynamic(this, &ANakedDesireCharacter::OnSettingsChanged);
}
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->IsBodyPartExposed(EBodyPart::Boobs))
{
UE_LOG(LogTemp, Warning, TEXT("Boobs hit"));
OutSeenLocation = BoobsBoneLocation;
OutSightStrength = 1.0f;
return UAISense_Sight::EVisibilityResult::Visible;
}
if ((!PelvisHit || PelvisHitResult.GetActor() == this) &&
(ClothingManager->IsBodyPartExposed(EBodyPart::Ass) || ClothingManager->IsBodyPartExposed(EBodyPart::Genitals)))
{
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()->HiddenBodyParts.Contains(EBodyPart::Ass))
{
AnalCensorship->SetVisibility(false);
}
if (ClothingItemInstance->GetClothingItem()->HiddenBodyParts.Contains(EBodyPart::Genitals))
{
VaginaCensorship->SetVisibility(false);
}
if (ClothingItemInstance->GetClothingItem()->HiddenBodyParts.Contains(EBodyPart::Boobs))
{
BoobLCensorship->SetVisibility(false);
BoobRCensorship->SetVisibility(false);
}
}
void ANakedDesireCharacter::OnClothingUnequip(const UClothingItemInstance* ClothingItemInstance)
{
if (!UNakedDesireUserSettings::GetNakedDesireUserSettings()->GetIsCensorshipEnabled() && !IS_DEMO)
return;
if (ClothingManager->IsBodyPartExposed(EBodyPart::Ass))
{
AnalCensorship->SetVisibility(true);
}
if (ClothingManager->IsBodyPartExposed(EBodyPart::Genitals))
{
VaginaCensorship->SetVisibility(true);
}
if (ClothingManager->IsBodyPartExposed(EBodyPart::Boobs))
{
BoobLCensorship->SetVisibility(true);
BoobRCensorship->SetVisibility(true);
}
}
void ANakedDesireCharacter::OnSettingsChanged(UNakedDesireUserSettings* Settings)
{
if (Settings->GetIsCensorshipEnabled())
{
if (ClothingManager->IsBodyPartExposed(EBodyPart::Ass))
{
AnalCensorship->SetVisibility(true);
}
if (ClothingManager->IsBodyPartExposed(EBodyPart::Genitals))
{
VaginaCensorship->SetVisibility(true);
}
if (ClothingManager->IsBodyPartExposed(EBodyPart::Boobs))
{
BoobLCensorship->SetVisibility(true);
BoobRCensorship->SetVisibility(true);
}
}
else if (!IS_DEMO)
{
AnalCensorship->SetVisibility(false);
VaginaCensorship->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::OnEquipmentPress(const FInputActionValue& Value)
{
BuildRadialMenuEntries();
RadialMenuController->OpenMenu();
}
void ANakedDesireCharacter::BuildRadialMenuEntries()
{
if (!SlotsData)
{
UE_LOG(LogTemp, Warning, TEXT("ANakedDesireCharacter::BuildRadialMenuEntries SlotsData not defined"));
return;
}
TArray<FRadialMenuEntry> Entries;
for (const auto& [Key, Value] : SlotsData->Slots)
{
FRadialMenuEntry Entry;
const UClothingItemInstance* EquippedItem = ClothingManager->GetSlotClothing(Key);
Entry.bEnabled = true;
Entry.DisplayName = Value.Name;
Entry.Icon = EquippedItem ? EquippedItem->GetClothingItem()->Icon : Value.Icon;
Entry.ItemId = FName(Value.Name.ToString());
Entries.Push(Entry);
}
RadialMenuController->Entries = Entries;
}
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;
}