com.spankingrpgs.scarletmoon.characters.CrimsonGameCharacter.java Source code

Java tutorial

Introduction

Here is the source code for com.spankingrpgs.scarletmoon.characters.CrimsonGameCharacter.java

Source

/*
 * CrimsonGlow is an adult computer roleplaying game with spanking content.
 * Copyright (C) 2015 Andrew Russell
 *
 *      This program is free software: you can redistribute it and/or modify
 *       it under the terms of the GNU General Public License as published by
 *       the Free Software Foundation, either version 3 of the License, or
 *       (at your option) any later version.
 *
 *       This program is distributed in the hope that it will be useful,
 *       but WITHOUT ANY WARRANTY; without even the implied warranty of
 *       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *       GNU General Public License for more details.
 *
 *       You should have received a copy of the GNU General Public License
 *       along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package com.spankingrpgs.scarletmoon.characters;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode;
import com.spankingrpgs.scarletmoon.characters.appearance.BodyType;
import com.spankingrpgs.scarletmoon.characters.appearance.EyeColor;
import com.spankingrpgs.scarletmoon.characters.appearance.HairColor;
import com.spankingrpgs.scarletmoon.characters.appearance.HairStyle;
import com.spankingrpgs.scarletmoon.characters.appearance.Height;
import com.spankingrpgs.scarletmoon.characters.appearance.Musculature;
import com.spankingrpgs.scarletmoon.characters.appearance.SkinColor;
import com.spankingrpgs.scarletmoon.items.CrimsonGlowEquipSlotNames;
import com.spankingrpgs.model.GameState;
import com.spankingrpgs.model.characters.AppearanceElement;
import com.spankingrpgs.model.characters.EquipSlot;
import com.spankingrpgs.model.characters.GameCharacter;
import com.spankingrpgs.model.characters.Gender;
import com.spankingrpgs.model.characters.SpankingRole;
import com.spankingrpgs.model.combat.CombatRange;
import com.spankingrpgs.model.items.Equipment;
import com.spankingrpgs.model.skills.Skill;
import com.spankingrpgs.model.story.EventDescription;
import com.spankingrpgs.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;

/**
 * Characters in Crimson Glow.
 */
public class CrimsonGameCharacter extends GameCharacter {
    private static final Logger LOG = Logger.getLogger(CrimsonGameCharacter.class.getName());
    private static final HashMap<String, Function<String, AppearanceElement>> APPEARANCE_VALUE_OFS = new HashMap<>();
    private static final int ENERGY_MODIFIER = 5;

    private void populateAppearanceElementsHydration() {
        if (!APPEARANCE_VALUE_OFS.isEmpty()) {
            return;
        }
        APPEARANCE_VALUE_OFS.put("bodytype", BodyType::valueOf);
        APPEARANCE_VALUE_OFS.put("eyecolor", EyeColor::valueOf);
        APPEARANCE_VALUE_OFS.put("haircolor", HairColor::valueOf);
        APPEARANCE_VALUE_OFS.put("hairstyle", HairStyle::valueOf);
        APPEARANCE_VALUE_OFS.put("height", Height::valueOf);
        APPEARANCE_VALUE_OFS.put("musculature", Musculature::valueOf);
        APPEARANCE_VALUE_OFS.put("skincolor", SkinColor::valueOf);
    }

    private void modifyFromWillpower() {
        int willpower = getStatistic(PrimaryStatisticName.WILLPOWER.name());
        setStatistic(SecondaryStatisticName.POWER.name(), willpower * 2);
        setStatistic(SecondaryStatisticName.MAXIMUM_ENERGY.name(),
                Math.max(1, ENERGY_MODIFIER * sumPrimaryStatistics()));
    }

    private void modifyFromSpeed() {
        int strength = getStatistic(PrimaryStatisticName.STRENGTH.name());
        int speed = getStatistic(PrimaryStatisticName.SPEED.name());
        setStatistic(SecondaryStatisticName.DISTANT.name(), speed * 2);
        setStatistic(SecondaryStatisticName.ARMSLENGTH.name(), strength + speed);
        setStatistic(SecondaryStatisticName.MAXIMUM_ENERGY.name(),
                Math.max(1, ENERGY_MODIFIER * sumPrimaryStatistics()));
    }

    private void modifyFromStrength() {
        int strength = getStatistic(PrimaryStatisticName.STRENGTH.name());
        int speed = getStatistic(PrimaryStatisticName.SPEED.name());
        setStatistic(SecondaryStatisticName.GRAPPLE.name(), strength * 2);
        setStatistic(SecondaryStatisticName.ARMSLENGTH.name(), strength + speed);
        setStatistic(SecondaryStatisticName.MAXIMUM_ENERGY.name(),
                Math.max(1, ENERGY_MODIFIER * sumPrimaryStatistics()));
    }

    /**
     * Computes the sum of all this character's primary, combat statistics.
     *
     * @return The sum of all this character's primary, combat statistics
     */
    private int sumPrimaryStatistics() {
        return getAllPrimaryStatistics().keySet().stream()
                .filter(statisticName -> PrimaryStatisticName.valueOf(statisticName).isCombatStatistic())
                .mapToInt(this::getStatistic).sum();
    }

