76408e86 |
import random
|
42741360 |
from main import Game
|
76408e86 |
import game
import objects
import libtcodpy as libtcod
|
539d5ff4 |
import items
|
76408e86 |
|
42741360 |
from main import game_instance
|
76408e86 |
class Monster(object):
|
160f4887 |
def init(self,*a): pass
|
76408e86 |
def take_turn(self): pass
|
92d5ef55 |
def load_data(self, data):
for k,v in data.items():
setattr(self, k,v)
return self
|
76408e86 |
class BasicMonster(Monster):
def take_turn(self):
monster = self.owner
if game_instance.player.can_see(monster.x, monster.y):
if monster.distance_to(game_instance.player) > 1:
dx,dy = monster.move_towards(game_instance.player.x, game_instance.player.y)
counter = 0
|
539d5ff4 |
while (dx,dy) == (0,0) and counter < 10: # wiggle around if stuck
|
76408e86 |
counter += 1
dx,dy = monster.move(random.randrange(-1,2,2), random.randrange(-1,2,2))
|
160f4887 |
#print 'wiggled %s times' % counter
|
76408e86 |
elif game_instance.player.fighter.hp > 0:
monster.fighter.attack(game_instance.player)
|
92d5ef55 |
class Thief(BasicMonster):
def init(self, level):
self.level = level
self.player = level.player
self.inventory = []
self.skill = self.skill / 100.0
def take_turn(self):
if self.player.distance(self.owner.x, self.owner.y) < 2 and random.random() < .7:
self.steal()
else:
BasicMonster.take_turn(self)
def steal(self):
|
2dda4cb6 |
if self.player.inventory.keys():
print self.player.inventory.keys()
|
92d5ef55 |
game_instance.message( ('%s can\'t find anything to steal'%self.owner.name).capitalize(), libtcod.orange )
|
2dda4cb6 |
obj = random.choice(self.player.inventory.keys())
game_instance.message( ('%s tries to steal %s'%(self.owner.name,obj)).capitalize(), libtcod.red)
if random.random() < self.skill:
game_instance.message( ('%s successfully steals %s'%(self.owner.name,obj)).capitalize(), libtcod.orange)
obj = self.player.inventory[obj]
self.inventory.append(obj)
del self.player.inventory[obj.name]
|
92d5ef55 |
def death(self):
monster_death(self.owner)
for item in self.inventory:
self.drop(item)
def drop(self, item):
|
58ab2b1d |
print 'drop'
|
92d5ef55 |
item.x, item.y = self.owner.pos
self.level.add_object(item)
self.inventory.remove(item)
|
160f4887 |
class DjikstraMonster(Monster):
maps = {}
def init(self, level):
self.level = level
|
2dda4cb6 |
self.owner.always_visible = True
|
160f4887 |
self.opos = self.owner.x, self.owner.y
self.ppos = None
map = level.map
def take_turn(self):
pos = self.owner.x, self.owner.y
dx,dy = 0,0
|
58ab2b1d |
player_room = self.level.player.get_room()
|
160f4887 |
if self.level.is_visible(*pos):
if self.level.player.distance(*pos) < 2:
self.owner.fighter.attack(game_instance.player)
else:
|
58ab2b1d |
dx, dy = self.owner.get_step_towards(*self.level.player.pos)
|
160f4887 |
|
67ab39f4 |
else:
dj = self.level.get_djikstra(*self.owner.pos)
path = libtcod.dijkstra_path_set(dj, *self.level.player.pos)
x,y = libtcod.dijkstra_path_walk(dj)
if x is not None:
dx = x - self.owner.x
dy = y - self.owner.y
else:
print '!'
|
160f4887 |
self.owner.move(dx,dy)
|
539d5ff4 |
class AdvancedMonster(Monster):
def perimeter(self, rect):
for dx,row in enumerate(rect, -1):
for dy, cell in enumerate(row, -1):
if (dx in {-1,1}) or (dy in {-1,1}):
yield dx,dy, cell
def take_turn(self):
monster = self.owner
if not game_instance.player.can_see(monster.x, monster.y):
return
elif monster.distance_to(game_instance.player) > 1:
x,y = monster.x, monster.y
player_x, player_y = game_instance.player.pos
neighborhood = [ [0,0,0], [0,0,0], [0,0,0] ]
for dx in range(-1,2):
for dy in range(-1,2):
new_x = x+dx
new_y = y+dy
neighborhood[dx+1][dy+1] += int(monster.level.is_blocked(x+dx, y+dy))
dx, dy = monster.get_step_towards(player_x, player_y)
if neighborhood[dx+1][dy+1]:
open = []
for dx,dy, cell in self.perimeter(neighborhood):
if not cell:
open.append( (dx,dy) )
open = sorted(open, key=lambda (a,b): abs(a-dx)+abs(b-dy))[:3]
dx,dy = random.choice(open)
monster.move(dx,dy)
else:
monster.fighter.attack(game_instance.player)
|
76408e86 |
class ConfusedMonster(Monster):
def __init__(self, num_turns=game_instance.CONFUSE_NUM_TURNS):
self.num_turns = num_turns
def attach(self, object):
self.old_ai = object.ai
self.owner = object
object.ai = self
def take_turn(self):
if self.num_turns > 0:
game.message('%s is confused' % self.owner.name)
op = get_closest_monster(self.owner, game_instance.player)
if self.owner.distance_to(op) >= 2:
self.owner.move_towards(op.x, op.y)
else:
game.message('%s attacks %s in his confusion' % (self.owner.name, op.name))
if self.owner.fighter:
self.owner.fighter.attack(op)
if op.fighter:
op.fighter.attack(self.owner)
self.num_turns -= 1
else:
self.owner.ai = self.old_ai
game.message('%s is no longer confused' % self.owner.name)
def monster_death(monster):
monster.char = '\x09'
monster.color = libtcod.dark_red
monster.blocks = False
monster.fighter = None
monster.ai = None
monster.name = 'remains of %s' % monster.name
monster.send_to_back()
import functools
monster_at = functools.partial(game_instance.player.object_at,
filter=lambda obj: obj.fighter and obj is not game_instance.player
)
get_closest_monster = functools.partial(game_instance.player.get_closest_object,
filter=lambda obj: obj.fighter
)
get_visible_monsters = functools.partial(game_instance.player.get_visible_objects,
filter=lambda obj: obj.fighter
)
#####
|
42741360 |
import yaml
import os.path
import glob
import monsters
class MonsterLoader(object):
def __init__(self, dir):
self.dir = dir
def load_monsters(self):
for fn in glob.glob(os.path.join(self.dir,'*.yml')):
print 'fn', fn
for doc in yaml.safe_load_all(file(fn)):
self.load_monster(doc)
def load_monster(self, doc):
color = doc.get('color', None)
if color is None:
color = libtcod.red
elif hasattr(color, 'upper'):
color = getattr(libtcod, color)
else:
color = libtcod.Color(*color)
ai_class = doc.get('ai_class', BasicMonster)
|
92d5ef55 |
cls_data = {}
|
42741360 |
if ai_class is not BasicMonster:
|
92d5ef55 |
cls_data = {}
if hasattr(ai_class, 'items'):
nm = ai_class.pop('class_name', 'monsters.BasicMonster')
cls_data.update(ai_class)
ai_class = nm
|
42741360 |
module, clas = ai_class.rsplit('.',1)
module = __import__(module)
ai_class = getattr(module, clas)
|
92d5ef55 |
death_func = getattr(ai_class, 'death', monster_death)
|
42741360 |
print 'loading', doc
Game.register_monster_type(
(lambda doc:
lambda map,level,con,x,y: objects.Object( map, con, x,y,
doc['char'],
|
1d2d26d2 |
doc.get('name_fmt', '%s the %s') % (
libtcod.namegen_generate(doc['namegen_class']).capitalize(),
doc['race_name'].capitalize()
),
|
42741360 |
color,
True,
fighter=objects.Fighter(
hp=doc['hp'],
defense=doc['defense'],
power=doc['power'],
|
58ab2b1d |
death_function=death_func
|
42741360 |
),
|
92d5ef55 |
ai=ai_class().load_data(cls_data),
|
42741360 |
level=level
)
)(doc), doc['spawn_chance'])
|
92d5ef55 |
Game.register_monster_type(
lambda map,level, con,x,y: objects.Object(map, con,
x,y, '\x02', '%s the Orc' % libtcod.namegen_generate('Fantasy male'),
libtcod.blue, True,
fighter=objects.Fighter(hp=10, defense=2, power=3, death_function=monster_death),
ai=AdvancedMonster(),
level=level
), 8)
Game.register_monster_type(
lambda map,level, con,x,y: objects.Object(map, con,
x,y, '\x01', '%s the Troll' % libtcod.namegen_generate('Norse male'),
libtcod.orange, True,
fighter=objects.Fighter(hp=16, defense=1, power=4, death_function=monster_death),
ai=AdvancedMonster(),
level=level
), 2)
|
76408e86 |
Game.register_monster_type(
lambda map,level, con,x,y: objects.Object(map, con,
x,y, '\x01', '%s the Olog-Hai' % libtcod.namegen_generate('Norse male'),
libtcod.amber, True,
fighter=objects.Fighter(hp=16, defense=1, power=7, death_function=monster_death),
|
92d5ef55 |
ai=BasicMonster(),
|
76408e86 |
level=level
), 1)
|
539d5ff4 |
Game.register_monster_type(None, 7)
|
76408e86 |
|