// © 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("Clothing Manager"); StatsManager = CreateDefaultSubobject("Stats Manager"); MissionsManager = CreateDefaultSubobject("Missions Manager"); InteractionManager = CreateDefaultSubobject("Interaction Manager"); NipplesMeshComponent = CreateDefaultSubobject("Nipples"); NipplesMeshComponent->SetupAttachment(GetMesh()); AnalMeshComponent = CreateDefaultSubobject("Anal"); AnalMeshComponent->SetupAttachment(GetMesh()); VaginaMeshComponent = CreateDefaultSubobject("Vagina"); VaginaMeshComponent->SetupAttachment(GetMesh()); HeadMeshComponent = CreateDefaultSubobject("Head"); HeadMeshComponent->SetupAttachment(GetMesh()); NeckMeshComponent = CreateDefaultSubobject("Neck"); NeckMeshComponent->SetupAttachment(GetMesh()); FaceMeshComponent = CreateDefaultSubobject("Face"); FaceMeshComponent->SetupAttachment(GetMesh()); EyesMeshComponent = CreateDefaultSubobject("Eyes"); EyesMeshComponent->SetupAttachment(GetMesh()); BodyMeshComponent = CreateDefaultSubobject("Body"); BodyMeshComponent->SetupAttachment(GetMesh()); TopMeshComponent = CreateDefaultSubobject("Top"); TopMeshComponent->SetupAttachment(GetMesh()); BottomMeshComponent = CreateDefaultSubobject("Bottom"); BottomMeshComponent->SetupAttachment(GetMesh()); BraMeshComponent = CreateDefaultSubobject("Bra"); BraMeshComponent->SetupAttachment(GetMesh()); PantiesMeshComponent = CreateDefaultSubobject("Panties"); PantiesMeshComponent->SetupAttachment(GetMesh()); SocksMeshComponent = CreateDefaultSubobject("Socks"); SocksMeshComponent->SetupAttachment(GetMesh()); ShoesMeshComponent = CreateDefaultSubobject("Shoes"); ShoesMeshComponent->SetupAttachment(GetMesh()); BoobLCensorship = CreateDefaultSubobject(TEXT("Boob L Censorship")); BoobLCensorship->SetupAttachment(GetMesh(), FName(TEXT("boob_l"))); BoobRCensorship = CreateDefaultSubobject(TEXT("Boob R Censorship")); BoobRCensorship->SetupAttachment(GetMesh(), FName(TEXT("boob_r"))); FrontBottomCensorship = CreateDefaultSubobject(TEXT("Front Bottom Censorship")); FrontBottomCensorship->SetupAttachment(GetMesh(), FName(TEXT("pelvis"))); BackBottomCensorship = CreateDefaultSubobject(TEXT("Back Bottom Censorship")); BackBottomCensorship->SetupAttachment(GetMesh(), FName(TEXT("pelvis"))); StimuliSourceComponent = CreateDefaultSubobject(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(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()) { if (MappingContext) { InputSystem->AddMappingContext(MappingContext, 0); } } } } } void ANakedDesireCharacter::BeginPlay() { Super::BeginPlay(); StimuliSourceComponent->RegisterForSense(TSubclassOf()); 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()) { InteractionManager->OnTargetEnter(OtherActor); } if (const ALocationTrigger* AreaTrigger = Cast(OtherActor)) { CurrentArea = AreaTrigger->GetLocationData(); OnAreaEnter.Broadcast(CurrentArea); } } void ANakedDesireCharacter::OnEndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex) { if (OtherActor->Implements()) { InteractionManager->OnTargetExit(OtherActor); } if (const ALocationTrigger* AreaTrigger = Cast(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(); AddControllerYawInput(MouseValue.X); AddControllerPitchInput(MouseValue.Y); } void ANakedDesireCharacter::OnMove(const FInputActionValue& Value) { const FVector2D MoveInput = Value.Get(); 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; }