packages feed

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

-- | NPC template definitions and character creation
-- (c) JP Moresmau 2009
module MoresmauJP.Rpg.NPC where

import Data.Array.IArray
import Data.Maybe
import Data.List
import MoresmauJP.Rpg.Actions
import MoresmauJP.Rpg.Inventory
import MoresmauJP.Rpg.Items
import MoresmauJP.Rpg.Character
import MoresmauJP.Util.Lists
import MoresmauJP.Util.Numbers
import MoresmauJP.Util.Random

troll=NPCTemplate {
	typeName="Troll",
	creatureType=Humanoid,
	attitudeRange=(3,8),
	traitRanges=[
		(Strength,(18,24)),
		(Dexterity,(7,11)),
		(Constitution,(16,26)),
		(Willpower,(10,12)),
		(Intelligence,(4,8)),
		(Balance,(4,8)),
		(Charisma,(3,5)),
		(Perception,(4,8))],
	possibleItems=[
		(RightHand,[
			(Nothing,3),
			(Just club,2),
			(Just bigclub,3)]
			)],
	possibleGold=(0,10)		
	}

goblin=NPCTemplate {
	typeName="Goblin",
	creatureType=Humanoid,
	attitudeRange=(5,10),
	traitRanges=[
		(Strength,(5,11)),
		(Dexterity,(8,11)),
		(Constitution,(7,12)),
		(Willpower,(5,8)),
		(Intelligence,(6,10)),
		(Balance,(4,8)),
		(Charisma,(3,7)),
		(Perception,(4,8))],
	possibleItems=[
		(RightHand,[
			(Just sword,2),
			(Just dagger,2),
			(Just hatchet,1)]
			),
		(LeftHand, [
			(Nothing,2),
			(Just smallShield,1)]
			),
		(Body, [
			(Nothing,3),
			(Just leatherArmor,1)
			]
			)
		],
	possibleGold=(0,30)	
	}

kobold=NPCTemplate {
	typeName="Kobold",
	creatureType=Humanoid,
	attitudeRange=(5,12),
	traitRanges=[
		(Strength,(4,8)),
		(Dexterity,(8,11)),
		(Constitution,(4,10)),
		(Willpower,(5,8)),
		(Intelligence,(7,12)),
		(Balance,(6,12)),
		(Charisma,(5,9)),
		(Perception,(8,12))],
	possibleItems=[
		(RightHand,[
			(Just sword,1),
			(Just dagger,3),
			(Just hatchet,1)]
			)], 
	possibleGold=(0,7)		
	}



giantRat=NPCTemplate {
	typeName="Giant Rat",
	creatureType=Animal,
	attitudeRange=(5,10),
	traitRanges=[
		(Strength,(3,6)),
		(Dexterity,(4,8)),
		(Constitution,(3,7)),
		(Willpower,(2,4)),
		(Intelligence,(2,3)),
		(Balance,(5,10)),
		(Charisma,(1,2)),
		(Perception,(4,8))],
	possibleItems=[],
	possibleGold=(0,0)	
	}

snake=NPCTemplate {
	typeName="Snake",
	creatureType=Animal,
	attitudeRange=(5,10),
	traitRanges=[
		(Strength,(3,6)),
		(Dexterity,(12,16)),
		(Constitution,(5,10)),
		(Willpower,(6,10)),
		(Intelligence,(2,4)),
		(Balance,(5,10)),
		(Charisma,(1,2)),
		(Perception,(8,10))],
	possibleItems=[],
	possibleGold=(0,0)	
	}	
	
vampire=NPCTemplate {
	typeName="Vampire",
	creatureType=Humanoid,
	attitudeRange=(3,8),	
	traitRanges=[
		(Strength,(12,18)),
		(Dexterity,(10,14)),
		(Constitution,(16,24)),
		(Willpower,(14,22)),
		(Intelligence,(14,20)),
		(Balance,(8,14)),
		(Charisma,(10,15)),
		(Perception,(6,10))],
	possibleItems=[],
	possibleGold=(10,60)	
	}
	