    private void modifyFromFatigue() {
        int fatigue = getStatistic(PrimaryStatisticName.FATIGUE.name());
        String maximumEnergyName = SecondaryStatisticName.MAXIMUM_ENERGY.name();
        setStatistic(maximumEnergyName, Math.max(1, getStatistic(maximumEnergyName) - fatigue));
    }

    private void modifyFromStress() {
        int stressPenalty = getStatistic(PrimaryStatisticName.STRESS.name()) / 5;
        String strength = PrimaryStatisticName.STRENGTH.name();
        String speed = PrimaryStatisticName.SPEED.name();
        String willpower = PrimaryStatisticName.WILLPOWER.name();
        decrementStatistic(strength, stressPenalty);
        decrementStatistic(speed, stressPenalty);
        decrementStatistic(willpower, stressPenalty);
    }

    private void modifyRisque() {
        Optional<Equipment> underwear = getEquipment(CrimsonGlowEquipSlotNames.UNDERWEAR.name());
        Optional<Equipment> lowerClothing = getEquipment(CrimsonGlowEquipSlotNames.LOWER.name());
        int risque = getEquipment().stream()
                .filter(equipment -> !lowerClothing.isPresent() || !equipment.equals(underwear.orElse(null)))
                .mapToInt(Equipment::getRisque).sum();
        setStatistic(SecondaryStatisticName.RISQUE.name(), risque);
    }

    /**
     * This constructor will compute the values of the secondary statistics using the modifySecondaryStatistics
     * method.
     * <p>
     * This constructor is best used when initializing a plain character, without any of their own special bonuses.
     *   @param name  The name of the character
     * @param printedNames  The name to be displayed to the player
     * @param battleName  The name to display for the character in battle
     * @param description  The description of the character
     * @param gender  The gender of the character
     * @param attackRanges  The ranges at which the character can attack
     * @param primaryStatistics  The character's primary statistics
     * @param secondaryStatistics The secondary statistics of the character
     * @param appearance  The character's appearance
     * @param equipSlots  The slots into which the character can put equipment
     * @param spankingEvents  This character's in-combat spanking events, as a mapping from spanking position to
     * @param role  The role this character typically takes in a spanking
     */
    public CrimsonGameCharacter(String name, Map<Gender, String> printedNames, String battleName,
            String description, Gender gender, List<CombatRange> attackRanges,
            LinkedHashMap<String, Integer> primaryStatistics, LinkedHashMap<String, Integer> secondaryStatistics,
            Map<String, AppearanceElement> appearance, LinkedHashMap<String, EquipSlot> equipSlots,
            Map<Skill, Integer> skills, Map<String, EventDescription> spankingEvents, SpankingRole role) {
        super(name, printedNames, battleName, description, gender, attackRanges, primaryStatistics,
                secondaryStatistics, appearance, equipSlots, skills, spankingEvents, role);
        populateAppearanceElementsHydration();
        modifyRisque();
        setStatistic(SecondaryStatisticName.ENERGY.name(),
                getStatistic(SecondaryStatisticName.MAXIMUM_ENERGY.name()));
    }

    public CrimsonGameCharacter(String name, Map<Gender, String> printedNames, String battleName,
            String description, Gender gender, List<CombatRange> attackRanges,
            LinkedHashMap<String, Integer> primaryStatistics, List<String> secondaryStatisticNames,
            Map<String, AppearanceElement> appearance, LinkedHashMap<String, EquipSlot> equipSlots,
            Map<Skill, Integer> skills, Map<String, EventDescription> spankingEvents, SpankingRole role) {
        super(name, printedNames, battleName, description, gender, attackRanges, primaryStatistics,
                secondaryStatisticNames, appearance, equipSlots, skills, spankingEvents, role);
        populateAppearanceElementsHydration();
        modifyRisque();
        setStatistic(SecondaryStatisticName.ENERGY.name(),
                getStatistic(SecondaryStatisticName.MAXIMUM_ENERGY.name()));
    }

    /**
     * Initializes a character with the specified system name, and a host of reasonable defaults.
     * <p>
     * @param name  The system name for the character
     */
    public CrimsonGameCharacter(String name) {
        super(name, Arrays.stream(Gender.values()).collect(Collectors.toMap(Function.identity(), ignored -> name)),
                name, name, Gender.UNKNOWN, Arrays.asList(CombatRange.ARMSLENGTH, CombatRange.GRAPPLE),
                Arrays.stream(PrimaryStatisticName.values())
                        .collect(CollectionUtils.toLinkedHashMap(Enum::name, ignored -> 2)),
                Arrays.stream(SecondaryStatisticName.values()).map(Enum::name).collect(Collectors.toList()),
                new LinkedHashMap<>(),
                Arrays.stream(CrimsonGlowEquipSlotNames.values())
                        .collect(CollectionUtils.toLinkedHashMap(Enum::name, slot -> new EquipSlot(slot.name()))),
                new LinkedHashMap<>(), SpankingRole.SWITCH);
    }

