Search in sources :

Example 6 with PenetrationType

use of com.lilithsthrone.game.sex.PenetrationType in project liliths-throne-public by Innoxia.

the class GameCharacter method saveAsXML.

@Override
public Element saveAsXML(Element parentElement, Document doc) {
    Element properties = doc.createElement("character");
    parentElement.appendChild(properties);
    // ************** Core information **************//
    Element characterCoreInfo = doc.createElement("core");
    Comment comment = doc.createComment("If you want to edit any of these values, just be warned that it might break the game...");
    properties.appendChild(characterCoreInfo);
    CharacterUtils.createXMLElementWithValue(doc, characterCoreInfo, "id", this.getId());
    CharacterUtils.createXMLElementWithValue(doc, characterCoreInfo, "pathName", this.getClass().getCanonicalName());
    Element name = doc.createElement("name");
    characterCoreInfo.appendChild(name);
    CharacterUtils.addAttribute(doc, name, "nameFeminine", this.getNameTriplet().getFeminine());
    CharacterUtils.addAttribute(doc, name, "nameAndrogynous", this.getNameTriplet().getAndrogynous());
    CharacterUtils.addAttribute(doc, name, "nameMasculine", this.getNameTriplet().getMasculine());
    CharacterUtils.createXMLElementWithValue(doc, characterCoreInfo, "surname", this.getSurname());
    CharacterUtils.createXMLElementWithValue(doc, characterCoreInfo, "description", this.getDescription());
    CharacterUtils.createXMLElementWithValue(doc, characterCoreInfo, "playerPetName", playerPetName);
    CharacterUtils.createXMLElementWithValue(doc, characterCoreInfo, "playerKnowsName", String.valueOf(this.isPlayerKnowsName()));
    CharacterUtils.createXMLElementWithValue(doc, characterCoreInfo, "level", String.valueOf(this.getTrueLevel()));
    CharacterUtils.createXMLElementWithValue(doc, characterCoreInfo, "version", Main.VERSION_NUMBER);
    CharacterUtils.createXMLElementWithValue(doc, characterCoreInfo, "history", this.getHistory().toString());
    CharacterUtils.createXMLElementWithValue(doc, characterCoreInfo, "personality", this.getPersonality().toString());
    CharacterUtils.createXMLElementWithValue(doc, characterCoreInfo, "sexualOrientation", this.getSexualOrientation().toString());
    CharacterUtils.createXMLElementWithValue(doc, characterCoreInfo, "obedience", String.valueOf(this.getObedienceValue()));
    CharacterUtils.createXMLElementWithValue(doc, characterCoreInfo, "genderIdentity", String.valueOf(this.getGenderIdentity()));
    CharacterUtils.createXMLElementWithValue(doc, characterCoreInfo, "experience", String.valueOf(this.getExperience()));
    CharacterUtils.createXMLElementWithValue(doc, characterCoreInfo, "perkPoints", String.valueOf(this.getPerkPoints()));
    CharacterUtils.createXMLElementWithValue(doc, characterCoreInfo, "health", String.valueOf(this.getHealth()));
    CharacterUtils.createXMLElementWithValue(doc, characterCoreInfo, "mana", String.valueOf(this.getMana()));
    // Knows area map:
    Element characterPlayerKnowsAreas = doc.createElement("playerKnowsAreas");
    characterCoreInfo.appendChild(characterPlayerKnowsAreas);
    for (CoverableArea area : getPlayerKnowsAreas()) {
        Element element = doc.createElement("area");
        characterPlayerKnowsAreas.appendChild(element);
        CharacterUtils.addAttribute(doc, element, "type", area.toString());
    }
    characterCoreInfo.getParentNode().insertBefore(comment, characterCoreInfo);
    // ************** Location Information **************//
    Element locationInformation = doc.createElement("locationInformation");
    properties.appendChild(locationInformation);
    CharacterUtils.createXMLElementWithValue(doc, locationInformation, "worldLocation", this.getWorldLocation().toString());
    CharacterUtils.createXMLElementWithValue(doc, locationInformation, "homeWorldLocation", this.getHomeWorldLocation().toString());
    Element location = doc.createElement("location");
    locationInformation.appendChild(location);
    CharacterUtils.addAttribute(doc, location, "x", String.valueOf(this.getLocation().getX()));
    CharacterUtils.addAttribute(doc, location, "y", String.valueOf(this.getLocation().getY()));
    location = doc.createElement("homeLocation");
    locationInformation.appendChild(location);
    CharacterUtils.addAttribute(doc, location, "x", String.valueOf(this.getHomeLocation().getX()));
    CharacterUtils.addAttribute(doc, location, "y", String.valueOf(this.getHomeLocation().getY()));
    // ************** Body **************//
    Element characterBody = doc.createElement("body");
    properties.appendChild(characterBody);
    this.body.saveAsXML(characterBody, doc);
    // ************** Inventory **************//
    this.inventory.saveAsXML(properties, doc);
    // ************** Attributes **************//
    // Attributes:
    Element characterCoreAttributes = doc.createElement("attributes");
    properties.appendChild(characterCoreAttributes);
    for (Attribute att : Attribute.values()) {
        if (this.getBaseAttributeValue(att) != att.getBaseValue()) {
            Element element = doc.createElement("attribute");
            characterCoreAttributes.appendChild(element);
            CharacterUtils.addAttribute(doc, element, "type", att.toString());
            CharacterUtils.addAttribute(doc, element, "value", String.valueOf(this.getBaseAttributeValue(att)));
        }
    }
    Element characterPotionAttributes = doc.createElement("potionAttributes");
    properties.appendChild(characterPotionAttributes);
    for (Entry<Attribute, Float> entry : getPotionAttributes().entrySet()) {
        Element element = doc.createElement("attribute");
        characterPotionAttributes.appendChild(element);
        CharacterUtils.addAttribute(doc, element, "type", entry.getKey().toString());
        CharacterUtils.addAttribute(doc, element, "value", String.valueOf(entry.getValue()));
    }
    // Perks:
    Element characterEquippedPerks = doc.createElement("traits");
    properties.appendChild(characterEquippedPerks);
    for (Perk p : this.getTraits()) {
        Element element = doc.createElement("perk");
        characterEquippedPerks.appendChild(element);
        CharacterUtils.addAttribute(doc, element, "type", p.toString());
    }
    Element characterPerks = doc.createElement("perks");
    properties.appendChild(characterPerks);
    for (Entry<Integer, Set<Perk>> p : this.getPerksMap().entrySet()) {
        for (Perk perk : p.getValue()) {
            Element element = doc.createElement("perk");
            characterPerks.appendChild(element);
            CharacterUtils.addAttribute(doc, element, "row", p.getKey().toString());
            CharacterUtils.addAttribute(doc, element, "type", perk.toString());
        }
    }
    // Fetishes:
    Element characterFetishes = doc.createElement("fetishes");
    properties.appendChild(characterFetishes);
    for (Fetish f : this.getFetishes()) {
        Element element = doc.createElement("fetish");
        characterFetishes.appendChild(element);
        CharacterUtils.addAttribute(doc, element, "type", f.toString());
    }
    Element fetishDesire = doc.createElement("fetishDesire");
    properties.appendChild(fetishDesire);
    for (Entry<Fetish, FetishDesire> entry : this.getFetishDesireMap().entrySet()) {
        Element fondenessEntry = doc.createElement("entry");
        fetishDesire.appendChild(fondenessEntry);
        CharacterUtils.addAttribute(doc, fondenessEntry, "fetish", entry.getKey().toString());
        CharacterUtils.addAttribute(doc, fondenessEntry, "desire", entry.getValue().toString());
    }
    Element fetishExperience = doc.createElement("fetishExperience");
    properties.appendChild(fetishExperience);
    for (Entry<Fetish, Integer> entry : this.getFetishExperienceMap().entrySet()) {
        Element expEntry = doc.createElement("entry");
        fetishExperience.appendChild(expEntry);
        CharacterUtils.addAttribute(doc, expEntry, "fetish", entry.getKey().toString());
        CharacterUtils.addAttribute(doc, expEntry, "experience", String.valueOf(entry.getValue()));
    }
    // Status effects:
    Element characterStatusEffects = doc.createElement("statusEffects");
    properties.appendChild(characterStatusEffects);
    for (StatusEffect se : this.getStatusEffects()) {
        Element element = doc.createElement("statusEffect");
        characterStatusEffects.appendChild(element);
        CharacterUtils.addAttribute(doc, element, "type", se.toString());
        CharacterUtils.addAttribute(doc, element, "value", String.valueOf(this.getStatusEffectDuration(se)));
    }
    // ************** Relationships **************//
    Element characterRelationships = doc.createElement("characterRelationships");
    properties.appendChild(characterRelationships);
    for (Entry<String, Float> entry : this.getAffectionMap().entrySet()) {
        Element relationship = doc.createElement("relationship");
        characterRelationships.appendChild(relationship);
        CharacterUtils.addAttribute(doc, relationship, "character", entry.getKey());
        CharacterUtils.addAttribute(doc, relationship, "value", String.valueOf(entry.getValue()));
    }
    // ************** Pregnancy **************//
    // Pregnancy:
    Element characterPregnancy = doc.createElement("pregnancy");
    properties.appendChild(characterPregnancy);
    CharacterUtils.addAttribute(doc, characterPregnancy, "timeProgressedToFinalPregnancyStage", String.valueOf(this.getTimeProgressedToFinalPregnancyStage()));
    Element characterPotentialPartnersAsMother = doc.createElement("potentialPartnersAsMother");
    characterPregnancy.appendChild(characterPotentialPartnersAsMother);
    for (PregnancyPossibility pregPoss : this.getPotentialPartnersAsMother()) {
        pregPoss.saveAsXML(characterPotentialPartnersAsMother, doc);
    }
    Element characterPotentialPartnersAsFather = doc.createElement("potentialPartnersAsFather");
    characterPregnancy.appendChild(characterPotentialPartnersAsFather);
    for (PregnancyPossibility pregPoss : this.getPotentialPartnersAsFather()) {
        pregPoss.saveAsXML(characterPotentialPartnersAsFather, doc);
    }
    Element characterPregnancyCurrentLitter = doc.createElement("pregnantLitter");
    characterPregnancy.appendChild(characterPregnancyCurrentLitter);
    if (this.getPregnantLitter() != null) {
        this.getPregnantLitter().saveAsXML(characterPregnancyCurrentLitter, doc);
    }
    Element characterPregnancyBirthedLitters = doc.createElement("birthedLitters");
    characterPregnancy.appendChild(characterPregnancyBirthedLitters);
    for (Litter litter : this.getLittersBirthed()) {
        litter.saveAsXML(characterPregnancyBirthedLitters, doc);
    }
    Element characterPregnancyLittersFathered = doc.createElement("littersFathered");
    characterPregnancy.appendChild(characterPregnancyLittersFathered);
    for (Litter litter : this.getLittersFathered()) {
        litter.saveAsXML(characterPregnancyLittersFathered, doc);
    }
    // ************** Family **************//
    Element characterFamily = doc.createElement("family");
    properties.appendChild(characterFamily);
    CharacterUtils.createXMLElementWithValue(doc, characterFamily, "motherId", this.getMotherId());
    CharacterUtils.createXMLElementWithValue(doc, characterFamily, "fatherId", this.getFatherId());
    CharacterUtils.createXMLElementWithValue(doc, characterFamily, "dayOfConception", String.valueOf(this.getDayOfConception()));
    CharacterUtils.createXMLElementWithValue(doc, characterFamily, "dayOfBirth", String.valueOf(this.getDayOfBirth()));
    // ************** Slavery **************//
    Element slaveryElement = doc.createElement("slavery");
    properties.appendChild(slaveryElement);
    Element slavesOwned = doc.createElement("slavesOwned");
    slaveryElement.appendChild(slavesOwned);
    for (String slave : this.getSlavesOwned()) {
        Element element = doc.createElement("slave");
        slavesOwned.appendChild(element);
        CharacterUtils.addAttribute(doc, element, "id", slave);
    }
    CharacterUtils.createXMLElementWithValue(doc, slaveryElement, "owner", this.getOwner() == null ? "" : this.getOwner().getId());
    CharacterUtils.createXMLElementWithValue(doc, slaveryElement, "slaveJob", this.getSlaveJob().toString());
    Element slaveJobSettings = doc.createElement("slaveJobSettings");
    slaveryElement.appendChild(slaveJobSettings);
    for (SlaveJobSetting setting : this.getSlaveJobSettings()) {
        Element element = doc.createElement("setting");
        slaveJobSettings.appendChild(element);
        CharacterUtils.addAttribute(doc, element, "value", setting.toString());
    }
    Element slavePermissionSettings = doc.createElement("slavePermissionSettings");
    slaveryElement.appendChild(slavePermissionSettings);
    for (Entry<SlavePermission, Set<SlavePermissionSetting>> entry : this.getSlavePermissionSettings().entrySet()) {
        Element element = doc.createElement("permission");
        slavePermissionSettings.appendChild(element);
        CharacterUtils.addAttribute(doc, element, "type", entry.getKey().toString());
        for (SlavePermissionSetting setting : entry.getValue()) {
            Element settingElement = doc.createElement("setting");
            element.appendChild(settingElement);
            CharacterUtils.addAttribute(doc, settingElement, "value", setting.toString());
        }
    }
    Element slaveWorkHours = doc.createElement("slaveWorkHours");
    slaveryElement.appendChild(slaveWorkHours);
    for (int i = 0; i < workHours.length; i++) {
        CharacterUtils.addAttribute(doc, slaveWorkHours, "hour" + String.valueOf(i), String.valueOf(workHours[i]));
    }
    // ************** Sex Stats **************//
    Element characterSexStats = doc.createElement("sexStats");
    properties.appendChild(characterSexStats);
    Element characterCummedInAreas = doc.createElement("cummedInAreas");
    characterSexStats.appendChild(characterCummedInAreas);
    for (OrificeType orifice : OrificeType.values()) {
        Element element = doc.createElement("entry");
        characterCummedInAreas.appendChild(element);
        CharacterUtils.addAttribute(doc, element, "orifice", orifice.toString());
        CharacterUtils.addAttribute(doc, element, "cumQuantity", String.valueOf(this.getCummedInAreaMap().get(orifice)));
    }
    CharacterUtils.createXMLElementWithValue(doc, characterSexStats, "sexConsensualCount", String.valueOf(this.getSexConsensualCount()));
    CharacterUtils.createXMLElementWithValue(doc, characterSexStats, "sexAsSubCount", String.valueOf(this.getSexAsSubCount()));
    CharacterUtils.createXMLElementWithValue(doc, characterSexStats, "sexAsDomCount", String.valueOf(this.getSexAsDomCount()));
    Element characterCumCount = doc.createElement("cumCounts");
    characterSexStats.appendChild(characterCumCount);
    for (SexParticipantType participant : SexParticipantType.values()) {
        for (PenetrationType pt : PenetrationType.values()) {
            for (OrificeType ot : OrificeType.values()) {
                if (this.getCumCount(new SexType(participant, pt, ot)) > 0) {
                    Element element = doc.createElement("cumCount");
                    characterCumCount.appendChild(element);
                    CharacterUtils.addAttribute(doc, element, "participantType", participant.toString());
                    CharacterUtils.addAttribute(doc, element, "penetrationType", pt.toString());
                    CharacterUtils.addAttribute(doc, element, "orificeType", ot.toString());
                    CharacterUtils.addAttribute(doc, element, "count", String.valueOf(this.getCumCount(new SexType(participant, pt, ot))));
                }
            }
        }
    }
    Element characterSexCount = doc.createElement("sexCounts");
    characterSexStats.appendChild(characterSexCount);
    for (SexParticipantType participant : SexParticipantType.values()) {
        for (PenetrationType pt : PenetrationType.values()) {
            for (OrificeType ot : OrificeType.values()) {
                if (this.getSexCount(new SexType(participant, pt, ot)) > 0) {
                    Element element = doc.createElement("sexCount");
                    characterSexCount.appendChild(element);
                    CharacterUtils.addAttribute(doc, element, "participantType", participant.toString());
                    CharacterUtils.addAttribute(doc, element, "penetrationType", pt.toString());
                    CharacterUtils.addAttribute(doc, element, "orificeType", ot.toString());
                    CharacterUtils.addAttribute(doc, element, "count", String.valueOf(this.getSexCount(new SexType(participant, pt, ot))));
                }
            }
        }
    }
    Element characterVirginityTakenBy = doc.createElement("virginityTakenBy");
    characterSexStats.appendChild(characterVirginityTakenBy);
    for (SexParticipantType participant : SexParticipantType.values()) {
        for (PenetrationType pt : PenetrationType.values()) {
            for (OrificeType ot : OrificeType.values()) {
                if (this.getVirginityLoss(new SexType(participant, pt, ot)) != null && !this.getVirginityLoss(new SexType(participant, pt, ot)).isEmpty()) {
                    Element element = doc.createElement("virginity");
                    characterVirginityTakenBy.appendChild(element);
                    CharacterUtils.addAttribute(doc, element, "participantType", participant.toString());
                    CharacterUtils.addAttribute(doc, element, "penetrationType", pt.toString());
                    CharacterUtils.addAttribute(doc, element, "orificeType", ot.toString());
                    CharacterUtils.addAttribute(doc, element, "takenBy", String.valueOf(this.getVirginityLoss(new SexType(participant, pt, ot))));
                }
            }
        }
    }
    Element sexPartnerMapElement = doc.createElement("sexPartnerMap");
    characterSexStats.appendChild(sexPartnerMapElement);
    for (String s : sexPartnerMap.keySet()) {
        Element element = doc.createElement("id");
        sexPartnerMapElement.appendChild(element);
        CharacterUtils.addAttribute(doc, element, "value", s);
        for (Entry<SexType, Integer> entry : sexPartnerMap.get(s).entrySet()) {
            Element entryElement = doc.createElement("entry");
            element.appendChild(entryElement);
            CharacterUtils.addAttribute(doc, entryElement, "participantType", entry.getKey().getAsParticipant().toString());
            CharacterUtils.addAttribute(doc, entryElement, "penetrationType", entry.getKey().getPenetrationType().toString());
            CharacterUtils.addAttribute(doc, entryElement, "orificeType", entry.getKey().getOrificeType().toString());
            CharacterUtils.addAttribute(doc, entryElement, "count", String.valueOf(entry.getValue()));
        }
    }
    // ************** Fluids **************//
    Element characterAddictionsCore = doc.createElement("addictionsCore");
    properties.appendChild(characterAddictionsCore);
    CharacterUtils.addAttribute(doc, characterAddictionsCore, "alcoholLevel", String.valueOf(alcoholLevel));
    Element characterAddictions = doc.createElement("addictions");
    characterAddictionsCore.appendChild(characterAddictions);
    for (Addiction add : addictions) {
        add.saveAsXML(characterAddictions, doc);
    }
    Element psychoactives = doc.createElement("psychoactiveFluids");
    characterAddictionsCore.appendChild(psychoactives);
    for (FluidType ft : this.getPsychoactiveFluidsIngested()) {
        Element element = doc.createElement("fluid");
        psychoactives.appendChild(element);
        CharacterUtils.addAttribute(doc, element, "value", ft.toString());
    }
    return properties;
}
Also used : FluidType(com.lilithsthrone.game.character.body.types.FluidType) SlaveJobSetting(com.lilithsthrone.game.slavery.SlaveJobSetting) CoverableArea(com.lilithsthrone.game.character.body.CoverableArea) EnumSet(java.util.EnumSet) Set(java.util.Set) HashSet(java.util.HashSet) ClothingSet(com.lilithsthrone.game.inventory.clothing.ClothingSet) Attribute(com.lilithsthrone.game.character.attributes.Attribute) Element(org.w3c.dom.Element) SlavePermission(com.lilithsthrone.game.slavery.SlavePermission) PenetrationType(com.lilithsthrone.game.sex.PenetrationType) SexParticipantType(com.lilithsthrone.game.sex.SexParticipantType) Fetish(com.lilithsthrone.game.character.fetishes.Fetish) Comment(org.w3c.dom.Comment) SexType(com.lilithsthrone.game.sex.SexType) OrificeType(com.lilithsthrone.game.sex.OrificeType) Perk(com.lilithsthrone.game.character.effects.Perk) StatusEffect(com.lilithsthrone.game.character.effects.StatusEffect) SlavePermissionSetting(com.lilithsthrone.game.slavery.SlavePermissionSetting) Addiction(com.lilithsthrone.game.character.effects.Addiction) FetishDesire(com.lilithsthrone.game.character.fetishes.FetishDesire)

Example 7 with PenetrationType

use of com.lilithsthrone.game.sex.PenetrationType in project liliths-throne-public by Innoxia.

the class NPC method generateSexChoices.

