All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.softwaremagico.tm.character.benefices.RandomBeneficeDefinition Maven / Gradle / Ivy

There is a newer version: 0.10.5
Show newest version
package com.softwaremagico.tm.character.benefices;

/*-
 * #%L
 * Think Machine (Core)
 * %%
 * Copyright (C) 2017 - 2018 Softwaremagico
 * %%
 * This software is designed by Jorge Hortelano Otero. Jorge Hortelano Otero
 *  Valencia (Spain).
 *
 * 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 2 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 .
 * #L%
 */

import com.softwaremagico.tm.InvalidXmlElementException;
import com.softwaremagico.tm.character.CharacterPlayer;
import com.softwaremagico.tm.character.characteristics.CharacteristicName;
import com.softwaremagico.tm.character.combat.CombatStyle;
import com.softwaremagico.tm.character.combat.CombatStyleGroup;
import com.softwaremagico.tm.character.creation.CostCalculator;
import com.softwaremagico.tm.character.creation.FreeStyleCharacterCreation;
import com.softwaremagico.tm.character.equipment.armours.Armour;
import com.softwaremagico.tm.character.equipment.armours.ArmourFactory;
import com.softwaremagico.tm.character.equipment.weapons.Weapon;
import com.softwaremagico.tm.character.equipment.weapons.WeaponFactory;
import com.softwaremagico.tm.character.exceptions.RestrictedElementException;
import com.softwaremagico.tm.character.exceptions.UnofficialElementNotAllowedException;
import com.softwaremagico.tm.log.RandomGenerationLog;
import com.softwaremagico.tm.random.RandomSelector;
import com.softwaremagico.tm.random.exceptions.ImpossibleToAssignMandatoryElementException;
import com.softwaremagico.tm.random.exceptions.InvalidRandomElementSelectedException;
import com.softwaremagico.tm.random.exceptions.NotExistingPreferenceException;
import com.softwaremagico.tm.random.exceptions.NotRemainingPointsException;
import com.softwaremagico.tm.random.selectors.*;

import java.util.*;

public class RandomBeneficeDefinition extends RandomSelector {
    private static final int MAX_AFFLICTIONS = 2;
    private static final String CASH_BENEFICE_ID = "cash";
    private static final String ESCAPED_PREFIX = "escaped";
    private final Set suggestedAvailableBenefices;
    private Integer totalCombatActions;

    public RandomBeneficeDefinition(CharacterPlayer characterPlayer, Set> preferences)
            throws InvalidXmlElementException, RestrictedElementException, UnofficialElementNotAllowedException {
        this(characterPlayer, preferences, new HashSet<>(), new HashSet<>(), new HashSet<>());
    }

    public RandomBeneficeDefinition(CharacterPlayer characterPlayer, Set> preferences,
                                    Set mandatoryBenefices, Set suggestedBenefices,
                                    Set suggestedAvailableBenefices)
            throws InvalidXmlElementException, RestrictedElementException, UnofficialElementNotAllowedException {
        super(characterPlayer, null, preferences, mandatoryBenefices, suggestedBenefices);
        this.suggestedAvailableBenefices = suggestedAvailableBenefices;
    }

    @Override
    public void assign() throws InvalidXmlElementException, InvalidRandomElementSelectedException {
        // Later, the others.
        while (CostCalculator.getBeneficesCosts(getCharacterPlayer()) < FreeStyleCharacterCreation
                .getTraitsPoints(getCharacterPlayer().getInfo().getAge()) && !getWeightedElements().isEmpty()) {
            // Select a benefice
            final BeneficeDefinition selectedBenefice = selectElementByWeight();
            try {
                assignBenefice(selectedBenefice,
                        FreeStyleCharacterCreation.getTraitsPoints(getCharacterPlayer().getInfo().getAge())
                                - CostCalculator.getBeneficesCosts(getCharacterPlayer()));
            } catch (RestrictedElementException | NotRemainingPointsException e) {
                //Ignore restricted benefice.
            }
        }
    }

    protected int assignBeneficeWithoutLevels(BeneficeDefinition benefice, int maxPoints) throws InvalidXmlElementException, RestrictedElementException {
        final Set beneficeLevels = AvailableBeneficeFactory.getInstance()
                .getAvailableBeneficesByDefinition(getCharacterPlayer().getLanguage(),
                        getCharacterPlayer().getModuleName(), benefice);
        int cost = 0;
        if (beneficeLevels.size() != 1) {
            throw new InvalidBeneficeException("Only benefices without multiples specializations can be use here.");
        }
        final AvailableBenefice availableBenefice = beneficeLevels.stream().findAny().get();
        if (availableBenefice.getCost() <= maxPoints) {
            cost = addBenefice(availableBenefice);
        }
        removeElementWeight(availableBenefice.getBeneficeDefinition());
        return cost;
    }

