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)