public void generateSexChoices(GameCharacter target) {
    List<SexType> foreplaySexTypes = new ArrayList<>();
    List<SexType> mainSexTypes = new ArrayList<>();
    if (isKeenToPerformFetishAction(target, Fetish.FETISH_BREASTS_OTHERS)) {
        foreplaySexTypes.add(new SexType(SexParticipantType.PITCHER, PenetrationType.FINGER, OrificeType.BREAST));
        foreplaySexTypes.add(new SexType(SexParticipantType.PITCHER, PenetrationType.TONGUE, OrificeType.BREAST));
        foreplaySexTypes.add(new SexType(SexParticipantType.PITCHER, PenetrationType.FINGER, OrificeType.NIPPLE));
        foreplaySexTypes.add(new SexType(SexParticipantType.PITCHER, PenetrationType.TONGUE, OrificeType.NIPPLE));
        foreplaySexTypes.add(new SexType(SexParticipantType.PITCHER, PenetrationType.PENIS, OrificeType.BREAST));
        mainSexTypes.add(new SexType(SexParticipantType.PITCHER, PenetrationType.PENIS, OrificeType.BREAST));
        mainSexTypes.add(new SexType(SexParticipantType.PITCHER, PenetrationType.TAIL, OrificeType.BREAST));
        mainSexTypes.add(new SexType(SexParticipantType.PITCHER, PenetrationType.PENIS, OrificeType.NIPPLE));
        mainSexTypes.add(new SexType(SexParticipantType.PITCHER, PenetrationType.TAIL, OrificeType.NIPPLE));
    }
    if (isKeenToPerformFetishAction(target, Fetish.FETISH_BREASTS_SELF)) {
        foreplaySexTypes.add(new SexType(SexParticipantType.CATCHER, PenetrationType.FINGER, OrificeType.BREAST));
        foreplaySexTypes.add(new SexType(SexParticipantType.CATCHER, PenetrationType.TONGUE, OrificeType.BREAST));
        foreplaySexTypes.add(new SexType(SexParticipantType.CATCHER, PenetrationType.FINGER, OrificeType.NIPPLE));
        foreplaySexTypes.add(new SexType(SexParticipantType.CATCHER, PenetrationType.TONGUE, OrificeType.NIPPLE));
        foreplaySexTypes.add(new SexType(SexParticipantType.CATCHER, PenetrationType.PENIS, OrificeType.BREAST));
        mainSexTypes.add(new SexType(SexParticipantType.CATCHER, PenetrationType.PENIS, OrificeType.BREAST));
        mainSexTypes.add(new SexType(SexParticipantType.CATCHER, PenetrationType.TAIL, OrificeType.BREAST));
        mainSexTypes.add(new SexType(SexParticipantType.CATCHER, PenetrationType.PENIS, OrificeType.NIPPLE));
        mainSexTypes.add(new SexType(SexParticipantType.CATCHER, PenetrationType.TAIL, OrificeType.NIPPLE));
    }
    if (isKeenToPerformFetishAction(target, Fetish.FETISH_ANAL_GIVING)) {
        foreplaySexTypes.add(new SexType(SexParticipantType.PITCHER, PenetrationType.FINGER, OrificeType.ANUS));
        foreplaySexTypes.add(new SexType(SexParticipantType.PITCHER, PenetrationType.TONGUE, OrificeType.ANUS));
        mainSexTypes.add(new SexType(SexParticipantType.PITCHER, PenetrationType.PENIS, OrificeType.ANUS));
        mainSexTypes.add(new SexType(SexParticipantType.PITCHER, PenetrationType.TAIL, OrificeType.ANUS));
    }
    if (isKeenToPerformFetishAction(target, Fetish.FETISH_ANAL_RECEIVING)) {
        foreplaySexTypes.add(new SexType(SexParticipantType.CATCHER, PenetrationType.FINGER, OrificeType.ANUS));
        foreplaySexTypes.add(new SexType(SexParticipantType.CATCHER, PenetrationType.TONGUE, OrificeType.ANUS));
        mainSexTypes.add(new SexType(SexParticipantType.CATCHER, PenetrationType.PENIS, OrificeType.ANUS));
        mainSexTypes.add(new SexType(SexParticipantType.CATCHER, PenetrationType.TAIL, OrificeType.ANUS));
    }
    if ((isKeenToPerformFetishAction(target, Fetish.FETISH_DEFLOWERING) && target.isVaginaVirgin()) || isKeenToPerformFetishAction(target, Fetish.FETISH_IMPREGNATION) || isKeenToPerformFetishAction(target, Fetish.FETISH_SEEDER)) {
        mainSexTypes.add(new SexType(SexParticipantType.PITCHER, PenetrationType.PENIS, OrificeType.VAGINA));
    }
    if (isKeenToPerformFetishAction(target, Fetish.FETISH_PREGNANCY) || isKeenToPerformFetishAction(target, Fetish.FETISH_BROODMOTHER)) {
        mainSexTypes.add(new SexType(SexParticipantType.CATCHER, PenetrationType.PENIS, OrificeType.VAGINA));
    }
    if (isKeenToPerformFetishAction(target, Fetish.FETISH_ORAL_RECEIVING)) {
        foreplaySexTypes.add(new SexType(SexParticipantType.CATCHER, PenetrationType.TONGUE, OrificeType.VAGINA));
        foreplaySexTypes.add(new SexType(SexParticipantType.PITCHER, PenetrationType.PENIS, OrificeType.MOUTH));
        mainSexTypes.add(new SexType(SexParticipantType.CATCHER, PenetrationType.TONGUE, OrificeType.VAGINA));
        mainSexTypes.add(new SexType(SexParticipantType.PITCHER, PenetrationType.PENIS, OrificeType.MOUTH));
    }
    if (isKeenToPerformFetishAction(target, Fetish.FETISH_ORAL_GIVING)) {
        foreplaySexTypes.add(new SexType(SexParticipantType.PITCHER, PenetrationType.TONGUE, OrificeType.VAGINA));
        foreplaySexTypes.add(new SexType(SexParticipantType.CATCHER, PenetrationType.PENIS, OrificeType.MOUTH));
        mainSexTypes.add(new SexType(SexParticipantType.PITCHER, PenetrationType.TONGUE, OrificeType.VAGINA));
        mainSexTypes.add(new SexType(SexParticipantType.CATCHER, PenetrationType.PENIS, OrificeType.MOUTH));
    }
    // If no preferences from fetishes, add all common foreplay actions:
    if (foreplaySexTypes.isEmpty()) {
        // Player penetrates:
        List<PenetrationType> penTypes = Util.newArrayListOfValues(new ListValue<>(PenetrationType.FINGER), new ListValue<>(PenetrationType.TONGUE));
        List<OrificeType> orificeTypes = Util.newArrayListOfValues(new ListValue<>(OrificeType.BREAST), new ListValue<>(OrificeType.NIPPLE), new ListValue<>(OrificeType.VAGINA));
        for (PenetrationType pen : penTypes) {
            for (OrificeType orifice : orificeTypes) {
                foreplaySexTypes.add(new SexType(SexParticipantType.CATCHER, pen, orifice));
                foreplaySexTypes.add(new SexType(SexParticipantType.PITCHER, pen, orifice));
            }
        }
        foreplaySexTypes.add(new SexType(SexParticipantType.CATCHER, PenetrationType.PENIS, OrificeType.MOUTH));
        foreplaySexTypes.add(new SexType(SexParticipantType.PITCHER, PenetrationType.PENIS, OrificeType.MOUTH));
    }
    // If no preferences from fetishes, add all common sex actions:
    if (mainSexTypes.isEmpty()) {
        // TODO make a better weighting method, rather than just adding multiple entries
        // Player penetrates:
        List<PenetrationType> penTypes = Util.newArrayListOfValues(new ListValue<>(PenetrationType.PENIS), new ListValue<>(PenetrationType.PENIS), new ListValue<>(PenetrationType.PENIS), new ListValue<>(PenetrationType.TAIL));
        List<OrificeType> orificeTypes = Util.newArrayListOfValues(new ListValue<>(OrificeType.BREAST), new ListValue<>(OrificeType.VAGINA), new ListValue<>(OrificeType.VAGINA), new ListValue<>(OrificeType.VAGINA));
        for (PenetrationType pen : penTypes) {
            for (OrificeType orifice : orificeTypes) {
                mainSexTypes.add(new SexType(SexParticipantType.CATCHER, pen, orifice));
                mainSexTypes.add(new SexType(SexParticipantType.PITCHER, pen, orifice));
            }
        }
    }
    // Penis:
    if (!target.hasPenis() || !target.isAbleToAccessCoverableArea(CoverableArea.PENIS, true)) {
        foreplaySexTypes.removeIf(sexType -> sexType.getPenetrationType() == PenetrationType.PENIS && !sexType.getAsParticipant().isUsingSelfPenetrationType());
        mainSexTypes.removeIf(sexType -> sexType.getPenetrationType() == PenetrationType.PENIS && !sexType.getAsParticipant().isUsingSelfPenetrationType());
    }
    if (!this.hasPenis() || !this.isAbleToAccessCoverableArea(CoverableArea.PENIS, true)) {
        foreplaySexTypes.removeIf(sexType -> sexType.getPenetrationType() == PenetrationType.PENIS && sexType.getAsParticipant().isUsingSelfPenetrationType());
        mainSexTypes.removeIf(sexType -> sexType.getPenetrationType() == PenetrationType.PENIS && sexType.getAsParticipant().isUsingSelfPenetrationType());
    }
    // Vagina:
    if (!target.hasVagina() || !target.isAbleToAccessCoverableArea(CoverableArea.VAGINA, true) || isKeenToAvoidFetishAction(target, Fetish.FETISH_VAGINAL_GIVING)) {
        foreplaySexTypes.removeIf(sexType -> sexType.getOrificeType() == OrificeType.VAGINA && !sexType.getAsParticipant().isUsingSelfOrificeType());
        mainSexTypes.removeIf(sexType -> sexType.getOrificeType() == OrificeType.VAGINA && !sexType.getAsParticipant().isUsingSelfOrificeType());
    }
    if (isKeenToPerformFetishAction(target, Fetish.FETISH_PURE_VIRGIN) || !this.hasVagina() || !this.isAbleToAccessCoverableArea(CoverableArea.VAGINA, true) || isKeenToAvoidFetishAction(target, Fetish.FETISH_VAGINAL_RECEIVING)) {
        foreplaySexTypes.removeIf(sexType -> sexType.getOrificeType() == OrificeType.VAGINA && sexType.getAsParticipant().isUsingSelfOrificeType());
        mainSexTypes.removeIf(sexType -> sexType.getOrificeType() == OrificeType.VAGINA && sexType.getAsParticipant().isUsingSelfOrificeType());
    }
    // Anus:
    if (!target.isAbleToAccessCoverableArea(CoverableArea.ANUS, true) || isKeenToAvoidFetishAction(target, Fetish.FETISH_ANAL_GIVING)) {
        foreplaySexTypes.removeIf(sexType -> sexType.getOrificeType() == OrificeType.ANUS && !sexType.getAsParticipant().isUsingSelfOrificeType());
        mainSexTypes.removeIf(sexType -> sexType.getOrificeType() == OrificeType.ANUS && !sexType.getAsParticipant().isUsingSelfOrificeType());
    }
    if (!this.isAbleToAccessCoverableArea(CoverableArea.ANUS, true) || isKeenToAvoidFetishAction(target, Fetish.FETISH_VAGINAL_RECEIVING)) {
        foreplaySexTypes.removeIf(sexType -> sexType.getOrificeType() == OrificeType.ANUS && sexType.getAsParticipant().isUsingSelfOrificeType());
        mainSexTypes.removeIf(sexType -> sexType.getOrificeType() == OrificeType.ANUS && sexType.getAsParticipant().isUsingSelfOrificeType());
    }
    // Oral:
    if (!target.isAbleToAccessCoverableArea(CoverableArea.MOUTH, true) || isKeenToAvoidFetishAction(target, Fetish.FETISH_ORAL_GIVING)) {
        foreplaySexTypes.removeIf(sexType -> sexType.getOrificeType() == OrificeType.MOUTH && !sexType.getAsParticipant().isUsingSelfOrificeType());
        foreplaySexTypes.removeIf(sexType -> sexType.getPenetrationType() == PenetrationType.TONGUE && !sexType.getAsParticipant().isUsingSelfPenetrationType());
        mainSexTypes.removeIf(sexType -> sexType.getOrificeType() == OrificeType.MOUTH && !sexType.getAsParticipant().isUsingSelfOrificeType());
        mainSexTypes.removeIf(sexType -> sexType.getPenetrationType() == PenetrationType.TONGUE && !sexType.getAsParticipant().isUsingSelfPenetrationType());
    }
    if (!this.isAbleToAccessCoverableArea(CoverableArea.MOUTH, true) || isKeenToAvoidFetishAction(target, Fetish.FETISH_ORAL_RECEIVING)) {
        foreplaySexTypes.removeIf(sexType -> sexType.getOrificeType() == OrificeType.MOUTH && sexType.getAsParticipant().isUsingSelfOrificeType());
        foreplaySexTypes.removeIf(sexType -> sexType.getPenetrationType() == PenetrationType.TONGUE && sexType.getAsParticipant().isUsingSelfPenetrationType());
        mainSexTypes.removeIf(sexType -> sexType.getOrificeType() == OrificeType.MOUTH && sexType.getAsParticipant().isUsingSelfOrificeType());
        mainSexTypes.removeIf(sexType -> sexType.getPenetrationType() == PenetrationType.TONGUE && sexType.getAsParticipant().isUsingSelfPenetrationType());
    }
    // Breasts:
    if (!target.isAbleToAccessCoverableArea(CoverableArea.NIPPLES, true) || (!target.hasBreasts() && !target.isBreastFuckableNipplePenetration()) || isKeenToAvoidFetishAction(target, Fetish.FETISH_BREASTS_OTHERS)) {
        foreplaySexTypes.removeIf(sexType -> sexType.getOrificeType() == OrificeType.NIPPLE && !sexType.getAsParticipant().isUsingSelfOrificeType());
        foreplaySexTypes.removeIf(sexType -> sexType.getOrificeType() == OrificeType.BREAST && !sexType.getAsParticipant().isUsingSelfOrificeType());
        mainSexTypes.removeIf(sexType -> sexType.getOrificeType() == OrificeType.NIPPLE && !sexType.getAsParticipant().isUsingSelfOrificeType());
        mainSexTypes.removeIf(sexType -> sexType.getOrificeType() == OrificeType.BREAST && !sexType.getAsParticipant().isUsingSelfOrificeType());
    }
    if (!this.isAbleToAccessCoverableArea(CoverableArea.NIPPLES, true) || (!this.hasBreasts() && !this.isBreastFuckableNipplePenetration()) || isKeenToAvoidFetishAction(target, Fetish.FETISH_BREASTS_SELF)) {
        foreplaySexTypes.removeIf(sexType -> sexType.getOrificeType() == OrificeType.NIPPLE && sexType.getAsParticipant().isUsingSelfOrificeType());
        foreplaySexTypes.removeIf(sexType -> sexType.getOrificeType() == OrificeType.BREAST && sexType.getAsParticipant().isUsingSelfOrificeType());
        mainSexTypes.removeIf(sexType -> sexType.getOrificeType() == OrificeType.NIPPLE && sexType.getAsParticipant().isUsingSelfOrificeType());
        mainSexTypes.removeIf(sexType -> sexType.getOrificeType() == OrificeType.BREAST && sexType.getAsParticipant().isUsingSelfOrificeType());
    }
    // Tail:
    if (!target.getTailType().isSuitableForPenetration()) {
        foreplaySexTypes.removeIf(sexType -> sexType.getPenetrationType() == PenetrationType.TAIL && !sexType.getAsParticipant().isUsingSelfPenetrationType());
        mainSexTypes.removeIf(sexType -> sexType.getPenetrationType() == PenetrationType.TAIL && !sexType.getAsParticipant().isUsingSelfPenetrationType());
    }
    if (!this.getTailType().isSuitableForPenetration()) {
        foreplaySexTypes.removeIf(sexType -> sexType.getPenetrationType() == PenetrationType.TAIL && sexType.getAsParticipant().isUsingSelfPenetrationType());
        mainSexTypes.removeIf(sexType -> sexType.getPenetrationType() == PenetrationType.TAIL && sexType.getAsParticipant().isUsingSelfPenetrationType());
    }
    if (foreplaySexTypes.isEmpty()) {
        foreplayPreference = null;
    } else {
        foreplayPreference = foreplaySexTypes.get(Util.random.nextInt(foreplaySexTypes.size()));
    // System.out.println("Foreplay: "+foreplayPreference.getPenetrationType().toString()+" "+foreplayPreference.getOrificeType().toString());
    }
    if (mainSexTypes.isEmpty()) {
        mainSexPreference = null;
    } else {
        mainSexPreference = mainSexTypes.get(Util.random.nextInt(mainSexTypes.size()));
    // System.out.println("Main: "+mainSexPreference.getPenetrationType().toString()+" "+mainSexPreference.getOrificeType().toString());
    }
}
Also used : SexType(com.lilithsthrone.game.sex.SexType) OrificeType(com.lilithsthrone.game.sex.OrificeType) ArrayList(java.util.ArrayList) PenetrationType(com.lilithsthrone.game.sex.PenetrationType)

Example 8 with PenetrationType

use of com.lilithsthrone.game.sex.PenetrationType in project liliths-throne-public by Innoxia.

the class SexManagerDefault method getPartnerSexAction.

/**
 * New:</br>
 * - Get accessible areas</br>
 * - Choose foreplay & main sex</br>
 * - Choose positions for each</br>
 * - Clothing for foreplay</br>
 * - position</br>
 * - foreplay (self-actions take minimum priority)</br>
 * - clothing for main</br>
 * - position</br>
 * - main (self-actions take minimum priority)</br>
 * - orgasm
 */