    protected int assignBenefice(BeneficeDefinition selectedBenefice, int maxPoints)
            throws InvalidXmlElementException, RestrictedElementException, NotRemainingPointsException {
        if (maxPoints <= 0) {
            throw new NotRemainingPointsException();
        }
        int pointsSpent = 0;
        // Select the range of the benefice.
        final AvailableBenefice selectedBeneficeWithLevel = assignLevelOfBenefice(selectedBenefice, maxPoints);
        if (selectedBeneficeWithLevel != null) {
            // Only a few afflictions.
            if (selectedBeneficeWithLevel.getBeneficeClassification() == BeneficeClassification.AFFLICTION) {
                if (getCharacterPlayer().getAfflictions().size() >= MAX_AFFLICTIONS) {
                    //Not add more afflictions
                    removeElementWeight(selectedBenefice);
                    return 0;
                }
            }

            //Only elements with enough Tech level.
            if (selectedBeneficeWithLevel.getBeneficeDefinition().getGroup() == BeneficeGroup.TECHNOLOGY) {
                try {
                    final Weapon weapon = WeaponFactory.getInstance().getElement(selectedBenefice.getId(), getCharacterPlayer().getLanguage(),
                            getCharacterPlayer().getModuleName());
                    if (weapon.getTechLevel() > getCharacterPlayer().getCharacteristicValue(CharacteristicName.TECH)) {
                        //Not enough tech level.
                        removeElementWeight(selectedBenefice);
                        return 0;
                    }
                } catch (InvalidXmlElementException e) {
                    // Benefice is not a weapon.
                }
                try {
                    final Armour armour = ArmourFactory.getInstance().getElement(selectedBenefice.getId(), getCharacterPlayer().getLanguage(),
                            getCharacterPlayer().getModuleName());
                    if (armour.getTechLevel() > getCharacterPlayer().getCharacteristicValue(CharacteristicName.TECH)) {
                        //Not enough tech level.
                        removeElementWeight(selectedBenefice);
                        return 0;
                    }
                } catch (InvalidXmlElementException e) {
                    // Benefice is not an armour.
                }
            }
            pointsSpent = addBenefice(selectedBeneficeWithLevel);
            RandomGenerationLog.info(this.getClass().getName(), "Remaining points '{}'.", maxPoints - selectedBeneficeWithLevel.getCost());
        }
        removeElementWeight(selectedBenefice);

        // Only few fighting style by character.
        if (getCharacterPlayer().getSelectedBenefices(BeneficeGroup.FIGHTING).size() >= getTotalCombatActions()) {
            if (selectedBenefice.getGroup().equals(BeneficeGroup.FIGHTING)) {
                for (final BeneficeDefinition beneficeDefinition : BeneficeDefinitionFactory.getInstance().getBenefices(
                        BeneficeGroup.FIGHTING, getCharacterPlayer().getLanguage(), getCharacterPlayer().getModuleName())) {
                    removeElementWeight(beneficeDefinition);
                }
            }
        }

        // Only one 'escaped' benefice
        if (selectedBenefice.getId().startsWith(ESCAPED_PREFIX)) {
            for (final BeneficeDefinition checkBenefice : getAllElements()) {
                if (checkBenefice.getId().startsWith(ESCAPED_PREFIX)) {
                    removeElementWeight(checkBenefice);
                }
            }
        }
        return pointsSpent;
    }

    private int addBenefice(AvailableBenefice availableBenefice) throws InvalidBeneficeException, RestrictedElementException {
        try {
            getCharacterPlayer().addBenefice(availableBenefice);
            RandomGenerationLog.info(this.getClass().getName(),
                    "Added benefice '{}'.", availableBenefice);
            return availableBenefice.getCost();
        } catch (BeneficeAlreadyAddedException e) {
            // If level is bigger... replace it.
            final AvailableBenefice originalBenefice = getCharacterPlayer()
                    .getBenefice(availableBenefice.getBeneficeDefinition().getId());
            if (originalBenefice.getCost() < availableBenefice.getCost()) {
                getCharacterPlayer().removeBenefice(originalBenefice);
                try {
                    getCharacterPlayer().addBenefice(availableBenefice);
                    RandomGenerationLog.info(this.getClass().getName(), "Replacing benefice '{}' with '{}'.",
                            originalBenefice, availableBenefice);
                    return availableBenefice.getCost();
                } catch (BeneficeAlreadyAddedException | UnofficialElementNotAllowedException e1) {
                    RandomGenerationLog.errorMessage(this.getClass().getName(), e1);
                }
            }
        } catch (IncompatibleBeneficeException | UnofficialElementNotAllowedException e) {
            //Incompatible. Cannot be added.
        }
        return 0;
    }

