diff --git a/1.3/Assemblies/RJW-Events.dll b/1.3/Assemblies/RJW-Events.dll new file mode 100644 index 0000000..31c857b Binary files /dev/null and b/1.3/Assemblies/RJW-Events.dll differ diff --git a/1.3/Assemblies/Rimworld-Animations-Patch.dll b/1.3/Assemblies/Rimworld-Animations-Patch.dll index 7039ed8..d07b122 100644 Binary files a/1.3/Assemblies/Rimworld-Animations-Patch.dll and b/1.3/Assemblies/Rimworld-Animations-Patch.dll differ diff --git a/1.3/Assemblies/Rimworld-Animations-Patch.pdb b/1.3/Assemblies/Rimworld-Animations-Patch.pdb new file mode 100644 index 0000000..c299208 Binary files /dev/null and b/1.3/Assemblies/Rimworld-Animations-Patch.pdb differ diff --git a/About/About.xml b/About/About.xml index 17eba0d..3fff182 100644 --- a/About/About.xml +++ b/About/About.xml @@ -47,11 +47,17 @@ This mod requires RJW and RimWorld-Animation to function. RimNudeWorld isn't req Here's a brief overview of whats included in this mod - 12 animations for casual sex - 7 animations for solo masturbation -- 1 animations for group sex +- 2 animations for group sex - Animated hands for certain animations - A need for privacy during sex +- Pawns will react differently to the different types of sex that they witness - their reaction depends on their trait and ideology +- A range of tweaks to how body parts are displayed, particularly when wearing different types of apparel A full list of changes can be found in the main discussion thread on the forum + +Known conflicts +- To avoid certain graphical issues, Dubs Apparel Tweaks should be loaded before this mod while Babies and Children should be loaded after (if you choose to use them). This will mean that you will not be able to run this mod with Dubs Apparel Tweaks and Babies and Children running at the same time +- This mod will result in graphical errors when using Pawnmorpher's 'Pawn scaling' setting diff --git a/About/Changelog_v1.2.1.txt b/About/Changelog_v1.2.2.txt similarity index 85% rename from About/Changelog_v1.2.1.txt rename to About/Changelog_v1.2.2.txt index b885e60..152dd7d 100644 --- a/About/Changelog_v1.2.1.txt +++ b/About/Changelog_v1.2.2.txt @@ -1,4 +1,21 @@ -Chnage log v 1.2.1 +Change log v 1.2.2 +- Fix: Fix for portraits appearing blank when using the 'crop apparel' feature +- Fix: Underwear counts as covering private parts when determining basic nudity +- Fix: A pawn won't think that they are being cheated on if the catch their partner having sex with an animal or corpse +- Fix: Animals, wild men, mechanoids and factions that are hostile to you no longer have opinions about witnessed sex +- Fix: Fixed an issue which was causing pawns attending orgies to get stuck +- Fix: Fixed an issue where some animations without an orgasm event included would result in an error occurring +- Fix: Pawns no longer have missing body parts while showering +- Fix: Penises no longer not show while wearing pants (unless a pawn is wearing nothing else) +- Change: Significant performance optimisations have been made. You can now host orgies involving 50 or more people. Disable animated hands to further improve performance +- Change: Individualized moodlets are now applied for witnessing different sex acts (currently supports rape, beastiality, xenophilia, necrophilia) +- Change: Pawns might run away and vomit up their lunch if they witness necrophilia +- Change: Pawns who witness a cheating partner or xenophilia might go on an insulting spree +- Addition: Added a setting that allows you toggle whether wearing underwear alone is sufficient to satisfy an ideological need for modesty +- Addition: Added moodlets for pawns that have their underwear showing. These moodlets can be toggled on or off in the settings +- Addition: Added a new threesome animation for one male and two females ('the sandwich') + +Change log v 1.2.1 - Change: Optimisation of the body part rendering code - there should be much better frame rates now. Just avoid hosting orgies involving 50 or more people... - Addition: Added option to the basic settings menu to toggle hair redrawing for portraits (helps make sure that body parts are properly covered by long hair) diff --git a/About/Manifest.xml b/About/Manifest.xml index 51fe9e0..bc478f8 100644 --- a/About/Manifest.xml +++ b/About/Manifest.xml @@ -1,6 +1,5 @@ - 1.2.0 - https://gitgud.io/AbstractConcept/rimworld-animations-patch/-/blob/abscon/About/Manifest.xml + 1.2.2 https://gitgud.io/AbstractConcept/rimworld-animations-patch diff --git a/Defs/AnimationDefs/Animation_threesome_sandwich.xml b/Defs/AnimationDefs/Animation_threesome_sandwich.xml new file mode 100644 index 0000000..cbc7917 --- /dev/null +++ b/Defs/AnimationDefs/Animation_threesome_sandwich.xml @@ -0,0 +1,1305 @@ + + + + threesome_sandwich + + true + +
  • Vaginal
  • +
  • Anal
  • +
    + +
  • AnalSex
  • +
  • VaginalSex
  • +
    + +
  • + +
  • Human
  • + + + (0, 0) + (0, -0.1) + (0.2, 0) + (0, -0.1) + + true + true + +
  • + +
  • Human
  • + + + (0, 0) + (0, 0.02) + (0, -0.05) + (0.2, 0.05) + (0, 0.22) + + true + true + +
  • + +
  • Human
  • + + + (0, 0) + (0, 0.04) + (0.075, 0) + (-0.03, 0.2) + (-0.22, 0.05) + + true + true + true + +
    + +
  • + Start + 0 + 450 + -1 + true + +
  • + LayingPawn + +
  • + 197 + 91.70703 + 73.69141 + 0 + 0.5363228 + 0.07183241 + 3 + 3 +
  • +
  • + 20 + 91.70703 + 73.69141 + 0 + 0.5363228 + 0.07183241 + 3 + 3 +
  • +
  • + 1 + 91.70703 + 73.69141 + 0 + 0.5363228 + 0.07183241 + 3 + 3 +
  • +
  • + 22 + 91.70703 + 90 + 0 + 0.5363228 + 0.06813855 + 2 + 2 +
  • +
  • + 1 + 91.70703 + 90 + 0 + 0.5363228 + 0.06813855 + 2 + 2 +
  • +
  • + 208 + 70 + 27 + 0 + 0.5208333 + -0.0520833321 + 1 + 1 +
  • +
  • + 1 + 70 + 27 + 0 + 0.5208333 + -0.0520833321 + 1 + 1 +
  • + + +
  • + PawnUnused + +
  • + 49 + -0 + -0 + 0 + -0.9119424 + 0.5005034 + 1 + 1 +
  • +
  • + 34 + -0 + -0 + 0 + -0.9119424 + 0.5005034 + 1 + 1 +
  • +
  • + 30 + -0 + 20.8763351 + 0 + -0.9119424 + 0.5005034 + 1 + 1 +
  • +
  • + 36 + -0 + -0 + 0 + -0.9119424 + 0.5005034 + 1 + 1 +
  • +
  • + 48 + -0 + -0 + 0 + -0.9119424 + 0.5005034 + 1 + 1 +
  • +
  • + 82 + -0 + -0 + 0 + -0.204245165 + 0.496527582 + 1 + 1 +
  • +
  • + 51 + -0 + -0 + 0 + -0.204245165 + 0.496527582 + 1 + 1 +
  • +
  • + 119 + 65 + 31 + 0 + 0.5208333 + 0.260416657 + 1 + 1 +
  • +
  • + 1 + 65 + 31 + 0 + 0.5208333 + 0.260416657 + 1 + 1 +
  • + + +
  • + Pawn + +
  • + 149 + 16.63095 + -0 + 0 + -0.136074975 + 0.3983891 + 3 + 1 +
  • +
  • + 48 + 16.63095 + -0 + 0 + -0.136074975 + 0.3983891 + 3 + 1 +
  • +
  • + 152 + -0 + -0 + 0 + -0.8040139 + 0.422244042 + 1 + 1 +
  • +
  • + 34 + -0 + -0 + 0 + -0.8040139 + 0.422244042 + 1 + 1 +
  • +
  • + 33 + -0 + -0 + 0 + -0.216691211 + 0.4111625 + 1 + 1 +
  • +
  • + 33 + -0 + -0 + 0 + -0.154846326 + 0.5305851 + 1 + 1 +
  • +
  • + 1 + 16.631 + 0 + 0 + -0.063 + 0.542 + 1 + 1 + 34 +
  • + + + + +
  • + SlowTop + 0 + 610 + 366 + true + +
  • + LayingPawn + +
  • + 28 + 70 + 27 + 0 + 0.5208333 + -0.0520833321 + 1 + 1 +
  • +
  • + 32 + 70 + 19.38095 + 0 + 0.5208333 + -0.0520833321 + 1 + 1 +
  • +
  • + 1 + 70 + 27 + 0 + 0.5208333 + -0.0520833321 + 1 + 1 +
  • + + +
  • + PawnUnused + +
  • + 28 + 65 + 31 + 0 + 0.5208333 + 0.260416657 + 1 + 1 +
  • +
  • + 32 + 65 + 20.35019 + 0 + 0.5625 + 0.2681614 + 1 + 1 +
  • +
  • + 1 + 65 + 31 + 0 + 0.5208333 + 0.260416657 + 1 + 1 +
  • + + +
  • + Pawn + +
  • + 28 + 16.631 + 0 + 0 + -0.063 + 0.542 + 1 + 1 + 34 +
  • +
  • + 32 + Sex + -15 + -5 + 0 + -0.063 + 0.542 + 1 + 1 + 34 +
  • +
  • + 1 + 16.631 + 0 + 0 + -0.063 + 0.542 + 1 + 1 + 34 +
  • + + + + +
  • + TransitionToBottom + 0 + 61 + -1 + true + +
  • + Pawn + +
  • + 19 + 70 + 27 + 0 + 0.5208333 + -0.0520833321 + 1 + 1 +
  • +
  • + 21 + 70 + 27 + 0 + 0.5208333 + -0.0520833321 + 1 + 1 +
  • +
  • + 20 + 70 + 27 + 0 + 0.5208333 + -0.0520833321 + 1 + 1 +
  • +
  • + 1 + 70 + 27 + 0 + 0.5208333 + -0.0520833321 + 1 + 1 +
  • + + +
  • + PawnUnused + +
  • + 19 + 65 + 31 + 0 + 0.5208333 + 0.260416657 + 1 + 1 +
  • +
  • + 21 + 65 + 31 + 0 + 0.5208333 + 0.260416657 + 1 + 1 +
  • +
  • + 20 + 65 + 31 + 0 + 0.5208333 + 0.260416657 + 1 + 1 +
  • +
  • + 1 + 65 + 31 + 0 + 0.5208333 + 0.260416657 + 1 + 1 +
  • + + +
  • + LayingPawn + +
  • + 19 + 16.631 + 0 + 0 + -0.063 + 0.542 + 1 + 1 + 34 +
  • +
  • + 21 + -0 + -0 + 0 + -0.208333328 + 0.5208333 + 1 + 1 +
  • +
  • + 20 + -0 + -0 + 0 + -0.208333328 + 0.204166666 + 1 + 1 +
  • +
  • + 1 + 16.631 + 0 + 0 + -0.074 + 0.205 + 1 + 1 + 34 +
  • + + + + +
  • + SlowBottom + 0 + 610 + 366 + true + +
  • + Pawn + +
  • + 28 + 70 + 27 + 0 + 0.521 + -0.052 + 1 + 1 +
  • +
  • + 32 + 70 + 14.5666809 + 0 + 0.5625 + -0.0559556969 + 1 + 1 +
  • +
  • + 1 + 70 + 27 + 0 + 0.5208333 + -0.0520833321 + 1 + 1 +
  • + + +
  • + PawnUnused + +
  • + 28 + 65 + 31 + 0 + 0.5208333 + 0.260416657 + 1 + 1 +
  • +
  • + 32 + 65 + 21.0732727 + 0 + 0.5208333 + 0.260416657 + 1 + 1 +
  • +
  • + 1 + 65 + 31 + 0 + 0.5208333 + 0.260416657 + 1 + 1 +
  • + + +
  • + LayingPawn + +
  • + 28 + 16.631 + 0 + 0 + -0.074 + 0.205 + 1 + 1 + 34 +
  • +
  • + 32 + Sex + -8.971 + 0 + 0 + -0.074 + 0.205 + 1 + 1 + 34 +
  • +
  • + 1 + 16.631 + 0 + 0 + -0.074 + 0.205 + 1 + 1 + 34 +
  • + + + + +
  • + TransitionToTop + 0 + 61 + -1 + true + +
  • + Pawn + +
  • + 20 + 70 + 27 + 0 + 0.5208333 + -0.0520833321 + 1 + 1 +
  • +
  • + 20 + 70 + 27 + 0 + 0.5208333 + -0.0520833321 + 1 + 1 +
  • +
  • + 20 + 70 + 27 + 0 + 0.5208333 + -0.0520833321 + 1 + 1 +
  • +
  • + 1 + 70 + 27 + 0 + 0.5208333 + -0.0520833321 + 1 + 1 +
  • + + +
  • + PawnUnused + +
  • + 20 + 65 + 31 + 0 + 0.5208333 + 0.260416657 + 1 + 1 +
  • +
  • + 20 + 65 + 31 + 0 + 0.5208333 + 0.260416657 + 1 + 1 +
  • +
  • + 20 + 65 + 31 + 0 + 0.5208333 + 0.260416657 + 1 + 1 +
  • +
  • + 1 + 65 + 31 + 0 + 0.5208333 + 0.260416657 + 1 + 1 +
  • + + +
  • + LayingPawn + +
  • + 20 + 16.631 + 0 + 0 + -0.074 + 0.205 + 1 + 1 + 34 +
  • +
  • + 20 + -0 + -0 + 0 + -0.208333328 + 0.204166666 + 1 + 1 +
  • +
  • + 20 + -0 + -0 + 0 + -0.208333328 + 0.5208333 + 1 + 1 +
  • +
  • + 1 + 16.631 + 0 + 0 + -0.063 + 0.542 + 1 + 1 + 34 +
  • + + + + +
  • + FastTop + 0 + 576 + 384 + true + +
  • + Pawn + +
  • + 11 + 70 + 27 + 0 + 0.5208333 + -0.0520833321 + 1 + 1 +
  • +
  • + 12 + 70 + 19.38095 + 0 + 0.5208333 + -0.0520833321 + 1 + 1 +
  • +
  • + 1 + 70 + 27 + 0 + 0.5208333 + -0.0520833321 + 1 + 1 +
  • + + +
  • + PawnUnused + +
  • + 11 + 65 + 31 + 0 + 0.5208333 + 0.260416657 + 1 + 1 +
  • +
  • + 12 + 65 + 20.35019 + 0 + 0.5625 + 0.2681614 + 1 + 1 +
  • +
  • + 1 + 65 + 31 + 0 + 0.5208333 + 0.260416657 + 1 + 1 +
  • + + +
  • + LayingPawn + +
  • + 11 + 16.631 + 0 + 0 + -0.063 + 0.542 + 1 + 1 + 34 +
  • +
  • + 12 + Sex + -15.7 + -6.127 + 0 + -0.063 + 0.542 + 1 + 1 + 34 +
  • +
  • + 1 + 16.631 + 0 + 0 + -0.063 + 0.542 + 1 + 1 + 34 +
  • + + + + +
  • + Cum + 0 + 456 + 304 + true + +
  • + Pawn + +
  • + 11 + 70 + 27 + 0 + 0.5208333 + -0.0520833321 + 1 + 1 +
  • +
  • + 12 + 70 + 19.38095 + 0 + 0.5208333 + -0.0520833321 + 1 + 1 +
  • +
  • + 12 + 70 + 27 + 0 + 0.5208333 + -0.0520833321 + 1 + 1 +
  • +
  • + 12 + 70 + 19.38095 + 0 + 0.5208333 + -0.0520833321 + 1 + 1 +
  • +
  • + 12 + 70 + 27 + 0 + 0.5208333 + -0.0520833321 + 1 + 1 +
  • +
  • + 12 + 70 + 19.38095 + 0 + 0.5208333 + -0.0520833321 + 1 + 1 +
  • +
  • + 12 + 70 + 27 + 0 + 0.5208333 + -0.0520833321 + 1 + 1 +
  • +
  • + 57 + Cum + 70 + 19.38095 + 0 + 0.5208333 + -0.0520833321 + 1 + 1 +
  • +
  • + 11 + 70 + 19.38095 + 0 + 0.5208333 + -0.0520833321 + 1 + 1 +
  • +
  • + 1 + 70 + 27 + 0 + 0.5208333 + -0.0520833321 + 1 + 1 +
  • + + +
  • + PawnUnused + +
  • + 11 + 65 + 31 + 0 + 0.5208333 + 0.260416657 + 1 + 1 +
  • +
  • + 12 + 65 + 20.35019 + 0 + 0.5625 + 0.2681614 + 1 + 1 +
  • +
  • + 12 + 65 + 31 + 0 + 0.5208333 + 0.260416657 + 1 + 1 +
  • +
  • + 12 + 65 + 20.35019 + 0 + 0.5625 + 0.2681614 + 1 + 1 +
  • +
  • + 12 + 65 + 31 + 0 + 0.5208333 + 0.260416657 + 1 + 1 +
  • +
  • + 12 + 65 + 20.35019 + 0 + 0.5625 + 0.2681614 + 1 + 1 +
  • +
  • + 12 + 65 + 31 + 0 + 0.5208333 + 0.260416657 + 1 + 1 +
  • +
  • + 57 + Cum + 65 + 20.35019 + 0 + 0.5625 + 0.2681614 + 1 + 1 + true +
  • +
  • + 11 + 65 + 20.35019 + 0 + 0.5625 + 0.2681614 + 1 + 1 +
  • +
  • + 1 + 65 + 31 + 0 + 0.5208333 + 0.260416657 + 1 + 1 +
  • + + +
  • + LayingPawn + +
  • + 11 + 16.631 + 0 + 0 + -0.063 + 0.542 + 1 + 1 + 34 +
  • +
  • + 12 + Sex + -15.7 + -6.127 + 0 + -0.063 + 0.542 + 1 + 1 + 34 +
  • +
  • + 12 + 16.631 + 0 + 0 + -0.063 + 0.542 + 1 + 1 + 34 +
  • +
  • + 12 + Sex + -15.7 + -6.127 + 0 + -0.063 + 0.542 + 1 + 1 + 34 +
  • +
  • + 12 + 16.631 + 0 + 0 + -0.063 + 0.542 + 1 + 1 + 34 +
  • +
  • + 12 + Sex + -15.7 + -6.127 + 0 + -0.063 + 0.542 + 1 + 1 + 34 +
  • +
  • + 12 + 16.631 + 0 + 0 + -0.063 + 0.542 + 1 + 1 + 34 +
  • +
  • + 57 + Cum + -15.7 + -6.127 + 0 + -0.063 + 0.542 + 1 + 1 + 34 + false +
  • +
  • + 11 + -15.7 + -6.127 + 0 + -0.063 + 0.542 + 1 + 1 + 34 +
  • +
  • + 1 + 16.631 + 0 + 0 + -0.063 + 0.542 + 1 + 1 + 34 +
  • + + + + +
    +
    +
    \ No newline at end of file diff --git a/Defs/HediffDefs.xml b/Defs/HediffDefs.xml new file mode 100644 index 0000000..6d1f207 --- /dev/null +++ b/Defs/HediffDefs.xml @@ -0,0 +1,53 @@ + + + + + + Disquiet + + This person saw something that upset them and it's weighting on their thoughts. + +
  • + + false +
  • +
    +
    + + + Panicked + + This person saw something that has shaked them to their core. Will they run or fight? + +
  • + + false +
  • +
    +
    + + + Nauseated + + This poor soul saw something something so utterly foul that they just might be sick from the thought. + +
  • + + false +
  • +
    +
    + + + Indignant + + This person witnessed something quite distasteful and is going to give the offender a piece of their mind. + +
  • + + false +
  • +
    +
    + +
    diff --git a/Defs/ThoughtDefs.xml b/Defs/ThoughtDefs.xml index c8e5c19..406dff9 100644 --- a/Defs/ThoughtDefs.xml +++ b/Defs/ThoughtDefs.xml @@ -40,7 +40,7 @@
  • - It's OK. You can look! + It's OK, come and have a closer look... 4
  • @@ -54,7 +54,7 @@
  • - Hope they enjoyed the show! + Heheh, hope they enjoyed the show! 6
  • @@ -98,7 +98,7 @@
  • - Damn, that was hot! + I think that awoke something in me... 4
  • @@ -112,7 +112,7 @@
  • - Wow, they were really going at it! + Damn. So hot! 6
  • diff --git a/Defs/ThoughtDefs/ThoughtDefsBeastiality.xml b/Defs/ThoughtDefs/ThoughtDefsBeastiality.xml new file mode 100644 index 0000000..1c4df94 --- /dev/null +++ b/Defs/ThoughtDefs/ThoughtDefsBeastiality.xml @@ -0,0 +1,90 @@ + + + + + + SawBestiality_Abhorrent + 3 + 3 + +
  • Zoophile
  • +
    + Panicked + 1 + +
  • + + How could that violate that poor animal like that?! + -20 +
  • +
    +
    + + + SawBestiality_Horrible + 3 + 3 + +
  • Zoophile
  • +
    + Disquiet + 1 + +
  • + + Get away from that poor creature! + -15 +
  • +
    +
    + + + SawBestiality_Disapproved + 3 + 3 + +
  • Zoophile
  • +
    + Disquiet + 1 + +
  • + + Ew. Just. Ew. + -10 +
  • +
    +
    + + + SawBestiality_Acceptable + 3 + 3 + +
  • Zoophile
  • +
    + 1 + +
  • + + Uhhh. Does your friend want some kibble? + -5 +
  • +
    +
    + + + SawBestiality_Honorable + 3 + 3 + 1 + +
  • + + The bond between us and our animals is strong. + +2 +
  • +
    +
    + +
    diff --git a/Defs/ThoughtDefs/ThoughtDefsNecrophilia.xml b/Defs/ThoughtDefs/ThoughtDefsNecrophilia.xml new file mode 100644 index 0000000..f1eafd9 --- /dev/null +++ b/Defs/ThoughtDefs/ThoughtDefsNecrophilia.xml @@ -0,0 +1,87 @@ + + + + + + SawNecrophilia_Abhorrent + 3 + 3 + +
  • Necrophiliac
  • +
    + Nauseated + 1 + +
  • + + *Urp* I'm gunna hurl! + -20 +
  • +
    +
    + + + SawNecrophilia_Horrible + 3 + 3 + +
  • Necrophiliac
  • +
    + Disquiet + 1 + +
  • + + What were they thinking?! + -15 +
  • +
    +
    + + + SawNecrophilia_Disapproved + 3 + 3 + +
  • Necrophiliac
  • +
    + Disquiet + 1 + +
  • + + Even the dead get no rest here. + -10 +
  • +
    +
    + + + SawNecrophilia_Acceptable + 3 + 3 + 1 + +
  • + + I just hope they bury it when they're done. + -5 +
  • +
    +
    + + + SawNecrophilia_Honorable + 3 + 3 + 1 + +
  • + + Passion transcends the grave. + +2 +
  • +
    +
    + +
    diff --git a/Defs/ThoughtDefs/ThoughtDefsRape.xml b/Defs/ThoughtDefs/ThoughtDefsRape.xml new file mode 100644 index 0000000..68e6b32 --- /dev/null +++ b/Defs/ThoughtDefs/ThoughtDefsRape.xml @@ -0,0 +1,87 @@ + + + + + + SawRape_Abhorrent + 3 + 3 + +
  • Rapist
  • +
    + Panicked + 1 + +
  • + + Someone please help that poor soul! + -20 +
  • +
    +
    + + + SawRape_Horrible + 3 + 3 + +
  • Rapist
  • +
    + Disquiet + 1 + +
  • + + I feel all shaky. + -15 +
  • +
    +
    + + + SawRape_Disapproved + 3 + 3 + +
  • Rapist
  • +
    + Disquiet + 1 + +
  • + + Is this what life is going to be here? + -10 +
  • +
    +
    + + + SawRape_Acceptable + 3 + 3 + 1 + +
  • + + Urgh, carry on. + -5 +
  • +
    +
    + + + SawRape_Honorable + 3 + 3 + 1 + +
  • + + The strong claim their due from the weak. + +2 +
  • +
    +
    + +
    diff --git a/Defs/ThoughtDefs/ThoughtDefsXenophilia.xml b/Defs/ThoughtDefs/ThoughtDefsXenophilia.xml new file mode 100644 index 0000000..00cd035 --- /dev/null +++ b/Defs/ThoughtDefs/ThoughtDefsXenophilia.xml @@ -0,0 +1,77 @@ + + + + + + SawHAR_AlienDating_Prohibited + 3 + 3 + Indignant + 1 + +
  • + + Filthy degenerates! + -15 +
  • +
    +
    + + + SawHAR_AlienDating_Horrible + 3 + 3 + Disquiet + 1 + +
  • + + Oh! That's not right... + -10 +
  • +
    +
    + + + SawHAR_AlienDating_Acceptable + 3 + 3 + 1 + +
  • + + Oops, beg your pardon! + -5 +
  • +
    +
    + + + SawHAR_AlienDating_Preferred + 3 + 3 + 1 + +
  • + + I wonder what their kids would look like? + +1 +
  • +
    +
    + + + SawHAR_AlienDating_Know_Honorable + 3 + 3 + 1 + +
  • + + A blending of species will make us stronger. + +2 +
  • +
    +
    + +
    diff --git a/Defs/ThoughtDefs/ThoughtsDefsUnderwear.xml b/Defs/ThoughtDefs/ThoughtsDefsUnderwear.xml new file mode 100644 index 0000000..93d0f0d --- /dev/null +++ b/Defs/ThoughtDefs/ThoughtsDefsUnderwear.xml @@ -0,0 +1,22 @@ + + + + + ExposeUnderwear + Rimworld_Animations_Patch.ThoughtWorker_ExposedUnderwear + true + +
  • + + People can see my underwear, it's embarassing. + -2 +
  • +
  • + + People can see my underwear. It's a bit thrilling, to be honest! + 1 +
  • +
    +
    + +
    diff --git a/Languages/English/Keyed/SettingsKeys.xml b/Languages/English/Keyed/SettingsKeys.xml index 4bcb1e2..bb64b25 100644 --- a/Languages/English/Keyed/SettingsKeys.xml +++ b/Languages/English/Keyed/SettingsKeys.xml @@ -43,10 +43,13 @@ Crop the bottoms of worn shirts, tank tops, etc. when not wearing pants or a skirt Only applies to torso covering apparel that lies directly on the skin and that does not cover the legs. Best used with mods that draw pants graphics, like the Visible Pants mod.\n\nRequires that the game to be reset when toggled off. Show discarded clothing on the floor while getting some lovin' - If a pawn undresses while lovin', these items of clothing will be piled on the floor nearby. This is a purely visual effect. + If a pawn undresses while lovin', these items of clothing will be piled on the floor nearby.\n\nThis is a purely visual effect. Preferred state of dress for people lovin' in a bed Changing this will update the clothing preference setting in RJW (and vice versa). Preferred state of dress for people having a quickie Nothing more to say. - + Underwear satisfies ideological needs for modesty + If an ideology requires that certain body parts must be covered, wearing underwear can help fulfill this requirement.\n\nYou may want to turn this setting off if you want ideologies to be more strict about what they consider to be 'modestly clothed'.\n\nIdeologies which prefer to wear fewer clothes than normal are not affected by this setting. + People feel embarassed if their underwear is showing + It's not as bad as being naked, but your colonists would prefer to wear some additional clothes.\n\nExhibitionists, however, will get a small thrill if their underwear is exposed. \ No newline at end of file diff --git a/Patches/ApparelCompProperties.xml b/Patches/ApparelCompProperties.xml index fc9a9a8..bea1e19 100644 --- a/Patches/ApparelCompProperties.xml +++ b/Patches/ApparelCompProperties.xml @@ -1,9 +1,38 @@  - Always + Normal + +
  • + Always + Defs/ThingDef[race][not(comps)] + + + +
  • +
  • + Always + Defs/AlienRace.ThingDef_AlienRace[not(comps)] + + + +
  • + +
  • + Defs/ThingDef[@Name="BasePawn"]/comps + +
  • + +
  • +
  • + Defs/AlienRace.ThingDef_AlienRace/comps + +
  • + +
  • +
  • /Defs/ThingDef[thingClass="Apparel"]/comps @@ -26,20 +55,23 @@
  • -
  • - /Defs/ThingDef[thingClass="Apparel"]/apparel/bodyPartGroups[li="Torso"] - -
  • ChestBPG
  • - + +
    \ No newline at end of file diff --git a/Source/.vs/Rimworld-Animations-Patch/v16/.suo b/Source/.vs/Rimworld-Animations-Patch/v16/.suo index 41ac4a3..e7c996d 100644 Binary files a/Source/.vs/Rimworld-Animations-Patch/v16/.suo and b/Source/.vs/Rimworld-Animations-Patch/v16/.suo differ diff --git a/Source/Rimworld-Animations-Patch.csproj b/Source/Rimworld-Animations-Patch.csproj index 87ab5b4..89ebe1e 100644 --- a/Source/Rimworld-Animations-Patch.csproj +++ b/Source/Rimworld-Animations-Patch.csproj @@ -74,6 +74,9 @@ ..\..\rjw-master\1.3\Assemblies\RJW.dll False + + ..\..\rjw-events-master\1.3\Assemblies\RJW-Events.dll + ..\..\rjw-toys-and-masturbation-master\Assemblies\RJW-ToysAndMasturbation.dll False @@ -104,16 +107,22 @@ - + + + + + + + diff --git a/Source/Scripts/Comps/CompApparelVisibility.cs b/Source/Scripts/Comps/CompApparelVisibility.cs index 0ccbc0c..4e3286a 100644 --- a/Source/Scripts/Comps/CompApparelVisibility.cs +++ b/Source/Scripts/Comps/CompApparelVisibility.cs @@ -15,7 +15,7 @@ namespace Rimworld_Animations_Patch public Vector3 position; public float rotation = 0f; - public bool isBeingWorn = true; + public bool? isBeingWorn = null; public bool coversChest = false; public bool coversGroin = false; public bool coversBelly = false; diff --git a/Source/Scripts/Comps/CompPawnSexData.cs b/Source/Scripts/Comps/CompPawnSexData.cs new file mode 100644 index 0000000..4eab434 --- /dev/null +++ b/Source/Scripts/Comps/CompPawnSexData.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using RimWorld; +using Verse; +using AlienRace; +using UnityEngine; + +namespace Rimworld_Animations_Patch +{ + public class CompPawnSexData : ThingComp + { + public HandAnimationDef handAnimationDef = null; + public Graphic handGraphic = null; + + public List hands = new List(); + public Dictionary bodyAddonData = new Dictionary(); + public Dictionary bodyAddonDataPortraits = new Dictionary(); + + private Pawn pawn; + private int lastExclaimationTick = -1; + private int exclaimationCoolDown = 90; + + public BodyAddonData GetBodyAddonData(AlienPartGenerator.BodyAddon bodyAddon, bool isPortrait) + { + if (pawn == null) + { pawn = parent as Pawn; } + + if (pawn == null || pawn.Map != Find.CurrentMap || bodyAddon == null) return null; + + if (isPortrait) + { + if (bodyAddonDataPortraits.TryGetValue(bodyAddon, out BodyAddonData bodyAddonDatum) == false) + { + bodyAddonDatum = new BodyAddonData(pawn, bodyAddon, true); + bodyAddonDataPortraits.Add(bodyAddon, bodyAddonDatum); + } + + return bodyAddonDatum; + } + + else + { + if (bodyAddonData.TryGetValue(bodyAddon, out BodyAddonData bodyAddonDatum) == false) + { + bodyAddonDatum = new BodyAddonData(pawn, bodyAddon); + bodyAddonData.Add(bodyAddon, bodyAddonDatum); + } + + return bodyAddonDatum; + } + } + + public void UpdateBodyAddonVisibility() + { + foreach (KeyValuePair kvp in bodyAddonData) + { kvp.Value.UpdateVisibility(); } + + foreach (KeyValuePair kvp in bodyAddonDataPortraits) + { kvp.Value.UpdateVisibility(); } + } + + public void UpdateHands() + { + hands = pawn?.health?.hediffSet?.GetNotMissingParts()?.Where(x => x.def.tags.Contains(BodyPartTagDefOf.ManipulationLimbCore))?.ToList(); + } + + public int GetNumberOfHands() + { + if (hands.NullOrEmpty()) return 0; + + return hands.Count; + } + + public void TryToExclaim() + { + if (Find.TickManager.TicksGame > exclaimationCoolDown + lastExclaimationTick) + { + lastExclaimationTick = Find.TickManager.TicksGame; + FleckMaker.ThrowMetaIcon(pawn.Position, pawn.Map, FleckDefOf.IncapIcon); + } + } + } +} diff --git a/Source/Scripts/Comps/CompProperties_ApparelVisibility.cs b/Source/Scripts/Comps/CompProperties_ApparelVisibility.cs index 392ed3b..3a8a6b2 100644 --- a/Source/Scripts/Comps/CompProperties_ApparelVisibility.cs +++ b/Source/Scripts/Comps/CompProperties_ApparelVisibility.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using Verse; -using RimWorld; namespace Rimworld_Animations_Patch { @@ -12,4 +10,4 @@ namespace Rimworld_Animations_Patch base.compClass = typeof(CompApparelVisibility); } } -} +} \ No newline at end of file diff --git a/Source/Scripts/Comps/CompProperties_PawnSexData.cs b/Source/Scripts/Comps/CompProperties_PawnSexData.cs new file mode 100644 index 0000000..37e7a3e --- /dev/null +++ b/Source/Scripts/Comps/CompProperties_PawnSexData.cs @@ -0,0 +1,13 @@ +using System; +using Verse; + +namespace Rimworld_Animations_Patch +{ + public class CompProperties_PawnSexData : CompProperties + { + public CompProperties_PawnSexData() + { + base.compClass = typeof(CompPawnSexData); + } + } +} \ No newline at end of file diff --git a/Source/Scripts/Defs/BodyAddonData.cs b/Source/Scripts/Defs/BodyAddonData.cs new file mode 100644 index 0000000..9f4c844 --- /dev/null +++ b/Source/Scripts/Defs/BodyAddonData.cs @@ -0,0 +1,191 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Verse; +using RimWorld; +using AlienRace; +using Rimworld_Animations; +using UnityEngine; +using HarmonyLib; + +namespace Rimworld_Animations_Patch +{ + public class BodyAddonData + { + public AlienPartGenerator.BodyAddon bodyAddon; + public BodyPartRecord bodyPartRecord; + public List bodyAddonOffsets = new List(); + public bool alignsWithHead = false; + + private Pawn pawn; + private string bodyType; + private PawnRenderFlags renderFlags; + private bool canDraw = false; + + public BodyAddonData(Pawn pawn, AlienPartGenerator.BodyAddon bodyAddon, bool isPortrait = false) + { + this.pawn = pawn; + this.bodyAddon = bodyAddon; + + if (isPortrait) + { renderFlags |= PawnRenderFlags.Portrait; } + + bodyPartRecord = pawn?.def?.race?.body?.AllParts?.FirstOrDefault(x => x.def.defName == bodyAddon?.bodyPart || x.customLabel == bodyAddon?.bodyPart); + alignsWithHead = bodyAddon.alignWithHead || (bodyPartRecord != null && bodyPartRecord.IsInGroup(BodyPartGroupDefOf.FullHead)); + + GenerateOffsets(); + UpdateVisibility(); + } + + public void GenerateOffsets() + { + bodyType = pawn.story.bodyType.defName; + bodyAddonOffsets.Clear(); + + int bodyAddonIndex = (pawn.def as ThingDef_AlienRace).alienRace.generalSettings.alienPartGenerator.bodyAddons.ToList().IndexOf(bodyAddon); + AlienPartGenerator.AlienComp alienComp = pawn.GetComp(); + Graphic addonGraphic = alienComp.addonGraphics[bodyAddonIndex]; + + for (int i = 0; i < 4; i++) + { + Rot4 apparentRotation = new Rot4(i); + + // Get basic offset for body addon + AlienPartGenerator.RotationOffset defaultOffsets = bodyAddon.defaultOffsets.GetOffset(apparentRotation); + Vector3 bodyTypeOffset = (defaultOffsets != null) ? defaultOffsets.GetOffset(renderFlags.FlagSet(PawnRenderFlags.Portrait), pawn.story.bodyType, alienComp.crownType) : Vector3.zero; + + AlienPartGenerator.RotationOffset rotationOffsets = bodyAddon.offsets.GetOffset(apparentRotation); + Vector3 bodyAddonOffset = bodyTypeOffset + ((rotationOffsets != null) ? rotationOffsets.GetOffset(renderFlags.FlagSet(PawnRenderFlags.Portrait), pawn.story.bodyType, alienComp.crownType) : Vector3.zero); + + // Offset private parts so that they render over tattoos but under apparel (rendering under tatoos looks weird) + if (bodyAddon.bodyPart == "Genitals" || bodyAddon.bodyPart == "Chest" || bodyAddon.bodyPart == "Anus" || addonGraphic.path.ToLower().Contains("belly")) + { + bodyAddonOffset.y = (bodyAddonOffset.y + 0.40f) / 1000f + 0.012f; + + // Erected penises should be drawn over apparel + if (pawn.RaceProps.Humanlike && + addonGraphic.path.ToLower().Contains("penis") && + addonGraphic.path.ToLower().Contains("flaccid") == false && + apparentRotation == Rot4.South) + { bodyAddonOffset.y += 0.010f; } + } + + // Otherwise use the standard offsets + else + { bodyAddonOffset.y = 0.3f + bodyAddonOffset.y; } + + // Draw addons infront of body + if (!bodyAddon.inFrontOfBody) + { bodyAddonOffset.y *= -1f; } + + // Adjust for facing + if (apparentRotation == Rot4.North) + { + if (bodyAddon.layerInvert) + { bodyAddonOffset.y = -bodyAddonOffset.y; } + } + + if (apparentRotation == Rot4.East) + { bodyAddonOffset.x = -bodyAddonOffset.x; } + + // Adjustment for body addons attached to the head that are not marked as such + if (alignsWithHead && bodyAddon.alignWithHead == false) + { bodyAddonOffset -= pawn.Drawer.renderer.BaseHeadOffsetAt(apparentRotation); } + + // Done + bodyAddonOffsets.Add(bodyAddonOffset); + } + } + + public Vector3 GetOffset(Rot4 facing) + { + if (pawn.story.bodyType.defName != bodyType) + { GenerateOffsets(); } + + return bodyAddonOffsets[facing.AsInt]; + } + + public bool CanDraw() + { + return pawn.Drawer.renderer.graphics.apparelGraphics.Any() == false || canDraw; + } + + public void UpdateVisibility() + { + canDraw = true; + + if (pawn.CurrentBed()?.def.building.bed_showSleeperBody == false && bodyAddon.drawnInBed == false) + {canDraw = false; return; } + + if (bodyAddon.backstoryRequirement.NullOrEmpty() == false && pawn.story.AllBackstories.Any((Backstory x) => x.identifier == bodyAddon.backstoryRequirement) == false) + { canDraw = false; return; } + + if (bodyAddon.drawnDesiccated == false && pawn?.Corpse?.GetRotStage() == RotStage.Dessicated) + { canDraw = false; return; } + + if (pawn.health.hediffSet.GetNotMissingParts().Contains(bodyPartRecord) == false) + { canDraw = false; return; } + + if (pawn.gender == Gender.Female && bodyAddon.drawForFemale == false || pawn.gender == Gender.Male && bodyAddon.drawForMale == false) + { canDraw = false; return; } + + if (bodyAddon.bodyTypeRequirement.NullOrEmpty() == false && pawn.story.bodyType.ToString() != bodyAddon.bodyTypeRequirement) + { canDraw = false; return; } + + if ((pawn.GetPosture() == PawnPosture.LayingOnGroundNormal || pawn.GetPosture() == PawnPosture.LayingOnGroundFaceUp) && bodyAddon.drawnOnGround == false) + { canDraw = false; return; } + + foreach (Apparel apparel in pawn.apparel.WornApparel) + { + CompApparelVisibility comp = apparel.TryGetComp(); + LoadRimNudeData(comp); + + if (comp.isBeingWorn == false) continue; + + if (bodyAddon.bodyPart == "Genitals" || bodyAddon.bodyPart == "Anus" || bodyAddon.bodyPart == "Chest" || bodyAddon.hediffGraphics?.Any(x => x.path.ToLower().Contains("belly")) == true) + { + if ((bodyAddon.bodyPart == "Genitals" || bodyAddon.bodyPart == "Anus") && comp.coversGroin) + { canDraw = false; return; }; + + if (bodyAddon.bodyPart == "Chest" && comp.coversChest) + { canDraw = false; return; }; + + if (bodyAddon.hediffGraphics?.Any(x => x.path.ToLower().Contains("belly")) == true && comp.coversBelly) + { canDraw = false; return; } + } + + else + { + if (bodyAddon.hiddenUnderApparelFor?.Any(x => apparel.def.apparel.hatRenderedFrontOfFace == false && apparel.def.apparel.bodyPartGroups.Contains(x)) == true) + { canDraw = false; return; }; + + if (bodyAddon.hiddenUnderApparelTag?.Any(x => apparel.def.apparel.hatRenderedFrontOfFace == false && apparel.def.apparel.tags.Contains(x)) == true) + { canDraw = false; return; }; + } + } + } + + public void LoadRimNudeData(CompApparelVisibility comp) + { + if (comp == null || comp.rimNudeDataStatus == RimNudeDataStatus.Unavailable) + { return; } + + if (comp.rimNudeDataStatus == RimNudeDataStatus.NotLoaded) + { + RimNudeData rimNudeData = ApparelSettings.GetRimNudeData(comp.apparel); + + if (rimNudeData == null) + { + comp.rimNudeDataStatus = RimNudeDataStatus.Unavailable; + return; + } + + comp.coversBelly = rimNudeData.coversBelly; + comp.coversChest = rimNudeData.coversChest; + comp.coversGroin = rimNudeData.coversGroin; + + comp.rimNudeDataStatus = RimNudeDataStatus.Loaded; + } + } + } +} diff --git a/Source/Scripts/Extensions/PawnExtension.cs b/Source/Scripts/Extensions/PawnExtension.cs index 2cb71f2..6040ddc 100644 --- a/Source/Scripts/Extensions/PawnExtension.cs +++ b/Source/Scripts/Extensions/PawnExtension.cs @@ -128,8 +128,14 @@ namespace Rimworld_Animations_Patch if (pawn.IsHavingSex() == false && pawn.IsMasturbating() == false) { return true; } + if (pawn.GetLord() != null && pawn.GetLord().LordJob is LordJob_Ritual) + { return true; } + + if (pawn.GetLord() != null && pawn.GetLord().LordJob is LordJob_Joinable_Party) + { return true; } + bool hasPrivacy = true; - bool isExhibitionist = pawn.HasTrait("Exhibitionist") || xxx.has_quirk(pawn, "Exhibitionist"); + bool isExhibitionist = xxx.has_quirk(pawn, "Exhibitionist"); pawn.IsInBed(out Building bed); @@ -192,22 +198,19 @@ namespace Rimworld_Animations_Patch public static List GetHands(this Pawn pawn) { - if (HandAnimationUtility.handDef == null) - { HandAnimationUtility.handDef = DefDatabase.GetNamed("Hand", false); } - - return pawn.health.hediffSet.GetNotMissingParts().Where(x => x.def == HandAnimationUtility.handDef)?.ToList(); + return pawn.health.hediffSet.GetNotMissingParts().Where(x => x.def == PatchBodyPartDefOf.Hand)?.ToList(); } - public static bool HasPreceptForIssue(this Pawn pawn, IssueDef issueDef, out Precept precept) + public static bool HasPreceptForIssue(this Pawn pawn, string issueDefName, out Precept precept) { precept = null; - if (pawn?.Ideo == null || issueDef == null) + if (pawn?.Ideo == null) { return false; } foreach (Precept _precept in pawn.Ideo.PreceptsListForReading) { - if (_precept.def.issue == issueDef) + if (_precept.def.issue.defName == issueDefName) { precept = _precept; return true; @@ -217,28 +220,6 @@ namespace Rimworld_Animations_Patch return false; } - public static bool IssueIsMajorTaboo(this Pawn pawn, IssueDef issueDef, out Precept precept) - { - if (HasPreceptForIssue(pawn, issueDef, out precept)) - { - if (precept.def.defName.Contains("Forbidden") || precept.def.defName.Contains("Prohibited") || precept.def.defName.Contains("Abhorrent")) - { return true; } - } - - return false; - } - - public static bool IssueIsMinorTaboo(this Pawn pawn, IssueDef issueDef, out Precept precept) - { - if (HasPreceptForIssue(pawn, issueDef, out precept)) - { - if (precept.def.defName.Contains("Horrible") || precept.def.defName.Contains("Despised") || precept.def.defName.Contains("Disapproved")) - { return true; } - } - - return false; - } - public static bool EnjoysViolence(this Pawn pawn) { if (pawn.IsAnimal() || pawn.RaceProps.IsMechanoid) @@ -275,6 +256,14 @@ namespace Rimworld_Animations_Patch if (traitDef == null) { traitDef = DefDatabase.GetNamedSilentFail(trait.ToLower()); } + return HasTrait(pawn, traitDef); + } + + public static bool HasTrait(this Pawn pawn, TraitDef traitDef) + { + if (pawn?.story?.traits?.allTraits == null || pawn.story.traits.allTraits.NullOrEmpty()) + { return false; } + if (traitDef == null) { return false; } diff --git a/Source/Scripts/Patches/HarmonyPatch_ApparelGraphicRecordGetter.cs b/Source/Scripts/Patches/HarmonyPatch_ApparelGraphicRecordGetter.cs index f9b0793..322ad55 100644 --- a/Source/Scripts/Patches/HarmonyPatch_ApparelGraphicRecordGetter.cs +++ b/Source/Scripts/Patches/HarmonyPatch_ApparelGraphicRecordGetter.cs @@ -31,6 +31,9 @@ namespace Rimworld_Animations_Patch graphic = GraphicMaskingUtility.ApplyGraphicWithMasks(graphic, graphicWithApparelMask, true); //DebugMode.Message("Applying apparel mask: Masks/apparel_shirt_mask_" + bodyType.defName + " to " + apparel.def.defName + " (" + graphic.path + ")"); + + if (apparel.Wearer != null) + { PortraitsCache.SetDirty(apparel.Wearer); } } } diff --git a/Source/Scripts/Patches/HarmonyPatch_PawnRenderer.cs b/Source/Scripts/Patches/HarmonyPatch_PawnRenderer.cs index 6c3ba57..26b9f7b 100644 --- a/Source/Scripts/Patches/HarmonyPatch_PawnRenderer.cs +++ b/Source/Scripts/Patches/HarmonyPatch_PawnRenderer.cs @@ -89,7 +89,7 @@ namespace Rimworld_Animations_Patch if (comp != null) { - comp.isBeingWorn = true; + comp.isBeingWorn = null; comp.rimNudeDataStatus = RimNudeDataStatus.NotLoaded; } } diff --git a/Source/Scripts/Patches/HarmonyPatch_Pawn_ApparelTracker.cs b/Source/Scripts/Patches/HarmonyPatch_Pawn_ApparelTracker.cs new file mode 100644 index 0000000..94475b4 --- /dev/null +++ b/Source/Scripts/Patches/HarmonyPatch_Pawn_ApparelTracker.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using RimWorld; +using Verse; +using HarmonyLib; + +namespace Rimworld_Animations_Patch +{ + [HarmonyPatch(typeof(Pawn_ApparelTracker), "HasBasicApparel")] + public static class HarmonyPatch_Pawn_ApparelTracker_HasBasicApparel + { + public static void Postfix(Pawn_ApparelTracker __instance, ref bool hasPants, ref bool hasShirt) + { + if (__instance?.pawn?.apparel?.WornApparel == null || __instance.pawn.apparel.WornApparel.NullOrEmpty()) return; + + if (hasPants == false) + { + if (__instance.pawn.apparel.WornApparel.Any(x => x.def.apparel.bodyPartGroups.Contains(PatchBodyPartGroupDefOf.GenitalsBPG))) + { hasPants = true; } + } + + if (hasShirt == false) + { + if (__instance.pawn.apparel.WornApparel.Any(x => x.def.apparel.bodyPartGroups.Contains(PatchBodyPartGroupDefOf.ChestBPG))) + { hasShirt = true; } + } + } + } + + [HarmonyPatch(typeof(Pawn_ApparelTracker), "Notify_ApparelChanged")] + public static class HarmonyPatch_Pawn_ApparelTracker_Notify_ApparelChanged + { + public static void Postfix(Pawn_ApparelTracker __instance) + { + __instance?.pawn?.TryGetComp()?.UpdateBodyAddonVisibility(); + } + } + + +} diff --git a/Source/Scripts/Patches/HarmonyPatch_RJW.cs b/Source/Scripts/Patches/HarmonyPatch_RJW.cs index 6e6d690..3c986a7 100644 --- a/Source/Scripts/Patches/HarmonyPatch_RJW.cs +++ b/Source/Scripts/Patches/HarmonyPatch_RJW.cs @@ -16,29 +16,30 @@ namespace Rimworld_Animations_Patch { public static void Postfix(ref JobDriver_Sex __instance) { + Pawn pawn = __instance.pawn; + // Sets ticks so that the orgasm meter starts empty, plus stop any running animations HarmonyPatch_JobDriver_Masturbate_setup_ticks.Postfix(ref __instance); // Invite another for a threesome? if (RJWHookupSettings.QuickHookupsEnabled && __instance is JobDriver_SexBaseInitiator && - __instance.pawn.GetAllSexParticipants().Count == 2 && + pawn.GetAllSexParticipants().Count == 2 && (__instance is JobDriver_JoinInSex) == false && Random.value < BasicSettings.chanceForOtherToJoinInSex) { DebugMode.Message("Find another to join in sex"); - Pawn pawn = __instance.pawn; List candidates = new List(); float radius = 4f; foreach (Thing thing in GenRadial.RadialDistinctThingsAround(pawn.Position, pawn.Map, radius, true)) { Pawn other = thing as Pawn; + ThoughtDef thoughtDef = SexInteractionUtility.GetThoughtsAboutSexAct(other, __instance, out Precept precept); // Find candidates to invite - if (other != null && (int)SexInteractionUtility.CheckSexJobAgainstMorals(other, __instance, out Precept precept) <= 0 && - SexInteractionUtility.PawnCanInvitePasserbyForSex(other, pawn.GetAllSexParticipants())) + if (other != null && thoughtDef?.hediff == null && SexInteractionUtility.InvitePasserbyForSex(other, pawn.GetAllSexParticipants())) { DebugMode.Message(other.NameShortColored + " is a potential candidate"); candidates.Add(other); @@ -436,7 +437,7 @@ namespace Rimworld_Animations_Patch { public static bool Prefix(Pawn pawn, bool keep_hat_on) { - if (!xxx.is_human(pawn)) return false; + if (pawn == null || !xxx.is_human(pawn)) return false; if (pawn.Map != Find.CurrentMap) return false; pawn.Drawer.renderer.graphics.ClearCache(); @@ -448,10 +449,11 @@ namespace Rimworld_Animations_Patch { CompApparelVisibility comp = apparel.TryGetComp(); - if ((comp == null || comp.isBeingWorn) && ApparelGraphicRecordGetter.TryGetGraphicApparel(apparel, pawn.story.bodyType, out ApparelGraphicRecord item)) + if (comp != null && comp.isBeingWorn == true && ApparelGraphicRecordGetter.TryGetGraphicApparel(apparel, pawn.story.bodyType, out ApparelGraphicRecord item)) { pawn.Drawer.renderer.graphics.apparelGraphics.Add(item); } } + pawn?.TryGetComp()?.UpdateBodyAddonVisibility(); GlobalTextureAtlasManager.TryMarkPawnFrameSetDirty(pawn); return false; diff --git a/Source/Scripts/Patches/HarmonyPatch_RimNudeWorld.cs b/Source/Scripts/Patches/HarmonyPatch_RimNudeWorld.cs index 9a07867..d2e57d0 100644 --- a/Source/Scripts/Patches/HarmonyPatch_RimNudeWorld.cs +++ b/Source/Scripts/Patches/HarmonyPatch_RimNudeWorld.cs @@ -12,7 +12,7 @@ namespace Rimworld_Animations_Patch [StaticConstructorOnStartup] public static class HarmonyPatch_RimNudeWorld { - /*static HarmonyPatch_RimNudeWorld() + static HarmonyPatch_RimNudeWorld() { try { @@ -21,7 +21,7 @@ namespace Rimworld_Animations_Patch if (LoadedModManager.RunningModsListForReading.Any(x => x.PackageIdPlayerFacing == "shauaputa.rimnudeworld")) { (new Harmony("Rimworld_Animations_Patch")).Patch(AccessTools.Method(AccessTools.TypeByName("RevealingApparel.HarmonyPatch_DrawAddons"), "Postfix"), - prefix: new HarmonyMethod(AccessTools.Method(typeof(HarmonyPatch_RimNudeWorld), "Prefix_DrawAddons"))); + prefix: new HarmonyMethod(AccessTools.Method(typeof(HarmonyPatch_RimNudeWorld), "Prefix_HarmonyPatch_DrawAddons"))); } }))(); } @@ -29,9 +29,9 @@ namespace Rimworld_Animations_Patch } // Patch RimNudeWorld to override the revealing apparel feature; this task is handled by the new apparel settings system - public static bool Prefix_DrawAddons() + public static bool Prefix_HarmonyPatch_DrawAddons() { return false; - }*/ + } } } diff --git a/Source/Scripts/Patches/HarmonyPatch_Rimworld_Animations.cs b/Source/Scripts/Patches/HarmonyPatch_Rimworld_Animations.cs index 2665074..a9af498 100644 --- a/Source/Scripts/Patches/HarmonyPatch_Rimworld_Animations.cs +++ b/Source/Scripts/Patches/HarmonyPatch_Rimworld_Animations.cs @@ -24,19 +24,42 @@ namespace Rimworld_Animations_Patch postfix: new HarmonyMethod(AccessTools.Method(typeof(HarmonyPatch_Rimworld_Animations), "Postfix_RerollAnimations"))); (new Harmony("Rimworld_Animations_Patch")).Patch(AccessTools.Method(AccessTools.TypeByName("Rimworld_Animations.HarmonyPatch_AlienRace"), "Prefix_AnimateHeadAddons"), prefix: new HarmonyMethod(AccessTools.Method(typeof(HarmonyPatch_Rimworld_Animations), "Prefix_DrawAddons"))); - (new Harmony("Rimworld_Animations_Patch")).Patch(AccessTools.Method(AccessTools.TypeByName("BodyAddon"), "CanDrawAddon"), - postfix: new HarmonyMethod(AccessTools.Method(typeof(HarmonyPatch_Rimworld_Animations), "Postfix_CanDrawAddon"))); + (new Harmony("Rimworld_Animations_Patch")).Patch(AccessTools.Method(AccessTools.TypeByName("Rimworld_Animations.CompBodyAnimator"), "StartAnimation"), + postfix: new HarmonyMethod(AccessTools.Method(typeof(HarmonyPatch_Rimworld_Animations), "Postfix_StartAnimation"))); + (new Harmony("Rimworld_Animations_Patch")).Patch(AccessTools.Method(AccessTools.TypeByName("Rimworld_Animations.AnimationUtility"), "tryFindAnimation"), + prefix: new HarmonyMethod(AccessTools.Method(typeof(HarmonyPatch_Rimworld_Animations), "Prefix_tryFindAnimation"))); + } + + // Update hand count for all participants before finding an animation + public static void Prefix_tryFindAnimation(ref List participants) + { + foreach (Pawn participant in participants) + { + CompPawnSexData comp = participant.TryGetComp(); + if (comp == null) return; + + comp.UpdateHands(); + } + } + + // Update hand animation def on anim start + public static void Postfix_StartAnimation(CompBodyAnimator __instance) + { + CompPawnSexData comp = __instance.pawn.TryGetComp(); + if (comp == null) return; + + comp.handAnimationDef = DefDatabase.AllDefs.FirstOrDefault(x => x.animationDefName == __instance.pawn?.GetAnimationData()?.animationDef?.defName); } // Extend the animation selector's body part check to include hands and whether the pawn is in bed or not public static void PostFix_AnimationUtility_GenitalCheckForPawn(ref bool __result, List requiredGenitals, Pawn pawn, ref string failReason) { - int handCount = 0; - bool pawnInBed = pawn.IsInBed(out Building bed); + int? handCount = pawn.TryGetComp()?.GetNumberOfHands(); + + if (handCount.HasValue == false) + { handCount = 0; } - var hands = pawn.health.hediffSet.GetNotMissingParts().Where(x => x.def.defName == "Hand"); - if (hands != null) - { handCount = hands.Count(); } + bool pawnInBed = pawn.IsInBed(out Building bed); if (requiredGenitals.NullOrEmpty()) { return; } @@ -82,28 +105,6 @@ namespace Rimworld_Animations_Patch } } - // Determine if a body addon is covered by apparel - public static bool BodyAddonCoveredByWornApparel(Pawn pawn, AlienPartGenerator.BodyAddon bodyAddon) - { - if (pawn?.apparel?.WornApparel == null || bodyAddon == null) - { return false; } - - foreach (Apparel apparel in pawn.apparel.WornApparel) - { - if (ApparelAnimationUtility.PrivatePartCoveredByApparel(apparel, bodyAddon.bodyPart)) - { return true; } - } - - return false; - } - - public static void Postfix_CanDrawAddon(AlienPartGenerator.BodyAddon __instance, ref bool __result, Pawn pawn) - { - if (__result == false) return; - - __result = BodyAddonCoveredByWornApparel(pawn, __instance) == false; - } - // Replacement patch for AlienRace to draw the body addons public static bool Prefix_DrawAddons(PawnRenderFlags renderFlags, Vector3 vector, Vector3 headOffset, Pawn pawn, Quaternion quat, Rot4 rotation) { @@ -113,14 +114,15 @@ namespace Rimworld_Animations_Patch if (ApparelSettings.clothesThrownOnGround) { ApparelAnimationUtility.TryToDrawApparelOnFloor(pawn); } - // Get components + // Get actor components and body addons List bodyAddons = alienProps.alienRace.generalSettings.alienPartGenerator.bodyAddons.ToList(); AlienPartGenerator.AlienComp alienComp = pawn.GetComp(); - CompBodyAnimator pawnAnimator = pawn.TryGetComp(); + CompBodyAnimator animatorComp = pawn.TryGetComp(); + CompPawnSexData sexDataComp = pawn.TryGetComp(); + if (sexDataComp == null) return true; // Get available hands - var hands = pawn.health.hediffSet.GetNotMissingParts().Where(x => x.def.defName == "Hand"); - int handsAvailableCount = hands.Count(); + int handsAvailableCount = sexDataComp.GetNumberOfHands(); // Sort addons by their layer offset, otherwise body parts will actualy be layered according to their position in the list // Note that sorting the addons directly seems to mess up relations between lists need by AlienRace @@ -131,129 +133,59 @@ namespace Rimworld_Animations_Patch { int i = idxBodyAddons[idx]; + // Get body addon components AlienPartGenerator.BodyAddon bodyAddon = bodyAddons[i]; Graphic addonGraphic = alienComp.addonGraphics[i]; + BodyAddonData bodyAddonDatum = sexDataComp.GetBodyAddonData(bodyAddon, renderFlags.FlagSet(PawnRenderFlags.Portrait)); + if (bodyAddonDatum == null) continue; - bool canDraw = addonGraphic.path.ToLower().Contains("featureless") == false && bodyAddon.CanDrawAddon(pawn); - bool drawHand = !renderFlags.FlagSet(PawnRenderFlags.Portrait) && BasicSettings.showHands && handsAvailableCount > 0 && HandAnimationUtility.BodyPartIsBeingTouched(pawn, addonGraphic.path, out var handData); - + // Can draw? + bool canDraw = addonGraphic.path.ToLower().Contains("featureless") == false && bodyAddonDatum.CanDraw(); + bool drawHand = BasicSettings.showHands && handsAvailableCount > 0 && renderFlags.FlagSet(PawnRenderFlags.Portrait) == false; + if (canDraw == false && drawHand == false) { continue; } - BodyPartRecord bodyPartRecord = AnimationPatchUtility.GetBodyPartRecord(pawn, bodyAddon.bodyPart); - bool alignWithHead = bodyAddon.alignWithHead || (bodyPartRecord != null && bodyPartRecord.IsInGroup(BodyPartGroupDefOf.FullHead)); + // Get body angle + float bodyAngle = animatorComp?.isAnimating == true && renderFlags.FlagSet(PawnRenderFlags.Portrait) == false ? animatorComp.bodyAngle : quat.eulerAngles.y; + bodyAngle = bodyAngle < 0f ? 360f + (bodyAngle % 360f) : bodyAngle % 360f; + // Get the apparent rotation and body addon angle Rot4 apparentRotation = rotation; - if (!renderFlags.FlagSet(PawnRenderFlags.Portrait) && pawnAnimator != null && pawnAnimator.isAnimating) - { apparentRotation = alignWithHead ? pawnAnimator.headFacing : pawnAnimator.bodyFacing; } - - AlienPartGenerator.RotationOffset defaultOffsets = bodyAddon.defaultOffsets.GetOffset(apparentRotation); - Vector3 bodyTypeOffset = (defaultOffsets != null) ? defaultOffsets.GetOffset(renderFlags.FlagSet(PawnRenderFlags.Portrait), pawn.story.bodyType, alienComp.crownType) : Vector3.zero; - AlienPartGenerator.RotationOffset offsets = bodyAddon.offsets.GetOffset(apparentRotation); - Vector3 vector2 = bodyTypeOffset + ((offsets != null) ? offsets.GetOffset(renderFlags.FlagSet(PawnRenderFlags.Portrait), pawn.story.bodyType, alienComp.crownType) : Vector3.zero); - - // Offset private parts so that they render over tattoos but under apparel (rendering under tatoos looks weird) - if ((bodyPartRecord != null && (bodyPartRecord.IsInGroup(PatchBodyPartGroupDefOf.GenitalsBPG) || bodyPartRecord.IsInGroup(PatchBodyPartGroupDefOf.ChestBPG) || bodyPartRecord.IsInGroup(PatchBodyPartGroupDefOf.AnusBPG))) || addonGraphic.path.ToLower().Contains("belly")) - { - vector2.y = (vector2.y + 0.40f) / 1000f + 0.012f; - - // Erected penises should be drawn over apparel - if (pawn.RaceProps.Humanlike && - addonGraphic.path.ToLower().Contains("penis") && - addonGraphic.path.ToLower().Contains("flaccid") == false && - apparentRotation == Rot4.South) - { vector2.y += 0.010f; } - } - - // Otherwise use the standard offsets - else - { vector2.y = 0.3f + vector2.y; } - - if (!bodyAddon.inFrontOfBody) - { vector2.y *= -1f; } - float bodyAddonAngle = bodyAddon.angle; - if (apparentRotation == Rot4.North) + if (renderFlags.FlagSet(PawnRenderFlags.Portrait) == false && animatorComp?.isAnimating == true) { - if (bodyAddon.layerInvert) - { vector2.y = -vector2.y; } + apparentRotation = bodyAddonDatum.alignsWithHead ? animatorComp.headFacing : animatorComp.bodyFacing; - bodyAddonAngle = 0f; + if (animatorComp.controlGenitalAngle && addonGraphic.path.ToLower().Contains("penis")) + { bodyAddonAngle += AnimationSettings.controlGenitalRotation ? animatorComp.genitalAngle : 0f; } + + if (bodyAddonDatum.alignsWithHead) + { bodyAngle = animatorComp.headAngle; } } - if (apparentRotation == Rot4.East) - { - bodyAddonAngle = -bodyAddonAngle; - vector2.x = -vector2.x; - } - - Quaternion addonRotation = quat; - Quaternion quatAdditional = Quaternion.identity; - - float finalAngle = 0; - - if (!renderFlags.FlagSet(PawnRenderFlags.Portrait) && pawnAnimator != null && pawnAnimator.isAnimating) - { - if (pawnAnimator.controlGenitalAngle && bodyAddon?.hediffGraphics != null && !bodyAddon.hediffGraphics.NullOrEmpty() && bodyAddon.hediffGraphics[0]?.path != null && (bodyAddon.hediffGraphics[0].path.Contains("Penis") || bodyAddon.hediffGraphics[0].path.Contains("penis"))) - { - float bodyAngle = pawnAnimator.bodyAngle; - addonRotation = Quaternion.AngleAxis(angle: bodyAngle, axis: Vector3.up); - - float anglePenis = AnimationSettings.controlGenitalRotation ? pawnAnimator.genitalAngle : 0f; - anglePenis = anglePenis < 0 ? 360 - (360 % anglePenis) : anglePenis; - quatAdditional = Quaternion.AngleAxis(angle: anglePenis, axis: Vector3.up); - - finalAngle = bodyAngle + anglePenis; - } - - else if (alignWithHead) - { - float headAngle = pawnAnimator.headAngle; - headAngle = headAngle < 0 ? 360 - (360 % headAngle) : headAngle; - addonRotation = Quaternion.AngleAxis(angle: headAngle, axis: Vector3.up); - - finalAngle = pawnAnimator.bodyAngle + headAngle; - } - - else - { - float bodyAngle = pawnAnimator.bodyAngle; - addonRotation = Quaternion.AngleAxis(angle: bodyAngle, axis: Vector3.up); - - finalAngle = bodyAngle; - } - } - - // Fixes 'leaning left' issue with Yayo's animations - else if (!renderFlags.FlagSet(PawnRenderFlags.Portrait) && (pawnAnimator == null || pawnAnimator.isAnimating == false)) - { - float bodyAngle = addonRotation.eulerAngles.y; - bodyAngle = bodyAngle < 0 ? 360 - (360 % bodyAngle) : bodyAngle; - addonRotation = Quaternion.AngleAxis(angle: bodyAngle, axis: Vector3.up); - } - - if (alignWithHead && bodyAddon.alignWithHead == false) - { vector2 -= pawn.Drawer.renderer.BaseHeadOffsetAt(apparentRotation); } - - Vector3 finalPosition = vector + (alignWithHead ? headOffset : Vector3.zero) + vector2.RotatedBy(angle: Mathf.Acos(f: Quaternion.Dot(a: Quaternion.identity, b: addonRotation)) * 2f * 57.29578f); + bodyAddonAngle = bodyAddonAngle < 0f ? 360f - (bodyAddonAngle % 360) : bodyAddonAngle % 360f; + float combinedAngle = (bodyAngle + bodyAddonAngle) < 0f ? 360f + ((bodyAngle + bodyAddonAngle) % 360) : (bodyAngle + bodyAddonAngle) % 360f; + Vector3 bodyAddonPosition = vector + (bodyAddonDatum.alignsWithHead ? headOffset : Vector3.zero) + bodyAddonDatum.GetOffset(rotation).RotatedBy(angle: bodyAngle); + // Draw the addon if visible if (canDraw) { - //DebugMode.Message("Drawing " + addonGraphic.path); - GenDraw.DrawMeshNowOrLater(mesh: addonGraphic.MeshAt(rot: apparentRotation), - loc: finalPosition, - quat: Quaternion.AngleAxis(angle: bodyAddonAngle, axis: Vector3.up) * quatAdditional * addonRotation, + loc: bodyAddonPosition, + quat: Mathf.Approximately(combinedAngle, 0f) ? quat : Quaternion.AngleAxis(angle: combinedAngle, axis: Vector3.up), mat: addonGraphic.MatAt(rot: apparentRotation), drawNow: renderFlags.FlagSet(PawnRenderFlags.DrawNow)); } // Draw hand over the body part if required if (drawHand) - { - if (HandAnimationUtility.TryToDrawHand(pawn, addonGraphic.path, finalPosition, finalAngle, rotation, renderFlags)) + { + float finalAngle = 0; + + if (HandAnimationUtility.TryToDrawHand(pawn, addonGraphic.path, bodyAddonPosition, finalAngle, rotation, renderFlags)) { handsAvailableCount--; } } } @@ -263,8 +195,8 @@ namespace Rimworld_Animations_Patch { var methodInfo = AccessTools.Method(typeof(PawnRenderer), "DrawHeadHair", null, null); - Rot4 headFacing = pawnAnimator != null && pawnAnimator.isAnimating && !renderFlags.FlagSet(PawnRenderFlags.Portrait) ? pawnAnimator.headFacing : rotation; - float headAngle = pawnAnimator != null && pawnAnimator.isAnimating && !renderFlags.FlagSet(PawnRenderFlags.Portrait) ? pawnAnimator.headAngle : quat.eulerAngles.y; + Rot4 headFacing = animatorComp != null && animatorComp.isAnimating && !renderFlags.FlagSet(PawnRenderFlags.Portrait) ? animatorComp.headFacing : rotation; + float headAngle = animatorComp != null && animatorComp.isAnimating && !renderFlags.FlagSet(PawnRenderFlags.Portrait) ? animatorComp.headAngle : quat.eulerAngles.y; RotDrawMode rotDrawMode = (RotDrawMode)AccessTools.Property(typeof(PawnRenderer), "CurRotDrawMode").GetValue(pawn.Drawer.renderer); methodInfo.Invoke(pawn.Drawer.renderer, new object[] { vector + new Vector3(0f, YOffset_Head, 0f), headOffset, headAngle, headFacing, headFacing, rotDrawMode, renderFlags }); @@ -303,5 +235,5 @@ namespace Rimworld_Animations_Patch Hair = rootLoc + YOffset_OnHead; (~ 0.029) Hat (over hair) = rootLoc + YOffset_PostHead; (~ 0.031) */ - } -} \ No newline at end of file + } + } \ No newline at end of file diff --git a/Source/Scripts/Patches/HarmonyPatch_ThoughtWorkers.cs b/Source/Scripts/Patches/HarmonyPatch_ThoughtWorkers.cs new file mode 100644 index 0000000..f0af349 --- /dev/null +++ b/Source/Scripts/Patches/HarmonyPatch_ThoughtWorkers.cs @@ -0,0 +1,123 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using RimWorld; +using Verse; +using HarmonyLib; +using RJW_Events; + +namespace Rimworld_Animations_Patch +{ + + [HarmonyPatch(typeof(ThinkNode_ConditionalNude), "Satisfied")] + public static class HarmonyPatch_ThinkNode_ConditionalNude + { + public static void Postfix(ref bool __result, Pawn pawn) + { + if (__result == false && pawn?.apparel?.WornApparel != null) + { + // If 'isBeingWorn' has a value, the apparel has already been checked if it should be discarded + if (pawn.apparel.WornApparel.Any(x => x.TryGetComp() != null && x.TryGetComp().isBeingWorn.HasValue)) + { __result = true; return; } + } + } + } + + [HarmonyPatch(typeof(ThoughtWorker_Precept_GroinChestHairOrFaceUncovered), "HasUncoveredGroinChestHairOrFace")] + public static class HarmonyPatch_ThoughtWorker_Precept_GroinChestHairOrFaceUncovered + { + public static void Postfix(ref bool __result, Pawn p) + { + if (__result == false) return; + + Pawn pawn = p; + if (ApparelSettings.underwearSufficentForIdeos == false) return; + + if (pawn?.apparel == null) + { __result = false; return; } + + if (pawn.apparel.WornApparel.NullOrEmpty()) + { __result = true; return; } + + bool fullHeadCovered = pawn.apparel.WornApparel.Any(x => x.def.apparel.bodyPartGroups.Contains(BodyPartGroupDefOf.FullHead)); + bool groinCovered = pawn.apparel.WornApparel.Any(x => x.def.apparel.bodyPartGroups.Contains(BodyPartGroupDefOf.Legs) || x.def.apparel.bodyPartGroups.Contains(PatchBodyPartGroupDefOf.GenitalsBPG)); + bool chestCovered = pawn.apparel.WornApparel.Any(x => x.def.apparel.bodyPartGroups.Contains(BodyPartGroupDefOf.Torso) || x.def.apparel.bodyPartGroups.Contains(PatchBodyPartGroupDefOf.ChestBPG)); + bool faceCovered = fullHeadCovered || pawn.apparel.WornApparel.Any(x => x.def.apparel.bodyPartGroups.Contains(BodyPartGroupDefOf.Eyes)); + bool hairCovered = fullHeadCovered || pawn.apparel.WornApparel.Any(x => x.def.apparel.bodyPartGroups.Contains(BodyPartGroupDefOf.UpperHead)); + + __result = !(groinCovered && chestCovered && faceCovered && hairCovered); + } + } + + [HarmonyPatch(typeof(ThoughtWorker_Precept_GroinChestOrHairUncovered), "HasUncoveredGroinChestOrHair")] + public static class HarmonyPatch_ThoughtWorker_Precept_GroinChestOrHairUncovered + { + public static void Postfix(ref bool __result, Pawn p) + { + if (__result == false) return; + + Pawn pawn = p; + if (ApparelSettings.underwearSufficentForIdeos == false) return; + + if (pawn?.apparel == null) + { __result = false; return; } + + if (pawn.apparel.WornApparel.NullOrEmpty()) + { __result = true; return; } + + bool fullHeadCovered = pawn.apparel.WornApparel.Any(x => x.def.apparel.bodyPartGroups.Contains(BodyPartGroupDefOf.FullHead)); + bool groinCovered = pawn.apparel.WornApparel.Any(x => x.def.apparel.bodyPartGroups.Contains(BodyPartGroupDefOf.Legs) || x.def.apparel.bodyPartGroups.Contains(PatchBodyPartGroupDefOf.GenitalsBPG)); + bool chestCovered = pawn.apparel.WornApparel.Any(x => x.def.apparel.bodyPartGroups.Contains(BodyPartGroupDefOf.Torso) || x.def.apparel.bodyPartGroups.Contains(PatchBodyPartGroupDefOf.ChestBPG)); + bool hairCovered = fullHeadCovered || pawn.apparel.WornApparel.Any(x => x.def.apparel.bodyPartGroups.Contains(BodyPartGroupDefOf.UpperHead)); + + __result = !(groinCovered && chestCovered && hairCovered); + } + } + + [HarmonyPatch(typeof(ThoughtWorker_Precept_GroinOrChestUncovered), "HasUncoveredGroinOrChest")] + public static class HarmonyPatch_ThoughtWorker_Precept_HasUncoveredGroinOrChest + { + public static void Postfix(ref bool __result, Pawn p) + { + if (__result == false) return; + + Pawn pawn = p; + if (ApparelSettings.underwearSufficentForIdeos == false) return; + + if (pawn?.apparel == null) + { __result = false; return; } + + if (pawn.apparel.WornApparel.NullOrEmpty()) + { __result = true; return; } + + bool groinCovered = pawn.apparel.WornApparel.Any(x => x.def.apparel.bodyPartGroups.Contains(BodyPartGroupDefOf.Legs) || x.def.apparel.bodyPartGroups.Contains(PatchBodyPartGroupDefOf.GenitalsBPG)); + bool chestCovered = pawn.apparel.WornApparel.Any(x => x.def.apparel.bodyPartGroups.Contains(BodyPartGroupDefOf.Torso) || x.def.apparel.bodyPartGroups.Contains(PatchBodyPartGroupDefOf.ChestBPG)); + + __result = !(groinCovered && chestCovered); + } + } + + [HarmonyPatch(typeof(ThoughtWorker_Precept_GroinUncovered), "HasUncoveredGroin")] + public static class HarmonyPatch_ThoughtWorker_Precept_GroinUncovered + { + public static void Postfix(ref bool __result, Pawn p) + { + if (__result == false) return; + + Pawn pawn = p; + if (ApparelSettings.underwearSufficentForIdeos == false) return; + + if (pawn?.apparel == null) + { __result = false; return; } + + if (pawn.apparel.WornApparel.NullOrEmpty()) + { __result = true; return; } + + bool groinCovered = pawn.apparel.WornApparel.Any(x => x.def.apparel.bodyPartGroups.Contains(BodyPartGroupDefOf.Legs) || x.def.apparel.bodyPartGroups.Contains(PatchBodyPartGroupDefOf.GenitalsBPG)); + + __result = !groinCovered; + } + } +} diff --git a/Source/Scripts/Settings/ApparelSettings.cs b/Source/Scripts/Settings/ApparelSettings.cs index c7e6793..48e8041 100644 --- a/Source/Scripts/Settings/ApparelSettings.cs +++ b/Source/Scripts/Settings/ApparelSettings.cs @@ -15,6 +15,8 @@ namespace Rimworld_Animations_Patch public static bool cropApparel = false; public static bool clothesThrownOnGround = true; public static RJWPreferenceSettings.Clothing apparelWornForQuickies = RJWPreferenceSettings.Clothing.Clothed; + public static bool underwearSufficentForIdeos = true; + public static bool exposedUnderwearMood = true; public override void ExposeData() { @@ -23,6 +25,8 @@ namespace Rimworld_Animations_Patch Scribe_Values.Look(ref cropApparel, "cropApparel", false); Scribe_Values.Look(ref clothesThrownOnGround, "clothesThrownOnGround", true); Scribe_Values.Look(ref apparelWornForQuickies, "apparelWornForQuickies", RJWPreferenceSettings.Clothing.Clothed); + Scribe_Values.Look(ref underwearSufficentForIdeos, "underwearSufficentForIdeos", true); + Scribe_Values.Look(ref underwearSufficentForIdeos, "exposedUnderwearMood", true); } public static RimNudeData GetRimNudeData(Apparel apparel) @@ -42,8 +46,8 @@ namespace Rimworld_Animations_Patch public class ApparelSettingsDisplay : Mod { - private const float windowY = 250f; - private const float windowHeight = 360f; + private const float windowY = 280f; + private const float windowHeight = 330f; private Vector2 scrollPosition; private const float scrollBarWidthMargin = 18f; @@ -83,6 +87,8 @@ namespace Rimworld_Animations_Patch foreach (Pawn pawn in Current.Game.CurrentMap.mapPawns.AllPawns) { pawn.Drawer.renderer.graphics.ResolveAllGraphics(); + pawn.TryGetComp()?.UpdateBodyAddonVisibility(); + PortraitsCache.SetDirty(pawn); GlobalTextureAtlasManager.TryMarkPawnFrameSetDirty(pawn); } @@ -138,6 +144,8 @@ namespace Rimworld_Animations_Patch listingStandard.CheckboxLabeled("clothes_thrown_on_ground".Translate(), ref ApparelSettings.clothesThrownOnGround, "clothes_thrown_on_ground_desc".Translate()); listingStandard.CheckboxLabeled("crop_apparel".Translate(), ref ApparelSettings.cropApparel, "crop_apparel_desc".Translate()); + listingStandard.CheckboxLabeled("underwear_sufficent_for_ideos".Translate(), ref ApparelSettings.underwearSufficentForIdeos, "underwear_sufficent_for_ideos_desc".Translate()); + listingStandard.CheckboxLabeled("exposed_underwear_mood".Translate(), ref ApparelSettings.exposedUnderwearMood, "exposed_underwear_mood_desc".Translate()); listingStandard.End(); base.DoSettingsWindowContents(inRect); diff --git a/Source/Scripts/Settings/BasicSettings.cs b/Source/Scripts/Settings/BasicSettings.cs index 0ef4bad..be7a99c 100644 --- a/Source/Scripts/Settings/BasicSettings.cs +++ b/Source/Scripts/Settings/BasicSettings.cs @@ -33,6 +33,8 @@ namespace Rimworld_Animations_Patch public static float humpingMasturbationChance = 0.25f; public static float otherMasturbationChance = 0.2f; + public static float sliderValue = 0f; + public override void ExposeData() { base.ExposeData(); @@ -110,6 +112,9 @@ namespace Rimworld_Animations_Patch listingStandard.Label("chance_for_other_to_join_in_sex".Translate() + ": " + BasicSettings.chanceForOtherToJoinInSex.ToString("F"), -1f, "chance_for_other_to_join_in_sex_desc".Translate()); BasicSettings.chanceForOtherToJoinInSex = listingStandard.Slider(BasicSettings.chanceForOtherToJoinInSex, 0f, 1f); + //listingStandard.Label("test slide: " + BasicSettings.sliderValue.ToString("F"), -1f); + //BasicSettings.sliderValue = listingStandard.Slider(BasicSettings.sliderValue, -2f, 2f); + listingStandard.CheckboxLabeled("hide_names_for_sex".Translate(), ref BasicSettings.hideNamesForSex, "hide_names_for_sex_desc".Translate()); listingStandard.CheckboxLabeled("debug_mode".Translate(), ref BasicSettings.debugMode, "debug_mode_desc".Translate()); diff --git a/Source/Scripts/ThoughtWorkers/ThoughtWorker_ExposedUnderwear.cs b/Source/Scripts/ThoughtWorkers/ThoughtWorker_ExposedUnderwear.cs new file mode 100644 index 0000000..933df5f --- /dev/null +++ b/Source/Scripts/ThoughtWorkers/ThoughtWorker_ExposedUnderwear.cs @@ -0,0 +1,37 @@ +using System; +using RimWorld; +using Verse; +using rjw; + +namespace Rimworld_Animations_Patch +{ + public class ThoughtWorker_ExposedUnderwear : ThoughtWorker + { + public static ThoughtState CurrentThoughtState(Pawn pawn) + { + if (xxx.has_quirk(pawn, "Exhibitionist")) + { + return ThoughtState.ActiveAtStage(1); + } + + return ThoughtState.ActiveAtStage(0); + } + + protected override ThoughtState CurrentStateInternal(Pawn pawn) + { + if (ApparelSettings.exposedUnderwearMood == false) return false; + + if (pawn?.apparel?.WornApparel == null || pawn.apparel.WornApparel.NullOrEmpty()) return false; + + if (pawn.apparel.WornApparel.Any(x => x.def.apparel.bodyPartGroups.Contains(PatchBodyPartGroupDefOf.GenitalsBPG)) && + pawn.apparel.WornApparel.Any(x => x.def.apparel.bodyPartGroups.Contains(BodyPartGroupDefOf.Legs)) == false) + { return CurrentThoughtState(pawn); } + + if (pawn.apparel.WornApparel.Any(x => x.def.apparel.bodyPartGroups.Contains(PatchBodyPartGroupDefOf.ChestBPG)) && + pawn.apparel.WornApparel.Any(x => x.def.apparel.bodyPartGroups.Contains(BodyPartGroupDefOf.Torso)) == false) + { return CurrentThoughtState(pawn); } + + return ThoughtState.Inactive; + } + } +} diff --git a/Source/Scripts/Utilities/AnimationPatchUtility.cs b/Source/Scripts/Utilities/AnimationPatchUtility.cs index 43f11e6..0425318 100644 --- a/Source/Scripts/Utilities/AnimationPatchUtility.cs +++ b/Source/Scripts/Utilities/AnimationPatchUtility.cs @@ -60,7 +60,7 @@ namespace Rimworld_Animations_Patch if (orgasmTick > ticks) { // Safeguard for penial, vaginal and anal sex - if (anim.actors[actorId].isFucked || anim.actors[actorId].isFucking || anim.actors[actorId].requiredGenitals.Any(x => x.ToLower().ContainsAny("penis", "vagina", "anus"))) + if (anim.actors[actorId].isFucked || anim.actors[actorId].isFucking || (anim.actors[actorId].requiredGenitals.NullOrEmpty() == false && anim.actors[actorId].requiredGenitals.Any(x => x.ToLower().ContainsAny("penis", "vagina", "anus")))) { orgasmTick = Mathf.Clamp(ticks - 5, 0, int.MaxValue); } // Actor does not orgasm @@ -72,18 +72,24 @@ namespace Rimworld_Animations_Patch } - // Extended version of PawnHeadRotInAnimation (prevents pawn hair from getting messed up when draw in portraits) - public static Rot4 PawnHeadRotInAnimation(Pawn pawn, Rot4 regularPos, PawnRenderFlags renderFlags) - { - if (!renderFlags.FlagSet(PawnRenderFlags.Portrait) && pawn?.TryGetComp() != null && pawn.TryGetComp().isAnimating) - { - return pawn.TryGetComp().headFacing; - } + // Extended version of PawnHeadRotInAnimation (prevents pawn hair from getting messed up when draw in portraits) + public static Rot4 PawnHeadRotInAnimation(Pawn pawn, Rot4 regularPos, PawnRenderFlags renderFlags) + { + if (!renderFlags.FlagSet(PawnRenderFlags.Portrait) && pawn?.TryGetComp() != null && pawn.TryGetComp().isAnimating) + { return pawn.TryGetComp().headFacing; } - return regularPos; - } + return regularPos; + } - public static BodyPartRecord GetBodyPartRecord(Pawn pawn, string bodyPart) + public static Rot4 PawnBodyRotInAnimation(Pawn pawn, Rot4 regularPos, PawnRenderFlags renderFlags) + { + if (!renderFlags.FlagSet(PawnRenderFlags.Portrait) && pawn?.TryGetComp() != null && pawn.TryGetComp().isAnimating) + { return pawn.TryGetComp().bodyFacing; } + + return regularPos; + } + + public static BodyPartRecord GetBodyPartRecord(Pawn pawn, string bodyPart) { if (bodyPart.NullOrEmpty()) { return null; } diff --git a/Source/Scripts/Utilities/ApparelAnimationUtility.cs b/Source/Scripts/Utilities/ApparelAnimationUtility.cs index 6f1e3e2..23c2e89 100644 --- a/Source/Scripts/Utilities/ApparelAnimationUtility.cs +++ b/Source/Scripts/Utilities/ApparelAnimationUtility.cs @@ -2,6 +2,7 @@ using System.Linq; using Verse; using RimWorld; +using Verse.AI.Group; using Rimworld_Animations; using UnityEngine; using AlienRace; @@ -54,23 +55,6 @@ namespace Rimworld_Animations_Patch if (comp == null || comp.rimNudeDataStatus == RimNudeDataStatus.Unavailable) { return false; } - if (comp.rimNudeDataStatus == RimNudeDataStatus.NotLoaded) - { - RimNudeData rimNudeData = ApparelSettings.GetRimNudeData(apparel); - - if (rimNudeData == null) - { - comp.rimNudeDataStatus = RimNudeDataStatus.Unavailable; - return false; - } - - comp.coversBelly = rimNudeData.coversBelly; - comp.coversChest = rimNudeData.coversChest; - comp.coversGroin = rimNudeData.coversGroin; - - comp.rimNudeDataStatus = RimNudeDataStatus.Loaded; - } - if (comp.isBeingWorn == false) { return false; } @@ -88,9 +72,7 @@ namespace Rimworld_Animations_Patch public static void DetermineApparelToKeepOn(Pawn pawn) { - JobDriver_Sex jobdriver = pawn.jobs.curDriver as JobDriver_Sex; - - if (pawn.RaceProps.Humanlike == false || pawn?.apparel?.WornApparel == null || jobdriver == null) + if (pawn?.apparel?.WornApparel == null) { return; } foreach (Apparel apparel in pawn.apparel.WornApparel) @@ -102,23 +84,20 @@ namespace Rimworld_Animations_Patch } ActorAnimationData animData = pawn.GetAnimationData(); - - if (animData == null) - { return; } - - AnimationDef anim = animData.animationDef; - int actorID = animData.actorID; - var clothingPreference = pawn.IsInBed(out Building bed) ? RJWPreferenceSettings.sex_wear : ApparelSettings.apparelWornForQuickies; if (xxx.has_quirk(pawn, "Endytophile")) { clothingPreference = RJWPreferenceSettings.Clothing.Clothed; } + // Get naked for rituals and parties + bool undressForRitual = pawn.GetLord() != null && pawn.GetLord().LordJob is LordJob_Ritual; + bool undressForParty = pawn.GetLord() != null && pawn.GetLord().LordJob is LordJob_Joinable_Party; + // Determine any obstructing apparel that must be removed foreach (Apparel apparel in pawn.apparel.WornApparel) { CompApparelVisibility comp = apparel.TryGetComp(); - + if (comp == null) { continue; } @@ -128,7 +107,7 @@ namespace Rimworld_Animations_Patch if (ApparelSettings.GetRimNudeData(apparel) != null && ApparelSettings.GetRimNudeData(apparel).sexWear) { continue; } - if (clothingPreference == RJWPreferenceSettings.Clothing.Nude) + if (clothingPreference == RJWPreferenceSettings.Clothing.Nude || undressForRitual || undressForParty) { comp.isBeingWorn = false; continue; @@ -142,7 +121,7 @@ namespace Rimworld_Animations_Patch continue; } - if (ApparelCoversPawnRequiredBodyParts(pawn, apparel, anim, actorID)) + if (animData != null && ApparelCoversPawnRequiredBodyParts(pawn, apparel, animData.animationDef, animData.actorID)) { comp.isBeingWorn = false; continue; diff --git a/Source/Scripts/Utilities/HandAnimationUtility.cs b/Source/Scripts/Utilities/HandAnimationUtility.cs index dd3421b..0d18c39 100644 --- a/Source/Scripts/Utilities/HandAnimationUtility.cs +++ b/Source/Scripts/Utilities/HandAnimationUtility.cs @@ -16,14 +16,10 @@ namespace Rimworld_Animations_Patch public static bool BodyPartIsBeingTouched(Pawn pawn, string bodypartFilePath, out List handAnimationData) { handAnimationData = new List(); - ActorAnimationData actorAnimationData = pawn.GetAnimationData(); + HandAnimationDef handAnimationDef = pawn?.TryGetComp()?.handAnimationDef; + ActorAnimationData actorAnimationData = pawn?.GetAnimationData(); - if (actorAnimationData == null) - { return false; } - - HandAnimationDef handAnimationDef = DefDatabase.AllDefs.FirstOrDefault(x => x.animationDefName == actorAnimationData.animationDef.defName); - - if (handAnimationDef == null) + if (handAnimationDef == null || actorAnimationData == null || bodypartFilePath.NullOrEmpty()) { return false; } foreach (HandAnimationData datum in handAnimationDef.handAnimationData) @@ -187,31 +183,39 @@ namespace Rimworld_Animations_Patch return handPosition; } - public static Graphic GetHandGraphic(Pawn touchingPawn, string touchedBodyAddonName, HandAnimationData handAnimationData) + public static Graphic GetHandGraphic(Pawn touchingPawn) { - string handGraphicPath = "Hands/HandClean"; - Color skinColour = touchingPawn.story.SkinColor; - float handSize = 0.6667f * touchingPawn.RaceProps.baseBodySize; - - return GraphicDatabase.Get(handGraphicPath, ShaderDatabase.Cutout, new Vector2(handSize, handSize), skinColour); + CompPawnSexData comp = touchingPawn?.TryGetComp(); + if (comp == null) return null; + + if (comp.handGraphic == null) + { + string handGraphicPath = "Hands/HandClean"; + comp.handGraphic = GraphicDatabase.Get(handGraphicPath, ShaderDatabase.Cutout, + new Vector2(0.6667f * touchingPawn.RaceProps.baseBodySize, 0.6667f * touchingPawn.RaceProps.baseBodySize), touchingPawn.story.SkinColor); + } + + return comp.handGraphic; } public static bool TryToDrawHand(Pawn pawn, string bodyAddonName, Vector3 bodyAddonPosition, float bodyAddonAngle, Rot4 bodyAddonRotation, PawnRenderFlags renderFlags) - { + { if (BodyPartIsBeingTouched(pawn, bodyAddonName, out List handAnimationData)) - { + { foreach (HandAnimationData datum in handAnimationData) - { + { Pawn touchingPawn = datum.touchingActorID >= 0 && pawn.GetAllSexParticipants().Count > datum.touchingActorID ? pawn.GetAllSexParticipants()[datum.touchingActorID] : pawn; - Graphic handgraphic = GetHandGraphic(touchingPawn, bodyAddonName, datum); + Graphic handGraphic = GetHandGraphic(touchingPawn); + if (handGraphic == null) return false; + Vector3 handPosition = GetHandPosition(pawn, datum, bodyAddonPosition, bodyAddonAngle); - - GenDraw.DrawMeshNowOrLater(mesh: handgraphic.MeshAt(rot: bodyAddonRotation), + + GenDraw.DrawMeshNowOrLater(mesh: handGraphic.MeshAt(rot: bodyAddonRotation), loc: handPosition + new Vector3(0f, 0.022f, 0f), quat: Quaternion.identity, - mat: handgraphic.MatAt(rot: bodyAddonRotation), renderFlags.FlagSet(PawnRenderFlags.DrawNow)); - + mat: handGraphic.MatAt(rot: bodyAddonRotation), renderFlags.FlagSet(PawnRenderFlags.DrawNow)); + return true; } } diff --git a/Source/Scripts/Utilities/PatchDefOf.cs b/Source/Scripts/Utilities/PatchDefOf.cs index 735dac4..fcec121 100644 --- a/Source/Scripts/Utilities/PatchDefOf.cs +++ b/Source/Scripts/Utilities/PatchDefOf.cs @@ -11,4 +11,10 @@ namespace Rimworld_Animations_Patch public static BodyPartGroupDef AnusBPG; public static BodyPartGroupDef ChestBPG; } + + [DefOf] + public static class PatchBodyPartDefOf + { + public static BodyPartDef Hand; + } } diff --git a/Source/Scripts/Utilities/SexInteractionUtility.cs b/Source/Scripts/Utilities/SexInteractionUtility.cs index c1aff20..dbbaae5 100644 --- a/Source/Scripts/Utilities/SexInteractionUtility.cs +++ b/Source/Scripts/Utilities/SexInteractionUtility.cs @@ -37,10 +37,13 @@ namespace Rimworld_Animations_Patch if (pawn.GetAllSexParticipants().Any(x => pawn.GetSpouseCount(false) > 0 && pawn.GetSpouses(false).Contains(x))) { return false; } + if (pawn.GetSexPartner().Dead || pawn.GetSexPartner().IsAnimal()) + { return false; } + return partner.IsLoverOfOther(pawn) && pawn.HasTrait("Polygamist") == false && partner.HasTrait("Polygamist") == false; } - public static bool PawnCanInvitePasserbyForSex(Pawn passerby, List participants) + public static bool InvitePasserbyForSex(Pawn passerby, List participants) { if (passerby == null || participants.NullOrEmpty() || participants.Contains(passerby) || passerby.AnimalOrWildMan() || passerby.RaceProps.IsMechanoid || passerby.Awake() == false || participants.All(x => x.CanSee(passerby) == false)) { return false; } @@ -48,6 +51,12 @@ namespace Rimworld_Animations_Patch if (participants.Any(x => x.IsForbidden(passerby) || x.HostileTo(passerby) || PawnIsCheatingOnPartner(x, passerby)) || CasualSex_Helper.CanHaveSex(passerby) == false || xxx.IsTargetPawnOkay(passerby) == false || participants.Count > 2) { return false; } + if (passerby.MentalState != null || + passerby.jobs.curDriver is JobDriver_Flee || + passerby.jobs.curDriver is JobDriver_AttackMelee || + passerby.jobs.curDriver is JobDriver_Vomit) + { return false; } + if (SexUtility.ReadyForHookup(passerby) && (passerby?.jobs?.curJob == null || (passerby.jobs.curJob.playerForced == false && CasualSex_Helper.quickieAllowedJobs.Contains(passerby.jobs.curJob.def))) && participants.Any(x => SexAppraiser.would_fuck(x, passerby) > 0.1f && SexAppraiser.would_fuck(passerby, x) > 0.1f) && @@ -59,182 +68,198 @@ namespace Rimworld_Animations_Patch return false; } - public static TabooStatus CheckSexJobAgainstMorals(Pawn pawn, JobDriver_Sex jobDriver, out Precept precept) - { - bool sexIsNecro = jobDriver.Partner != null && jobDriver.Partner.Dead; - bool sexIsBeastial = jobDriver.Partner != null && jobDriver.Partner.RaceProps.Animal; - bool sexIsRape = sexIsBeastial == false && sexIsNecro == false && - (jobDriver is JobDriver_Rape || jobDriver is JobDriver_RapeEnemy || jobDriver is JobDriver_SexBaseRecieverRaped) && - jobDriver.Partner.IsPrisoner == false && jobDriver.Partner.IsSlave == false; - bool sexIsSlaveRape = sexIsBeastial == false && sexIsNecro == false && - (jobDriver is JobDriver_Rape || jobDriver is JobDriver_RapeEnemy || jobDriver is JobDriver_SexBaseRecieverRaped) && - (jobDriver.Partner.IsPrisoner || jobDriver.Partner.IsSlave); - bool sexIsXeno = jobDriver.Partner != null && jobDriver.Partner.def.defName != jobDriver.pawn.def.defName; - - TabooStatus tabooStatus = TabooStatus.NotTaboo; + public static ThoughtDef GetThoughtsAboutSexAct(Pawn pawn, JobDriver_Sex jobDriver, out Precept precept) + { + ThoughtDef thoughtDef = null; precept = null; + if (pawn == null || jobDriver == null) + { return null; } + + bool sexIsNecro = jobDriver.Partner != null && jobDriver.Partner.Dead; + bool sexIsBeastial = jobDriver.Partner != null && jobDriver.Partner.RaceProps.Animal; + bool sexIsRape = sexIsBeastial == false && sexIsNecro == false && + (jobDriver is JobDriver_Rape || jobDriver is JobDriver_RapeEnemy || jobDriver is JobDriver_SexBaseRecieverRaped); + bool sexIsSlaveRape = sexIsRape && (jobDriver.Partner.IsPrisoner || jobDriver.Partner.IsSlave); + bool sexIsXeno = jobDriver.Partner != null && jobDriver.Partner.def.defName != jobDriver.pawn.def.defName; + bool isXenophobe = pawn.HasTrait("Xenophobia") && pawn.story.traits.DegreeOfTrait(DefDatabase.GetNamedSilentFail("Xenophobia")) > 0; + bool isXenophile = pawn.HasTrait("Xenophobia") && pawn.story.traits.DegreeOfTrait(DefDatabase.GetNamedSilentFail("Xenophobia")) < 0; + if (BasicSettings.worryAboutNecro && sexIsNecro && xxx.is_necrophiliac(pawn) == false) - { tabooStatus = GetTabooStatusOfIssue(pawn, DefDatabase.GetNamedSilentFail("Necrophilia"), TabooStatus.MajorTaboo, out precept); } + { + thoughtDef = xxx.is_necrophiliac(pawn) ? DefDatabase.GetNamedSilentFail("SawNecrophilia_Honorable") : + pawn.HasPreceptForIssue("Necrophilia", out precept) ? DefDatabase.GetNamedSilentFail("Saw" + precept.def.defName) : + DefDatabase.GetNamedSilentFail("SawNecrophilia_Abhorrent"); + } - else if (BasicSettings.worryAboutBeastiality && sexIsBeastial && xxx.is_zoophile(pawn) == false) - { tabooStatus = GetTabooStatusOfIssue(pawn, DefDatabase.GetNamedSilentFail("Beastility"), TabooStatus.MajorTaboo, out precept); } + else if (BasicSettings.worryAboutBeastiality && sexIsBeastial) + { + thoughtDef = xxx.is_zoophile(pawn) ? DefDatabase.GetNamedSilentFail("SawBeastility_Honorable") : + pawn.HasPreceptForIssue("Beastility", out precept) ? DefDatabase.GetNamedSilentFail("Saw" + precept.def.defName) : + DefDatabase.GetNamedSilentFail("SawBeastility_Abhorrent"); + } - else if (BasicSettings.worryAboutRape && sexIsRape && xxx.is_rapist(pawn) == false) - { tabooStatus = GetTabooStatusOfIssue(pawn, DefDatabase.GetNamedSilentFail("Rape"), TabooStatus.MajorTaboo, out precept); } + else if (BasicSettings.worryAboutRape && BasicSettings.ignoreSlaveRape == false && sexIsSlaveRape) + { + thoughtDef = xxx.is_rapist(pawn) ? DefDatabase.GetNamedSilentFail("SawRape_Honorable") : + pawn.HasPreceptForIssue("Rape", out precept) ? DefDatabase.GetNamedSilentFail("Saw" + precept.def.defName) : + DefDatabase.GetNamedSilentFail("SawRape_Abhorrent"); + } - else if (BasicSettings.worryAboutRape && BasicSettings.ignoreSlaveRape == false && sexIsSlaveRape && xxx.is_rapist(pawn) == false) - { tabooStatus = GetTabooStatusOfIssue(pawn, DefDatabase.GetNamedSilentFail("Rape"), TabooStatus.MajorTaboo, out precept); } - - else if (BasicSettings.worryAboutXeno && sexIsXeno && pawn.HasTrait("Xenophobia") && pawn.story.traits.DegreeOfTrait(DefDatabase.GetNamedSilentFail("Xenophobia")) > 0) - { tabooStatus = TabooStatus.MajorTaboo; } + else if (BasicSettings.worryAboutRape && sexIsRape) + { + thoughtDef = xxx.is_rapist(pawn) ? DefDatabase.GetNamedSilentFail("SawRape_Honorable") : + pawn.HasPreceptForIssue("Rape", out precept) ? DefDatabase.GetNamedSilentFail("Saw" + precept.def.defName) : + DefDatabase.GetNamedSilentFail("SawRape_Abhorrent"); + } else if (BasicSettings.worryAboutXeno && sexIsXeno) - { tabooStatus = GetTabooStatusOfIssue(pawn, DefDatabase.GetNamedSilentFail("HAR_AlienDating"), TabooStatus.NotTaboo, out precept); } + { + thoughtDef = isXenophobe ? DefDatabase.GetNamedSilentFail("SawHAR_AlienDating_Prohibited") : + isXenophile ? DefDatabase.GetNamedSilentFail("SawHAR_AlienDating_Honorable") : + pawn.HasPreceptForIssue("HAR_AlienDating", out precept) ? DefDatabase.GetNamedSilentFail("Saw" + precept.def.defName) : + DefDatabase.GetNamedSilentFail("SawHAR_AlienDating_Acceptable"); + } - //DebugMode.Message("Sex job is: " + jobDriver + " Issue is: " + (precept?.def?.issue?.defName).ToStringSafe() + " Opinion is: " + (precept?.def?.defName).ToStringSafe() + " Judgement is: " + tabooStatus.ToString()); + //DebugMode.Message("Sex job is: " + jobDriver + " Issue is: " + (precept?.def?.issue?.defName).ToStringSafe() + " Opinion is: " + (precept?.def?.defName).ToStringSafe() + " Thought is: " + (thoughtDef?.defName).ToStringSafe()); - return tabooStatus; + return thoughtDef; } - public static TabooStatus GetTabooStatusOfIssue(Pawn pawn, IssueDef issueDef, TabooStatus defaultTabboStatus, out Precept precept) + public static void TriggerReactionInWitness(Pawn witness, Pawn otherPawn, string reaction) { - if (pawn.IssueIsMajorTaboo(issueDef, out precept)) - { return TabooStatus.MajorTaboo; } + if (BasicSettings.majorTabooCanStartFights == false || reaction.NullOrEmpty()) + { return; } - if (pawn.IssueIsMinorTaboo(issueDef, out precept)) - { return TabooStatus.MinorTaboo; } + if (witness.MentalState != null || + witness.jobs.curDriver is JobDriver_Flee || + witness.jobs.curDriver is JobDriver_AttackMelee || + witness.jobs.curDriver is JobDriver_Vomit) + { return; } - return defaultTabboStatus; + // Panicked + if (reaction == "Panicked" || (reaction == "Indignant" && Random.value <= 0.5f)) + { + // Fight + if (otherPawn.RaceProps.Humanlike && witness.RaceProps.Humanlike && witness.DislikesViolence() == false && (Random.value <= 0.2f || witness.EnjoysViolence()) && witness.HostileTo(otherPawn) == false && InteractionUtility.TryGetRandomVerbForSocialFight(witness, out Verb verbToUse)) + { witness.interactions.StartSocialFight(otherPawn, "MessageSocialFight"); } + + // Flight + else + { + Job job = JobMaker.MakeJob(JobDefOf.FleeAndCower, CellFinderLoose.GetFleeDest(witness, new List() { otherPawn }, 24f), otherPawn); + witness.jobs.EndCurrentJob(JobCondition.InterruptForced, false, false); + witness.jobs.StartJob(job); + } + } + + // Vomit + else if (reaction == "Nauseated") + { + Job jobVomit = JobMaker.MakeJob(JobDefOf.Vomit); + Job jobFlee = JobMaker.MakeJob(JobDefOf.FleeAndCower, CellFinderLoose.GetFleeDest(witness, new List() { otherPawn }, 24f), otherPawn); + + witness.jobs.EndCurrentJob(JobCondition.InterruptForced, false, false); + + if (Random.value <= 0.2f) + { + witness.jobs.StartJob(jobVomit); + witness.jobs.jobQueue.EnqueueFirst(jobFlee); + } + + else + { witness.jobs.StartJob(jobFlee); } + } + + // Indignant + else if (reaction == "Indignant") + { + witness.mindState.mentalStateHandler.TryStartMentalState(DefDatabase.GetNamedSilentFail("TargetedInsultingSpree"), null, true, false, null, true, false, false); + (witness.mindState.mentalStateHandler.CurState as MentalState_TargetedInsultingSpree).target = otherPawn; + } } public static bool ResolveThoughtsForWhenSexIsWitnessed(Pawn pawn, Pawn witness, out bool witnessJoiningSex) { - witnessJoiningSex = false; + witnessJoiningSex = Random.value < BasicSettings.chanceForOtherToJoinInSex && InvitePasserbyForSex(witness, pawn.GetAllSexParticipants()); - if (pawn.IsAnimal() || pawn.RaceProps.IsMechanoid || pawn.Dead) + // Exit clauses + if (witness.AnimalOrWildMan() || witness.RaceProps.IsMechanoid || witness.Dead || witness.Faction == null || witness.Faction.HostileTo(Faction.OfPlayer)) { return false; } - if (witness.IsAnimal() || witness.RaceProps.IsMechanoid || witness.Dead) - { return false; } - - JobDriver_Sex jobDriver = pawn.jobs.curDriver as JobDriver_Sex; - + // Get basic thoughts string pawnThoughtDefName = pawn.IsMasturbating() ? "SeenMasturbating" : "SeenHavingSex"; string witnessThoughtDefName = pawn.IsMasturbating() ? "SawMasturbation" : "SawSex"; - bool pawnIsExhibitionist = pawn.HasTrait("Exhibitionist") || xxx.has_quirk(pawn, "Exhibitionist"); - if (pawnIsExhibitionist) - { pawnThoughtDefName += "Exhibitionist"; } + ThoughtDef pawnThoughtDef = BasicSettings.needPrivacy ? DefDatabase.GetNamedSilentFail(pawnThoughtDefName) : null; + ThoughtDef witnessThoughtDef = BasicSettings.needPrivacy ? DefDatabase.GetNamedSilentFail(witnessThoughtDefName) : null; - bool witnessIsVoyeur = witness.HasTrait("Voyeur") || xxx.has_quirk(witness, "Voyeur"); - if (witnessIsVoyeur) - { witnessThoughtDefName += "Voyeur"; } + // Exhibitionist pawn + if (xxx.has_quirk(pawn, "Exhibitionist")) + { pawnThoughtDef = DefDatabase.GetNamedSilentFail(pawnThoughtDefName + "Exhibitionist"); } + // Voyeuristic witness + if (xxx.has_quirk(witness, "Voyeur")) + { witnessThoughtDef = DefDatabase.GetNamedSilentFail(witnessThoughtDefName + "Voyeur"); } + + // Mediating cirumstances bool sexIsRitual = pawn.GetLord() != null && pawn.GetLord().LordJob is LordJob_Ritual && witness?.Ideo == pawn?.Ideo; + bool sexIsParty = pawn.GetLord() != null && pawn.GetLord().LordJob is LordJob_Joinable_Party; bool pawnIsVictim = pawn.CurJob.def == xxx.gettin_raped || pawn.Dead; - bool pawnIsCheating = pawnIsVictim == false && PawnIsCheatingOnPartner(pawn, witness); + bool pawnIsCheating = sexIsRitual == false && sexIsParty == false && pawnIsVictim == false && PawnIsCheatingOnPartner(pawn, witness); - witnessJoiningSex = Random.value < BasicSettings.chanceForOtherToJoinInSex && PawnCanInvitePasserbyForSex(witness, pawn.GetAllSexParticipants()); + // Wipe thoughts if pawn is a victim + if (pawnIsVictim) + { + pawnThoughtDef = null; + witnessThoughtDef = null; + } - // Determine if there are any issues with the witness' morals - TabooStatus tabooStatus = CheckSexJobAgainstMorals(witness, jobDriver, out Precept precept); + // Add thought if pawn is cheating + if (pawnIsCheating) + { + if (pawn.needs.mood.thoughts.memories.GetFirstMemoryOfDef(DefDatabase.GetNamedSilentFail("CaughtCheating")) == null) + { pawn.needs.mood.thoughts.memories.TryGainMemory(DefDatabase.GetNamedSilentFail("CaughtCheating"), witness); } - if (tabooStatus == TabooStatus.MajorTaboo) - { witnessThoughtDefName = "SawMajorTaboo"; witnessJoiningSex = false; } - - else if (tabooStatus == TabooStatus.MinorTaboo) - { witnessThoughtDefName = "SawTaboo"; witnessJoiningSex = false; } + if (witness.needs.mood.thoughts.memories.GetFirstMemoryOfDef(ThoughtDefOf.CheatedOnMe) == null) + { witness.needs.mood.thoughts.memories.TryGainMemory(ThoughtDefOf.CheatedOnMe, pawn); } - else if (pawnIsCheating) - { witnessThoughtDefName = "CheatedOnMe"; witnessJoiningSex = false; } + witnessJoiningSex = false; + } - else if (BasicSettings.needPrivacy == false) - { witnessThoughtDefName = ""; } + // Determine if there are any issues with the sex event and the witness' morals + Precept precept = null; + + if (sexIsRitual == false && sexIsParty == false && pawnIsVictim == false) + { witnessThoughtDef = GetThoughtsAboutSexAct(witness, pawn.jobs.curDriver as JobDriver_Sex, out precept); } // Apply thoughts to witness - ThoughtDef witnessThoughtDef = DefDatabase.GetNamedSilentFail(witnessThoughtDefName); - - if (witnessThoughtDef != null && pawnIsVictim == false && witnessJoiningSex == false && sexIsRitual == false) + if (witnessThoughtDef != null) { witness.needs.mood.thoughts.memories.TryGainMemory(witnessThoughtDef, pawn, precept); if (witnessThoughtDef.stages[0].baseMoodEffect < 0) - { FleckMaker.ThrowMetaIcon(witness.Position, witness.Map, FleckDefOf.IncapIcon); } + { witness?.TryGetComp()?.TryToExclaim(); } - // Fight or flight reaction - if (BasicSettings.majorTabooCanStartFights && - (tabooStatus == TabooStatus.MajorTaboo || pawnIsCheating) && - witness.Drafted == false && - witness.jobs.curDriver is JobDriver_Flee == false && - witness.jobs.curDriver is JobDriver_AttackMelee == false && - witness.jobs.curDriver is JobDriver_Vomit == false) - { - // Fight - if (pawn.RaceProps.Humanlike && witness.RaceProps.Humanlike && witness.DislikesViolence() == false && (Random.value < 0.2f || witness.EnjoysViolence()) && witness.HostileTo(pawn) == false && InteractionUtility.TryGetRandomVerbForSocialFight(witness, out Verb verbToUse)) - { - if (witness.LastAttackedTarget.Pawn != pawn || (pawn.mindState.lastAttackTargetTick < 0 && pawn.mindState.lastAttackTargetTick + Find.TickManager.TicksGame > 180)) - { - pawn.mindState.lastAttackTargetTick = Find.TickManager.TicksGame; - string message = witness.LabelShort + " is going to punish " + pawn.LabelShort + " for " + GenderUtility.GetPossessive(pawn.gender) + " transgression."; - Messages.Message(message, pawn, MessageTypeDefOf.NegativeEvent); - } - - Job job = JobMaker.MakeJob(JobDefOf.SocialFight, pawn); - job.maxNumMeleeAttacks = 1; - job.verbToUse = verbToUse; - - witness.jobs.EndCurrentJob(JobCondition.InterruptForced, false, false); - witness.jobs.StartJob(job); - } - - // Vomit - else if (jobDriver.Partner != null && jobDriver.Partner.Dead) - { - Job jobVomit = JobMaker.MakeJob(JobDefOf.Vomit); - Job jobFlee = JobMaker.MakeJob(JobDefOf.FleeAndCower, CellFinderLoose.GetFleeDest(witness, new List() { pawn }, 24f), pawn); - - witness.jobs.EndCurrentJob(JobCondition.InterruptForced, false, false); - witness.jobs.StartJob(jobVomit); - witness.jobs.jobQueue.EnqueueFirst(jobFlee); - } - - // Flight - else - { - Job job = JobMaker.MakeJob(JobDefOf.FleeAndCower, CellFinderLoose.GetFleeDest(witness, new List() { pawn }, 24f), pawn); - witness.jobs.EndCurrentJob(JobCondition.InterruptForced, false, false); - witness.jobs.StartJob(job); - } - } + witnessJoiningSex = witnessThoughtDef.hediff != null ? false : witnessJoiningSex; } - // Check issue against pawn precepts - tabooStatus = CheckSexJobAgainstMorals(pawn, jobDriver, out precept); - - if (tabooStatus == TabooStatus.MajorTaboo) - { pawnThoughtDefName = "SeenCommittingMajorTaboo"; witnessJoiningSex = false; } - - else if (tabooStatus == TabooStatus.MinorTaboo) - { pawnThoughtDefName = "SeenCommittingTaboo"; witnessJoiningSex = false; } + // Extreme reaction + if (witnessThoughtDef?.hediff != null) + { TriggerReactionInWitness(witness, pawn, witnessThoughtDef.hediff.defName); } else if (pawnIsCheating) - { pawnThoughtDefName = "CaughtCheating"; witnessJoiningSex = false; } - - else if (BasicSettings.needPrivacy == false) - { pawnThoughtDefName = ""; } + { TriggerReactionInWitness(witness, pawn, "Indignant"); } // Apply thoughts to pawn - ThoughtDef pawnThoughtDef = DefDatabase.GetNamedSilentFail(pawnThoughtDefName); - - if (pawnThoughtDef != null && pawnIsVictim == false && witnessJoiningSex == false && sexIsRitual == false) + if (pawnThoughtDef != null) { - pawn.needs.mood.thoughts.memories.TryGainMemory(pawnThoughtDef, witness, precept); + pawn.needs.mood.thoughts.memories.TryGainMemory(pawnThoughtDef, witness, null); if (pawnThoughtDef.stages[0].baseMoodEffect < 0) - { FleckMaker.ThrowMetaIcon(pawn.Position, pawn.Map, FleckDefOf.IncapIcon); } + { pawn?.TryGetComp()?.TryToExclaim(); } } - + return witnessJoiningSex; } } diff --git a/Source/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache b/Source/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache index edf5057..e79a668 100644 Binary files a/Source/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache and b/Source/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache differ diff --git a/Source/obj/Debug/Rimworld-Animations-Patch.csproj.CopyComplete b/Source/obj/Debug/Rimworld-Animations-Patch.csproj.CopyComplete new file mode 100644 index 0000000..e69de29 diff --git a/Source/obj/Debug/Rimworld-Animations-Patch.csproj.CoreCompileInputs.cache b/Source/obj/Debug/Rimworld-Animations-Patch.csproj.CoreCompileInputs.cache index afaf89a..3e3d80b 100644 --- a/Source/obj/Debug/Rimworld-Animations-Patch.csproj.CoreCompileInputs.cache +++ b/Source/obj/Debug/Rimworld-Animations-Patch.csproj.CoreCompileInputs.cache @@ -1 +1 @@ -d5a3e9402b3033575abd239e1b750dc876117ae0 +3f4f0c89e9095de19b2f487c386783c64b9bb50a diff --git a/Source/obj/Debug/Rimworld-Animations-Patch.csproj.FileListAbsolute.txt b/Source/obj/Debug/Rimworld-Animations-Patch.csproj.FileListAbsolute.txt index 94ebc10..3cfea08 100644 --- a/Source/obj/Debug/Rimworld-Animations-Patch.csproj.FileListAbsolute.txt +++ b/Source/obj/Debug/Rimworld-Animations-Patch.csproj.FileListAbsolute.txt @@ -3,4 +3,5 @@ C:\Program Files (x86)\Steam\SteamApps\common\RimWorld\Mods\rimworld-animations- C:\Program Files (x86)\Steam\SteamApps\common\RimWorld\Mods\rimworld-animations-patch-abscon\1.3\Assemblies\Rimworld-Animations-Patch.pdb C:\Program Files (x86)\Steam\SteamApps\common\RimWorld\Mods\rimworld-animations-patch-abscon\Source\obj\Debug\Rimworld-Animations-Patch.dll C:\Program Files (x86)\Steam\SteamApps\common\RimWorld\Mods\rimworld-animations-patch-abscon\Source\obj\Debug\Rimworld-Animations-Patch.pdb +C:\Program Files (x86)\Steam\SteamApps\common\RimWorld\Mods\rimworld-animations-patch-abscon\Source\obj\Debug\Rimworld-Animations-Patch.csproj.CopyComplete C:\Program Files (x86)\Steam\SteamApps\common\RimWorld\Mods\rimworld-animations-patch-abscon\Source\obj\Debug\Rimworld-Animations-Patch.csprojAssemblyReference.cache diff --git a/Source/obj/Debug/Rimworld-Animations-Patch.csprojAssemblyReference.cache b/Source/obj/Debug/Rimworld-Animations-Patch.csprojAssemblyReference.cache index 2ad85a4..4cd5302 100644 Binary files a/Source/obj/Debug/Rimworld-Animations-Patch.csprojAssemblyReference.cache and b/Source/obj/Debug/Rimworld-Animations-Patch.csprojAssemblyReference.cache differ diff --git a/Source/obj/Debug/Rimworld-Animations-Patch.dll b/Source/obj/Debug/Rimworld-Animations-Patch.dll index 7039ed8..d07b122 100644 Binary files a/Source/obj/Debug/Rimworld-Animations-Patch.dll and b/Source/obj/Debug/Rimworld-Animations-Patch.dll differ diff --git a/Source/obj/Debug/Rimworld-Animations-Patch.pdb b/Source/obj/Debug/Rimworld-Animations-Patch.pdb index b5f2831..c299208 100644 Binary files a/Source/obj/Debug/Rimworld-Animations-Patch.pdb and b/Source/obj/Debug/Rimworld-Animations-Patch.pdb differ