packages feed

MazesOfMonad-1.0.3: src/MoresmauJP/Rpg/Character.hs

-- | Character handling
-- (c) JP Moresmau 2009
module MoresmauJP.Rpg.Character where

import Control.Monad.Writer


import Data.Array.IArray
import Data.List

import Text.Printf
import Text.Regex.Posix

import MoresmauJP.Core.Screen
import MoresmauJP.Rpg.Inventory
import MoresmauJP.Util.Numbers


type Name=String
type CharacteristicRatings=Array Characteristic Rating

data Character = Character {
	name::Name
	,gender::Gender
	,traits::CharacteristicRatings
	,inventory::Inventory
	, affects::[Affect]
	, spells::[Spell]
	} deriving (Eq,Show,Read)
	
data Gender = Male 
	| Female
	deriving (Show, Read,Eq)

data Affect=Affect {
	affected::Characteristic,
	affect::Int,
	untilTick::Int,
	source::String,
	affectDescription::String,
	liftDescription::String
	}
	deriving (Show, Read,Eq)

data Characteristic = Strength | Dexterity | Constitution | Willpower
	| Intelligence | Balance | Charisma | Perception | Physical | Mental
	deriving (Show,Read,Eq,Enum,Bounded,Ord,Ix)
	
data RatingScoreType=Normal |
	Current |
	Experience
	deriving (Show,Read,Enum,Eq,Ord,Bounded,Ix)

data RatingScore=RatingScore RatingScoreType Int
	deriving (Show,Read,Eq)

data Rating=Rating (Array RatingScoreType Int)
	deriving (Eq)

instance Show Rating where
	show r=printf "%d/%d(%d)" (getR Current r) (getR Normal r) (getR Experience r)
	
instance Read Rating where
	readsPrec _ s= let
		(_,_,after,(c:n:e:[]))= (s=~"([0-9]+)/([0-9]+)\\(([0-9]+)\\)") :: (String,String,String,[String])
		in [(Rating (array (Normal,Experience) [(Normal,(read n)),(Current,(read c)),(Experience,(read e))]),after)]
	
data SpellImpact=Negative | Positive | Recovery
	deriving (Show,Read,Eq)

data SpellDuration=Permanent | Temporary 
	deriving (Show,Read,Eq)	
	
data Spell=Spell {
	spellName::String,
	impactedChar::Characteristic,
	impact::SpellImpact,
	spellDuration::SpellDuration
	} deriving (Show,Read,Eq)	
	
	
allCharacteristics= [Strength .. Perception]
allHealth=[Physical .. Mental]
			
getDefaultHealth :: CharacteristicRatings -> CharacteristicRatings
getDefaultHealth crs=array (Strength,Mental) ((assocs crs) ++ [(Physical,(mkRating $ getCharacteristic crs Normal Constitution)),(Mental,(mkRating $ getCharacteristic crs Normal Balance))])

isOK :: Character -> Bool
isOK a = not (isOutOfService a)

isOutOfService :: Character -> Bool
isOutOfService a = (isDead a) || (isMad a)

isDead :: Character -> Bool
isDead a= (getCharacteristic' a Current Physical) <= 0  

isMad :: Character -> Bool
isMad a= (getCharacteristic' a Current Mental ) <= 0  

characterLevel :: Character -> Int
characterLevel c=avg $ map (getCharacteristic' c Normal) allCharacteristics

mkRating :: Int -> Rating
mkRating v = Rating (array (Normal,Experience) [(Normal,v),(Current, v),(Experience, 0)])
	

getR :: RatingScoreType -> Rating -> Int
getR rst (Rating array)= array ! rst

addCharacteristic :: CharacteristicRatings -> RatingScoreType -> Characteristic -> Int -> CharacteristicRatings
addCharacteristic cr rst char inc= let 
	(Rating arr)=cr ! char
	oldVal=arr ! rst
	arr2=(if (rst==Experience) 
		then nextLevel
		else id) (arr // [(rst,max 0 (oldVal+inc))])
	in cr // [(char,Rating arr2)]

setCharacteristic :: CharacteristicRatings -> RatingScoreType -> Characteristic -> Int -> CharacteristicRatings
setCharacteristic cr rst char inc= let 
	(Rating arr)=cr ! char
	arr2=(if (rst==Experience) 
		then nextLevel 
		else id) (arr // [(rst,inc)])
	in cr // [(char,Rating arr2)]

nextLevel :: (Array RatingScoreType Int) -> (Array RatingScoreType Int)
nextLevel arr = let
	experience=arr ! Experience
	normal=arr ! Normal
	nextLevelRequired=round (((fromIntegral normal) ** 3) / 3)
	in if (experience>nextLevelRequired)
		then
			let 
				experience'=experience-nextLevelRequired
				normal'=normal+1
				current=(arr ! Current) +1
				arr2=array (Normal,Experience) [(Normal,normal'),(Current, current),(Experience, experience')]
			in nextLevel arr2 	
		else
			arr

getCharacteristic :: CharacteristicRatings -> RatingScoreType -> Characteristic -> Int
getCharacteristic cr rst char = getR rst $ cr ! char

addCharacteristic' :: Character -> RatingScoreType -> Characteristic -> Int -> Character
addCharacteristic' c rst char inc= c{traits=addCharacteristic (traits c) rst char inc}

setCharacteristic' :: Character -> RatingScoreType -> Characteristic -> Int -> Character
setCharacteristic' c rst char inc= c{traits=setCharacteristic (traits c) rst char inc}

getCharacteristic' :: Character -> RatingScoreType -> Characteristic -> Int
getCharacteristic' c = getCharacteristic (traits c)

getHealthSummary :: Character -> [String]
getHealthSummary c= map (\x->printf "%s: %s" (show x) (show ((traits c) ! x))) allHealth

-- | add an affect to the character
addAffect :: Character -- ^ the character to affect
	-> Affect -- ^ the affect
	-> Character -- ^ the new character
addAffect c@(Character{affects=a}) aff=let
	c1=addCharacteristic' c Current (affected aff) (affect aff)
	in c1{affects=(aff:a)}

-- | remove any expired affects on the character
expireAffects:: Character -- ^ the character to test
	-> Int  -- ^ the current tick count
	-> (Character,[String]) -- ^ the new character and the description of lifted effects
expireAffects c@(Character{affects=a}) tc=let
	(a',a'')=partition (\x->(untilTick x)>tc) a
	in (c{affects=a'},map liftDescription a'')
	
recover :: (MonadWriter ScreenMessages m) => Character
	-> Characteristic
	-> Int
	-> m (Character,Int)
recover c ch n=do
	let
		current = getCharacteristic' c Current ch
		normal = getCharacteristic' c Normal ch
		updated = min normal (current + n)
		diff = updated-current
	when (diff>0) (addScreenMessage (printf "You recover %s!" (points ch diff)))
	return (setCharacteristic' c Current ch updated,(updated-current))
	
	
reflective :: Character -> String
reflective c=case gender c of
	Male -> "himself"
	Female -> "herself"
	
possessive :: Character -> String
possessive c=case gender c of
	Male -> "his"
	Female -> "her"
	
points :: Characteristic -> Int -> String
points c diff=let
	end=if diff==1 
			then "point"
			else "points"
	in (printf ("%d %s " ++ end) diff (show c))