    private int getTotalCombatActions() {
        if (totalCombatActions == null) {
            final CombatActionsPreferences combatActionsPreferences = CombatActionsPreferences.getSelected(getPreferences());
            totalCombatActions = combatActionsPreferences.randomGaussian();
        }
        return totalCombatActions;
    }

    private boolean getCombatStyleGroupSelected(BeneficeDefinition benefice) throws NotExistingPreferenceException {
        final CombatActionsGroupPreferences combatActionsGroupPreferences = CombatActionsGroupPreferences.getSelected(getPreferences());
        if (combatActionsGroupPreferences == null) {
            throw new NotExistingPreferenceException("No combat action group selected.");
        }
        final CombatStyle combatStyle = CombatStyle.getCombatStyle(benefice, getCharacterPlayer().getLanguage(),
                getCharacterPlayer().getModuleName());
        if (combatStyle != null) {
            switch (combatActionsGroupPreferences) {
                case FIGHT:
                    if (combatStyle.getGroup() == CombatStyleGroup.FIGHT) {
                        return true;
                    }
                    break;
                case MELEE:
                    if (combatStyle.getGroup() == CombatStyleGroup.MELEE) {
                        return true;
                    }
                    break;
                case RANGED:
                    if (combatStyle.getGroup() == CombatStyleGroup.RANGED) {
                        return true;
                    }
                    break;
            }
        }
        return false;
    }

    @Override
    protected Collection getAllElements() throws InvalidXmlElementException {
        return BeneficeDefinitionFactory.getInstance().getElements(getCharacterPlayer().getLanguage(),
                getCharacterPlayer().getModuleName());
    }

    @Override
    protected int getWeight(BeneficeDefinition benefice) throws InvalidRandomElementSelectedException {
        // No restricted benefices.
        if ((benefice.getRestrictedToFactionGroup() != null && getCharacterPlayer().getFaction() != null
                && benefice.getRestrictedToFactionGroup() != getCharacterPlayer().getFaction().getFactionGroup()) &&
                (!benefice.getRestrictedToFactions().isEmpty() && (getCharacterPlayer().getFaction() == null ||
                        !benefice.getRestrictedToFactions().contains(getCharacterPlayer().getFaction())))) {
            throw new InvalidRandomElementSelectedException(
                    "Benefice '" + benefice + "' is restricted to '" + benefice.getRestrictedToFactionGroup() + "'.");
        }

        // No special benefices
        if (benefice.isRestricted()) {
            throw new InvalidRandomElementSelectedException("Benefice '" + benefice + "' is restricted.");
        }

        // No benefices limited to race
        if (!benefice.getRestrictedToRaces().isEmpty() && !benefice.getRestrictedToRaces().contains(getCharacterPlayer().getRace())) {
            throw new InvalidRandomElementSelectedException("Benefice '" + benefice + "' is restricted to races '" + benefice.getRestrictedToRaces() + "'.");
        }

        // Set faction limitations.
        if (getCharacterPlayer().getFaction() != null) {
            for (final RestrictedBenefice restrictedBenefice : getCharacterPlayer().getFaction()
                    .getRestrictedBenefices()) {
                if (Objects.equals(restrictedBenefice.getBeneficeDefinition(), benefice)) {
                    if (restrictedBenefice.getMaxValue() == null) {
                        throw new InvalidRandomElementSelectedException("Benefice '" + benefice
                                + "' is restricted by faction '" + getCharacterPlayer().getFaction() + "'.");
                    }
                    break;
                }
            }
        }

        // Suggested benefices by faction.
        if (getCharacterPlayer().getFaction() != null) {
            for (final SuggestedBenefice suggestedBenefice : getCharacterPlayer().getFaction()
                    .getSuggestedBenefices()) {
                if (Objects.equals(suggestedBenefice.getBeneficeDefinition(), benefice)) {
                    return VERY_GOOD_PROBABILITY;
                }

            }
        }

        // PNJs likes money changes.
        if (benefice.getId().equalsIgnoreCase(CASH_BENEFICE_ID)) {
            return GOOD_PROBABILITY;
        }

        // Add extra probability to fight styles
        final CombatActionsPreferences combatActionsPreferences = CombatActionsPreferences.getSelected(getPreferences());
        if (benefice.getGroup() == BeneficeGroup.FIGHTING && combatActionsPreferences.minimum() > 0) {
            try {
                if (getCombatStyleGroupSelected(benefice)) {
                    return VERY_GOOD_PROBABILITY;
                }
            } catch (NotExistingPreferenceException e) {
                return GOOD_PROBABILITY;
            }
        }

        // No faction preference selected. All benefices has the same
        // probability.
        return 1;
    }