    /**
     * Performs a deep copy of the specified character.
     *
     * @param character  The character to perform a deep copy of
     */
    public CrimsonGameCharacter(CrimsonGameCharacter character) {
        super(character);
    }

    @Override
    public GameCharacter copy() {
        return new CrimsonGameCharacter(this);
    }

    @Override
    public void load(JsonNode characterObject) {
        setName(characterObject.get("name").asText().trim());
        setGender(Gender.valueOf(characterObject.get("gender").asText().trim()));
        JsonNode descriptionNode = characterObject.get("rawDescription");
        String description = descriptionNode.isNull() ? null : descriptionNode.asText().trim();
        setDescription(description);
        setBattleName(characterObject.get("battleName").asText().trim());
        loadPrintedNames(characterObject.get("bothPrintedNames"));
        loadAttackRanges(characterObject.get("attackRanges"));
        loadStatistics(characterObject.get("primaryStatistics"));
        loadEquipment(characterObject.get("equipmentNames"));
        loadAppearance(characterObject.get("appearance"));
        loadSkills(characterObject.get("skills"));
        loadStatistics(characterObject.get("secondaryStatistics"));
    }

    private void loadAppearance(JsonNode appearance) {
        Iterator<String> appearanceElements = appearance.fieldNames();
        while (appearanceElements.hasNext()) {
            String appearanceType = appearanceElements.next();
            String appearanceElementString = appearance.get(appearanceType).asText();
            Function<String, AppearanceElement> valueOfFunction = APPEARANCE_VALUE_OFS.get(appearanceType);
            if (valueOfFunction == null) {
                String msg = String.format("Invalid appearance type: %s", appearanceType);
                LOG.log(Level.SEVERE, msg);
                throw new IllegalArgumentException(msg);
            }
            changeAppearance(appearanceType, valueOfFunction.apply(appearanceElementString));
        }
    }

    private void loadAttackRanges(JsonNode attackRangesNode) {
        List<CombatRange> attackRanges = new ArrayList<>();
        Iterator<JsonNode> attackRangeElements = attackRangesNode.elements();
        while (attackRangeElements.hasNext()) {
            attackRanges.add(CombatRange.valueOf(attackRangeElements.next().asText().trim()));
        }
        setAttackRanges(attackRanges);
    }

    private void loadPrintedNames(JsonNode bothPrintedNames) {
        Iterator<String> genders = bothPrintedNames.fieldNames();
        Map<Gender, String> printedNames = new HashMap<>(2);
        while (genders.hasNext()) {
            String genderString = genders.next();
            printedNames.put(Gender.valueOf(genderString), bothPrintedNames.get(genderString).asText().trim());
        }
        setBothPrintedNames(printedNames);
    }

    private void loadEquipment(JsonNode equipmentNames) {
        Iterator<JsonNode> equipmentNodes = equipmentNames.elements();
        while (equipmentNodes.hasNext()) {
            equip((Equipment) GameState.getInstance().getItem(equipmentNodes.next().asText().trim()));
        }
    }

    private void loadStatistics(JsonNode statisticsNode) {
        if (statisticsNode == null) {
            return;
        }
        Iterator<String> statisticNames = statisticsNode.fieldNames();
        while (statisticNames.hasNext()) {
            String statisticName = statisticNames.next();
            setStatistic(statisticName, statisticsNode.get(statisticName).intValue());
        }
    }

    private void loadSkills(JsonNode skillNode) {
        clearSkills();
        if (skillNode == null) {
            return;
        }
        Iterator<String> skillNames = skillNode.fieldNames();
        while (skillNames.hasNext()) {
            String skillName = skillNames.next();
            addSkill(skillName, skillNode.get(skillName).intValue());
        }
    }

    @Override
    protected void modifySecondaryStatistics(String primaryStatisticName) {
        if (primaryStatisticName.equals(PrimaryStatisticName.STRENGTH.name())) {
            modifyFromStrength();
        } else if (primaryStatisticName.equalsIgnoreCase(PrimaryStatisticName.SPEED.name())) {
            modifyFromSpeed();
        } else if (primaryStatisticName.equalsIgnoreCase(PrimaryStatisticName.WILLPOWER.name())) {
            modifyFromWillpower();
        } else if (primaryStatisticName.equalsIgnoreCase(PrimaryStatisticName.STRESS.name())) {
            modifyFromStress();
        } else if (primaryStatisticName.equalsIgnoreCase(PrimaryStatisticName.FATIGUE.name())) {
            modifyFromFatigue();
        }
    }

    @JsonProperty("secondaryStatistics")
    @Override
    public Map<String, Integer> getSerializedSecondaryStatistics() {
        String energyName = SecondaryStatisticName.ENERGY.name();
        return Collections.singletonMap(energyName, getStatistic(energyName));
    }

    @Override
    protected void modifySecondaryStatisticsOnEquip(Equipment ignored) {
        modifyRisque();
    }

    @Override
    protected void modifySecondaryStatisticsOnUnEquip(Equipment item) {
        decrementStatistic(SecondaryStatisticName.RISQUE.name(), item.getRisque());
    }
}