@Override
public SexActionInterface getPartnerSexAction(SexActionInterface sexActionPlayer) {
    possibleActions.clear();
    bannedActions.clear();
    List<SexActionInterface> availableActions = Sex.getAvailableSexActionsPartner();
    if (Sex.getActivePartner().getArousal() >= ArousalLevel.FIVE_ORGASM_IMMINENT.getMaximumValue() && SexFlags.playerPreparedForOrgasm) {
        List<SexActionInterface> priorityOrgasms = new ArrayList<>();
        for (SexActionInterface action : availableActions) {
            for (GameCharacter character : Sex.getAllParticipants()) {
                if (action.getAreasCummedIn(Sex.getActivePartner(), character) != null) {
                    if ((action.getAreasCummedIn(Sex.getActivePartner(), character).contains(OrificeType.VAGINA) && (Sex.getActivePartner().hasFetish(Fetish.FETISH_IMPREGNATION) || Sex.getActivePartner().hasFetish(Fetish.FETISH_SEEDER))) || SexFlags.playerRequestedCreampie) {
                        priorityOrgasms.add(action);
                    } else if (SexFlags.playerRequestedPullOut && (Sex.isConsensual() || Sex.isSubHasEqualControl())) {
                        priorityOrgasms.add(action);
                    }
                }
                if (action.getAreasCummedIn(character, Sex.getActivePartner()) != null) {
                    if ((action.getAreasCummedIn(character, Sex.getActivePartner()).contains(OrificeType.VAGINA) && (Sex.getActivePartner().hasFetish(Fetish.FETISH_PREGNANCY) || Sex.getActivePartner().hasFetish(Fetish.FETISH_BROODMOTHER)))) {
                        priorityOrgasms.add(action);
                    }
                }
            }
        }
        if (!priorityOrgasms.isEmpty()) {
            return priorityOrgasms.get(Util.random.nextInt(priorityOrgasms.size()));
        } else {
            return availableActions.get(Util.random.nextInt(availableActions.size()));
        }
    }
    // If the partner is resisting, they will not want to remove any clothing, and will instead simply use an available option. (Which will be a SUB_RESIST or neutral pace one.)
    if (Sex.getSexPace(Sex.getActivePartner()) == SexPace.SUB_RESISTING) {
        possibleActions.addAll(Sex.getAvailableSexActionsPartner());
        if (!possibleActions.isEmpty()) {
            return possibleActions.get(Util.random.nextInt(possibleActions.size()));
        } else {
            return SexActionUtility.PARTNER_NONE;
        }
    }
    if (!Sex.getActivePartner().getSexPositionPreferences().contains(Sex.getSexPositionSlot(Sex.getActivePartner()))) {
        for (SexActionInterface action : availableActions) {
            if (action.getActionType() == SexActionType.PARTNER_POSITIONING) {
                possibleActions.add(action);
            }
        }
        // Choose a random position:
        if (!possibleActions.isEmpty()) {
            // }
            return possibleActions.get(Util.random.nextInt(possibleActions.size()));
        }
    } else {
        for (SexActionInterface action : availableActions) {
            if (action.getActionType() == SexActionType.PARTNER_POSITIONING) {
                bannedActions.add(action);
            }
        }
    }
    // Skip over remove clothing if action is of HIGH or MAX priority
    if (Sex.getAvailableSexActionsPartner().get(0).getPriority() != SexActionPriority.HIGH && Sex.getAvailableSexActionsPartner().get(0).getPriority() != SexActionPriority.UNIQUE_MAX) {
        List<CoverableArea> playerAreasToBeExposed = new ArrayList<>();
        List<CoverableArea> partnerAreasToBeExposed = new ArrayList<>();
        if (Sex.isInForeplay()) {
            if (Sex.getActivePartner().getForeplayPreference() != null) {
                PenetrationType pen = Sex.getActivePartner().getForeplayPreference().getPenetrationType();
                SexParticipantType participantType = Sex.getActivePartner().getForeplayPreference().getAsParticipant();
                if (participantType == SexParticipantType.CATCHER) {
                    if (pen == PenetrationType.PENIS && !Main.game.getPlayer().isCoverableAreaExposed(CoverableArea.PENIS) && Main.game.getPlayer().isAbleToAccessCoverableArea(CoverableArea.PENIS, true)) {
                        playerAreasToBeExposed.add(CoverableArea.PENIS);
                    } else if (pen == PenetrationType.TONGUE && !Main.game.getPlayer().isCoverableAreaExposed(CoverableArea.MOUTH) && Main.game.getPlayer().isAbleToAccessCoverableArea(CoverableArea.MOUTH, true)) {
                        playerAreasToBeExposed.add(CoverableArea.MOUTH);
                    }
                } else {
                    if (pen == PenetrationType.PENIS && !Sex.getActivePartner().isCoverableAreaExposed(CoverableArea.PENIS) && Sex.getActivePartner().isAbleToAccessCoverableArea(CoverableArea.PENIS, true)) {
                        partnerAreasToBeExposed.add(CoverableArea.PENIS);
                    } else if (pen == PenetrationType.TONGUE && !Sex.getActivePartner().isCoverableAreaExposed(CoverableArea.MOUTH) && Sex.getActivePartner().isAbleToAccessCoverableArea(CoverableArea.MOUTH, true)) {
                        partnerAreasToBeExposed.add(CoverableArea.MOUTH);
                    }
                }
                OrificeType orifice = Sex.getActivePartner().getForeplayPreference().getOrificeType();
                if (participantType == SexParticipantType.PITCHER) {
                    if (orifice == OrificeType.ANUS && !Main.game.getPlayer().isCoverableAreaExposed(CoverableArea.ANUS) && Main.game.getPlayer().isAbleToAccessCoverableArea(CoverableArea.ANUS, true)) {
                        playerAreasToBeExposed.add(CoverableArea.ANUS);
                    } else if ((orifice == OrificeType.BREAST || orifice == OrificeType.NIPPLE) && !Main.game.getPlayer().isCoverableAreaExposed(CoverableArea.NIPPLES) && Main.game.getPlayer().isAbleToAccessCoverableArea(CoverableArea.NIPPLES, true)) {
                        playerAreasToBeExposed.add(CoverableArea.NIPPLES);
                    } else if (orifice == OrificeType.MOUTH && !Main.game.getPlayer().isCoverableAreaExposed(CoverableArea.MOUTH) && Main.game.getPlayer().isAbleToAccessCoverableArea(CoverableArea.MOUTH, true)) {
                        playerAreasToBeExposed.add(CoverableArea.MOUTH);
                    } else if (orifice == OrificeType.URETHRA && !Main.game.getPlayer().isCoverableAreaExposed(CoverableArea.PENIS) && Main.game.getPlayer().isAbleToAccessCoverableArea(CoverableArea.PENIS, true)) {
                        playerAreasToBeExposed.add(CoverableArea.PENIS);
                    } else if (orifice == OrificeType.VAGINA && !Main.game.getPlayer().isCoverableAreaExposed(CoverableArea.VAGINA) && Main.game.getPlayer().isAbleToAccessCoverableArea(CoverableArea.VAGINA, true)) {
                        playerAreasToBeExposed.add(CoverableArea.VAGINA);
                    }
                } else {
                    if (orifice == OrificeType.ANUS && !Sex.getActivePartner().isCoverableAreaExposed(CoverableArea.ANUS) && Sex.getActivePartner().isAbleToAccessCoverableArea(CoverableArea.ANUS, true)) {
                        partnerAreasToBeExposed.add(CoverableArea.ANUS);
                    } else if ((orifice == OrificeType.BREAST || orifice == OrificeType.NIPPLE) && !Sex.getActivePartner().isCoverableAreaExposed(CoverableArea.NIPPLES) && Sex.getActivePartner().isAbleToAccessCoverableArea(CoverableArea.NIPPLES, true)) {
                        partnerAreasToBeExposed.add(CoverableArea.NIPPLES);
                    } else if (orifice == OrificeType.MOUTH && !Sex.getActivePartner().isCoverableAreaExposed(CoverableArea.MOUTH) && Sex.getActivePartner().isAbleToAccessCoverableArea(CoverableArea.MOUTH, true)) {
                        partnerAreasToBeExposed.add(CoverableArea.MOUTH);
                    } else if (orifice == OrificeType.URETHRA && !Sex.getActivePartner().isCoverableAreaExposed(CoverableArea.PENIS) && Sex.getActivePartner().isAbleToAccessCoverableArea(CoverableArea.PENIS, true)) {
                        partnerAreasToBeExposed.add(CoverableArea.PENIS);
                    } else if (orifice == OrificeType.VAGINA && !Sex.getActivePartner().isCoverableAreaExposed(CoverableArea.VAGINA) && Sex.getActivePartner().isAbleToAccessCoverableArea(CoverableArea.VAGINA, true)) {
                        partnerAreasToBeExposed.add(CoverableArea.VAGINA);
                    }
                }
            } else {
                if (playerAreasToBeExposed.isEmpty()) {
                    // }
                    if (!Main.game.getPlayer().isCoverableAreaExposed(CoverableArea.NIPPLES) && Main.game.getPlayer().isAbleToAccessCoverableArea(CoverableArea.NIPPLES, true)) {
                        playerAreasToBeExposed.add(CoverableArea.NIPPLES);
                    } else if (!Main.game.getPlayer().isCoverableAreaExposed(CoverableArea.MOUTH) && Main.game.getPlayer().isAbleToAccessCoverableArea(CoverableArea.MOUTH, true)) {
                        playerAreasToBeExposed.add(CoverableArea.MOUTH);
                    }
                }
                if (partnerAreasToBeExposed.isEmpty()) {
                    // }
                    if (!Sex.getActivePartner().isCoverableAreaExposed(CoverableArea.NIPPLES) && Sex.getActivePartner().isAbleToAccessCoverableArea(CoverableArea.NIPPLES, true)) {
                        partnerAreasToBeExposed.add(CoverableArea.NIPPLES);
                    } else if (!Sex.getActivePartner().isCoverableAreaExposed(CoverableArea.MOUTH) && Sex.getActivePartner().isAbleToAccessCoverableArea(CoverableArea.MOUTH, true)) {
                        partnerAreasToBeExposed.add(CoverableArea.MOUTH);
                    }
                }
            }
        } else {
            if (Sex.getActivePartner().getMainSexPreference() != null) {
                PenetrationType pen = Sex.getActivePartner().getMainSexPreference().getPenetrationType();
                SexParticipantType participantType = Sex.getActivePartner().getMainSexPreference().getAsParticipant();
                if (participantType == SexParticipantType.CATCHER) {
                    if (pen == PenetrationType.PENIS && !Main.game.getPlayer().isCoverableAreaExposed(CoverableArea.PENIS) && Main.game.getPlayer().isAbleToAccessCoverableArea(CoverableArea.PENIS, true)) {
                        playerAreasToBeExposed.add(CoverableArea.PENIS);
                    } else if (pen == PenetrationType.TONGUE && !Main.game.getPlayer().isCoverableAreaExposed(CoverableArea.MOUTH) && Main.game.getPlayer().isAbleToAccessCoverableArea(CoverableArea.MOUTH, true)) {
                        playerAreasToBeExposed.add(CoverableArea.MOUTH);
                    }
                } else {
                    if (pen == PenetrationType.PENIS && !Sex.getActivePartner().isCoverableAreaExposed(CoverableArea.PENIS) && Sex.getActivePartner().isAbleToAccessCoverableArea(CoverableArea.PENIS, true)) {
                        partnerAreasToBeExposed.add(CoverableArea.PENIS);
                    } else if (pen == PenetrationType.TONGUE && !Sex.getActivePartner().isCoverableAreaExposed(CoverableArea.MOUTH) && Sex.getActivePartner().isAbleToAccessCoverableArea(CoverableArea.MOUTH, true)) {
                        partnerAreasToBeExposed.add(CoverableArea.MOUTH);
                    }
                }
                OrificeType orifice = Sex.getActivePartner().getMainSexPreference().getOrificeType();
                if (participantType == SexParticipantType.PITCHER) {
                    if (orifice == OrificeType.ANUS && !Main.game.getPlayer().isCoverableAreaExposed(CoverableArea.ANUS) && Main.game.getPlayer().isAbleToAccessCoverableArea(CoverableArea.ANUS, true)) {
                        playerAreasToBeExposed.add(CoverableArea.ANUS);
                    } else if ((orifice == OrificeType.BREAST || orifice == OrificeType.NIPPLE) && !Main.game.getPlayer().isCoverableAreaExposed(CoverableArea.NIPPLES) && Main.game.getPlayer().isAbleToAccessCoverableArea(CoverableArea.NIPPLES, true)) {
                        playerAreasToBeExposed.add(CoverableArea.NIPPLES);
                    } else if (orifice == OrificeType.MOUTH && !Main.game.getPlayer().isCoverableAreaExposed(CoverableArea.MOUTH) && Main.game.getPlayer().isAbleToAccessCoverableArea(CoverableArea.MOUTH, true)) {
                        playerAreasToBeExposed.add(CoverableArea.MOUTH);
                    } else if (orifice == OrificeType.URETHRA && !Main.game.getPlayer().isCoverableAreaExposed(CoverableArea.PENIS) && Main.game.getPlayer().isAbleToAccessCoverableArea(CoverableArea.PENIS, true)) {
                        playerAreasToBeExposed.add(CoverableArea.PENIS);
                    } else if (orifice == OrificeType.VAGINA && !Main.game.getPlayer().isCoverableAreaExposed(CoverableArea.VAGINA) && Main.game.getPlayer().isAbleToAccessCoverableArea(CoverableArea.VAGINA, true)) {
                        playerAreasToBeExposed.add(CoverableArea.VAGINA);
                    }
                } else {
                    if (orifice == OrificeType.ANUS && !Sex.getActivePartner().isCoverableAreaExposed(CoverableArea.ANUS) && Sex.getActivePartner().isAbleToAccessCoverableArea(CoverableArea.ANUS, true)) {
                        partnerAreasToBeExposed.add(CoverableArea.ANUS);
                    } else if ((orifice == OrificeType.BREAST || orifice == OrificeType.NIPPLE) && !Sex.getActivePartner().isCoverableAreaExposed(CoverableArea.NIPPLES) && Sex.getActivePartner().isAbleToAccessCoverableArea(CoverableArea.NIPPLES, true)) {
                        partnerAreasToBeExposed.add(CoverableArea.NIPPLES);
                    } else if (orifice == OrificeType.MOUTH && !Sex.getActivePartner().isCoverableAreaExposed(CoverableArea.MOUTH) && Sex.getActivePartner().isAbleToAccessCoverableArea(CoverableArea.MOUTH, true)) {
                        partnerAreasToBeExposed.add(CoverableArea.MOUTH);
                    } else if (orifice == OrificeType.URETHRA && !Sex.getActivePartner().isCoverableAreaExposed(CoverableArea.PENIS) && Sex.getActivePartner().isAbleToAccessCoverableArea(CoverableArea.PENIS, true)) {
                        partnerAreasToBeExposed.add(CoverableArea.PENIS);
                    } else if (orifice == OrificeType.VAGINA && !Sex.getActivePartner().isCoverableAreaExposed(CoverableArea.VAGINA) && Sex.getActivePartner().isAbleToAccessCoverableArea(CoverableArea.VAGINA, true)) {
                        partnerAreasToBeExposed.add(CoverableArea.VAGINA);
                    }
                }
            }
            // else {
            if (playerAreasToBeExposed.isEmpty()) {
                // } else
                if (Main.game.getPlayer().hasPenis() && !Main.game.getPlayer().isCoverableAreaExposed(CoverableArea.PENIS) && Main.game.getPlayer().isAbleToAccessCoverableArea(CoverableArea.PENIS, true)) {
                    playerAreasToBeExposed.add(CoverableArea.PENIS);
                } else if (Main.game.getPlayer().hasVagina() && !Main.game.getPlayer().isCoverableAreaExposed(CoverableArea.VAGINA) && Main.game.getPlayer().isAbleToAccessCoverableArea(CoverableArea.VAGINA, true)) {
                    playerAreasToBeExposed.add(CoverableArea.VAGINA);
                }
            // else if(!Main.game.getPlayer().isCoverableAreaExposed(CoverableArea.NIPPLES) && Main.game.getPlayer().isAbleToAccessCoverableArea(CoverableArea.NIPPLES, true)) {
            // playerAreasToBeExposed.add(CoverableArea.NIPPLES);
            // } else if(!Main.game.getPlayer().isCoverableAreaExposed(CoverableArea.MOUTH) && Main.game.getPlayer().isAbleToAccessCoverableArea(CoverableArea.MOUTH, true)) {
            // playerAreasToBeExposed.add(CoverableArea.MOUTH);
            // }
            }
            if (partnerAreasToBeExposed.isEmpty()) {
                // } else
                if (Sex.getActivePartner().hasPenis() && !Sex.getActivePartner().isCoverableAreaExposed(CoverableArea.PENIS) && Sex.getActivePartner().isAbleToAccessCoverableArea(CoverableArea.PENIS, true)) {
                    partnerAreasToBeExposed.add(CoverableArea.PENIS);
                } else if (Sex.getActivePartner().hasVagina() && !Sex.getActivePartner().isCoverableAreaExposed(CoverableArea.VAGINA) && Sex.getActivePartner().isAbleToAccessCoverableArea(CoverableArea.VAGINA, true)) {
                    partnerAreasToBeExposed.add(CoverableArea.VAGINA);
                }
            // else if(!Sex.getPartner().isCoverableAreaExposed(CoverableArea.NIPPLES) && Sex.getPartner().isAbleToAccessCoverableArea(CoverableArea.NIPPLES, true)) {
            // partnerAreasToBeExposed.add(CoverableArea.NIPPLES);
            // } else if(!Sex.getPartner().isCoverableAreaExposed(CoverableArea.MOUTH) && Sex.getPartner().isAbleToAccessCoverableArea(CoverableArea.MOUTH, true)) {
            // partnerAreasToBeExposed.add(CoverableArea.MOUTH);
            // }
            }
        // }
        }
        if (!partnerAreasToBeExposed.isEmpty() && Sex.isCanRemoveSelfClothing(Sex.getActivePartner())) {
            Collections.shuffle(partnerAreasToBeExposed);
            if (partnerAreasToBeExposed.get(0) == CoverableArea.MOUND) {
                return Sex.partnerManageClothingToAccessCoverableArea(false, CoverableArea.VAGINA);
            } else {
                return Sex.partnerManageClothingToAccessCoverableArea(false, partnerAreasToBeExposed.get(0));
            }
        }
        if (!playerAreasToBeExposed.isEmpty() && Sex.isCanRemoveOthersClothing(Sex.getActivePartner())) {
            Collections.shuffle(playerAreasToBeExposed);
            if (playerAreasToBeExposed.get(0) == CoverableArea.MOUND) {
                return Sex.partnerManageClothingToAccessCoverableArea(true, CoverableArea.VAGINA);
            } else {
                return Sex.partnerManageClothingToAccessCoverableArea(true, playerAreasToBeExposed.get(0));
            }
        }
    }
    // Ban all penetrations if the partner is a virgin in the associated orifice:
    for (SexActionInterface action : availableActions) {
        // TODO
        if (action.getAssociatedOrificeType() != null && action.getActionType() == SexActionType.PARTNER_PENETRATION && action.getParticipantType().isUsingSelfOrificeType()) {
            switch(action.getAssociatedOrificeType()) {
                case ANUS:
                    if (Sex.getActivePartner().isAssVirgin() && Sex.getSexPace(Sex.getActivePartner()) != SexPace.SUB_EAGER) {
                        bannedActions.add(action);
                    }
                    break;
                case MOUTH:
                    if (Sex.getActivePartner().isFaceVirgin() && Sex.getSexPace(Sex.getActivePartner()) != SexPace.SUB_EAGER) {
                        bannedActions.add(action);
                    }
                    break;
                case NIPPLE:
                    if (Sex.getActivePartner().isNippleVirgin() && Sex.getSexPace(Sex.getActivePartner()) != SexPace.SUB_EAGER) {
                        bannedActions.add(action);
                    }
                    break;
                case URETHRA:
                    if (Sex.getActivePartner().isUrethraVirgin() && Sex.getSexPace(Sex.getActivePartner()) != SexPace.SUB_EAGER) {
                        bannedActions.add(action);
                    }
                    break;
                case VAGINA:
                    if (Sex.getActivePartner().hasStatusEffect(StatusEffect.FETISH_PURE_VIRGIN) || Sex.getActivePartner().hasStatusEffect(StatusEffect.FETISH_PURE_VIRGIN_LUSTY_MAIDEN) || (Sex.getActivePartner().isVaginaVirgin() && Sex.getSexPace(Sex.getActivePartner()) != SexPace.SUB_EAGER)) {
                        bannedActions.add(action);
                    }
                    break;
                default:
                    break;
            }
        }
    }
    // --- Priority 6 | Perform actions based on foreplay or sex ---
    // Perform foreplay action if arousal is < 25 and haven't orgasmed yet:
    SexAction actionToPerform = null;
    if (Sex.isInForeplay()) {
        actionToPerform = performForeplayAction(sexActionPlayer);
        if (actionToPerform != null) {
            return actionToPerform;
        }
    } else {
        actionToPerform = performSexAction(sexActionPlayer);
        if (actionToPerform != null) {
            return actionToPerform;
        }
    }
    // --- Priority 7 using other options at random ---
    possibleActions.addAll(availableActions);
    possibleActions.removeAll(bannedActions);
    if (!possibleActions.isEmpty()) {
        return possibleActions.get(Util.random.nextInt(possibleActions.size()));
    }
    // Priority 9 (last resort):
    return SexActionUtility.PARTNER_NONE;
}
Also used : CoverableArea(com.lilithsthrone.game.character.body.CoverableArea) OrificeType(com.lilithsthrone.game.sex.OrificeType) ArrayList(java.util.ArrayList) GameCharacter(com.lilithsthrone.game.character.GameCharacter) SexActionInterface(com.lilithsthrone.game.sex.sexActions.SexActionInterface) SexAction(com.lilithsthrone.game.sex.sexActions.SexAction) PenetrationType(com.lilithsthrone.game.sex.PenetrationType) SexParticipantType(com.lilithsthrone.game.sex.SexParticipantType)

Example 9 with PenetrationType

use of com.lilithsthrone.game.sex.PenetrationType in project liliths-throne-public by Innoxia.

the class Body method getAssDescription.