    /**
     * Returns a cost for a benefice depending on the preferences of the character.
     */
    private AvailableBenefice assignLevelOfBenefice(BeneficeDefinition benefice, int maxPoints)
            throws InvalidXmlElementException {

        if (suggestedAvailableBenefices != null) {
            for (final AvailableBenefice availableBenefice : suggestedAvailableBenefices) {
                if (Objects.equals(availableBenefice.getBeneficeDefinition(), benefice)) {
                    return availableBenefice;
                }
            }
        }

        IGaussianDistribution selectedTraitCost = TraitCostPreferences.getSelected(getPreferences());

        if (benefice.getId().equalsIgnoreCase(CASH_BENEFICE_ID)) {
            final CashPreferences cashPreferences = CashPreferences.getSelected(getPreferences());
            if (cashPreferences != null) {
                selectedTraitCost = cashPreferences;
            }
        }

        if (benefice.getGroup() != null && benefice.getGroup().equals(BeneficeGroup.RANK)) {
            // Status has also special preference.
            final IGaussianDistribution selectedRanks = RankPreferences.getSelected(getPreferences());
            if (selectedRanks != null) {
                selectedTraitCost = selectedRanks;
            }
        }

        int maxRangeSelected = selectedTraitCost.randomGaussian();

        // Suggested benefices values by faction.
        if (getCharacterPlayer().getFaction() != null) {
            for (final SuggestedBenefice suggestedBenefice : getCharacterPlayer().getFaction()
                    .getSuggestedBenefices()) {
                if (Objects.equals(suggestedBenefice.getBeneficeDefinition(), benefice)) {
                    if (suggestedBenefice.getValue() != null) {
                        RandomGenerationLog.debug(this.getClass().getName(), "Suggested benefice '{}' has a value of '{}'.",
                                benefice, suggestedBenefice.getValue());
                        maxRangeSelected = suggestedBenefice.getValue();
                    }
                }
            }
        }

        // Maximum points to be spent.
        if (maxRangeSelected > maxPoints) {
            maxRangeSelected = maxPoints;
        }

        // Set faction limitations.
        if (getCharacterPlayer().getFaction() != null) {
            for (final RestrictedBenefice restrictedBenefice : getCharacterPlayer().getFaction()
                    .getRestrictedBenefices()) {
                if (Objects.equals(restrictedBenefice.getBeneficeDefinition(), benefice)) {
                    if (maxRangeSelected > restrictedBenefice.getMaxValue()) {
                        RandomGenerationLog.debug(this.getClass().getName(), "Suggested benefice '{}' has a restriction of value of '{}}'.",
                                benefice, restrictedBenefice.getMaxValue());
                        maxRangeSelected = restrictedBenefice.getMaxValue();
                    }
                    break;
                }
            }
        }

        RandomGenerationLog.info(this.getClass().getName(),
                "MaxPoints of '{}' are '{}'.", benefice, maxRangeSelected);
        Set beneficeLevels = AvailableBeneficeFactory.getInstance()
                .getAvailableBeneficesByDefinition(getCharacterPlayer().getLanguage(),
                        getCharacterPlayer().getModuleName(), benefice);
        // Cannot be null, but...
        if (beneficeLevels == null) {
            beneficeLevels = new HashSet<>();
        }
        final List sortedBenefices = new ArrayList<>(beneficeLevels);
        // Sort by cost (descending). Adding if a benefice has preferences
        // (ascending).
        sortedBenefices.sort((o1, o2) -> {
            final double o1Preferred = getRandomDefinitionBonus(o1.getRandomDefinition());
            final double o2Preferred = getRandomDefinitionBonus(o2.getRandomDefinition());

            if ((int) (o1Preferred - o2Preferred) != 0) {
                return (int) (o1Preferred - o2Preferred);
            }
            return Integer.compare(o2.getCost(), o1.getCost());
        });
        RandomGenerationLog.info(this.getClass().getName(),
                "Available benefice levels of '{}' are '{}'.", benefice, sortedBenefices);
        for (final AvailableBenefice availableBenefice : sortedBenefices) {
            try {
                validateElement(availableBenefice.getRandomDefinition());
            } catch (InvalidRandomElementSelectedException e) {
                continue;
            }
            if (Math.abs(availableBenefice.getCost()) <= maxRangeSelected
                    // Or it is mandatory and is the last one of the list. Add it always if is possible
                    // (at least, the last one must be added).
                    || (isMandatory(availableBenefice.getBeneficeDefinition())
                    && Objects.equals(availableBenefice, sortedBenefices.get(sortedBenefices.size() - 1)))) {
                if (availableBenefice.getCost() <= maxPoints) {
                    return availableBenefice;
                }
            }
        }
        return null;
    }

