module CreatureData (Creature(..), CreatureGender(..), CreatureAptitude(..), CreatureInteractionMode(..), CreatureAbility(..), CreatureEndo(..), CreatureScore(..), FavoredClass(..), creatureGender, creatureAbilityScore, isFavoredClass, empty_creature) where import CharacterData import Alignment import Data.Maybe import FactionData import Data.Monoid import qualified Data.Map as Map import qualified Data.Set as Set import SpeciesData import TerrainData data Creature = Creature { creature_aptitude :: Map.Map CreatureAptitude Integer, creature_ability :: Map.Map CreatureAbility Integer, creature_ethical :: Map.Map EthicalAlignment Integer, creature_levels :: Map.Map CharacterClass Integer, creature_favored_classes :: Set.Set CharacterClass, creature_gender :: CreatureGender, creature_species :: Species, creature_random_id :: Integer, -- random number attached to the creature, not unique creature_damage :: Integer, creature_faction :: Faction } deriving (Read,Show) -- | Creature having no attributes and undefined 'creature_species', 'creature_random_id', and 'creature_faction' -- empty_creature :: Creature empty_creature = Creature { creature_aptitude = Map.empty, creature_ability = Map.empty, creature_ethical = Map.empty, creature_levels = Map.empty, creature_favored_classes = Set.empty, creature_gender = Neuter, creature_species = error "empty_creature: undefined creature_species", creature_random_id = error "empty_creature: undefined creature_random_id", creature_damage = 0, creature_faction = error "empty_creature: undefined creature_faction" } data CreatureGender = Male | Female | Neuter deriving (Eq,Read,Show) -- | Endomorphisms over a 'Creature'. These are types that contribute some feature to a 'Creature', so that 'Creature's can be defined concisely by those properties. class CreatureEndo a where applyToCreature :: a -> Creature -> Creature -- | Primitive numeric properties of a Creature. class CreatureScore s where rawScore :: s -> Creature -> Integer instance (CreatureEndo a,Integral i) => CreatureEndo (a,i) where applyToCreature (_,i) | i <= 0 = id applyToCreature (a,i) = applyToCreature (a,toInteger i - 1) . applyToCreature a instance (CreatureEndo a) => CreatureEndo [a] where applyToCreature = appEndo . mconcat . map (Endo . applyToCreature) instance CreatureEndo CreatureGender where applyToCreature g c = c { creature_gender = g } -- | The seven aptitudes. data CreatureAptitude = Strength | Speed | Constitution | Intellect | Perception | Charisma | Mindfulness deriving (Eq,Read,Show,Ord,Enum,Bounded) instance CreatureEndo CreatureAptitude where applyToCreature aptitude c = c { creature_aptitude = Map.insertWith (+) aptitude 1 $ creature_aptitude c } instance CreatureScore CreatureAptitude where rawScore aptitude c = fromMaybe 0 $ Map.lookup aptitude (creature_aptitude c) -- | Combat modes: -- Melee is armed close-quarters combat with bladed or blunt weapons -- Ranged is combat with projectile weapons -- Unarmed is close-quarters hand-to-hand -- Splash represts diffuse damage caused by things like explosions or falling into lava. data CreatureInteractionMode = Melee | Ranged | Unarmed | Splash deriving (Eq,Read,Show,Ord) data CreatureAbility = ToughnessTrait | AttackSkill CreatureInteractionMode | DefenseSkill CreatureInteractionMode | DamageSkill CreatureInteractionMode | DamageReductionTrait CreatureInteractionMode | ReloadSkill CreatureInteractionMode | TerrainAffinity TerrainPatch | HideSkill | SpotSkill | JumpSkill | InventorySkill deriving (Eq,Read,Show,Ord) instance CreatureEndo CreatureAbility where applyToCreature ability c = c { creature_ability = Map.insertWith (+) ability 1 $ creature_ability c } instance CreatureScore CreatureAbility where rawScore ability c = fromMaybe 0 $ Map.lookup ability $ creature_ability c instance CreatureEndo EthicalAlignment where applyToCreature ethical c = c { creature_ethical = Map.insertWith (+) ethical 1 $ creature_ethical c } instance CreatureScore EthicalAlignment where rawScore ethical c = fromMaybe 0 $ Map.lookup ethical $ creature_ethical c instance CreatureEndo CharacterClass where applyToCreature character_class c = c { creature_levels = Map.insertWith (+) character_class 1 $ creature_levels c } instance CreatureScore CharacterClass where rawScore character_class c = fromMaybe 0 $ Map.lookup character_class $ creature_levels c newtype FavoredClass = FavoredClass CharacterClass instance CreatureEndo FavoredClass where applyToCreature (FavoredClass favored_class) c = c { creature_favored_classes = Set.insert favored_class $ creature_favored_classes c } -- | Calculator to determine how many ranks a creature has in an ability. -- Number of aptitude points plus n times number of ability points figureAbility :: [CreatureAptitude] -> (CreatureAbility,Integer) -> Creature -> Integer figureAbility aptitude (ability,n) c = sum (map (flip rawScore c) aptitude) + rawScore ability c * n creatureAbilityScore :: CreatureAbility -> Creature -> Integer creatureAbilityScore ToughnessTrait = figureAbility [Strength,Speed,Constitution,Mindfulness] (ToughnessTrait,3) creatureAbilityScore (AttackSkill Melee) = figureAbility [Strength] (AttackSkill Melee,2) creatureAbilityScore (DefenseSkill Melee) = figureAbility [Strength] (DefenseSkill Melee,2) creatureAbilityScore (DamageSkill Melee) = figureAbility [Strength] (DamageSkill Melee,2) creatureAbilityScore (DamageReductionTrait Melee) = figureAbility [Constitution] (DamageReductionTrait Melee,1) creatureAbilityScore (ReloadSkill Melee) = figureAbility [Speed] (ReloadSkill Melee,1) creatureAbilityScore (AttackSkill Ranged) = figureAbility [Perception] (AttackSkill Ranged,2) creatureAbilityScore (DefenseSkill Ranged) = figureAbility [Perception] (DefenseSkill Ranged,2) creatureAbilityScore (DamageSkill Ranged) = figureAbility [Perception] (DamageSkill Ranged,2) creatureAbilityScore (DamageReductionTrait Ranged) = figureAbility [Constitution] (DamageReductionTrait Ranged,1) creatureAbilityScore (ReloadSkill Ranged) = figureAbility [Speed] (ReloadSkill Ranged,1) creatureAbilityScore (AttackSkill Unarmed) = figureAbility [Speed] (AttackSkill Unarmed,2) creatureAbilityScore (DefenseSkill Unarmed) = figureAbility [Speed] (DefenseSkill Unarmed,2) creatureAbilityScore (DamageSkill Unarmed) = figureAbility [Speed] (DamageSkill Unarmed,2) creatureAbilityScore (DamageReductionTrait Unarmed) = figureAbility [Constitution] (DamageReductionTrait Unarmed,1) creatureAbilityScore (ReloadSkill Unarmed) = figureAbility [Speed] (ReloadSkill Unarmed,1) creatureAbilityScore (AttackSkill Splash) = figureAbility [Intellect] (AttackSkill Splash,2) creatureAbilityScore (DefenseSkill Splash) = figureAbility [Intellect] (DefenseSkill Splash,2) creatureAbilityScore (DamageSkill Splash) = figureAbility [Intellect] (DamageSkill Splash,2) creatureAbilityScore (DamageReductionTrait Splash) = figureAbility [Constitution] (DamageReductionTrait Splash,1) creatureAbilityScore (ReloadSkill Splash) = figureAbility [Speed] (ReloadSkill Splash,1) creatureAbilityScore (TerrainAffinity terrain_type) = figureAbility [] (TerrainAffinity terrain_type,1) creatureAbilityScore HideSkill = figureAbility [Perception] (HideSkill,2) creatureAbilityScore SpotSkill = figureAbility [Perception] (SpotSkill,2) creatureAbilityScore JumpSkill = figureAbility [Strength] (JumpSkill,2) creatureAbilityScore InventorySkill = figureAbility [Strength,Speed,Constitution] (InventorySkill,2) -- | -- Answers the gender of this creature. -- creatureGender :: Creature -> CreatureGender creatureGender = creature_gender -- | -- Answers true if the specified class is a favored class for this creature. -- isFavoredClass :: CharacterClass -> Creature -> Bool isFavoredClass character_class creature = character_class `Set.member` (creature_favored_classes creature)