public String getAssDescription(GameCharacter owner) {
    descriptionSB = new StringBuilder();
    boolean isPlayer = owner.isPlayer();
    switch(ass.getType()) {
        case HUMAN:
            if (isPlayer) {
                descriptionSB.append("You have a human, [pc.anusFullDescription(true)]");
            } else {
                descriptionSB.append("[npc.She] has a human, [npc.anusFullDescription(true)]");
            }
            break;
        case ANGEL:
            if (isPlayer) {
                descriptionSB.append("You have an angelic, [pc.anusFullDescription(true)]");
            } else {
                descriptionSB.append("[npc.She] has an angelic, [npc.anusFullDescription(true)]");
            }
            break;
        case DEMON_COMMON:
            if (isPlayer) {
                descriptionSB.append("You have a demonic, [pc.anusFullDescription(true)]");
            } else {
                descriptionSB.append("[npc.She] has a demonic, [npc.anusFullDescription(true)]");
            }
            break;
        case IMP:
            if (isPlayer) {
                descriptionSB.append("You have an impish, [pc.anusFullDescription(true)]");
            } else {
                descriptionSB.append("[npc.She] has an impish, [npc.anusFullDescription(true)]");
            }
            break;
        case DOG_MORPH:
            if (isPlayer) {
                descriptionSB.append("You have a canine, [pc.anusFullDescription(true)]");
            } else {
                descriptionSB.append("[npc.She] has a canine, [npc.anusFullDescription(true)]");
            }
            break;
        case WOLF_MORPH:
            if (isPlayer) {
                descriptionSB.append("You have a lupine, [pc.anusFullDescription(true)]");
            } else {
                descriptionSB.append("[npc.She] has a lupine, [npc.anusFullDescription(true)]");
            }
            break;
        case CAT_MORPH:
            if (isPlayer) {
                descriptionSB.append("You have a feline, [pc.anusFullDescription(true)]");
            } else {
                descriptionSB.append("[npc.She] has a feline, [npc.anusFullDescription(true)]");
            }
            break;
        case SQUIRREL_MORPH:
            if (isPlayer) {
                descriptionSB.append("You have a rodent, [pc.anusFullDescription(true)]");
            } else {
                descriptionSB.append("[npc.She] has a rodent, [npc.anusFullDescription(true)]");
            }
            break;
        case ALLIGATOR_MORPH:
            if (isPlayer) {
                descriptionSB.append("You have a reptilian, [pc.anusFullDescription(true)]");
            } else {
                descriptionSB.append("[npc.She] has a reptilian, [npc.anusFullDescription(true)]");
            }
            break;
        case HORSE_MORPH:
            if (isPlayer) {
                descriptionSB.append("You have an equine, [pc.anusFullDescription(true)]");
            } else {
                descriptionSB.append("[npc.She] has an equine, [npc.anusFullDescription(true)]");
            }
            break;
        case REINDEER_MORPH:
            if (isPlayer) {
                descriptionSB.append("You have a rangiferine, [pc.anusFullDescription(true)]");
            } else {
                descriptionSB.append("[npc.She] has a rangiferine, [npc.anusFullDescription(true)]");
            }
            break;
        case COW_MORPH:
            if (isPlayer) {
                descriptionSB.append("You have a bovine, [pc.anusFullDescription(true)]");
            } else {
                descriptionSB.append("[npc.She] has a bovine, [npc.anusFullDescription(true)]");
            }
            break;
        case HARPY:
            if (isPlayer) {
                descriptionSB.append("You have an avian, [pc.anusFullDescription(true)]");
            } else {
                descriptionSB.append("[npc.She] has an avian, [npc.anusFullDescription(true)]");
            }
            break;
    }
    // Colour:
    if (ass.getAnus().isBleached()) {
        if (isPlayer) {
            descriptionSB.append(", which has been bleached so that the rim is no darker than the [pc.assSkin] around it.");
        } else {
            descriptionSB.append(", which has been bleached so that the rim is no darker than the [npc.assSkin] around it.");
        }
    } else {
        if (isPlayer) {
            descriptionSB.append(", the rim being slightly darker than the [pc.assSkin] around it.");
        } else {
            descriptionSB.append(", the rim being slightly darker than the [npc.assSkin] around it.");
        }
    }
    descriptionSB.append(" <span style='color:" + Colour.GENERIC_SEX.toWebHexString() + ";'>It is " + Capacity.getCapacityFromValue(ass.getAnus().getOrificeAnus().getStretchedCapacity()).getDescriptor() + ", and can comfortably take " + Capacity.getCapacityFromValue(ass.getAnus().getOrificeAnus().getStretchedCapacity()).getMaximumSizeComfortableWithLube().getDescriptor() + " cocks with enough lube.</span>");
    if (isPlayer) {
        if (ass.getAnus().getOrificeAnus().isVirgin()) {
            descriptionSB.append(" <span style='color:" + Colour.GENERIC_GOOD.toWebHexString() + ";'>You have retained your anal virginity.</span>");
        } else {
            for (PenetrationType pt : PenetrationType.values()) {
                if (Main.game.getPlayer().getVirginityLoss(new SexType(SexParticipantType.CATCHER, pt, OrificeType.ANUS)) != null && !Main.game.getPlayer().getVirginityLoss(new SexType(SexParticipantType.CATCHER, pt, OrificeType.ANUS)).isEmpty()) {
                    descriptionSB.append(" <span style='color:" + Colour.GENERIC_ARCANE.toWebHexString() + ";'>You lost your anal virginity to " + Main.game.getPlayer().getVirginityLoss(new SexType(SexParticipantType.CATCHER, pt, OrificeType.ANUS)) + ".</span>");
                    break;
                }
            }
        }
    }
    // Ass wetness:
    switch(ass.getAnus().getOrificeAnus().getWetness(owner)) {
        case ZERO_DRY:
            descriptionSB.append(" <span style='color:" + Colour.GENERIC_SEX.toWebHexString() + ";'>It is completely dry, and would need lubricating before sex.</span>");
            break;
        case ONE_SLIGHTLY_MOIST:
            descriptionSB.append(" <span style='color:" + Colour.GENERIC_SEX.toWebHexString() + ";'>It is slightly moist, but would still need lubricating before sex.</span>");
            break;
        case TWO_MOIST:
            descriptionSB.append(" <span style='color:" + Colour.GENERIC_SEX.toWebHexString() + ";'>It is moist, but would still need lubricating before sex.</span>");
            break;
        case THREE_WET:
            descriptionSB.append(" <span style='color:" + Colour.GENERIC_SEX.toWebHexString() + ";'>Its surface constantly beads with wet droplets, which provides enough natural lubrication for sex.</span>");
            break;
        case FOUR_SLIMY:
            descriptionSB.append(" <span style='color:" + Colour.GENERIC_SEX.toWebHexString() + ";'>Its surface is always slimy and ready for penetration.</span>");
            break;
        case FIVE_SLOPPY:
            descriptionSB.append(" <span style='color:" + Colour.GENERIC_SEX.toWebHexString() + ";'>Its surface is always slimy, and the interior is constantly sloppy and ready for sex.</span>");
            break;
        case SIX_SOPPING_WET:
            descriptionSB.append(" <span style='color:" + Colour.GENERIC_SEX.toWebHexString() + ";'>It's constantly sopping wet from natural lubrication and ready for penetration.</span>");
            break;
        case SEVEN_DROOLING:
            descriptionSB.append(" <span style='color:" + Colour.GENERIC_SEX.toWebHexString() + ";'>It constantly drools with natural lubrication, and its sopping wet entrance is always ready for penetration.</span>");
            break;
        default:
            break;
    }
    // Ass elasticity & plasticity:
    switch(ass.getAnus().getOrificeAnus().getElasticity()) {
        case ZERO_UNYIELDING:
            descriptionSB.append(" <span style='color:" + Colour.GENERIC_SEX.toWebHexString() + ";'>It is extremely unyielding,");
            break;
        case ONE_RIGID:
            descriptionSB.append(" <span style='color:" + Colour.GENERIC_SEX.toWebHexString() + ";'>It takes a huge amount of effort to stretch it out,");
            break;
        case TWO_FIRM:
            descriptionSB.append(" <span style='color:" + Colour.GENERIC_SEX.toWebHexString() + ";'>It does not stretch very easily,");
            break;
        case THREE_FLEXIBLE:
            descriptionSB.append(" <span style='color:" + Colour.GENERIC_SEX.toWebHexString() + ";'>It reluctantly stretches out when used as a sexual orifice,");
            break;
        case FOUR_LIMBER:
            descriptionSB.append(" <span style='color:" + Colour.GENERIC_SEX.toWebHexString() + ";'>It is somewhat resistant to being stretched out,");
            break;
        case FIVE_STRETCHY:
            descriptionSB.append(" <span style='color:" + Colour.GENERIC_SEX.toWebHexString() + ";'>It stretches out fairly easily,");
            break;
        case SIX_SUPPLE:
            descriptionSB.append(" <span style='color:" + Colour.GENERIC_SEX.toWebHexString() + ";'>It stretches out very easily,");
            break;
        case SEVEN_ELASTIC:
            descriptionSB.append(" <span style='color:" + Colour.GENERIC_SEX.toWebHexString() + ";'>It is extremely elastic,");
            break;
        default:
            break;
    }
    switch(ass.getAnus().getOrificeAnus().getPlasticity()) {
        case ZERO_RUBBERY:
            descriptionSB.append(" and will instantly return to its original size.</span>");
            break;
        case ONE_SPRINGY:
            descriptionSB.append(" and returns to its original size within a matter of hours.</span>");
            break;
        case TWO_TENSILE:
            descriptionSB.append(" and returns to its original size within a day or so.</span>");
            break;
        case THREE_RESILIENT:
            descriptionSB.append(" and will return to its original size after a couple of days.</span>");
            break;
        case FOUR_ACCOMMODATING:
            descriptionSB.append(" and takes a while to return to its original size.</span>");
            break;
        case FIVE_YIELDING:
            descriptionSB.append(" and struggles to return to its original size.</span>");
            break;
        case SIX_MALLEABLE:
            descriptionSB.append(" and loses a good portion of its original tightness.</span>");
            break;
        case SEVEN_MOULDABLE:
            descriptionSB.append(" and once stretched out, it stays that way.</span>");
            break;
        default:
            break;
    }
    if (Main.game.isBodyHairEnabled()) {
        if (owner.isPlayer()) {
            switch(ass.getAnus().getAssHair()) {
                case ZERO_NONE:
                    descriptionSB.append(" There is no trace of any " + owner.getAssHairType().getName(owner) + " around your asshole.");
                    break;
                case ONE_STUBBLE:
                    descriptionSB.append(" You have a few strands of " + owner.getAssHairType().getFullDescription(owner, true) + " around your asshole.");
                    break;
                case TWO_MANICURED:
                    descriptionSB.append(" You have a very small amount of " + owner.getAssHairType().getFullDescription(owner, true) + " around your asshole.");
                    break;
                case THREE_TRIMMED:
                    descriptionSB.append(" You have a small amount of " + owner.getAssHairType().getFullDescription(owner, true) + " around your asshole.");
                    break;
                case FOUR_NATURAL:
                    descriptionSB.append(" You have a natural amount of " + owner.getAssHairType().getFullDescription(owner, true) + " around your asshole.");
                    break;
                case FIVE_UNKEMPT:
                    descriptionSB.append(" You have an unkempt mass of " + owner.getAssHairType().getFullDescription(owner, true) + " around your asshole.");
                    break;
                case SIX_BUSHY:
                    descriptionSB.append(" You have a thick, bushy mass of " + owner.getAssHairType().getFullDescription(owner, true) + " around your asshole.");
                    break;
                case SEVEN_WILD:
                    descriptionSB.append(" You have a wild, bushy mass of " + owner.getAssHairType().getFullDescription(owner, true) + " around your asshole.");
                    break;
            }
        } else {
            switch(ass.getAnus().getAssHair()) {
                case ZERO_NONE:
                    descriptionSB.append(" There is no trace of any " + owner.getAssHairType().getName(owner) + " around [npc.her] asshole.");
                    break;
                case ONE_STUBBLE:
                    descriptionSB.append(" [npc.She] has a few strands of " + owner.getAssHairType().getFullDescription(owner, true) + " around [npc.her] asshole.");
                    break;
                case TWO_MANICURED:
                    descriptionSB.append(" [npc.She] has a very small amount of " + owner.getAssHairType().getFullDescription(owner, true) + " around [npc.her] asshole.");
                    break;
                case THREE_TRIMMED:
                    descriptionSB.append(" [npc.She] has a small amount of " + owner.getAssHairType().getFullDescription(owner, true) + " around [npc.her] asshole.");
                    break;
                case FOUR_NATURAL:
                    descriptionSB.append(" [npc.She] has a natural amount of " + owner.getAssHairType().getFullDescription(owner, true) + " around [npc.her] asshole.");
                    break;
                case FIVE_UNKEMPT:
                    descriptionSB.append(" [npc.She] has an unkempt mass of " + owner.getAssHairType().getFullDescription(owner, true) + " around [npc.her] asshole.");
                    break;
                case SIX_BUSHY:
                    descriptionSB.append(" [npc.She] has a thick, bushy mass of " + owner.getAssHairType().getFullDescription(owner, true) + " around [npc.her] asshole.");
                    break;
                case SEVEN_WILD:
                    descriptionSB.append(" [npc.She] has a wild, bushy mass of " + owner.getAssHairType().getFullDescription(owner, true) + " around [npc.her] asshole.");
                    break;
            }
        }
    }
    for (OrificeModifier om : OrificeModifier.values()) {
        if (owner.hasAssOrificeModifier(om)) {
            if (owner.isPlayer()) {
                switch(om) {
                    case MUSCLE_CONTROL:
                        descriptionSB.append(" You have a series of internal muscles lining the inside of your [pc.asshole], allowing you to expertly squeeze and grip down on any intruding object.");
                        break;
                    case PUFFY:
                        descriptionSB.append(" The rim of your [pc.asshole] has swollen up into a puffy, doughnut-like ring.");
                        break;
                    case RIBBED:
                        descriptionSB.append(" The inside of your [pc.asshole] is lined with sensitive, fleshy ribs, which grant you extra pleasure when stimulated.");
                        break;
                    case TENTACLED:
                        descriptionSB.append(" Your [pc.asshole] is filled with tiny little tentacles, which wriggle and squirm with a mind of their own.");
                        break;
                }
            } else {
                switch(om) {
                    case MUSCLE_CONTROL:
                        descriptionSB.append(" [npc.She] has a series of internal muscles lining the inside of [npc.her] [npc.asshole], allowing [npc.herHim] to expertly squeeze and grip down on any intruding object.");
                        break;
                    case PUFFY:
                        descriptionSB.append(" The rim of [npc.her] [npc.asshole] has swollen up into a puffy, doughnut-like ring.");
                        break;
                    case RIBBED:
                        descriptionSB.append(" The inside of [npc.her] [npc.asshole] is lined with sensitive, fleshy ribs, which grant [npc.herHim] extra pleasure when stimulated.");
                        break;
                    case TENTACLED:
                        descriptionSB.append(" [npc.Her] [npc.asshole] is filled with tiny little tentacles, which wriggle and squirm with a mind of their own.");
                        break;
                }
            }
        }
    }
    return UtilText.parse(owner, descriptionSB.toString());
}
Also used : SexType(com.lilithsthrone.game.sex.SexType) PenetrationType(com.lilithsthrone.game.sex.PenetrationType) OrificeModifier(com.lilithsthrone.game.character.body.valueEnums.OrificeModifier)

Example 10 with PenetrationType

use of com.lilithsthrone.game.sex.PenetrationType in project liliths-throne-public by Innoxia.

the class Body method getDescription.

/**
 * @param owner
 * @param playerKnowledgeOfThroat
 * @param playerKnowledgeOfBreasts
 * @param playerKnowledgeOfGroin
 * @return
 */