    @Override
    protected void assignIfMandatory(BeneficeDefinition benefice)
            throws InvalidXmlElementException, ImpossibleToAssignMandatoryElementException, RestrictedElementException {
        int remainingPoints = FreeStyleCharacterCreation.getFreeAvailablePoints(getCharacterPlayer().getInfo().getAge(), getCharacterPlayer().getRace())
                - CostCalculator.getCost(getCharacterPlayer());
        // Set status of the character.
        try {
            if ((benefice.getGroup() != null && benefice.getGroup().equals(BeneficeGroup.RANK))
                    && getWeight(benefice) > 0 && getCharacterPlayer().getFaction() != null &&
                    //Restricted to faction group or restricted to factions.
                    ((benefice.getRestrictedToFactionGroup() == null && benefice.getRestrictedToFactions().isEmpty()) ||
                            (Objects.equals(benefice.getRestrictedToFactionGroup(),
                                    getCharacterPlayer().getFaction().getFactionGroup()) ||
                                    benefice.getRestrictedToFactions().contains(getCharacterPlayer().getFaction())))) {
                final IGaussianDistribution selectedRanks = RankPreferences.getSelected(getPreferences());
                if (selectedRanks != null) {
                    RandomGenerationLog.debug(this.getClass().getName(),
                            "Searching grade '{}' of benefice '{}'.", selectedRanks.maximum(), benefice);
                    try {
                        remainingPoints -= assignBenefice(benefice, Math.min(remainingPoints, selectedRanks.randomGaussian()));
                    } catch (NotRemainingPointsException e) {
                        // Do not add it.
                    }
                }
            }
        } catch (InvalidRandomElementSelectedException e) {
            // Weight is zero. Do nothing.
        }

        // Money requirements for equipment.
        if (Objects.equals(benefice.getId(), "cash")) {
            final IGaussianDistribution selectedCash = CashPreferences.getSelected(getPreferences());
            if (selectedCash != null) {
                final int grade = selectedCash.randomGaussian();
                RandomGenerationLog.debug(this.getClass().getName(),
                        "Searching grade '{}' of benefice '{}'.", grade, benefice);
                try {
                    remainingPoints -= assignBenefice(benefice, Math.min(remainingPoints, grade));
                } catch (NotRemainingPointsException e) {
                    // Do not add it.
                }
            }
        }

        //Some combat styles are mandatory.
        if (benefice.getGroup() == BeneficeGroup.FIGHTING &&
                getCharacterPlayer().getSelectedBenefices(BeneficeGroup.FIGHTING).size() < getTotalCombatActions()) {
            try {
                if (getCombatStyleGroupSelected(benefice)) {
                    remainingPoints -= assignBeneficeWithoutLevels(benefice, remainingPoints);
                }
            } catch (NotExistingPreferenceException e) {
                //No special group selected, choose any.
                remainingPoints -= assignBeneficeWithoutLevels(benefice, remainingPoints);
            }
        }
    }

    @Override
    protected void assignMandatoryValues(Set mandatoryValues) throws InvalidXmlElementException, RestrictedElementException {
        int remainingPoints = FreeStyleCharacterCreation.getFreeAvailablePoints(getCharacterPlayer().getInfo().getAge(), getCharacterPlayer().getRace())
                - CostCalculator.getCost(getCharacterPlayer());
        for (final BeneficeDefinition selectedBenefice : mandatoryValues) {
            // Mandatory benefices can exceed the initial traits points.
            try {
                remainingPoints -= assignBenefice(selectedBenefice, remainingPoints);
            } catch (NotRemainingPointsException e) {
                break;
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy