Java tutorial
/* * 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()); } }