public String getDescription(GameCharacter owner) {
    StringBuilder sb = new StringBuilder();
    // Describe race:
    if (owner.isPlayer()) {
        sb.append("<p>" + "You are [pc.name], <span style='color:" + getGender().getColour().toWebHexString() + ";'>[pc.a_gender]</span> [pc.raceStage] [pc.race]. " + owner.getAppearsAsGenderDescription() + " Standing at full height, you measure [pc.heightFeetInches] ([pc.heightCm]cm).");
    } else {
        if (owner.getPlayerKnowsAreas().contains(CoverableArea.PENIS) && owner.getPlayerKnowsAreas().contains(CoverableArea.VAGINA)) {
            sb.append("<p>" + "[npc.Name] is <span style='color:" + getGender().getColour().toWebHexString() + ";'>[npc.a_gender]</span> [npc.raceStage] [npc.race]. " + owner.getAppearsAsGenderDescription() + " Standing at full height, [npc.she] measures [npc.heightFeetInches] ([npc.heightCm]cm).");
        } else {
            if (Main.game.getPlayer().hasTrait(Perk.OBSERVANT, true)) {
                sb.append("<p>" + "Thanks to your observant perk, you can detect that [npc.name] is <span style='color:" + getGender().getColour().toWebHexString() + ";'>[npc.a_gender]</span> [npc.raceStage] [npc.race]. " + owner.getAppearsAsGenderDescription() + " Standing at full height, [npc.she] measures [npc.heightFeetInches] ([npc.heightCm]cm).");
            } else {
                sb.append("<p>" + "[npc.Name] is a [npc.raceStage] [npc.race]. " + owner.getAppearsAsGenderDescription() + " Standing at full height, [npc.she] measures [npc.heightFeetInches] ([npc.heightCm]cm).");
            }
        }
    }
    sb.append("</p>");
    switch(this.getBodyMaterial()) {
        case FIRE:
            if (owner.isPlayer()) {
                sb.append("<p>" + "Your entire body, save for a small obsidian sphere in the place where your heart should be, is made out of <b style='color:" + this.getBodyMaterial().getColour().toWebHexString() + ";'>fire and smoke</b>!" + "</p>");
            } else {
                sb.append("<p>" + "[npc.Name]'s entire body, save for a small obsidian sphere in the place where [npc.her] heart should be, is made out of <b style='color:" + this.getBodyMaterial().getColour().toWebHexString() + ";'>fire and smoke</b>!" + "</p>");
            }
            break;
        case FLESH:
            break;
        case ICE:
            if (owner.isPlayer()) {
                sb.append("<p>" + "Your entire body, save for a small obsidian sphere in the place where your heart should be, is made out of <b style='color:" + this.getBodyMaterial().getColour().toWebHexString() + ";'>ice</b>!" + "</p>");
            } else {
                sb.append("<p>" + "[npc.Name]'s entire body, save for a small obsidian sphere in the place where [npc.her] heart should be, is made out of <b style='color:" + this.getBodyMaterial().getColour().toWebHexString() + ";'>ice</b>!" + "</p>");
            }
            break;
        case RUBBER:
            if (owner.isPlayer()) {
                sb.append("<p>" + "Your entire body, save for a small obsidian sphere in the place where your heart should be, is made out of <b style='color:" + this.getBodyMaterial().getColour().toWebHexString() + ";'>rubber</b>!" + "</p>");
            } else {
                sb.append("<p>" + "[npc.Name]'s entire body, save for a small obsidian sphere in the place where [npc.her] heart should be, is made out of <b style='color:" + this.getBodyMaterial().getColour().toWebHexString() + ";'>rubber</b>!" + "</p>");
            }
            break;
        case SLIME:
            if (owner.isPlayer()) {
                sb.append("<p>" + "Your entire body, save for a small, glowing sphere in the place where your heart should be, is made out of [pc.skinFullDescription(true)]!" + "</p>");
            } else {
                sb.append("<p>" + "[npc.Name]'s entire body, save for a small, glowing sphere in the place where [npc.her] heart should be, is made out of [npc.skinFullDescription(true)]!" + "</p>");
            }
            break;
    }
    // Femininity:
    if (owner.isPlayer()) {
        sb.append("<p>" + "You have ");
    } else {
        sb.append("<p>" + "[npc.She] has ");
    }
    if (Femininity.valueOf(femininity) == Femininity.MASCULINE_STRONG) {
        sb.append(UtilText.returnStringAtRandom("a <span style='color:" + Colour.MASCULINE_PLUS.toWebHexString() + ";'>very masculine</span>", "an <span style='color:" + Colour.MASCULINE_PLUS.toWebHexString() + ";'>extremely handsome</span>"));
    } else if (Femininity.valueOf(femininity) == Femininity.MASCULINE) {
        sb.append(UtilText.returnStringAtRandom("a <span style='color:" + Colour.MASCULINE.toWebHexString() + ";'>masculine</span>", "a <span style='color:" + Colour.MASCULINE.toWebHexString() + ";'>handsome</span>"));
    } else if (Femininity.valueOf(femininity) == Femininity.ANDROGYNOUS) {
        sb.append("an <span style='color:" + Colour.ANDROGYNOUS.toWebHexString() + ";'>androgynous</span>");
    } else if (Femininity.valueOf(femininity) == Femininity.FEMININE) {
        sb.append(UtilText.returnStringAtRandom("a <span style='color:" + Colour.FEMININE.toWebHexString() + ";'>pretty</span>", "a <span style='color:" + Colour.FEMININE.toWebHexString() + ";'>feminine</span>", "a <span style='color:" + Colour.FEMININE.toWebHexString() + ";'>cute</span>"));
    } else {
        sb.append(UtilText.returnStringAtRandom("a <span style='color:" + Colour.FEMININE_PLUS.toWebHexString() + ";'>very feminine</span>", "a <span style='color:" + Colour.FEMININE_PLUS.toWebHexString() + ";'>beautiful</span>", "a <span style='color:" + Colour.FEMININE_PLUS.toWebHexString() + ";'>gorgeous</span>"));
    }
    // Face and eyes:
    switch(face.getType()) {
        case HUMAN:
            sb.append(" face.");
            break;
        case ANGEL:
            sb.append(", perfectly proportioned face.");
            break;
        case DEMON_COMMON:
            sb.append(", perfectly proportioned face.");
            break;
        case IMP:
            sb.append(", impish face.");
            break;
        case DOG_MORPH:
            sb.append(", anthropomorphic dog-like face, complete with a canine muzzle.");
            break;
        case LYCAN:
            sb.append(", anthropomorphic wolf-like face, complete with a lupine muzzle.");
            break;
        case CAT_MORPH:
            sb.append(", anthropomorphic cat-like face, with a cute little feline muzzle.");
            break;
        case ALLIGATOR_MORPH:
            sb.append(", anthropomorphic alligator-like face, with a long flat muzzle.");
            break;
        case COW_MORPH:
            sb.append(", anthropomorphic cow-like face, with a cute little bovine muzzle.");
            break;
        case SQUIRREL_MORPH:
            sb.append(", anthropomorphic squirrel-like face, with a cute little muzzle.");
            break;
        case HORSE_MORPH:
            sb.append(", anthropomorphic horse-like face, with a long, equine muzzle.");
            break;
        case REINDEER_MORPH:
            sb.append(", anthropomorphic reindeer-like face, with a long, reindeer-like muzzle.");
            break;
        case HARPY:
            sb.append(", anthropomorphic bird-like face, complete with beak.");
            break;
    }
    if (owner.getBlusher().getPrimaryColour() != Colour.COVERING_NONE) {
        if (owner.isPlayer()) {
            sb.append(" You are wearing " + owner.getBlusher().getColourDescriptor(owner, true, false) + " blusher.");
        } else {
            sb.append(" [npc.She] is wearing " + owner.getBlusher().getColourDescriptor(owner, true, false) + " blusher.");
        }
    }
    if (owner.isPlayer() && hair.getRawLengthValue() == 0) {
        sb.append(" You are completely bald.");
    } else if (!owner.isPlayer() && hair.getRawLengthValue() == 0) {
        sb.append(" [npc.She] is completely bald.");
    } else {
        if (owner.isPlayer()) {
            sb.append(" You have [pc.hairLength], [pc.hairColour(true)]");
        } else {
            sb.append(" [npc.She] has [npc.hairLength], [npc.hairColour(true)]");
        }
        switch(hair.getType()) {
            case HUMAN:
                sb.append(" hair");
                break;
            case DEMON_COMMON:
                sb.append(", silken hair");
                break;
            case IMP:
                sb.append(", silken hair");
                break;
            case ANGEL:
                sb.append(", silken hair");
                break;
            case DOG_MORPH:
                sb.append(", fur-like hair");
                break;
            case LYCAN:
                sb.append(", fur-like hair");
                break;
            case CAT_MORPH:
                sb.append(", fur-like hair");
                break;
            case COW_MORPH:
                sb.append(", fur-like hair");
                break;
            case ALLIGATOR_MORPH:
                sb.append(", coarse hair");
                break;
            case SQUIRREL_MORPH:
                sb.append(", fur-like hair");
                break;
            case HORSE_MORPH:
                sb.append(", horse-like hair");
                break;
            case REINDEER_MORPH:
                sb.append(", reindeer-like hair");
                break;
            case HARPY:
                sb.append(" feathers in place of hair");
                break;
        }
        switch(hair.getStyle()) {
            case BRAIDED:
                sb.append(", which " + (hair.getType().isDefaultPlural() ? "have" : "has") + " been woven into a long braid.");
                break;
            case CURLY:
                sb.append(", which " + (hair.getType().isDefaultPlural() ? "have" : "has") + " been curled and left loose.");
                break;
            case LOOSE:
                sb.append(", which " + (hair.getType().isDefaultPlural() ? "are" : "is") + " left loose and unstyled.");
                break;
            case NONE:
                sb.append(", which " + (hair.getType().isDefaultPlural() ? "are" : "is") + " unstyled.");
                break;
            case PONYTAIL:
                sb.append(", which " + (hair.getType().isDefaultPlural() ? "have" : "has") + " been styled into a ponytail.");
                break;
            case STRAIGHT:
                sb.append(", which " + (hair.getType().isDefaultPlural() ? "have" : "has") + " been straightened and left loose.");
                break;
            case TWIN_TAILS:
                sb.append(", which " + (hair.getType().isDefaultPlural() ? "have" : "has") + " been styled into twin tails.");
                break;
            case WAVY:
                sb.append(", which " + (hair.getType().isDefaultPlural() ? "have" : "has") + " been styled into waves and left loose.");
                break;
            case MOHAWK:
                sb.append(", which " + (hair.getType().isDefaultPlural() ? "have" : "has") + " been styled into a mohawk.");
                break;
            case AFRO:
                sb.append(", which " + (hair.getType().isDefaultPlural() ? "have" : "has") + " been styled into an afro.");
                break;
            case SIDECUT:
                sb.append(", which " + (hair.getType().isDefaultPlural() ? "have" : "has") + " been styled into a sidecut.");
                break;
            case BOB_CUT:
                sb.append(", which " + (hair.getType().isDefaultPlural() ? "have" : "has") + " been styled into a bob cut.");
                break;
            case PIXIE:
                sb.append(", which " + (hair.getType().isDefaultPlural() ? "have" : "has") + " been styled into a pixie-cut.");
                break;
            case SLICKED_BACK:
                sb.append(", which " + (hair.getType().isDefaultPlural() ? "have" : "has") + " been slicked back.");
                break;
            case MESSY:
                sb.append(", which " + (hair.getType().isDefaultPlural() ? "are" : "is") + " unstyled and very messy.");
                break;
            case HIME_CUT:
                sb.append(", which " + (hair.getType().isDefaultPlural() ? "have" : "has") + " been straightened and styled into a hime cut.");
                break;
            case CHONMAGE:
                sb.append(", which " + (hair.getType().isDefaultPlural() ? "have" : "has") + " been straightened, oiled and styled into a chonmage topknot.");
                break;
            case TOPKNOT:
                sb.append(", which " + (hair.getType().isDefaultPlural() ? "have" : "has") + " been styled into a topknot.");
                break;
            case DREADLOCKS:
                sb.append(", which " + (hair.getType().isDefaultPlural() ? "have" : "has") + " been styled into dreadlocks.");
                break;
            case BIRD_CAGE:
                sb.append(", which " + (hair.getType().isDefaultPlural() ? "have" : "has") + " been styled into an elaborate bird cage" + UtilText.returnStringAtRandom(".", ", birds not included."));
                break;
            case TWIN_BRAIDS:
                sb.append(", which " + (hair.getType().isDefaultPlural() ? "have" : "has") + " been woven into long twin braids.");
                break;
            case DRILLS:
                sb.append(", which " + (hair.getType().isDefaultPlural() ? "have" : "has") + " been styled into drills.");
                break;
            case LOW_PONYTAIL:
                sb.append(", which " + (hair.getType().isDefaultPlural() ? "have" : "has") + " been styled into a low ponytail.");
                break;
            case CROWN_BRAID:
                sb.append(", which " + (hair.getType().isDefaultPlural() ? "have" : "has") + " been woven into a " + UtilText.returnStringAtRandom("crown of braids.", "braided crown."));
                break;
        }
    }
    switch(horn.getType()) {
        case NONE:
            sb.append("");
            break;
        case CURLED:
            if (owner.isPlayer()) {
                sb.append(" " + Util.capitaliseSentence(horn.getDeterminer(owner)) + " " + horn.getHornLength().getDescriptor() + ", [pc.hornColour(true)], circular-curling horns protrude from the upper sides of your forehead.");
            } else {
                sb.append(" " + Util.capitaliseSentence(horn.getDeterminer(owner)) + " " + horn.getHornLength().getDescriptor() + ", [npc.hornColour(true)], circular-curling horns protrude from the upper sides of [npc.her] forehead.");
            }
            break;
        case CURVED:
        case BOVINE_CURVED:
            if (owner.isPlayer()) {
                sb.append(" " + Util.capitaliseSentence(horn.getDeterminer(owner)) + " " + horn.getHornLength().getDescriptor() + ", [pc.hornColour(true)], curved horns protrude from the upper sides of your forehead.");
            } else {
                sb.append(" " + Util.capitaliseSentence(horn.getDeterminer(owner)) + " " + horn.getHornLength().getDescriptor() + ", [npc.hornColour(true)], curved horns protrude from the upper sides of [npc.her] forehead.");
            }
            break;
        case REINDEER_RACK:
            if (owner.isPlayer()) {
                sb.append(" " + Util.capitaliseSentence(horn.getDeterminer(owner)) + " " + horn.getHornLength().getDescriptor() + ", [pc.hornColour(true)], multi-branched antlers protrude from the upper sides of your forehead.");
            } else {
                sb.append(" " + Util.capitaliseSentence(horn.getDeterminer(owner)) + " " + horn.getHornLength().getDescriptor() + ", [npc.hornColour(true)], multi-branched antlers protrude from the upper sides of [npc.her] forehead.");
            }
            break;
        case SPIRAL:
            if (owner.isPlayer()) {
                sb.append(" " + Util.capitaliseSentence(horn.getDeterminer(owner)) + " " + horn.getHornLength().getDescriptor() + ", [pc.hornColour(true)], spiralling horns protrude from the upper sides of your forehead.");
            } else {
                sb.append(" " + Util.capitaliseSentence(horn.getDeterminer(owner)) + " " + horn.getHornLength().getDescriptor() + ", [npc.hornColour(true)], spiralling horns protrude from the upper sides of [npc.her] forehead.");
            }
            break;
        case STRAIGHT:
        case BOVINE_STRAIGHT:
            if (owner.isPlayer()) {
                sb.append(" " + Util.capitaliseSentence(horn.getDeterminer(owner)) + " " + horn.getHornLength().getDescriptor() + ", [pc.hornColour(true)], straight horns protrude from the upper sides of your forehead.");
            } else {
                sb.append(" " + Util.capitaliseSentence(horn.getDeterminer(owner)) + " " + horn.getHornLength().getDescriptor() + ", [npc.hornColour(true)], straight horns protrude from the upper sides of [npc.her] forehead.");
            }
            break;
        case SWEPT_BACK:
            if (owner.isPlayer()) {
                sb.append(" " + Util.capitaliseSentence(horn.getDeterminer(owner)) + " " + horn.getHornLength().getDescriptor() + ", [pc.hornColour(true)], swept-back horns protrude from the upper sides of your forehead.");
            } else {
                sb.append(" " + Util.capitaliseSentence(horn.getDeterminer(owner)) + " " + horn.getHornLength().getDescriptor() + ", [npc.hornColour(true)], swept-back horns protrude from the upper sides of [npc.her] forehead.");
            }
            break;
    }
    switch(antenna.getType()) {
        case NONE:
            sb.append("");
            break;
        default:
            if (owner.isPlayer())
                sb.append(" [pc.A_antennae+] protrude from your upper forehead.");
            else
                sb.append(" [npc.A_antennae+] protrude from [npc.her] upper forehead.");
    }
    if (face.isPiercedNose()) {
        if (owner.isPlayer()) {
            sb.append(" Your [pc.nose] has been pierced.");
        } else {
            sb.append(" [npc.Her] [npc.nose] has been pierced.");
        }
    }
    if (owner.isPlayer()) {
        sb.append(" You have [pc.eyePairs] ");
    } else {
        sb.append(" [npc.She] has [npc.eyePairs] ");
    }
    switch(eye.getType()) {
        case ANGEL:
            sb.append(" angelic eyes");
            break;
        case CAT_MORPH:
            sb.append(" cat-like eyes");
            break;
        case COW_MORPH:
            sb.append(" cow-like eyes");
            break;
        case DEMON_COMMON:
            sb.append(" demonic eyes");
            break;
        case IMP:
            sb.append(" impish eyes");
            break;
        case DOG_MORPH:
            sb.append(" dog-like eyes");
            break;
        case ALLIGATOR_MORPH:
            sb.append(" reptilian eyes");
            break;
        case HARPY:
            sb.append(" bird-like eyes");
            break;
        case HORSE_MORPH:
            sb.append(" horse-like eyes");
            break;
        case REINDEER_MORPH:
            sb.append(" reindeer-like eyes");
            break;
        case HUMAN:
            sb.append(" normal, human eyes");
            break;
        case LYCAN:
            sb.append(" wolf-like eyes");
            break;
        case SQUIRREL_MORPH:
            sb.append(" squirrel-like eyes");
            break;
    }
    if (owner.isPlayer()) {
        if (owner.getCovering(owner.getEyeType().getBodyCoveringType(owner)).getPattern() == CoveringPattern.EYE_IRISES_HETEROCHROMATIC) {
            sb.append(", with [pc.irisShape], heterochromatic [pc.irisPrimaryColour(true)]-and-[pc.irisSecondaryColour(true)] irises ");
        } else {
            sb.append(", with [pc.irisShape], [pc.irisPrimaryColour(true)] irises ");
        }
        if (owner.getCovering(BodyCoveringType.EYE_PUPILS).getPattern() == CoveringPattern.EYE_PUPILS_HETEROCHROMATIC) {
            sb.append("and [pc.pupilShape], heterochromatic [pc.pupilPrimaryColour(true)]-and-[pc.pupilSecondaryColour(true)] pupils.");
        } else {
            sb.append("and [pc.pupilShape], [pc.pupilPrimaryColour(true)] pupils.");
        }
    } else {
        if (owner.getCovering(owner.getEyeType().getBodyCoveringType(owner)).getPattern() == CoveringPattern.EYE_IRISES_HETEROCHROMATIC) {
            sb.append(", with [npc.irisShape], heterochromatic [npc.irisPrimaryColour(true)]-and-[npc.irisSecondaryColour(true)] irises, ");
        } else {
            sb.append(", with [npc.irisShape], [npc.irisPrimaryColour(true)] irises ");
        }
        if (owner.getCovering(BodyCoveringType.EYE_PUPILS).getPattern() == CoveringPattern.EYE_PUPILS_HETEROCHROMATIC) {
            sb.append("and [npc.pupilShape], heterochromatic [npc.pupilPrimaryColour(true)]-and-[npc.pupilSecondaryColour(true)] pupils.");
        } else {
            sb.append("and [npc.pupilShape], [npc.pupilPrimaryColour(true)] pupils.");
        }
    }
    // Eye makeup:
    if (owner.getEyeLiner().getPrimaryColour() != Colour.COVERING_NONE) {
        if (owner.isPlayer()) {
            sb.append(" Around your [pc.eyes], you've got a layer of " + owner.getEyeLiner().getColourDescriptor(owner, true, false) + " eye liner.");
        } else {
            sb.append(" Around [npc.her] [npc.eyes], [npc.she]'s got a layer of " + owner.getEyeLiner().getColourDescriptor(owner, true, false) + " eye liner.");
        }
    }
    if (owner.getEyeShadow().getPrimaryColour() != Colour.COVERING_NONE) {
        if (owner.isPlayer()) {
            sb.append(" You're wearing a tasteful amount of " + owner.getEyeShadow().getFullDescription(owner, true) + ".");
        } else {
            sb.append(" [npc.She]'s wearing a tasteful amount of " + owner.getEyeShadow().getFullDescription(owner, true) + ".");
        }
    }
    // Ear:
    switch(ear.getType()) {
        case ANGEL:
            if (owner.isPlayer())
                sb.append(" You have a pair of perfectly-formed angelic ears, which are " + getCoveredInDescriptor(owner) + " [pc.earFullDescription(true)]" + (ear.isPierced() ? ", and which have been pierced" : "") + ".");
            else
                sb.append(" [npc.She] has a pair of perfectly-formed angelic ears, which are " + getCoveredInDescriptor(owner) + " [npc.earFullDescription(true)]" + (ear.isPierced() ? ", and which have been pierced" : "") + ".");
            break;
        case HUMAN:
            if (owner.isPlayer())
                sb.append(" You have a pair of normal, human ears, which are " + getCoveredInDescriptor(owner) + " [pc.earFullDescription(true)]" + (ear.isPierced() ? ", and which have been pierced" : "") + ".");
            else
                sb.append(" [npc.She] has a pair of normal, human ears, which are " + getCoveredInDescriptor(owner) + " [npc.earFullDescription(true)]" + (ear.isPierced() ? ", and which have been pierced" : "") + ".");
            break;
        case DEMON_COMMON:
            if (owner.isPlayer())
                sb.append(" You have a pair of pointed, demonic ears, which are " + getCoveredInDescriptor(owner) + " [pc.earFullDescription(true)]" + (ear.isPierced() ? ", and which have been pierced" : "") + ".");
            else
                sb.append(" [npc.She] has a pair of pointed, demonic ears, which are " + getCoveredInDescriptor(owner) + " [npc.earFullDescription(true)]" + (ear.isPierced() ? ", and which have been pierced" : "") + ".");
            break;
        case IMP:
            if (owner.isPlayer())
                sb.append(" You have a pair of pointed, impish ears, which are " + getCoveredInDescriptor(owner) + " [pc.earFullDescription(true)]" + (ear.isPierced() ? ", and which have been pierced" : "") + ".");
            else
                sb.append(" [npc.She] has a pair of pointed, impish ears, which are " + getCoveredInDescriptor(owner) + " [npc.earFullDescription(true)]" + (ear.isPierced() ? ", and which have been pierced" : "") + ".");
            break;
        case DOG_MORPH:
            if (owner.isPlayer())
                sb.append(" You have a pair of floppy, " + (ear.isPierced() ? "pierced, " : "") + "dog-like ears, which are positioned high up on your head and are are " + getCoveredInDescriptor(owner) + " [pc.earFullDescription(true)].");
            else
                sb.append(" [npc.She] has a pair of floppy, " + (ear.isPierced() ? "pierced, " : "") + "dog-like ears, which are positioned high up on [npc.her] head and are " + getCoveredInDescriptor(owner) + " [npc.earFullDescription(true)].");
            break;
        case DOG_MORPH_POINTED:
            if (owner.isPlayer())
                sb.append(" You have a pair of pointed, " + (ear.isPierced() ? "pierced, " : "") + "dog-like ears, which are positioned high up on your head and are are " + getCoveredInDescriptor(owner) + " [pc.earFullDescription(true)].");
            else
                sb.append(" [npc.She] has a pair of pointed, " + (ear.isPierced() ? "pierced, " : "") + "dog-like ears, which are positioned high up on [npc.her] head and are " + getCoveredInDescriptor(owner) + " [npc.earFullDescription(true)].");
            break;
        case LYCAN:
            if (owner.isPlayer())
                sb.append(" You have a pair of " + (ear.isPierced() ? "pierced, " : "") + "upright, wolf-like ears, which are positioned high up on your head and are are " + getCoveredInDescriptor(owner) + " [pc.earFullDescription(true)].");
            else
                sb.append(" [npc.She] has a pair of " + (ear.isPierced() ? "pierced, " : "") + "upright, wolf-like ears, which are positioned high up on [npc.her] head and are " + getCoveredInDescriptor(owner) + " [npc.earFullDescription(true)].");
            break;
        case CAT_MORPH:
            if (owner.isPlayer())
                sb.append(" You have a pair of " + (ear.isPierced() ? "pierced, " : "") + "upright, cat-like ears, which are positioned high up on your head and are are " + getCoveredInDescriptor(owner) + " [pc.earFullDescription(true)].");
            else
                sb.append(" [npc.She] has a pair of " + (ear.isPierced() ? "pierced, " : "") + "upright, cat-like ears, which are positioned high up on [npc.her] head and are " + getCoveredInDescriptor(owner) + " [npc.earFullDescription(true)].");
            break;
        case COW_MORPH:
            if (owner.isPlayer())
                sb.append(" You have a pair of oval-shaped, cow-like ears, which are " + getCoveredInDescriptor(owner) + " [pc.earFullDescription(true)]" + (ear.isPierced() ? ", and which have been pierced" : "") + ".");
            else
                sb.append(" [npc.She] has a pair of oval-shaped, cow-like ears, which are " + getCoveredInDescriptor(owner) + " [npc.earFullDescription(true)]" + (ear.isPierced() ? ", and which have been pierced" : "") + ".");
            break;
        case ALLIGATOR_MORPH:
            if (owner.isPlayer())
                sb.append(" Your ears are an internal part of your head, and are covered by a fan of <span style='color:[pc.earColourHex];'>[pc.earColour] scales</span>." + (ear.isPierced() ? " They have been cleverly pierced so as to allow you to wear ear-specific jewellery." : ""));
            else
                sb.append(" [npc.Her] ears are an internal part of [npc.her] head, and are covered by a fan of <span style='color:[npc.earColourHex];'>[npc.earColour] scales</span>." + (ear.isPierced() ? " They have been cleverly pierced so as to allow [npc.herHim] to wear ear-specific jewellery." : ""));
            break;
        case SQUIRREL_MORPH:
            if (owner.isPlayer())
                sb.append(" You have a pair of " + (ear.isPierced() ? "pierced, " : "") + "rounded, squirrel-like ears, which are positioned high up on your head and are are " + getCoveredInDescriptor(owner) + " [pc.earFullDescription(true)].");
            else
                sb.append(" [npc.She] has a pair of " + (ear.isPierced() ? "pierced, " : "") + "rounded, squirrel-like ears, which are positioned high up on [npc.her] head and are " + getCoveredInDescriptor(owner) + " [npc.earFullDescription(true)].");
            break;
        case HORSE_MORPH:
            if (owner.isPlayer())
                sb.append(" You have a pair of " + (ear.isPierced() ? "pierced, " : "") + "upright, horse-like ears, which are positioned high up on your head and are are " + getCoveredInDescriptor(owner) + " [pc.earFullDescription(true)].");
            else
                sb.append(" [npc.She] has a pair of " + (ear.isPierced() ? "pierced, " : "") + "upright, horse-like ears, which are positioned high up on [npc.her] head and are " + getCoveredInDescriptor(owner) + " [npc.earFullDescription(true)].");
            break;
        case REINDEER_MORPH:
            if (owner.isPlayer())
                sb.append(" You have a pair of oval-shaped, reindeer-like ears, which are " + getCoveredInDescriptor(owner) + " [pc.earFullDescription(true)]" + (ear.isPierced() ? ", and which have been pierced" : "") + ".");
            else
                sb.append(" [npc.She] has a pair of oval-shaped, reindeer-like ears, which are " + getCoveredInDescriptor(owner) + " [npc.earFullDescription(true)]" + (ear.isPierced() ? ", and which have been pierced" : "") + ".");
            break;
        case HARPY:
            if (owner.isPlayer())
                sb.append(" Your ears are an internal part of your head, and are covered by a fan of <span style='color:[pc.earColourHex];'>[pc.earColour] feathers</span>." + (ear.isPierced() ? " They have been cleverly pierced so as to allow you to wear ear-specific jewellery." : ""));
            else
                sb.append(" [npc.Her] ears are an internal part of [npc.her] head, and are covered by a fan of <span style='color:[npc.earColourHex];'>[npc.earColour] feathers</span>." + (ear.isPierced() ? " They have been cleverly pierced so as to allow [npc.herHim] to wear ear-specific jewellery." : ""));
            break;
    }
    sb.append("</p>" + "<p>");
    if (Main.game.isFacialHairEnabled()) {
        if (owner.isPlayer()) {
            if (owner.getFacialHairType().getType() == BodyCoveringType.BODY_HAIR_SCALES_ALLIGATOR) {
                switch(owner.getFacialHair()) {
                    case ZERO_NONE:
                        if (!owner.isFeminine()) {
                            sb.append(" You don't have any trace of rough, stubbly " + owner.getFacialHairType().getName(owner) + " growing on your [pc.face].");
                        }
                        break;
                    case ONE_STUBBLE:
                        sb.append(" You have a stubbly patch of rough " + owner.getFacialHairType().getFullDescription(owner, true) + " growing on your [pc.face].");
                        break;
                    case TWO_MANICURED:
                        sb.append(" You have a small amount of rough " + owner.getFacialHairType().getFullDescription(owner, true) + " growing on your [pc.face], which resembles a beard.");
                        break;
                    case THREE_TRIMMED:
                        sb.append(" You have a rough patch of " + owner.getFacialHairType().getFullDescription(owner, true) + " growing on your [pc.face], which resembles a beard.");
                        break;
                    case FOUR_NATURAL:
                        sb.append(" You have a natural-looking rough patch of " + owner.getFacialHairType().getFullDescription(owner, true) + " growing on your [pc.face], which resembles a beard.");
                        break;
                    case FIVE_UNKEMPT:
                        sb.append(" You have an unkempt, rough patch of " + owner.getFacialHairType().getFullDescription(owner, true) + " growing on your [pc.face], which resembles a beard.");
                        break;
                    case SIX_BUSHY:
                        sb.append(" You have a thick, rough patch of " + owner.getFacialHairType().getFullDescription(owner, true) + " growing on your [pc.face], which resembles a beard.");
                        break;
                    case SEVEN_WILD:
                        sb.append(" You have a wild, rough patch of " + owner.getFacialHairType().getFullDescription(owner, true) + " growing on your [pc.face], which resembles a beard.");
                        break;
                }
            } else {
                switch(owner.getFacialHair()) {
                    case ZERO_NONE:
                        if (!owner.isFeminine()) {
                            sb.append(" You don't have any trace of facial " + owner.getFacialHairType().getName(owner) + ".");
                        }
                        break;
                    case ONE_STUBBLE:
                        sb.append(" You have a stubbly patch of " + owner.getFacialHairType().getFullDescription(owner, true) + " growing on your [pc.face].");
                        break;
                    case TWO_MANICURED:
                        sb.append(" You have a small amount of " + owner.getFacialHairType().getFullDescription(owner, true) + " growing on your [pc.face].");
                        break;
                    case THREE_TRIMMED:
                        sb.append(" You have a well-trimmed beard of " + owner.getFacialHairType().getFullDescription(owner, true) + " growing on your [pc.face].");
                        break;
                    case FOUR_NATURAL:
                        sb.append(" You have a natural-looking beard of " + owner.getFacialHairType().getFullDescription(owner, true) + " growing on your [pc.face].");
                        break;
                    case FIVE_UNKEMPT:
                        sb.append(" You have an unkempt, bushy beard of " + owner.getFacialHairType().getFullDescription(owner, true) + " growing on your [pc.face].");
                        break;
                    case SIX_BUSHY:
                        sb.append(" You have a thick, bushy beard of " + owner.getFacialHairType().getFullDescription(owner, true) + " growing on your [pc.face].");
                        break;
                    case SEVEN_WILD:
                        sb.append(" You have a wild, bushy beard of " + owner.getFacialHairType().getFullDescription(owner, true) + " growing on your [pc.face].");
                        break;
                }
            }
        } else {
            if (owner.getFacialHairType().getType() == BodyCoveringType.BODY_HAIR_SCALES_ALLIGATOR) {
                switch(owner.getFacialHair()) {
                    case ZERO_NONE:
                        if (!owner.isFeminine()) {
                            sb.append(" [npc.She] doesn't have any trace of rough, stubbly " + owner.getFacialHairType().getName(owner) + ".");
                        }
                        break;
                    case ONE_STUBBLE:
                        sb.append(" [npc.She] has a stubbly patch of " + owner.getFacialHairType().getFullDescription(owner, true) + " growing on [npc.her] [npc.face].");
                        break;
                    case TWO_MANICURED:
                        sb.append(" [npc.She] has a small, rough patch of " + owner.getFacialHairType().getFullDescription(owner, true) + " growing on [npc.her] [npc.face], which resembles a beard.");
                        break;
                    case THREE_TRIMMED:
                        sb.append(" [npc.She] has a rough patch of " + owner.getFacialHairType().getFullDescription(owner, true) + " growing on [npc.her] [npc.face], which resembles a beard.");
                        break;
                    case FOUR_NATURAL:
                        sb.append(" [npc.She] has a natural-looking rough patch of " + owner.getFacialHairType().getFullDescription(owner, true) + " growing on [npc.her] [npc.face], which resembles a beard.");
                        break;
                    case FIVE_UNKEMPT:
                        sb.append(" [npc.She] has a unkempt, rough patch of " + owner.getFacialHairType().getFullDescription(owner, true) + " growing on [npc.her] [npc.face], which resembles a beard.");
                        break;
                    case SIX_BUSHY:
                        sb.append(" [npc.She] has a thick, rough patch of " + owner.getFacialHairType().getFullDescription(owner, true) + " growing on [npc.her] [npc.face], which resembles a beard.");
                        break;
                    case SEVEN_WILD:
                        sb.append(" [npc.She] has a wild, rough patch of " + owner.getFacialHairType().getFullDescription(owner, true) + " growing on [npc.her] [npc.face], which resembles a beard.");
                        break;
                }
            } else {
                switch(owner.getFacialHair()) {
                    case ZERO_NONE:
                        if (!owner.isFeminine()) {
                            sb.append(" [npc.She] doesn't have any trace of facial " + owner.getFacialHairType().getName(owner) + ".");
                        }
                        break;
                    case ONE_STUBBLE:
                        sb.append(" [npc.She] has a stubbly patch of " + owner.getFacialHairType().getFullDescription(owner, true) + " growing on [npc.her] [npc.face].");
                        break;
                    case TWO_MANICURED:
                        sb.append(" [npc.She] has a small amount of " + owner.getFacialHairType().getFullDescription(owner, true) + " growing on [npc.her] [npc.face].");
                        break;
                    case THREE_TRIMMED:
                        sb.append(" [npc.She] has a well-trimmed beard of " + owner.getFacialHairType().getFullDescription(owner, true) + " growing on [npc.her] [npc.face].");
                        break;
                    case FOUR_NATURAL:
                        sb.append(" [npc.She] has a natural-looking beard of " + owner.getFacialHairType().getFullDescription(owner, true) + " growing on [npc.her] [npc.face].");
                        break;
                    case FIVE_UNKEMPT:
                        sb.append(" [npc.She] has a unkempt, bushy beard of " + owner.getFacialHairType().getFullDescription(owner, true) + " growing on [npc.her] [npc.face].");
                        break;
                    case SIX_BUSHY:
                        sb.append(" [npc.She] has a thick, bushy beard of " + owner.getFacialHairType().getFullDescription(owner, true) + " growing on [npc.her] [npc.face].");
                        break;
                    case SEVEN_WILD:
                        sb.append(" [npc.She] has a wild, bushy beard of " + owner.getFacialHairType().getFullDescription(owner, true) + " growing on [npc.her] [npc.face].");
                        break;
                }
            }
        }
    }
    // Mouth & lips:
    if (owner.isPlayer()) {
        sb.append(" You have [pc.lipSize], [pc.mouthColourPrimary(true)] [pc.lips]");
        if (owner.getLipstick().getPrimaryColour() != Colour.COVERING_NONE) {
            sb.append((owner.isPiercedLip() ? ", which have been pierced, and" : ", which") + " are currently " + getCoveredInDescriptor(owner) + " " + owner.getLipstick().getFullDescription(owner, true) + ".");
        } else {
            sb.append((owner.isPiercedLip() ? ", which have been pierced." : "."));
        }
        sb.append(" Your throat is [pc.mouthColourSecondary(true)] in colour.");
    } else {
        sb.append(" [npc.She] has [npc.lipSize], [npc.mouthColourPrimary(true)] [pc.lips]");
        if (owner.getLipstick().getPrimaryColour() != Colour.COVERING_NONE) {
            sb.append((owner.isPiercedLip() ? ", which have been pierced, and" : ", which") + " are currently " + getCoveredInDescriptor(owner) + " " + owner.getLipstick().getFullDescription(owner, true) + ".");
        } else {
            sb.append((owner.isPiercedLip() ? ", which have been pierced." : "."));
        }
        sb.append(" [npc.Her] throat is [npc.mouthColourSecondary(true)] in colour.");
    }
    // Throat modifiers:
    for (OrificeModifier om : OrificeModifier.values()) {
        if (owner.hasFaceOrificeModifier(om)) {
            if (owner.isPlayer()) {
                switch(om) {
                    case PUFFY:
                        sb.append(" Your [pc.lips] have swollen up to be far puffier than what would be considered normal.");
                        break;
                    case MUSCLE_CONTROL:
                        sb.append(" You have a series of internal muscles lining the inside of your throat, allowing you to expertly squeeze and grip down on any intruding object.");
                        break;
                    case RIBBED:
                        sb.append(" The inside of your throat is lined with sensitive, fleshy ribs, which grant you extra pleasure when stimulated.");
                        break;
                    case TENTACLED:
                        sb.append(" Your throat is filled with tiny little tentacles, which wriggle and squirm with a mind of their own.");
                        break;
                }
            } else {
                switch(om) {
                    case PUFFY:
                        sb.append(" [npc.Her] [npc.lips] have swollen up to be far puffier than what would be considered normal.");
                        break;
                    case MUSCLE_CONTROL:
                        sb.append(" [npc.She] has a series of internal muscles lining the inside of [npc.her] throat, allowing [npc.herHim] to expertly squeeze and grip down on any intruding object.");
                        break;
                    case RIBBED:
                        sb.append(" The inside of [npc.her] throat is lined with sensitive, fleshy ribs, which grant [npc.herHim] extra pleasure when stimulated.");
                        break;
                    case TENTACLED:
                        sb.append(" [npc.Her] throat is filled with tiny little tentacles, which wriggle and squirm with a mind of their own.");
                        break;
                }
            }
        }
    }
    // Tongue & blowjob:
    if (owner.isPlayer()) {
        sb.append(" Your mouth holds [pc.a_tongueLength], [pc.tongueColour(true)] [pc.tongue]" + (face.getTongue().isPierced() ? ", which has been pierced." : "."));
    } else {
        sb.append(" [npc.Her] mouth holds [npc.a_tongueLength], [npc.tongueColour(true)] [npc.tongue]" + (face.getTongue().isPierced() ? ", which has been pierced." : "."));
    }
    for (TongueModifier tm : TongueModifier.values()) {
        if (owner.hasTongueModifier(tm)) {
            if (owner.isPlayer()) {
                switch(tm) {
                    case RIBBED:
                        sb.append(" It's lined with hard, fleshy ribs, which are sure to grant extra pleasure to any orifice that they penetrate.");
                        break;
                    case TENTACLED:
                        sb.append(" A series of little tentacles coat its surface, which wriggle and squirm with a mind of their own.");
                        break;
                    case BIFURCATED:
                        sb.append(" Near the tip, it's split in two, leaving your tongue bifurcated, like a snake.");
                        break;
                }
            } else {
                switch(tm) {
                    case RIBBED:
                        sb.append(" It's lined with hard, fleshy ribs, which are sure to grant extra pleasure to any orifice that they penetrate.");
                        break;
                    case TENTACLED:
                        sb.append(" A series of little tentacles coat its surface, which wriggle and squirm with a mind of their own.");
                        break;
                    case BIFURCATED:
                        sb.append(" Near the tip, it's split in two, leaving [npc.her] tongue bifurcated, like a snake.");
                        break;
                }
            }
        }
    }
    if (owner.isPlayer()) {
        if (face.getMouth().getOrificeMouth().isVirgin()) {
            sb.append(" <span style='color:" + Colour.GENERIC_SEX.toWebHexString() + ";'>You've never given head before, so you don't know what you could fit down your throat.</span>");
        } else {
            switch(face.getMouth().getOrificeMouth().getCapacity().getMaximumSizeComfortableWithLube()) {
                // case NEGATIVE_UTILITY_VALUE:
                case ZERO_MICROSCOPIC:
                    sb.append(" [style.colourSex(You're terrible at giving head)], and struggle to fit the tip of even a tiny cock into your mouth without gagging.");
                    break;
                case ONE_TINY:
                    sb.append(" [style.colourSex(You're really bad at giving head)], and struggle to fit even a tiny cocks into your mouth without gagging.");
                    // sb.append(" You find anything larger than "+face.getRawCapacityValue()+" inches to be uncomfortable.");
                    break;
                case TWO_AVERAGE:
                    sb.append(" [style.colourSex(You're not great at giving head)], and anything larger than an average-sized human cock will cause you to gag.");
                    // sb.append(" You find anything larger than "+face.getRawCapacityValue()+" inches to be uncomfortable.");
                    break;
                case THREE_LARGE:
                    sb.append(" [style.colourSex(You're somewhat competent at giving head)], and can suppress your gag reflex enough to comfortably suck large cocks.");
                    // sb.append(" You find anything larger than "+face.getRawCapacityValue()+" inches to be uncomfortable.");
                    break;
                case FOUR_HUGE:
                    sb.append(" [style.colourSex(You're pretty good at giving head)], and can comfortably suck huge cocks without gagging.");
                    // sb.append(" You find anything larger than "+face.getRawCapacityValue()+" inches to be uncomfortable.");
                    break;
                case FIVE_ENORMOUS:
                    sb.append(" [style.colourSex(You're somewhat of an expert at giving head)], and can suck enormous cocks without too much difficulty.");
                    // sb.append(" You find anything larger than "+face.getRawCapacityValue()+" inches to be uncomfortable.");
                    break;
                case SIX_GIGANTIC:
                    sb.append(" [style.colourSex(You're amazing at giving head)], and can comfortably suck all but the most absurdly-sized of cocks with ease.");
                    // sb.append(" You find anything larger than "+face.getRawCapacityValue()+" inches to be uncomfortable.");
                    break;
                case SEVEN_STALLION:
                    sb.append(" [style.colourSex(You are)]" + " [style.colourLegendary(legendary)]" + " [style.colourSex(at giving head)]; it's almost as though your throat was purposefully designed to fit phallic objects of any size or shape.");
                    break;
                default:
                    break;
            }
            for (PenetrationType pt : PenetrationType.values()) {
                if (Main.game.getPlayer().getVirginityLoss(new SexType(SexParticipantType.CATCHER, pt, OrificeType.MOUTH)) != null && !Main.game.getPlayer().getVirginityLoss(new SexType(SexParticipantType.CATCHER, pt, OrificeType.MOUTH)).isEmpty()) {
                    sb.append(" <span style='color:" + Colour.GENERIC_ARCANE.toWebHexString() + ";'>The first time you performed oral sex was to " + Main.game.getPlayer().getVirginityLoss(new SexType(SexParticipantType.CATCHER, pt, OrificeType.MOUTH)) + ".</span>");
                    break;
                }
            }
        }
    } else {
        if (owner.getPlayerKnowsAreas().contains(CoverableArea.MOUTH)) {
            if (face.getMouth().getOrificeMouth().isVirgin()) {
                sb.append(" <span style='color:" + Colour.GENERIC_SEX.toWebHexString() + ";'>[npc.She]'s never given head before.</span>");
            } else {
                switch(face.getMouth().getOrificeMouth().getCapacity().getMaximumSizeComfortableWithLube()) {
                    // case NEGATIVE_UTILITY_VALUE:
                    case ZERO_MICROSCOPIC:
                        sb.append(" <span style='color:" + Colour.GENERIC_SEX.toWebHexString() + ";'>[npc.She]'s terrible at giving head</span>, and struggles to fit the tip of even the smallest of cocks into [npc.her] mouth without gagging.");
                        break;
                    case ONE_TINY:
                        sb.append(" <span style='color:" + Colour.GENERIC_SEX.toWebHexString() + ";'>[npc.She]'s really bad at giving head</span>, and struggles to fit even tiny cocks into [npc.her] mouth without gagging.");
                        // sb.append(" [npc.She] finds anything larger than "+face.getRawCapacityValue()+" inches to be uncomfortable.");
                        break;
                    case TWO_AVERAGE:
                        sb.append(" <span style='color:" + Colour.GENERIC_SEX.toWebHexString() + ";'>[npc.She]'s not great at giving head</span>, and anything larger than an average-sized human cock will cause [npc.her] to gag.");
                        // sb.append(" [npc.She] finds anything larger than "+face.getRawCapacityValue()+" inches to be uncomfortable.");
                        break;
                    case THREE_LARGE:
                        sb.append(" <span style='color:" + Colour.GENERIC_SEX.toWebHexString() + ";'>[npc.She]'s somewhat competent at giving head</span>, and can suppress [npc.her] gag reflex enough to comfortably suck large cocks.");
                        // sb.append(" [npc.She] finds anything larger than "+face.getRawCapacityValue()+" inches to be uncomfortable.");
                        break;
                    case FOUR_HUGE:
                        sb.append(" <span style='color:" + Colour.GENERIC_SEX.toWebHexString() + ";'>[npc.She]'s pretty good at giving head</span>, and can comfortably suck huge cocks without gagging.");
                        // sb.append(" [npc.She] finds anything larger than "+face.getRawCapacityValue()+" inches to be uncomfortable.");
                        break;
                    case FIVE_ENORMOUS:
                        sb.append(" <span style='color:" + Colour.GENERIC_SEX.toWebHexString() + ";'>[npc.She]'s somewhat of an expert at giving head</span>, and can suck enormous cocks without too much difficulty.");
                        // sb.append(" [npc.She] finds anything larger than "+face.getRawCapacityValue()+" inches to be uncomfortable.");
                        break;
                    case SIX_GIGANTIC:
                        sb.append(" <span style='color:" + Colour.GENERIC_SEX.toWebHexString() + ";'>[npc.She]'s amazing at giving head</span>, and can comfortably suck all but the most absurdly-sized of cocks with ease.");
                        // sb.append(" [npc.She] finds anything larger than "+face.getRawCapacityValue()+" inches to be uncomfortable.");
                        break;
                    case SEVEN_STALLION:
                        sb.append(" <span style='color:" + Colour.GENERIC_SEX.toWebHexString() + ";'>[npc.She] is</span>" + " <span style='color:" + Colour.GENERIC_EXCELLENT.toWebHexString() + ";'>legendary</span>" + " <span style='color:" + Colour.GENERIC_SEX.toWebHexString() + ";'>at giving head</span>;" + " it's almost as though [npc.her] throat was purposefully designed to fit phallic objects of any size or shape.");
                        break;
                    default:
                        break;
                }
            }
        } else {
            sb.append(" <span style='color:" + Colour.GENERIC_SEX.toWebHexString() + ";'>You don't know [npc.herHim] well enough to know how competent [npc.she] is at performing oral sex.</span>");
        }
    }
    sb.append("</p>");
    // Describe body:
    sb.append("<p>");
    if (owner.isPlayer()) {
        sb.append("Your torso has");
    } else {
        sb.append("[npc.Her] torso has");
    }
    if (femininity <= Femininity.MASCULINE_STRONG.getMaximumFemininity()) {
        sb.append(UtilText.returnStringAtRandom(" an <span style='color:" + Colour.MASCULINE_PLUS.toWebHexString() + ";'>extremely masculine</span> appearance", " a <span style='color:" + Colour.MASCULINE_PLUS.toWebHexString() + ";'>very masculine</span> appearance"));
    } else if (femininity <= Femininity.MASCULINE.getMaximumFemininity()) {
        sb.append(UtilText.returnStringAtRandom(" a <span style='color:" + Colour.MASCULINE.toWebHexString() + ";'>masculine</span> appearance", " a <span style='color:" + Colour.MASCULINE.toWebHexString() + ";'>boyish</span> appearance"));
    } else if (femininity <= Femininity.ANDROGYNOUS.getMaximumFemininity()) {
        sb.append(UtilText.returnStringAtRandom(" a very <span style='color:" + Colour.ANDROGYNOUS.toWebHexString() + ";'>androgynous</span> appearance"));
    } else if (femininity <= Femininity.FEMININE.getMaximumFemininity()) {
        sb.append(UtilText.returnStringAtRandom(" a <span style='color:" + Colour.FEMININE.toWebHexString() + ";'>feminine</span> appearance", " a <span style='color:" + Colour.FEMININE.toWebHexString() + ";'>pretty</span> appearance"));
    } else {
        sb.append(UtilText.returnStringAtRandom(" an <span style='color:" + Colour.FEMININE_PLUS.toWebHexString() + ";'>extremely feminine</span> appearance", " a <span style='color:" + Colour.FEMININE_PLUS.toWebHexString() + ";'>gorgeous</span> appearance", " a <span style='color:" + Colour.FEMININE_PLUS.toWebHexString() + ";'>jaw-droppingly beautiful</span> appearance"));
    }
    if (owner.isPlayer()) {
        sb.append(", and is " + getCoveredInDescriptor(owner) + " [pc.skinFullDescription(true)].");
    } else {
        sb.append(", and is " + getCoveredInDescriptor(owner) + " [npc.skinFullDescription(true)].");
    }
    if (owner.isPlayer()) {
        sb.append(" You have <span style='color:" + owner.getBodySize().getColour().toWebHexString() + ";'>[pc.a_bodySize]</span>, " + "<span style='color:" + owner.getMuscle().getColour().toWebHexString() + ";'>[pc.muscle]</span>" + " body, which gives you <span style='color:" + owner.getBodyShape().toWebHexStringColour() + ";'>[pc.a_bodyShape]</span> body shape.");
    } else {
        sb.append(" [npc.She] has <span style='color:" + BodySize.valueOf(getBodySize()).getColour().toWebHexString() + ";'>" + BodySize.valueOf(getBodySize()).getName(true) + "</span>, " + "<span style='color:" + Muscle.valueOf(getMuscle()).getColour().toWebHexString() + ";'>" + Muscle.valueOf(getMuscle()).getName(false) + "</span>" + " body, giving [npc.herHim] <span style='color:" + owner.getBodyShape().toWebHexStringColour() + ";'>[npc.a_bodyShape]</span> body shape.");
    }
    // Pregnancy:
    if (owner.hasStatusEffect(StatusEffect.PREGNANT_1)) {
        if (owner.isPlayer())
            sb.append(" Your belly is slightly swollen, and it's clear to anyone who takes a closer look that <span style='color:" + Colour.GENERIC_ARCANE.toWebHexString() + ";'>you're pregnant</span>.");
        else
            sb.append(" [npc.Her] belly is slightly swollen, and it's clear to anyone who takes a closer look that <span style='color:" + Colour.GENERIC_ARCANE.toWebHexString() + ";'>[npc.she]'s pregnant</span>.");
    } else if (owner.hasStatusEffect(StatusEffect.PREGNANT_2)) {
        if (owner.isPlayer())
            sb.append(" Your belly is heavily swollen, and it's clear to anyone who glances your way that <span style='color:" + Colour.GENERIC_ARCANE.toWebHexString() + ";'>you're pregnant</span>.");
        else
            sb.append(" [npc.Her] belly is heavily swollen, and it's clear to anyone who glances [npc.her] way that <span style='color:" + Colour.GENERIC_ARCANE.toWebHexString() + ";'>[npc.she]'s pregnant</span>.");
    } else if (owner.hasStatusEffect(StatusEffect.PREGNANT_3)) {
        if (owner.isPlayer())
            sb.append(" Your belly is massively swollen, and it's completely obvious to anyone who glances your way that" + " <span style='color:" + Colour.GENERIC_ARCANE.toWebHexString() + ";'>you're expecting to give birth very soon</span>.");
        else
            sb.append(" [npc.Her] belly is massively swollen, and it's completely obvious to anyone who glances [npc.her] way that" + " <span style='color:" + Colour.GENERIC_ARCANE.toWebHexString() + ";'>[npc.she]'s expecting to give birth very soon</span>.");
    }
    sb.append("</p>");
    // Breasts:
    sb.append("<p>");
    Breast viewedBreast = breast;
    if (Main.game.getPlayer().hasIngestedPsychoactiveFluidType(FluidTypeBase.MILK)) {
        viewedBreast = new Breast(breast.getType(), breast.getShape(), (int) (breast.getRawSizeValue() * (1.75f)), (int) ((breast.getRawMilkStorageValue() + 100) * (2.25f)), breast.getRows(), breast.getNipples().getNippleSizeValue(), breast.getNipples().getNippleShape(), breast.getNipples().getAreolaeSizeValue(), breast.getNippleCountPerBreast(), breast.getNipples().getOrificeNipples().getRawCapacityValue(), breast.getNipples().getOrificeNipples().getElasticity().getValue(), breast.getNipples().getOrificeNipples().getPlasticity().getValue(), breast.getNipples().getOrificeNipples().isVirgin());
        sb.append(" <i style='color:" + Colour.PSYCHOACTIVE.toWebHexString() + ";'>The psychoactive milk you recently ingested is causing your view of " + (owner.isPlayer() ? "your" : "[npc.name]'s") + " breasts to be distorted!</i>");
    }
    if (owner.isPlayer()) {
        if (viewedBreast.getRawSizeValue() > 0) {
            sb.append(" You have " + Util.intToString(viewedBreast.getRows()) + " pair" + (viewedBreast.getRows() == 1 ? "" : "s") + " of " + viewedBreast.getSize().getDescriptor() + " [pc.breasts]");
            if (viewedBreast.getRows() == 1) {
                if (viewedBreast.getSize().isTrainingBraSize()) {
                    sb.append(", which fit comfortably into a training bra.");
                } else {
                    sb.append(", which fit comfortably into " + UtilText.generateSingularDeterminer(viewedBreast.getSize().getCupSizeName()) + " " + viewedBreast.getSize().getCupSizeName() + "-cup bra.");
                }
            } else if (viewedBreast.getRows() == 2) {
                if (viewedBreast.getSize().isTrainingBraSize()) {
                    sb.append(", with your top pair fitting comfortably into a training bra, and the pair below being slightly smaller.");
                } else {
                    sb.append(", with your top pair fitting comfortably into " + UtilText.generateSingularDeterminer(viewedBreast.getSize().getCupSizeName()) + " " + viewedBreast.getSize().getCupSizeName() + "-cup bra, and the pair below being slightly smaller.");
                }
            } else if (viewedBreast.getRows() > 2) {
                if (viewedBreast.getSize().isTrainingBraSize()) {
                    sb.append(", with your top pair fitting comfortably into a training bra, and the pairs below each being slightly smaller than the ones above.");
                } else {
                    sb.append(", with your top pair fitting comfortably into " + UtilText.generateSingularDeterminer(viewedBreast.getSize().getCupSizeName()) + " " + viewedBreast.getSize().getCupSizeName() + "-cup bra, and the pairs below each being slightly smaller than the ones above.");
                }
            }
        } else {
            sb.append(" You have a completely flat chest");
            if (viewedBreast.getRows() == 1) {
                sb.append(", with a single pair of pecs.");
            } else {
                sb.append(", with " + Util.intToString(viewedBreast.getRows()) + " pairs of pecs.");
            }
        }
    } else {
        if (viewedBreast.getRawSizeValue() > 0) {
            sb.append(" [npc.She] has " + Util.intToString(viewedBreast.getRows()) + " pair" + (viewedBreast.getRows() == 1 ? "" : "s") + " of " + viewedBreast.getSize().getDescriptor() + " [npc.breasts]");
            if (viewedBreast.getRows() == 1) {
                if (viewedBreast.getSize().isTrainingBraSize()) {
                    sb.append(", which fit comfortably into a training bra.");
                } else {
                    sb.append(", which fit comfortably into " + UtilText.generateSingularDeterminer(viewedBreast.getSize().getCupSizeName()) + " " + viewedBreast.getSize().getCupSizeName() + "-cup bra.");
                }
            } else if (viewedBreast.getRows() == 2) {
                if (viewedBreast.getSize().isTrainingBraSize()) {
                    sb.append(", with [npc.her] top pair fitting comfortably into a training bra, and the pair below being slightly smaller.");
                } else {
                    sb.append(", with [npc.her] top pair fitting comfortably into " + UtilText.generateSingularDeterminer(viewedBreast.getSize().getCupSizeName()) + " " + viewedBreast.getSize().getCupSizeName() + "-cup bra, and the pair below being slightly smaller.");
                }
            } else if (viewedBreast.getRows() > 2) {
                if (viewedBreast.getSize().isTrainingBraSize()) {
                    sb.append(", with [npc.her] top pair fitting comfortably into a training bra, and the pairs below each being slightly smaller than the ones above.");
                } else {
                    sb.append(", with [npc.her] top pair fitting comfortably into " + UtilText.generateSingularDeterminer(viewedBreast.getSize().getCupSizeName()) + " " + viewedBreast.getSize().getCupSizeName() + "-cup bra, and the pairs below each being slightly smaller than the ones above.");
                }
            }
        } else {
            sb.append(" [npc.She] has a completely flat chest");
            if (viewedBreast.getRows() == 1) {
                sb.append(", with a single pair of pecs.");
            } else {
                sb.append(", with " + Util.intToString(viewedBreast.getRows()) + " pairs of pecs.");
            }
        }
    }
    // Nipples & piercings
    sb.append(" " + getBreastDescription(owner, viewedBreast));
    sb.append("</p>");
    // Arms and legs:
    sb.append("<p>");
    // Arms:
    String armDeterminer = "a pair of";
    if (arm.getArmRows() == 3) {
        armDeterminer = "three pairs of";
    } else if (arm.getArmRows() == 2) {
        armDeterminer = "two pairs of";
    }
    switch(arm.getType()) {
        case HUMAN:
            if (owner.isPlayer())
                sb.append("You have " + armDeterminer + " normal human arms and hands, which are " + getCoveredInDescriptor(owner) + " [pc.armFullDescription(true)].");
            else
                sb.append("[npc.She] has " + armDeterminer + " normal human arms and hands, which are " + getCoveredInDescriptor(owner) + " [npc.armFullDescription(true)].");
            break;
        case ANGEL:
            if (owner.isPlayer())
                sb.append("You have " + armDeterminer + " human-like arms and hands, which are " + getCoveredInDescriptor(owner) + " [pc.armFullDescription(true)].");
            else
                sb.append("[npc.She] has " + armDeterminer + " human-like arms and hands, which are " + getCoveredInDescriptor(owner) + " [npc.armFullDescription(true)].");
            break;
        case DEMON_COMMON:
            if (owner.isPlayer())
                sb.append("You have " + armDeterminer + " slender, human-looking arms and hands, which are " + getCoveredInDescriptor(owner) + " [pc.armFullDescription(true)].");
            else
                sb.append("[npc.She] has " + armDeterminer + " slender human-looking arms and hands, which are " + getCoveredInDescriptor(owner) + " [npc.armFullDescription(true)].");
            break;
        case DOG_MORPH:
            if (owner.isPlayer())
                sb.append("You have " + armDeterminer + " arms, which are " + getCoveredInDescriptor(owner) + " [pc.armFullDescription(true)]." + " Your hands are formed into anthropomorphic, dog-like hands, complete with little blunt claws and leathery pads.");
            else
                sb.append("[npc.She] has " + armDeterminer + " arms, which are " + getCoveredInDescriptor(owner) + " [npc.armFullDescription(true)]." + " [npc.Her] hands are formed into anthropomorphic, dog-like hands, complete with little blunt claws and leathery pads.");
            break;
        case ALLIGATOR_MORPH:
            if (owner.isPlayer())
                sb.append("You have " + armDeterminer + " arms, which are " + getCoveredInDescriptor(owner) + " [pc.armFullDescription(true)]." + " Your hands are formed into anthropomorphic, alligator-like hands, complete with little claws.");
            else
                sb.append("[npc.She] has " + armDeterminer + " arms, which are " + getCoveredInDescriptor(owner) + " [npc.armFullDescription(true)]." + " [npc.Her] hands are formed into anthropomorphic, alligator-like hands, complete with little claws.");
            break;
        case LYCAN:
            if (owner.isPlayer())
                sb.append("You have " + armDeterminer + " arms, which are " + getCoveredInDescriptor(owner) + " [pc.armFullDescription(true)]." + " Your hands are formed into anthropomorphic, wolf-like hands, complete with sharp claws and tough leathery pads.");
            else
                sb.append("[npc.She] has " + armDeterminer + " arms, which are " + getCoveredInDescriptor(owner) + " [npc.armFullDescription(true)]." + " [npc.Her] hands are formed into anthropomorphic, wolf-like hands, complete with sharp claws and tough leathery pads.");
            break;
        case CAT_MORPH:
            if (owner.isPlayer())
                sb.append("You have " + armDeterminer + " arms, which are " + getCoveredInDescriptor(owner) + " [pc.armFullDescription(true)]." + " Your hands are formed into anthropomorphic, cat-like hands, complete with retractable claws and pink pads.");
            else
                sb.append("[npc.She] has " + armDeterminer + " arms, which are " + getCoveredInDescriptor(owner) + " [npc.armFullDescription(true)]." + " [npc.Her] hands are formed into anthropomorphic, cat-like hands, complete with retractable claws and pink pads.");
            break;
        case COW_MORPH:
            if (owner.isPlayer())
                sb.append("You have " + armDeterminer + " arms, which are " + getCoveredInDescriptor(owner) + " [pc.armFullDescription(true)]." + " Your hands, while human in shape, have tough little hoof-like nails.");
            else
                sb.append("[npc.She] has " + armDeterminer + " arms, which are " + getCoveredInDescriptor(owner) + " [npc.armFullDescription(true)]." + " [npc.Her] hands, while human in shape, have tough little hoof-like nails.");
            break;
        case SQUIRREL_MORPH:
            if (owner.isPlayer())
                sb.append("You have " + armDeterminer + " arms, which are " + getCoveredInDescriptor(owner) + " [pc.armFullDescription(true)]." + " Your hands are formed into anthropomorphic, squirrel-like hands, complete with claws.");
            else
                sb.append("[npc.She] has " + armDeterminer + " arms, which are " + getCoveredInDescriptor(owner) + " [npc.armFullDescription(true)]." + " [npc.Her] hands are formed into anthropomorphic, squirrel-like hands, complete with claws.");
            break;
        case HORSE_MORPH:
            if (owner.isPlayer())
                sb.append("You have " + armDeterminer + " arms, which are " + getCoveredInDescriptor(owner) + " [pc.armFullDescription(true)]." + " Your hands, while human in shape, have tough little hoof-like nails.");
            else
                sb.append("[npc.She] has " + armDeterminer + " arms, which are " + getCoveredInDescriptor(owner) + " [npc.armFullDescription(true)]." + " [npc.Her] hands, while human in shape, have tough little hoof-like nails.");
            break;
        case REINDEER_MORPH:
            if (owner.isPlayer())
                sb.append("You have " + armDeterminer + " arms, which are " + getCoveredInDescriptor(owner) + " [pc.armFullDescription(true)]." + " Your hands, while human in shape, have tough little hoof-like nails.");
            else
                sb.append("[npc.She] has " + armDeterminer + " arms, which are " + getCoveredInDescriptor(owner) + " [npc.armFullDescription(true)]." + " [npc.Her] hands, while human in shape, have tough little hoof-like nails.");
            break;
        case HARPY:
            if (owner.isPlayer())
                sb.append("Your arms have transformed into " + armDeterminer + " huge wings, and are " + getCoveredInDescriptor(owner) + " beautiful [pc.armFullDescription(true)]." + " Where your hands should be, you have two feathered forefingers and a thumb, each of which ends in a little blunt claw." + " Although slightly less dexterous than a human hand, you're still able to use your remaining digits to form a hand-like grip.");
            else
                sb.append("In place of arms and hands, [npc.she] has " + armDeterminer + " huge wings, which are " + getCoveredInDescriptor(owner) + " beautiful [npc.armFullDescription(true)]." + " Where [npc.her] hands should be, [npc.she] has two feathered forefingers and a thumb, each of which ends in a little blunt claw." + " Although slightly less dexterous than a human hand, [npc.she]'s still able to use [npc.her] digits to form a hand-like grip.");
            break;
        default:
            break;
    }
    if (owner.isPlayer()) {
        if (owner.getHandNailPolish().getPrimaryColour() != Colour.COVERING_NONE) {
            if (owner.getArmType() == ArmType.HARPY) {
                sb.append(" The little claw on your thumb has been painted in " + owner.getCovering(BodyCoveringType.MAKEUP_NAIL_POLISH_HANDS).getFullDescription(owner, true) + ".");
            } else {
                sb.append(" Your fingernails have been painted in " + owner.getCovering(BodyCoveringType.MAKEUP_NAIL_POLISH_HANDS).getFullDescription(owner, true) + ".");
            }
        }
    } else {
        if (owner.getHandNailPolish().getPrimaryColour() != Colour.COVERING_NONE) {
            if (owner.getArmType() == ArmType.HARPY) {
                sb.append(" The little claw on [npc.her] thumb has been painted in " + owner.getCovering(BodyCoveringType.MAKEUP_NAIL_POLISH_HANDS).getFullDescription(owner, true) + ".");
            } else {
                sb.append(" [npc.Her] fingernails have been painted in " + owner.getCovering(BodyCoveringType.MAKEUP_NAIL_POLISH_HANDS).getFullDescription(owner, true) + ".");
            }
        }
    }
    if (Main.game.isBodyHairEnabled()) {
        if (owner.isPlayer()) {
            if (owner.getUnderarmHairType().getType() == BodyCoveringType.BODY_HAIR_SCALES_ALLIGATOR) {
                switch(owner.getUnderarmHair()) {
                    case ZERO_NONE:
                        sb.append(" There's no trace of any rough " + owner.getUnderarmHairType().getName(owner) + " in your armpits.");
                        break;
                    case ONE_STUBBLE:
                        sb.append(" You have a small, rough patch of " + owner.getUnderarmHairType().getFullDescription(owner, true) + " in each of your armpits.");
                        break;
                    case TWO_MANICURED:
                        sb.append(" You have a rough patch of " + owner.getUnderarmHairType().getFullDescription(owner, true) + " in each of your armpits.");
                        break;
                    case THREE_TRIMMED:
                        sb.append(" You have a rough patch of " + owner.getUnderarmHairType().getFullDescription(owner, true) + " in each of your armpits.");
                        break;
                    case FOUR_NATURAL:
                        sb.append(" You have a natural amount of rough " + owner.getUnderarmHairType().getFullDescription(owner, true) + " in each of your armpits.");
                        break;
                    case FIVE_UNKEMPT:
                        sb.append(" You have an unkempt mass of rough " + owner.getUnderarmHairType().getFullDescription(owner, true) + " in each of your armpits.");
                        break;
                    case SIX_BUSHY:
                        sb.append(" You have a thick, rough mass of " + owner.getUnderarmHairType().getFullDescription(owner, true) + " in each of your armpits.");
                        break;
                    case SEVEN_WILD:
                        sb.append(" You have a wild, rough mass of " + owner.getUnderarmHairType().getFullDescription(owner, true) + " in each of your armpits.");
                        break;
                }
            } else {
                switch(owner.getUnderarmHair()) {
                    case ZERO_NONE:
                        sb.append(" There is no trace of any " + owner.getUnderarmHairType().getName(owner) + " in your armpits.");
                        break;
                    case ONE_STUBBLE:
                        sb.append(" You have a stubbly patch of " + owner.getUnderarmHairType().getFullDescription(owner, true) + " in each of your armpits.");
                        break;
                    case TWO_MANICURED:
                        sb.append(" You have a well-manicured patch of " + owner.getUnderarmHairType().getFullDescription(owner, true) + " in each of your armpits.");
                        break;
                    case THREE_TRIMMED:
                        sb.append(" You have a trimmed patch of " + owner.getUnderarmHairType().getFullDescription(owner, true) + " in each of your armpits.");
                        break;
                    case FOUR_NATURAL:
                        sb.append(" You have a natural amount of " + owner.getUnderarmHairType().getFullDescription(owner, true) + " in each of your armpits.");
                        break;
                    case FIVE_UNKEMPT:
                        sb.append(" You have an unkempt mass of " + owner.getUnderarmHairType().getFullDescription(owner, true) + " in each of your armpits.");
                        break;
                    case SIX_BUSHY:
                        sb.append(" You have a thick, bushy mass of " + owner.getUnderarmHairType().getFullDescription(owner, true) + " in each of your armpits.");
                        break;
                    case SEVEN_WILD:
                        sb.append(" You have a wild, bushy mass of " + owner.getUnderarmHairType().getFullDescription(owner, true) + " in each of your armpits.");
                        break;
                }
            }
        } else {
            if (owner.getUnderarmHairType().getType() == BodyCoveringType.BODY_HAIR_SCALES_ALLIGATOR) {
                switch(owner.getUnderarmHair()) {
                    case ZERO_NONE:
                        sb.append(" There is no trace of any rough " + owner.getUnderarmHairType().getName(owner) + " in [npc.her] armpits.");
                        break;
                    case ONE_STUBBLE:
                        sb.append(" [npc.She] has a rough patch of " + owner.getUnderarmHairType().getFullDescription(owner, true) + " in each of [npc.her] armpits.");
                        break;
                    case TWO_MANICURED:
                        sb.append(" [npc.She] has a rough patch of " + owner.getUnderarmHairType().getFullDescription(owner, true) + " in each of [npc.her] armpits.");
                        break;
                    case THREE_TRIMMED:
                        sb.append(" [npc.She] has a rough patch of " + owner.getUnderarmHairType().getFullDescription(owner, true) + " in each of [npc.her] armpits.");
                        break;
                    case FOUR_NATURAL:
                        sb.append(" [npc.She] has a natural amount of rough " + owner.getUnderarmHairType().getFullDescription(owner, true) + " in each of [npc.her] armpits.");
                        break;
                    case FIVE_UNKEMPT:
                        sb.append(" [npc.She] has a unkempt mass of rough " + owner.getUnderarmHairType().getFullDescription(owner, true) + " in each of [npc.her] armpits.");
                        break;
                    case SIX_BUSHY:
                        sb.append(" [npc.She] has a thick, rough mass of " + owner.getUnderarmHairType().getFullDescription(owner, true) + " in each of [npc.her] armpits.");
                        break;
                    case SEVEN_WILD:
                        sb.append(" [npc.She] has a wild, rough mass of " + owner.getUnderarmHairType().getFullDescription(owner, true) + " in each of [npc.her] armpits.");
                        break;
                }
            } else {
                switch(owner.getUnderarmHair()) {
                    case ZERO_NONE:
                        sb.append(" There is no trace of any " + owner.getUnderarmHairType().getName(owner) + " in [npc.her] armpits.");
                        break;
                    case ONE_STUBBLE:
                        sb.append(" [npc.She] has a stubbly patch of " + owner.getUnderarmHairType().getFullDescription(owner, true) + " in each of [npc.her] armpits.");
                        break;
                    case TWO_MANICURED:
                        sb.append(" [npc.She] has a well-manicured patch of " + owner.getUnderarmHairType().getFullDescription(owner, true) + " in each of [npc.her] armpits.");
                        break;
                    case THREE_TRIMMED:
                        sb.append(" [npc.She] has a trimmed patch of " + owner.getUnderarmHairType().getFullDescription(owner, true) + " in each of [npc.her] armpits.");
                        break;
                    case FOUR_NATURAL:
                        sb.append(" [npc.She] has a natural amount of " + owner.getUnderarmHairType().getFullDescription(owner, true) + " in each of [npc.her] armpits.");
                        break;
                    case FIVE_UNKEMPT:
                        sb.append(" [npc.She] has a unkempt mass of " + owner.getUnderarmHairType().getFullDescription(owner, true) + " in each of [npc.her] armpits.");
                        break;
                    case SIX_BUSHY:
                        sb.append(" [npc.She] has a thick, bushy mass of " + owner.getUnderarmHairType().getFullDescription(owner, true) + " in each of [npc.her] armpits.");
                        break;
                    case SEVEN_WILD:
                        sb.append(" [npc.She] has a wild, bushy mass of " + owner.getUnderarmHairType().getFullDescription(owner, true) + " in each of [npc.her] armpits.");
                        break;
                }
            }
        }
    }
    sb.append("</br>");
    // Legs:
    switch(leg.getType()) {
        case HUMAN:
            if (owner.isPlayer())
                sb.append("You have a pair of human legs and feet, which are " + getCoveredInDescriptor(owner) + " [pc.legFullDescription(true)].");
            else
                sb.append("[npc.Her] legs and feet are human, and are " + getCoveredInDescriptor(owner) + " [npc.legFullDescription(true)].");
            break;
        case ANGEL:
            if (owner.isPlayer())
                sb.append("Your legs and feet are human in shape, but are " + getCoveredInDescriptor(owner) + " [pc.legFullDescription(true)].");
            else
                sb.append("[npc.Her] legs and feet are human in shape, but are " + getCoveredInDescriptor(owner) + " [npc.legFullDescription(true)].");
            break;
        case DEMON_COMMON:
            if (owner.isPlayer())
                sb.append("Your legs and feet are human in shape, but are " + getCoveredInDescriptor(owner) + " [pc.legFullDescription(true)].");
            else
                sb.append("[npc.Her] legs and feet are human in shape, but are " + getCoveredInDescriptor(owner) + " [npc.legFullDescription(true)].");
            break;
        case DOG_MORPH:
            if (owner.isPlayer())
                sb.append("Your legs are " + getCoveredInDescriptor(owner) + " [pc.legFullDescription(true)]," + " and your feet are formed into anthropomorphic dog-like paws, complete with little blunt claws and leathery pads.");
            else
                sb.append("[npc.Her] legs are " + getCoveredInDescriptor(owner) + " [npc.legFullDescription(true)]," + " and [npc.her] feet are formed into anthropomorphic dog-like paws, complete with little blunt claws and leathery pads.");
            break;
        case LYCAN:
            if (owner.isPlayer())
                sb.append("Your legs are " + getCoveredInDescriptor(owner) + " [pc.legFullDescription(true)]," + " and your feet are formed into anthropomorphic wolf-like paws, complete with sharp claws and tough leathery pads.");
            else
                sb.append("[npc.Her] legs are " + getCoveredInDescriptor(owner) + " [npc.legFullDescription(true)]," + " and [npc.her] feet are formed into anthropomorphic wolf-like paws, complete with sharp claws and tough leathery pads.");
            break;
        case ALLIGATOR_MORPH:
            if (owner.isPlayer())
                sb.append("Your legs are " + getCoveredInDescriptor(owner) + " [pc.legFullDescription(true)]," + " and your feet are formed into anthropomorphic alligator-like feet, complete with sharp claws.");
            else
                sb.append("[npc.Her] legs are " + getCoveredInDescriptor(owner) + " [npc.legFullDescription(true)]," + " and [npc.her] feet are formed into anthropomorphic alligator-like feet, complete with sharp claws.");
            break;
        case CAT_MORPH:
            if (owner.isPlayer())
                sb.append("Your legs are " + getCoveredInDescriptor(owner) + " [pc.legFullDescription(true)]," + " and your feet are formed into anthropomorphic cat-like paws, complete with retractable claws and pink pads.");
            else
                sb.append("[npc.Her] legs are " + getCoveredInDescriptor(owner) + " [npc.legFullDescription(true)]," + " and [npc.her] feet are formed into anthropomorphic cat-like paws, complete with retractable claws and pink pads.");
            break;
        case SQUIRREL_MORPH:
            if (owner.isPlayer())
                sb.append("Your legs are " + getCoveredInDescriptor(owner) + " [pc.legFullDescription(true)]," + " and your feet are formed into anthropomorphic squirrel-like paws, complete with claws and pink pads.");
            else
                sb.append("[npc.Her] legs are " + getCoveredInDescriptor(owner) + " [npc.legFullDescription(true)]," + " and [npc.her] feet are formed into anthropomorphic squirrel-like paws, complete with claws and pink pads.");
            break;
        case HORSE_MORPH:
            if (owner.isPlayer())
                sb.append("Your legs are " + getCoveredInDescriptor(owner) + " [pc.legFullDescription(true)]," + " and your feet are formed into anthropomorphic horse-like hooves.");
            else
                sb.append("[npc.Her] legs are " + getCoveredInDescriptor(owner) + " [npc.legFullDescription(true)]," + " and [npc.her] feet are formed into anthropomorphic horse-like hooves.");
            break;
        case REINDEER_MORPH:
            if (owner.isPlayer())
                sb.append("Your legs are " + getCoveredInDescriptor(owner) + " <span style='color:[pc.legColourHex];'>[pc.legColour] [pc.legSkin]</span>," + " and your feet are formed into anthropomorphic reindeer-like hooves.");
            else
                sb.append("[npc.Her] legs are " + getCoveredInDescriptor(owner) + " <span style='color:[npc.legColourHex];'>[npc.legColour] [npc.legSkin]</span>," + " and [npc.her] feet are formed into anthropomorphic reindeer-like hooves.");
            break;
        case COW_MORPH:
            if (owner.isPlayer())
                sb.append("Your legs are " + getCoveredInDescriptor(owner) + " [pc.legFullDescription(true)]," + " and your feet are formed into anthropomorphic cow-like hooves.");
            else
                sb.append("[npc.Her] legs are " + getCoveredInDescriptor(owner) + " [npc.legFullDescription(true)]," + " and [npc.her] feet are formed into anthropomorphic cow-like hooves.");
            break;
        case HARPY:
            if (owner.isPlayer())
                sb.append("Your upper thighs are " + getCoveredInDescriptor(owner) + " [pc.legFullDescription(true)], which transition into leathery bird-like skin just above your knee." + " While your legs still retain a human-like shape, your feet have transformed into bird-like talons.");
            else
                sb.append("[npc.Her] upper thighs are " + getCoveredInDescriptor(owner) + " [npc.legFullDescription(true)], which transition into leathery bird-like skin just above [npc.her] knee." + " While [npc.her] legs still retain a human-like shape, [npc.her] feet have transformed into bird-like talons.");
            break;
        default:
            break;
    }
    if (owner.isPlayer()) {
        if (owner.getFootNailPolish().getPrimaryColour() != Colour.COVERING_NONE) {
            if (owner.getLegType() == LegType.HARPY) {
                sb.append(" The claws on your talons have been painted in " + owner.getCovering(BodyCoveringType.MAKEUP_NAIL_POLISH_FEET).getFullDescription(owner, true) + ".");
            } else if (owner.getLegType() == LegType.HORSE_MORPH) {
                sb.append(" Your hooves have been painted in " + owner.getCovering(BodyCoveringType.MAKEUP_NAIL_POLISH_FEET).getFullDescription(owner, true) + ".");
            } else if (owner.getLegType() == LegType.COW_MORPH) {
                sb.append(" Your hooves have been painted in " + owner.getCovering(BodyCoveringType.MAKEUP_NAIL_POLISH_FEET).getFullDescription(owner, true) + ".");
            } else {
                sb.append(" Your toenails have been painted in " + owner.getCovering(BodyCoveringType.MAKEUP_NAIL_POLISH_FEET).getFullDescription(owner, true) + ".");
            }
        }
    } else {
        if (owner.getFootNailPolish().getPrimaryColour() != Colour.COVERING_NONE) {
            if (owner.getLegType() == LegType.HARPY) {
                sb.append(" The claws on [npc.her] talons have been painted in " + owner.getCovering(BodyCoveringType.MAKEUP_NAIL_POLISH_FEET).getFullDescription(owner, true) + ".");
            } else if (owner.getLegType() == LegType.HORSE_MORPH) {
                sb.append(" [npc.Her] hooves have been painted in " + owner.getCovering(BodyCoveringType.MAKEUP_NAIL_POLISH_FEET).getFullDescription(owner, true) + ".");
            } else if (owner.getLegType() == LegType.COW_MORPH) {
                sb.append(" [npc.Her] hooves have been painted in " + owner.getCovering(BodyCoveringType.MAKEUP_NAIL_POLISH_FEET).getFullDescription(owner, true) + ".");
            } else {
                sb.append(" [npc.Her] toenails have been painted in " + owner.getCovering(BodyCoveringType.MAKEUP_NAIL_POLISH_FEET).getFullDescription(owner, true) + ".");
            }
        }
    }
    sb.append("</br>");
    if (owner.isPlayer()) {
        sb.append(" All of your limbs ");
    } else {
        sb.append(" All of [npc.her] limbs ");
    }
    if (femininity <= Femininity.MASCULINE_STRONG.getMaximumFemininity()) {
        sb.append(UtilText.returnStringAtRandom("<span style='color:" + Colour.MASCULINE_PLUS.toWebHexString() + ";'>have a very masculine shape to them</span>."));
    } else if (femininity <= Femininity.MASCULINE.getMaximumFemininity()) {
        sb.append(UtilText.returnStringAtRandom("<span style='color:" + Colour.MASCULINE.toWebHexString() + ";'>have a masculine shape to them</span>."));
    } else if (femininity <= Femininity.ANDROGYNOUS.getMaximumFemininity()) {
        sb.append("<span style='color:" + Colour.ANDROGYNOUS.toWebHexString() + ";'>look quite androgynous, and could easily belong to either a male or female</span>.");
    } else if (femininity <= Femininity.FEMININE.getMaximumFemininity()) {
        sb.append(UtilText.returnStringAtRandom("<span style='color:" + Colour.FEMININE.toWebHexString() + ";'>are slender and feminine-looking</span>."));
    } else {
        sb.append(UtilText.returnStringAtRandom("<span style='color:" + Colour.FEMININE_PLUS.toWebHexString() + ";'>have an extremely feminine shape to them</span>."));
    }
    sb.append("</p>");
    // Tail, wings & ass:
    if (wing.getType() != WingType.NONE || tail.getType() != TailType.NONE) {
        sb.append("<p>");
        // Wing:
        switch(wing.getType()) {
            case DEMON_COMMON:
                if (owner.isPlayer()) {
                    sb.append("Growing from your shoulder-blades, you have a pair of [pc.wingSize] bat-like wings.");
                } else {
                    sb.append("Growing from [npc.her] shoulder-blades, [npc.she] has a pair of [npc.wingSize] bat-like wings.");
                }
                break;
            case ANGEL:
                if (owner.isPlayer()) {
                    sb.append("Growing from your shoulder-blades, you have [pc.a_wingSize] pair of white feathered wings.");
                } else {
                    sb.append("Growing from [npc.her] shoulder-blades, [npc.she] has [pc.a_wingSize] pair of white feathered wings.");
                }
                break;
            default:
                break;
        }
        if (wing.getType().allowsFlight()) {
            if (wing.getSizeValue() >= WingSize.TWO_AVERAGE.getValue()) {
                if (owner.isPlayer()) {
                    sb.append(" They are large enough that they can be used to allow you to fly.");
                } else {
                    sb.append(" They are large enough that they can be used to allow [npc.her] to fly.");
                }
            } else {
                if (owner.isPlayer()) {
                    sb.append(" They aren't large enough to allow you to fly.");
                } else {
                    sb.append(" They aren't large enough to allow [npc.her] to fly.");
                }
            }
        } else if (wing.getType() != WingType.NONE) {
            sb.append(" They are entirely incapable of flight.");
        }
        if (tail.getType() != TailType.NONE) {
            if (owner.isPlayer()) {
                sb.append(" Growing out from just above your ass, you have ");
            } else {
                sb.append(" Growing out from just above [npc.her] ass, [npc.she] has ");
            }
        }
        if (owner.getTailCount() == 1) {
            switch(owner.getTailType()) {
                case CAT_MORPH:
                    if (owner.isPlayer()) {
                        sb.append("a furry, [pc.tailColour(true)] cat-like tail, which you can control well enough to grant you significantly improved balance.");
                    } else {
                        sb.append("a furry, [npc.tailColour(true)] cat-like tail, which [npc.she] can control well enough to grant [npc.herHim] significantly improved balance.");
                    }
                    break;
                case DEMON_COMMON:
                    if (owner.isPlayer()) {
                        sb.append("a spaded, [pc.tailColour(true)] demonic tail, over which you have complete control, and you can easily use it to grip and hold objects.");
                    } else {
                        sb.append("a spaded, [npc.tailColour(true)] demonic tail, over which [npc.she] has complete control, and [npc.she] can easily use it to grip and hold objects.");
                    }
                    break;
                case IMP:
                    if (owner.isPlayer()) {
                        sb.append("a spaded, [pc.tailColour(true)] impish tail, over which you have complete control, and you can easily use it to grip and hold objects.");
                    } else {
                        sb.append("a spaded, [npc.tailColour(true)] impish tail, over which [npc.she] has complete control, and [npc.she] can easily use it to grip and hold objects.");
                    }
                    break;
                case DOG_MORPH:
                    if (owner.isPlayer()) {
                        sb.append("a furry, [pc.tailColour(true)] dog-like tail, which wags uncontrollably when you get excited.");
                    } else {
                        sb.append("a furry, [npc.tailColour(true)] dog-like tail, which wags uncontrollably when [npc.she] gets excited.");
                    }
                    break;
                case DOG_MORPH_STUBBY:
                    if (owner.isPlayer()) {
                        sb.append("a stubby, [pc.tailColour(true)] dog-like tail, which wags uncontrollably when you get excited.");
                    } else {
                        sb.append("a stubby, [npc.tailColour(true)] dog-like tail, which wags uncontrollably when [npc.she] gets excited.");
                    }
                    break;
                case ALLIGATOR_MORPH:
                    if (owner.isPlayer()) {
                        sb.append("a long, [pc.tailColour(true)] alligator-like tail, which you can swipe from side to side with considerable force.");
                    } else {
                        sb.append("a long, [npc.tailColour(true)] alligator-like tail, which [npc.she] can swipe from side to side with considerable force.");
                    }
                    break;
                case HARPY:
                    if (owner.isPlayer()) {
                        sb.append("a plume of beautiful, [pc.tailColour(true)] tail-feathers, which you can rapidly move up and down to help you keep your balance and to control your path when in flight.");
                    } else {
                        sb.append("a plume of beautiful, [npc.tailColour(true)] tail-feathers, which [npc.she] can rapidly move up and down to help [npc.herHim] keep [npc.her] balance and to control [npc.her] path when in flight.");
                    }
                    break;
                case HORSE_MORPH:
                    if (owner.isPlayer()) {
                        sb.append("a long, [pc.tailColour(true)] horse-like tail, which you can swipe from side to side, but other than that, you don't have much control over it.");
                    } else {
                        sb.append("a long, [npc.tailColour(true)] horse-like tail, which [npc.she] can swipe from side to side, but other than that, [npc.she] doesn't have much control over it.");
                    }
                    break;
                case REINDEER_MORPH:
                    if (owner.isPlayer()) {
                        sb.append("a short, [pc.tailColour(true)] reindeer-like tail.");
                    } else {
                        sb.append("a short, [npc.tailColour(true)] reindeer-like tail.");
                    }
                    break;
                case COW_MORPH:
                    if (owner.isPlayer()) {
                        sb.append("a long, [pc.tailColour(true)] cow-like tail, which you can swipe from side to side, but other than that, you don't have much control over it.");
                    } else {
                        sb.append("a long, [npc.tailColour(true)] cow-like tail, which [npc.she] can swipe from side to side, but other than that, [npc.she] doesn't have much control over it.");
                    }
                    break;
                case LYCAN:
                    if (owner.isPlayer()) {
                        sb.append("a furry, [pc.tailColour(true)] wolf-like tail.");
                    } else {
                        sb.append("a furry, [npc.tailColour(true)] wolf-like tail.");
                    }
                    break;
                case SQUIRREL_MORPH:
                    if (owner.isPlayer()) {
                        sb.append("a fluffy, [pc.tailColour(true)] squirrel-like tail, which you can control well enough to grant you significantly improved balance.");
                    } else {
                        sb.append("a fluffy, [npc.tailColour(true)] squirrel-like tail, which [npc.she] can control well enough to grant [npc.herHim] significantly improved balance.");
                    }
                    break;
                case NONE:
                    break;
            }
        } else {
            sb.append(Util.intToString(owner.getTailCount()) + " ");
            switch(owner.getTailType()) {
                case CAT_MORPH:
                    if (owner.isPlayer()) {
                        sb.append("furry, [pc.tailColour(true)] cat-like tails, which you can control well enough to grant you significantly improved balance.");
                    } else {
                        sb.append("furry, [npc.tailColour(true)] cat-like tails, which [npc.she] can control well enough to grant [npc.herHim] significantly improved balance.");
                    }
                    break;
                case DEMON_COMMON:
                    if (owner.isPlayer()) {
                        sb.append("spaded, [pc.tailColour(true)] demonic tails, over which you have complete control, and you can easily use them to grip and hold objects.");
                    } else {
                        sb.append("spaded, [npc.tailColour(true)] demonic tails, over which [npc.she] has complete control, and [npc.she] can easily use them to grip and hold objects.");
                    }
                    break;
                case IMP:
                    if (owner.isPlayer()) {
                        sb.append("spaded, [pc.tailColour(true)] impish tails, over which you have complete control, and you can easily use them to grip and hold objects.");
                    } else {
                        sb.append("spaded, [npc.tailColour(true)] impish tails, over which [npc.she] has complete control, and [npc.she] can easily use them to grip and hold objects.");
                    }
                    break;
                case DOG_MORPH:
                    if (owner.isPlayer()) {
                        sb.append("furry, [pc.tailColour(true)] dog-like tails, which wag uncontrollably when you get excited.");
                    } else {
                        sb.append("furry, [npc.tailColour(true)] dog-like tails, which wag uncontrollably when [npc.she] gets excited.");
                    }
                    break;
                case DOG_MORPH_STUBBY:
                    if (owner.isPlayer()) {
                        sb.append("stubby, [pc.tailColour(true)] dog-like tails, which wag uncontrollably when you get excited.");
                    } else {
                        sb.append("stubby, [npc.tailColour(true)] dog-like tails, which wag uncontrollably when [npc.she] gets excited.");
                    }
                    break;
                case ALLIGATOR_MORPH:
                    if (owner.isPlayer()) {
                        sb.append("long, [pc.tailColour(true)] alligator-like tails, which you can swipe from side to side with considerable force.");
                    } else {
                        sb.append("long, [npc.tailColour(true)] alligator-like tails, which [npc.she] can swipe from side to side with considerable force.");
                    }
                    break;
                case HARPY:
                    if (owner.isPlayer()) {
                        sb.append("plumes of beautiful, [pc.tailColour(true)] tail-feathers, which you can rapidly move up and down to help you keep your balance and to control your path when in flight.");
                    } else {
                        sb.append("plumes of beautiful, [npc.tailColour(true)] tail-feathers, which [npc.she] can rapidly move up and down to help [npc.herHim] keep [npc.her] balance and to control [npc.her] path when in flight.");
                    }
                    break;
                case HORSE_MORPH:
                    if (owner.isPlayer()) {
                        sb.append("long, [pc.tailColour(true)] horse-like tails, which you can swipe from side to side, but other than that, you don't have much control over them.");
                    } else {
                        sb.append("long, [npc.tailColour(true)] horse-like tails, which [npc.she] can swipe from side to side, but other than that, [npc.she] doesn't have much control over them.");
                    }
                    break;
                case REINDEER_MORPH:
                    if (owner.isPlayer()) {
                        sb.append("short, [pc.tailColour(true)] reindeer-like tails.");
                    } else {
                        sb.append("short, [npc.tailColour(true)] reindeer-like tails.");
                    }
                    break;
                case COW_MORPH:
                    if (owner.isPlayer()) {
                        sb.append("long, [pc.tailColour(true)] cow-like tails, which you can swipe from side to side, but other than that, you don't have much control over them.");
                    } else {
                        sb.append("long, [npc.tailColour(true)] cow-like tails, which [npc.she] can swipe from side to side, but other than that, [npc.she] doesn't have much control over them.");
                    }
                    break;
                case LYCAN:
                    if (owner.isPlayer()) {
                        sb.append("furry, [pc.tailColour(true)] wolf-like tails.");
                    } else {
                        sb.append("furry, [npc.tailColour(true)] wolf-like tails.");
                    }
                    break;
                case SQUIRREL_MORPH:
                    if (owner.isPlayer()) {
                        sb.append("fluffy, [pc.tailColour(true)] squirrel-like tails, which you can control well enough to grant you significantly improved balance.");
                    } else {
                        sb.append("fluffy, [npc.tailColour(true)] squirrel-like tails, which [npc.she] can control well enough to grant [npc.herHim] significantly improved balance.");
                    }
                    break;
                case NONE:
                    break;
            }
        }
        sb.append("</p>");
    }
    sb.append("<p>");
    // Ass & hips:
    if (owner.isPlayer()) {
        sb.append("Your [pc.hips+] and [pc.assSize] [pc.ass] are " + getCoveredInDescriptor(owner) + " [pc.assFullDescription(true)].");
    } else {
        sb.append("[npc.Her] [npc.hips+] and [npc.assSize] [npc.ass] are " + getCoveredInDescriptor(owner) + " [npc.assFullDescription(true)].");
    }
    if (owner.getPlayerKnowsAreas().contains(CoverableArea.ANUS)) {
        sb.append(" " + getAssDescription(owner));
        sb.append("</p>");
    } else {
        sb.append(" <span style='color:" + Colour.GENERIC_SEX.toWebHexString() + ";'>You haven't seen [npc.her] naked ass before, so you don't know what [npc.her] asshole looks like.</span>");
        sb.append("</p>");
    }
    // TODO pubic hair
    if (owner.getPlayerKnowsAreas().contains(CoverableArea.VAGINA) && owner.getPlayerKnowsAreas().contains(CoverableArea.PENIS)) {
        // Vagina, virgin/capacity, wetness:
        if (vagina.getType() == VaginaType.NONE && penis.getType() == PenisType.NONE) {
            sb.append("<p>" + getMoundDescription(owner) + "</p>");
        }
    }
    if (owner.getPlayerKnowsAreas().contains(CoverableArea.PENIS)) {
        // Penises, cum production, testicle size, capacity:
        if (penis.getType() != PenisType.NONE) {
            sb.append("<p>" + getPenisDescription(owner) + "</p>");
        }
    } else {
        sb.append(" <p>" + "<span style='color:" + Colour.GENERIC_SEX.toWebHexString() + ";'>You haven't seen [npc.her] naked groin before, so you don't know what [npc.her] cock looks like, or even if [npc.she] has one.</span>" + "</p>");
    }
    if (owner.getPlayerKnowsAreas().contains(CoverableArea.VAGINA)) {
        // Vagina, virgin/capacity, wetness:
        if (vagina.getType() != VaginaType.NONE) {
            sb.append("<p>" + getVaginaDescription(owner) + "</p>");
        }
    } else {
        sb.append(" <p>" + "<span style='color:" + Colour.GENERIC_SEX.toWebHexString() + ";'>You haven't seen [npc.her] naked groin before, so you don't know what [npc.her] pussy looks like, or even if [npc.she] has one.</span>" + "</p>");
    }
    if (!owner.isPlayer()) {
        sb.append(getSexDetails(owner));
        sb.append(getPregnancyDetails(owner));
    }
    return UtilText.parse(owner, sb.toString());
}
Also used : SexType(com.lilithsthrone.game.sex.SexType) TongueModifier(com.lilithsthrone.game.character.body.valueEnums.TongueModifier) PenetrationType(com.lilithsthrone.game.sex.PenetrationType) OrificeModifier(com.lilithsthrone.game.character.body.valueEnums.OrificeModifier)

Aggregations

PenetrationType (com.lilithsthrone.game.sex.PenetrationType)10 SexType (com.lilithsthrone.game.sex.SexType)7 OrificeModifier (com.lilithsthrone.game.character.body.valueEnums.OrificeModifier)5 OrificeType (com.lilithsthrone.game.sex.OrificeType)5 ArrayList (java.util.ArrayList)3 GameCharacter (com.lilithsthrone.game.character.GameCharacter)2 CoverableArea (com.lilithsthrone.game.character.body.CoverableArea)2 FluidModifier (com.lilithsthrone.game.character.body.valueEnums.FluidModifier)2 SexParticipantType (com.lilithsthrone.game.sex.SexParticipantType)2 Attribute (com.lilithsthrone.game.character.attributes.Attribute)1 FluidType (com.lilithsthrone.game.character.body.types.FluidType)1 PenisModifier (com.lilithsthrone.game.character.body.valueEnums.PenisModifier)1 TongueModifier (com.lilithsthrone.game.character.body.valueEnums.TongueModifier)1 Addiction (com.lilithsthrone.game.character.effects.Addiction)1 Perk (com.lilithsthrone.game.character.effects.Perk)1 StatusEffect (com.lilithsthrone.game.character.effects.StatusEffect)1 Fetish (com.lilithsthrone.game.character.fetishes.Fetish)1 FetishDesire (com.lilithsthrone.game.character.fetishes.FetishDesire)1 ClothingSet (com.lilithsthrone.game.inventory.clothing.ClothingSet)1 SexAction (com.lilithsthrone.game.sex.sexActions.SexAction)1