ghoul=NPCTemplate {
	typeName="Ghoul",
	creatureType=Humanoid,
	attitudeRange=(1,6),	
	traitRanges=[
		(Strength,(8,14)),
		(Dexterity,(8,12)),
		(Constitution,(10,14)),
		(Willpower,(10,14)),
		(Intelligence,(6,10)),
		(Balance,(6,10)),
		(Charisma,(4,8)),
		(Perception,(3,7))],
	possibleItems=[],
	possibleGold=(0,20)	
	}

outlaw=NPCTemplate {
	typeName="Outlaw",
	creatureType=Human,
	attitudeRange=(8,12),	
	traitRanges=[
		(Strength,(8,14)),
		(Dexterity,(8,14)),
		(Constitution,(8,12)),
		(Willpower,(5,8)),
		(Intelligence,(6,10)),
		(Balance,(8,12)),
		(Charisma,(6,10)),
		(Perception,(10,15))],
	possibleItems=[(RightHand,[
			(Just sword,2),
			(Just hatchet,1)]
			),
		(LeftHand, [
			(Nothing,2),
			(Just smallShield,1),
			(Just dagger,2)
			]
			),
		(Body, [
			(Nothing,3),
			(Just leatherArmor,1)
			]
			),
		(Head,[
			(Nothing, 3),
			(Just leathercap,1),
			(Just helmet,1)
			])],
		possibleGold=(0,50)		
	}

blackKnight=NPCTemplate {
	typeName="Black Knight",
	creatureType=Human,
	attitudeRange=(5,10),	
	traitRanges=[
		(Strength,(12,18)),
		(Dexterity,(12,18)),
		(Constitution,(12,18)),
		(Willpower,(10,15)),
		(Intelligence,(6,12)),
		(Balance,(8,12)),
		(Charisma,(8,14)),
		(Perception,(8,14))],
	possibleItems=[(RightHand,[
			(Just sword,2),
			(Just twoHandedWord,1),
			(Just battleaxe,1)]
			),
		(LeftHand, [
			(Nothing,2),
			(Just smallShield,2),
			(Just bigShield,1)
			]
			),
		(Body, [
			(Just leatherArmor,3),
			(Just chainMail,2),
			(Just fullPlate,1)
			]
			),
		(Head,[
			(Nothing, 3),
			(Just helmet,2),
			(Just heaume,1)
			])],
	possibleGold=(10,30)	
	}

minotaur=NPCTemplate {
	typeName="Minotaur",
	creatureType=Animal,
	attitudeRange=(2,5),	
	traitRanges=[
		(Strength,(14,20)),
		(Dexterity,(10,16)),
		(Constitution,(14,22)),
		(Willpower,(10,16)),
		(Intelligence,(6,10)),
		(Balance,(10,14)),
		(Charisma,(4,8)),
		(Perception,(5,10))],
	possibleItems=[],
	possibleGold=(0,0)
	}

peddler=NPCTemplate {
	typeName="Peddler",
	creatureType=Human,
	attitudeRange=(20,20),
	traitRanges=[
		(Strength,(6,12)),
		(Dexterity,(8,13)),
		(Constitution,(8,14)),
		(Willpower,(5,12)),
		(Intelligence,(8,14)),
		(Balance,(6,12)),
		(Charisma,(10,14)),
		(Perception,(8,12))],
	possibleItems=[
		(RightHand,[
			(Nothing,3),
			(Just dagger,2),
			(Just sword,3)]
			),
		(Body, [
			(Nothing,3),
			(Just leatherArmor,1)
			]
			)],
	possibleGold=(100,200)		
	}


allNPCTemplates :: [NPCTemplate]
allNPCTemplates= [troll,goblin,kobold,giantRat,snake,vampire,ghoul,outlaw,blackKnight,minotaur,peddler]

getNPCOccurences :: Int -> [(NPCTemplate,Int)]
getNPCOccurences characterLevel=
	let 
		totalTemplates=length allNPCTemplates
		ponderedLevel=div (characterLevel * 8) 10
		likelyhood npc=(npc,totalTemplates - (abs ((templateLevel npc) - ponderedLevel)))
	in map likelyhood allNPCTemplates
		
