packages feed

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

-- | Action resolution, some kind of D20 system
-- (c) JP Moresmau 2009
module MoresmauJP.Rpg.Actions where

import Control.Monad.Writer

import MoresmauJP.Core.Screen
import MoresmauJP.Rpg.Character
import MoresmauJP.Util.Numbers
import MoresmauJP.Util.Random

import Text.Printf

initiative = [Dexterity,Willpower]
melee = [Strength,Dexterity]
archery = [Dexterity,Perception,Strength]
conversion=[Charisma,Willpower,Willpower]
trade=[Charisma,Charisma,Intelligence]
spellcasting=[Intelligence,Willpower]
spelllearning=[Intelligence]
escape=[Dexterity]
medecine=[Intelligence,Intelligence,Dexterity,Perception]
pray=[Charisma,Willpower]
detectTrap=[Perception]
disableTrap=[Dexterity,Perception]
steal=[Dexterity,Dexterity,Intelligence,Perception,Perception]

d20=(1,20)

roll :: (MonadRandom m) => (Int,Int) -> m Int
roll (low,high)= getRandomRange (low,high)

rolls :: (MonadRandom m) => Int -> (Int,Int) -> m [Int]
rolls a (low,high)= replicateM a (roll (low,high)) 

action :: (MonadRandom m,MonadWriter ScreenMessages m) => Character -> [Characteristic] -> Difficulty -> m (Character,RollResult)	
action c cs d= do
	r <- roll d20
	processAction c cs d r

actionNoExperience :: (MonadRandom m,MonadWriter ScreenMessages m) => Character -> [Characteristic] -> Difficulty -> m RollResult	
actionNoExperience c cs d=liftM snd (action c cs d)

diffResult :: (MonadRandom m,MonadWriter ScreenMessages m) => Character -> [Characteristic] -> Difficulty -> m Int
diffResult c cs d= do
	r <- roll d20
	let avgCs=score c cs
	let myScore=avgCs+d
	return (myScore-r)

diffResult' :: (MonadRandom m,MonadWriter ScreenMessages m) => Character -> [Characteristic] -> Difficulty -> (Int -> Int) -> m Int	
diffResult' c cs d f=do
	diff<-diffResult c cs d
	let diff'=f diff
	return 
		(if (diff<0)
			then (-diff')
			else diff')

competeWithDiff :: (MonadRandom m,MonadWriter ScreenMessages m) => Character -> Character-> [Characteristic] -> Int -> m ((Character,Character),RollResult)
competeWithDiff c1 c2 cs d=do
	r <- roll d20
	processCompeteDiff c1 c2 cs r d
	
compete :: (MonadRandom m,MonadWriter ScreenMessages m) => Character -> Character-> [Characteristic] -> m ((Character,Character),RollResult)
compete c1 c2 cs=competeWithDiff c1 c2 cs 0
	
processCompete :: (MonadWriter ScreenMessages m) => Character -> Character-> [Characteristic] -> Int -> m ((Character,Character),RollResult)
processCompete c1 c2 cs r =processCompeteDiff c1 c2 cs r 0

processCompeteDiff :: (MonadWriter ScreenMessages m) => Character -> Character-> [Characteristic] -> Int -> Int -> m ((Character,Character),RollResult)
processCompeteDiff c1 c2 cs r di= do
	let
		avgCs1=score c1 cs
		avgCs2=score c2 cs
		d=(div (avgCs1-avgCs2) 2)+di
	(c1b,rr1) <- (processAction c1 cs d r)
	(c2b,_) <- (processAction c2 cs (-d) r)
	return ((c1b,c2b),rr1)
	
score :: Character -> [Characteristic] -> Int
score c cs = avg (map (getCharacteristic' c Current) cs)	
	
processAction :: (MonadWriter ScreenMessages m) => Character-> [Characteristic] -> Difficulty -> Int -> m (Character,RollResult)	
processAction c cs d roll= do
	let 
		avgCs=score c cs
		myScore=bindInt (1,19) (avgCs+d)
		(rr,em)=evalResult roll myScore
		expGain=experienceGain myScore em (length cs)
		previousNormal=map (getCharacteristic' c Normal) allCharacteristics
		c3=foldr (\b c2->addCharacteristic' c2 Experience b expGain) c cs
		newNormal=map (getCharacteristic' c3 Normal) allCharacteristics
		augmented=filter (\(_,b)->b>0) $map (\(a,b,c)->(a,c-b)) $ zip3 allCharacteristics previousNormal newNormal
	mapM_ (\(a,b)->addScreenMessage (printf (msg b) (name c3) b (show a))) augmented
	return (c3,rr)
	where 
		msg 1="%s gains %d point in %s"
		msg _="%s gains %d points in %s"	

experienceGain :: Int -> Int -> Int -> Int
experienceGain score expM l= div ((max 1 (div ((20-score)^2) 10)) * expM) l
	
evalResult:: Int -> Int -> RollResultExp
evalResult roll score
	| (roll < (div score 5)) = (Success Exceptional (score-roll),4)
	| (roll < (div score 2)) = (Success Remarkable (score-roll),3)
	| (roll <= score) = (Success Standard (score-roll),2)
	| (roll > (20-(div score 5))) = (Failure Exceptional (roll-score),1)
	| (roll > (20-(div score 2))) = (Failure Remarkable (roll-score),1)
	| otherwise  = (Failure Standard (roll-score),1)


type Difficulty=Int

type RollResultExp=(RollResult,ExperienceMultiplier)
type ExperienceMultiplier=Int

data DifficultyLevel=NearImpossible | VeryHard | Hard | RatherHard | Neutral 
	| RatherEasy | Easy | VeryEasy | NearUnmissable
	deriving (Eq,Show,Read,Ord,Bounded,Enum)

toIntLevel :: DifficultyLevel -> Difficulty
toIntLevel level =3 * ((fromEnum level) - 4)

data RollResult = 
	Failure {
		grade::Grade,
		diff::Int} 
	| Success {
		grade::Grade,
		diff:: Int} 
	deriving (Show, Eq, Read)

isSuccess :: RollResult -> Bool
isSuccess (Success {})=True
isSuccess _ =False

subsequentDifficulty:: Grade -> Difficulty
subsequentDifficulty Standard=0
subsequentDifficulty Remarkable=2
subsequentDifficulty Exceptional=6

resultMultiplier :: Grade -> Int -> Float
resultMultiplier Standard i= fromIntegral i ** 1.5
resultMultiplier Remarkable i= fromIntegral i ** 2
resultMultiplier Exceptional i= fromIntegral i ** 3

resultMultiplierHigh :: Int -> RollResult -> Int
resultMultiplierHigh i (Failure gr d)=max 0 (i  - round((fromIntegral i * (resultMultiplier gr d))/100))
resultMultiplierHigh i (Success gr d)=i + round ((fromIntegral i * (resultMultiplier gr d))/100)

resultMultiplierLow :: Int -> RollResult -> Int
resultMultiplierLow i (Success gr d)=max 0 (i  - round((fromIntegral i * (resultMultiplier gr d))/100))
resultMultiplierLow i (Failure gr d)=i + round ((fromIntegral i * (resultMultiplier gr d))/100)

resultExtra :: Int -> RollResult -> Int
resultExtra i (Failure {})=i
resultExtra i rr=resultMultiplierHigh i rr

data Grade =
	Standard
	| Remarkable
	| Exceptional
	deriving (Show,Enum,Read,Eq)