packages feed

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

-- | Magic spells casting resolution
-- (c) JP Moresmau 2009
module MoresmauJP.Rpg.Magic where

import Control.Monad.Writer

import MoresmauJP.Core.Screen
import MoresmauJP.Rpg.Actions
import MoresmauJP.Rpg.Character

import MoresmauJP.Util.Lists
import MoresmauJP.Util.Random

import Text.Printf
	
type MagicStatus a= ((a,a),Bool)
	
allSpells :: [Spell]
allSpells= [
	Spell "Feel Better" Physical Recovery Permanent
	,Spell "Fire Ball" Physical Negative Permanent
	,Spell "Nimble Fingers" Dexterity Positive Temporary
	,Spell "Greasy Fingers" Dexterity Negative Temporary 
	,Spell "Madness" Mental Negative Permanent
	,Spell "Sanity" Mental Recovery Permanent
	,Spell "Focus... Focus... Focus..." Willpower Positive Temporary
	,Spell "Think! Think! Think!" Intelligence Positive Temporary
	,Spell "Doh!" Intelligence Negative Temporary
	,Spell "Ox Strength" Strength Positive Temporary
	,Spell "Troll Face" Charisma Negative Temporary
	,Spell "Drop dead gorgeous" Charisma Positive Temporary
	]
	
spellToAffect :: Spell -> RollResult -> Int -> Affect
spellToAffect spell rr tc=Affect (impactedChar spell)
	(diff rr)
	(tc + (((diff rr)+1) ^ 2))
	(spellName spell)
	(printf "Under the influence of %s (%s)" (spellName spell) (points (impactedChar spell) (diff rr)))
	(printf "Spell %s is lifting" (spellName spell))

spellsToMyself :: Character -> [Spell]
spellsToMyself c=removedAlreadyAffecting c $ filter ((Negative /=) . impact) (spells c)

spellsToOpponent :: Character -> Character -> [Spell]
spellsToOpponent c opponent=removedAlreadyAffecting opponent $ filter ((Negative ==) . impact) (spells c)

removedAlreadyAffecting :: Character -> [Spell] -> [Spell]
removedAlreadyAffecting c spells=let
	affectSources=map source (affects c)
	in filter (\x-> notElem (spellName x) affectSources) spells

spellToMyself ::  (MonadRandom m,MonadWriter ScreenMessages m) => Character -> Spell -> Int ->  m Character
spellToMyself c s tc=do
	(c2,rr)<-action c spellcasting (toIntLevel Neutral)
	spellToMyselfEffect c2 s tc rr

spellToMyselfEffect :: 	(MonadRandom m,MonadWriter ScreenMessages m) => Character -> Spell -> Int -> RollResult -> m Character
spellToMyselfEffect c s tc rr@(Failure {grade=Exceptional})=do
	fe<-randomPickp [ForgetSpell .. maxBound]
	damagesFumble c s tc rr fe
spellToMyselfEffect c s _ (Failure {})=do
	addMessage $ Message (c,s,Fail)
	return c
spellToMyselfEffect c s tc rr@(Success {})=
	case spellDuration s of
		Temporary->do
			addMessage  $ Message (c,s,Myself)
			let aff=(spellToAffect s rr tc)
			addScreenMessage $ affectDescription aff
			return (addAffect c aff)
		Permanent->do
			addMessage $ Message (c,s,Myself)
			(a,_)<-recover c (impactedChar s) (diff rr)
			return a
	
damagesFumble :: (MonadWriter ScreenMessages m) => Character -> Spell -> Int ->  RollResult  -> FumbleEvent -> m Character
damagesFumble c s tc rr SpellBounce= do
	let c12=addAffect c (spellToAffect s rr{diff=(-diff rr)} tc)
	addMessage $ Message (c12,s,Fumble SpellBounce 0)
	return c12
damagesFumble c s _ _ ForgetSpell= do
	let c12=c{spells=filter (s /=) (spells c)}
	addMessage $ Message (c12,s,Fumble ForgetSpell 0)
	return c12
damagesFumble c s _ _ MentalLoss= do
	let c12=addCharacteristic' c Current Mental (-1)
	addMessage $ Message (c12,s,Fumble MentalLoss 1)
	return c12
damagesFumble c s _ _ IntelligenceLoss= do
	let c12=addCharacteristic' c Current Intelligence (-1)
	addMessage $ Message (c12,s,Fumble IntelligenceLoss 1)
	return c12


addMessage :: (MonadWriter ScreenMessages m)=> Message -> m ()
addMessage =addScreenMessage . show 
	
spellToOpponent ::(MonadRandom m,MonadWriter ScreenMessages m)=> Character -> Character -> Spell -> Int -> m (MagicStatus Character)
spellToOpponent c1 c2 s tc= do
	((c1b,c2b),rr) <- compete c1 c2 spellcasting
	spellToOpponentEffect c1b c2b s tc rr
	
spellToOpponentEffect :: (MonadRandom m,MonadWriter ScreenMessages m) => Character -> Character -> Spell -> Int -> RollResult -> m (MagicStatus Character)
spellToOpponentEffect c1 c2 s tc rr@(Failure {grade=Exceptional})=do
	fe<-randomPickp [minBound .. maxBound]
	c1'<-damagesFumble c1 s tc rr fe
	return $ ((c1',c2),isOutOfService c1')
spellToOpponentEffect c1 c2 s _ (Failure {})=do
	addMessage $ Message (c1,s,Fail)
	return $ ((c1,c2),False)
spellToOpponentEffect c1 c2 s tc rr@(Success {})=
	case spellDuration s of
		Temporary->do
			addMessage $ Message (c1,s,Opponent)
			let c2'=addAffect c2 (spellToAffect s rr tc)
			return ((c1,c2'),isOutOfService c2')
		Permanent->do
			addMessage $ Message (c1,s,Opponent)
			let c2'=(addCharacteristic' c2 Current (impactedChar s) (-(diff rr)))
			return ((c1,c2'),isOutOfService c2')
		
-- complexity
-- danger
-- curse: add difficulty to all actions
-- need a time counter to evaluate when spell is finished

newtype Message=Message (Character,Spell,MessageType)

data MessageType=Fumble FumbleEvent Int | Fail |Myself | Opponent

data FumbleEvent = SpellBounce | ForgetSpell | MentalLoss | IntelligenceLoss
	deriving (Show,Read,Eq,Bounded,Enum,Ord)

instance Show Message where
	showsPrec _ (Message (_,s,Fumble MentalLoss a))=showString $ printf "The spell %s backfires on you and causes you to lose %s!" (spellName s) (points Mental a)
	showsPrec _ (Message (_,s,Fumble ForgetSpell _))=showString $ printf "The spell %s backfires and you promptly forget it!" (spellName s)
	showsPrec _ (Message (_,s,Fumble SpellBounce _))=showString $ printf "The spell %s bounces back and hits you!" (spellName s)
	showsPrec _ (Message (_,s,Fumble IntelligenceLoss a))=showString $ printf "The spell %s confuses you and causes you to lose %s!" (spellName s) (points Intelligence a)
	showsPrec _ (Message (_,s,Fail))=showString $printf "The spell %s fails" (spellName s)
	showsPrec _ (Message (_,s,Myself))=showString $printf "The spell %s worked" (spellName s)
	showsPrec _ (Message (c,s,Opponent))=showString $printf "%s casts %s" (name c) (spellName s)