templateLevel :: NPCTemplate -> Int
templateLevel t=avg $ map (\(_,(low,high))-> div (low+high) 2) (traitRanges t)
		
type CharacteristicRanges=Array Characteristic (Int,Int)

data NPCTemplate = NPCTemplate {
	typeName::Name,
	creatureType::NPCType,
	attitudeRange::(Int,Int),
	traitRanges::[(Characteristic,(Int,Int))],
	possibleItems::[(Position,[(Maybe ItemType,Int)])],
	possibleGold::(Gold,Gold)
	} deriving (Show,Read,Eq)

data NPCCharacter = NPCCharacter {
	npcCharacter::Character,
	npcType::NPCType,
	npcAttitude::Int
	}deriving (Show,Read,Eq)

data NPCType=Animal | Humanoid | Human
	deriving (Show,Read,Eq,Ord,Enum,Bounded)

{--data AttitudeLevel=Rather | Quite | Very
	deriving (Show,Read,Eq,Ord,Enum,Bounded)

data Attitude=Friendly | Neutral | Aggressive | Hostile
	deriving (Show,Read,Eq,Ord,Enum,Bounded)

allAttitudeLevels=[(x,y) | y<-[Friendly .. Hostile], x<-[Rather .. Very]]
--}

data InteractionFromNPC=NPCFight | NPCTrade | NPCWait
	deriving (Show,Read,Eq,Enum,Bounded)

getInitialAttitude :: (MonadRandom m)=> NPCCharacter -> m (InteractionFromNPC)
getInitialAttitude NPCCharacter{npcAttitude=att}=do
	r <- (roll d20)
	let (rr,_)=evalResult r att
	return (case rr of
		Failure {}	-> NPCFight
		Success	{}	-> NPCWait)

data InteractionToNPC=Ignore | Fight | Trade | Convert | Steal 
	deriving (Show,Read,Eq,Enum,Bounded)

possibleNPCInteractions :: NPCCharacter -> [InteractionToNPC]
possibleNPCInteractions NPCCharacter{npcType=tp}=case tp of 
	Animal 		-> [Ignore .. Fight]
	Humanoid 	-> [Ignore .. Trade]
	Human 		-> [Ignore .. Steal]

maxPosBag :: NPCTemplate -> Int
maxPosBag nt=case creatureType nt of
	Animal 		-> 0
	Humanoid 	-> 0
	Human 		-> 10

generateFromTemplate :: (MonadRandom m)=> NPCTemplate -> m (NPCCharacter)
generateFromTemplate template=do
	let name=typeName template
	-- get gender
	rnd <- getRandomRange (1,2)
	let gender= if rnd==1
		then Male
		else Female
	-- generate ratings
	ranges<-mapM generateFromRange (sortBy (\x y-> compare (fst x) (fst y)) (traitRanges template))
	-- fill ratings with health
	let traits=getDefaultHealth $ array (head allCharacteristics,last allCharacteristics) ranges
	-- get item or Nothing for each position
	inv<-mapM (\(pos,items)->do
		it<-randomPickp (occurenceList items)
		return (pos,it)) (possibleItems template)
	-- only keep items, filter out Nothings
	let inv2=map (\(x,y)->(x,fromJust y)) (filter (isJust . snd) inv)
	gold<-getRandomRange $ possibleGold template
	attitudeLevel<-getRandomRange $ attitudeRange template
	let maxPos=maxPosBag template
	return (NPCCharacter (Character {
		name=name,
		gender=gender,
		traits=traits,
		inventory=makeFullInventory inv2 maxPos gold,
		affects=[],
		spells=[]
	}) (creatureType template) attitudeLevel)
	
generateFromRange :: (MonadRandom m)=>(Characteristic,(Int,Int))->m (Characteristic,Rating)
generateFromRange (c,(low,high))=do 
	rnd<-getRandomRange (low,high)
	return (c,mkRating rnd)