class Engine(object):
    
    __world_objects = WeakKeyDictionary()
    
    def __init__(self):
        self.__message_queue = []
        self.__cur_stat_surf = None
        self.__actors = []
        self.__items = []
        self.__gfx = None
        self.__quit_loop = False
        self.__last_id = 0
        self.__id_gen = self.__gen_id()
        self.__actors_on_screen = []
        self.__timer = 0
        self.__world_time = 0
        self.__load_fonts()
        self.__build_surf_cache()
        self.__set_game_instance()
        self.__player_actions = PlayerActions(self)
        self.__state_worker = StateWorker(self)

        self.__actor_grid = []
        self.__item_grid = []
        for y in xrange(200):
            aline = []
            iline = []
            for x in xrange(200):
                aline.append([])
                iline.append([])
            self.__actor_grid.append(aline)
            self.__item_grid.append(iline)
                    
        self.map = None
        self.dungeon = dungeon.DungeonsOfGogadan()
        self.quit_mes = QUIT
        
        self.stats = [att.Att('Strength', 'Important for Melee-Fighter', 20),
                      att.Att('Endurance', 'Important for Melee-Fighter'),
                      att.Att('Mind', 'Important for Spellcaster'),
                      att.Att('Health', 'How much can you take?', 45)]
        
        self.camera = Camera(20, 26)
        self.state = S_RUN
        self.__await_target = None
        
        Debug.init_debug(self)
        Debug.debug('init pygame')

        pygame.init()
        self.screen = pygame.display.set_mode((1024, 768))
        self.__clock = pygame.time.Clock()
        self.item_to_throw = None
        self.cursor = Cursor(self)
        self._items_to_choose = {}
        self._symbols = []
        c = 'abcdefghijklmonpqrstuvwxyz'
        for s in c:
            self._symbols.append(s)
    
    def call_pl_item_throw(self):
        self.__player_actions.throw_fire()        
        
    def re_init(self):
        Debug.debug('re_init')
        self.__quit_loop = False
        self.quit_mes = QUIT
                
        self.__load_fonts()
        self.__build_surf_cache()
        self.__set_game_instance()
        self.__clock = pygame.time.Clock()
        self.__id_gen = self.__gen_id()
        for act in self.__actors:
            self.__world_objects[act] = True
        for item in self.__items:
            self.__world_objects[item] = True
        self.__world_objects[self.map] = True
        self.__clear_surfaces()
        if hasattr(self, 'map'):
            self.redraw_map()
    
    def get_symbol(self):
        s = self._symbols.pop(0)
        return s
    
    def free_symbol(self, s):
        if s == None: return
        self._symbols.append(s)
        self._symbols.sort()
        
    def get_actor_at(self, pos):
        x, y = pos
        act = self.__actor_grid[y][x]
        return act[0] if len(act) > 0 else None
        #for actor in self.__actors:
        #    if actor.pos() == pos:
        #        return actor
        #return None
    def get_all_srd_actors(self, pos, radius=1, null_pos=False):
        mo = []
        for x in xrange(-radius, radius + 1):
            for y in xrange(-radius, radius + 1):
                mo.append((x, y))
        
        if not null_pos:
            mo.remove((0, 0))
        
        poss = []
        for m in mo:
            poss.append((m[0] + pos[0], m[1] + pos[1]))
        
        actors = []
        for p in poss:
            acts = self.get_actor_at(p)
            if not acts == None:
                actors.append(acts)
        return actors
        #for act in self.__actors:
        #    if act.pos() in poss:
        #        actors.append(act)
        #return actors
    def get_free_adj(self, pos):
        new_pos = None
        mo = [(-1, -1), (-1, 0), (-1, 1),
              (1, -1), (1, 0), (1, 1),
              (0, -1), (0, 1)]

        random.shuffle(mo) 
        while new_pos == None and len(mo) > 0: 
            t = mo.pop()
            new_pos = pos[0] + t[0], pos[1] + t[1]
            
            if not self.map.map_array[new_pos[1]][new_pos[0]][MT_FLAGS] & F_WALKABLE:
                new_pos = None
            else:
                for actor in self.__actors:
                    if actor.pos() == new_pos:
                        new_pos = None
                        break
        
        return new_pos
    def get_sc_up_pos(self):
        y = 0
        x = 0
        pos = None
        for line in self.map.map_array:
            x = 0
            for t in line:
                if t == MAP_TILE_up:
                    pos = x, y
                x += 1
            y += 1
        return pos if pos != None else self.map.get_random_pos()
    def get_sc_down_pos(self):
        y = 0
        x = 0
        pos = None
        for line in self.map.map_array:
            x = 0
            for t in line:
                if t == MAP_TILE_down:
                    pos = x, y
                x += 1
            y += 1
        return pos if pos != None else self.map.get_random_pos()
    def get_actor_by_id(self, id):
        for act in self.__actors:
            if act.id == id:
                return act
    def shout(self, text):
        self.__message_queue.insert(0, text)
        print text
    def change_map(self, down=True):
        
        if self.map == None:
            level = 1 
        elif down:
            level = self.map.level + 1
            self.__save_map()
        else:
            level = self.map.level - 1
            self.__save_map()

        if self.__load_map(level):
            return
        if level == 0:
            self.game_over()    
        
        self.__actors = []
        self.__actors.append(self.player)
        self.__items = []
        self.map = self.dungeon.get_map(level)
        
        for act in self.__actors:
            act.sc = sc(self.map.map_array)
            
        if down:
            pos = self.get_sc_up_pos()
        else:
            pos = self.get_sc_down_pos()
        self.player.set_pos(pos)
            
        r = self.camera.adjust(self.player.pos())
        while r:
            r = self.camera.adjust(self.player.pos())
    def create_gold(self, amount, pos):
        gold = dungeon.Populator.create_item('Gold', 'basic_stuff', 0)
        gold.amount = amount
        gold.set_pos(pos)
        self.add_item(gold)
        
    def summon_monster(self, caster, name, file, pos):
        mon = dungeon.Populator.create_creature(name, file)
        mon.ai = henchmanAI.HenchmanAI(mon)
        mon.ai.friends.add(caster.id)
        caster.ai.friends.add(mon.id)
        mon.set_pos(pos)
        mon.sc = sc(self.map.map_array)
        self.add_actor(mon, True)
        
    def create_humanoid(self):
        #man = races[1][3](True, 1)
        man = races[2][3](True, 1)
        #man.classkit = classkits[2][1](man)
        man.classkit = classkits[0][1](man)
        from ai import simpleai
        man.ai = henchmanAI.HenchmanAI(man)
        man.name = 'Nigg Yshur'
        pos = self.get_free_adj(self.player.pos())
        man.set_pos(pos)
        man.ai.friends.add(self.player.id)
        self.player.ai.friends.add(man.id)
        man.sc = sc(self.map.map_array)
                        #self.player = races[race][3](True, gender)
                        #self.player.classkit = classkits[classkit][1](self.player)
                        #self.player.classkit = classkits[classkit][1](self.player)
                        #b = dungeon.Populator.create_item('TomeOfVileUmbrages','tovu',100)
                        #self.player.pick_up(b)
    
    def create_character(self):
        c_res = Res('dc-pl.png', TILESIZE)
        g = 'female', 'male'
        gender = 1
        race = 0
        classkit = 0
        OK = False
        name = ''
        title = 'He'
        title2 = 'His'
        story = ['This is the incredible story of our hero',
               'Long time ago, there was a hero named',
               'There once was a time, long ago, when']
        
        s = random.choice(story)
        
        while not OK:
            self.screen.fill(BLACK)
            
            self.__render_text(self.screen, 'Build your character:', WHITE, ((35, 30)), 'big')
            
            self.screen.blit(self.__surf_cache['mes_block2'], (60, 65))
            
            
            img = pygame.transform.smoothscale(c_res.get(races[race][1 + gender]), (TILESIZE * 2, TILESIZE * 2))
            self.screen.blit(img , (75, 100))
            
            
            self.__render_text(self.screen, name, WHITE, ((73, 170)))
            
            y = 100
            self.__render_text(self.screen, s, WHITE, ((145, y)))
            self.__render_text(self.screen, name, GREEN, ((395, y)))
            y += 20
            self.__render_text(self.screen, 'the' , WHITE, ((145, y)))
            self.__render_text(self.screen, g[gender] + ' ' + races[race][0] + ' ' + classkits[classkit][0] + '.', GREEN, ((167, y)))
            y += 20
            self.__render_text(self.screen, title + ' ' + races[race][3].desc, WHITE, ((145, y)))
            y += 20
            self.__render_text(self.screen, classkits[classkit][1].desc.replace('$$$', title.lower()).replace('%%%', title2.lower()), WHITE, ((145, y)))
            
            
            self.__render_text(self.screen, 'Type in your name', WHITE, ((600, 100)))
            self.__render_text(self.screen, 'press F1 / F2 to change race', WHITE, ((600, 120)))
            self.__render_text(self.screen, 'press F3 / F4 to change class', WHITE, ((600, 140)))
            self.__render_text(self.screen, 'press F5 to change gender', WHITE, ((600, 160)))
            self.__render_text(self.screen, 'press Enter to start', WHITE, ((600, 180)))
            
            pygame.display.flip()
            for e in pygame.event.get():
                if e.type == pygame.QUIT:
                    sys.exit()
                if e.type == pygame.KEYDOWN:
                    if e.key == pygame.K_F5:
                        if gender == 1:
                            gender -= 1
                            title = 'She'
                            title2 = 'Her'
                        else:
                            gender += 1
                            title = 'He'
                            title2 = 'His'
                    if e.key == pygame.K_F3:
                        classkit += 1
                        if classkit >= len(classkits):
                            classkit = 0
                    elif e.key == pygame.K_F4:
                        classkit -= 1
                        if classkit < 0:
                            classkit = len(classkits) - 1
                    
                    elif e.key == pygame.K_F1:
                        race += 1
                        if race >= len(races):
                            race = 0
                    elif e.key == pygame.K_F2:
                        race -= 1
                        if race < 0:
                            race = len(races) - 1
                            
                    elif e.key == pygame.K_RETURN:
                        self.player = races[race][3](True, gender)
                        self.player.classkit = classkits[classkit][1](self.player)
                        b = dungeon.Populator.create_item('TomeOfVileUmbrages', 'tovu', 100)
                        self.player.pick_up(b)
                        self.player.clear_surfaces()
                        self.player.timer = 0
                        OK = True
                    elif e.key == pygame.K_BACKSPACE:
                        if len(name) > 0:
                            name = name[:-1]
                    else:
                        kn = pygame.key.name(e.key)
                        if kn in 'abcdefghijklmnopqrstuvwxyz' and len(name) < 10:
                            if len(name) > 0:
                                name += pygame.key.name(e.key)
                            else:
                                name += pygame.key.name(e.key).upper()
 
        
        
    
    def start(self, ts):
        
        if ts:
            self.create_character()
            self.change_map()
            self.create_humanoid()
            
        Debug.debug('starting mainloop')
        return self._main_loop()
    
    def is_inside_map(self, pos):
        return not (pos[0] < 0 or pos[1] < 0 or pos[0] >= self.map.width or pos[1] >= self.map.height)

    def update_item_pos(self, item, new_pos):
        x, y = item.pos()
        nx, ny = new_pos
        if item in self.__item_grid[y][x]: 
            self.__item_grid[y][x].remove(item)
        self.__item_grid[ny][nx].append(item)
        
    def update_actor_pos(self, act, new_pos):
        x, y = act.pos()
        nx, ny = new_pos
        if act in self.__actor_grid[y][x]:
            self.__actor_grid[y][x].remove(act)
        self.__actor_grid[ny][nx].append(act)
        
    def is_move_valid(self, actor, new_pos):
        if not self.is_inside_map(new_pos):
            return False   

        nx, ny = new_pos
        act = self.__actor_grid[ny][nx]
        if len(act) > 0:
            return act[0]
            
        valid = self.map.can_enter(new_pos, actor.move_mode)
        if valid and actor == self.player: 
            self.map.cur_surf = None    
            items = self.get_items_at(new_pos)
            if len(items) == 1:
                self.shout('You see a %s' % (items[0].get_name()))
            if len(items) > 1:
                 self.shout('You see several items here')
        
        return valid
    
    def get_range_target(self, cpos, tpos):
        return self.get_actor_at(tpos)
#        if cpos != tpos:
#            poss = line(cpos[0], cpos[1], tpos[0], tpos[1])
#            poss.pop(0)
#            for pos in poss:
#                actor = self.get_actor_at(pos)
#                if actor != None:
#                    return actor
#        else:
#            return self.caster
#        return None
    
    def throw_item(self, attacker, item, target_pos):
        t_pos = target_pos
        s_pos = attacker.pos()
        victim = self.get_range_target(attacker.pos(), target_pos)
        if victim != None:
            t_pos = victim.pos()
        dir = attacker.locateDirection(t_pos)
        gfx = throw.ThrowFX(dir, s_pos, t_pos, item)
        self.drawGFX(gfx)
        item.set_pos(t_pos)
        
    def range_attack(self, attacker, target_pos):
        t_pos = target_pos
        s_pos = attacker.pos()
        victim = self.get_range_target(attacker.pos(), target_pos)
        
        if victim != None:
            t_pos = victim.pos()
                
        dir = attacker.locateDirection(t_pos)
        gfx = projectile.ProjectileFX(dir, s_pos, t_pos)
        self.drawGFX(gfx)
        
        #while self.__gfx != None:
        #    self._world_draw()
        
        if victim != None:
            self.attack(attacker, victim, True)
    
    def __c_end_friendship(self, attacker, victim):
        victim.ai.friends.discard(attacker.id)
        victim.ai.hostile.add(attacker.id)
        attacker.ai.friends.discard(victim.id)
        attacker.ai.hostile.add(victim.id)
        todel = []
        for id in victim.ai.friends:
            act = self.get_actor_by_id(id)
            if act == None:
                todel.append(id)
            else:
                act.ai.friends.discard(attacker.id)
                act.ai.hostile.add(attacker.id)
        for id in todel:
            victim.ai.friends.discard(id)
        
        todel = []
        for id in attacker.ai.friends:
            act = self.get_actor_by_id(id)
            if act == None:
                todel.append(id)
            else:
                act.ai.friends.discard(victim.id)
                act.ai.hostile.add(victim.id)
        for id in todel:
            attacker.ai.friends.discard(id)
        
    def __c_apply_effects(self, attacker, victim):
        for fx in attacker.get_av_fx():
            if d(100) <= fx[1]:
                Debug.debug('Applied effect %s to %s by %s' % (fx[0], victim, attacker))
                f = fx[0](victim, attacker)
                f.tick()
            
        for fx in victim.get_dv_fx():
            skip = False
            for nt in fx[0].notrigger:
                if attacker.slot.weapon.flags & nt:
                    skip = True
            
            if not skip and d(100) <= fx[1]:
                Debug.debug('Applied effect %s to %s by %s' % (fx[0], attacker, victim))
                f = fx[0](attacker, victim)
                f.tick()
    
    def attack(self, attacker, victim, ranged=False):
        
        self.__c_end_friendship(attacker, victim)
        
        vi_adress = (victim == self.player and ['you'] or ['the ' + victim.name])[0]
        at_miss_adress = (attacker == self.player and ['You miss'] or [attacker.name + ' misses'])[0]
        at_hit_adress = (attacker == self.player and ['You hit'] or ['The ' + attacker.name + ' hits'])[0]
        at_kill_adress = (attacker == self.player and ['You killed'] or ['The ' + attacker.name + ' killed'])[0]
         
        wpn = attacker.weapon
        if wpn == None:
            wpn = attacker.unarmed_weapon

        highest_skill = None, 0
        for s in wpn.skills:
            v = getattr(attacker.skills, s)
            if v > highest_skill[1]:
                highest_skill = s, v
        
        attack_roll = d(100)
        
        if attack_roll > highest_skill:
            self.shout('%s %s.' % (at_miss_adress, vi_adress))
            Debug.debug('Miss!')
            return
        
        if victim.get_RA() > 0 and not victim.unconscious:
            victim.RA -= 1
            dodge_skill = victim.skills.Dodge
            dodge_roll = d(100)
            if dodge_roll <= dodge_skill:
                self.shout('%s dodged the attack' % (vi_adress))
                return
        
        self.__c_apply_effects(attacker, victim)
        
        hit_zone = victim.HP.get_random_zone()
        z = getattr(victim.HP, hit_zone)[2]
        
        if wpn.type != I_VOID:
            damage = wpn.damage[1]() + attacker.get_DM()[0]()
        else:
            damage = d(3) + attacker.get_DM()[0]()
        dam=self.do_damage(victim, damage, hit_zone, source=attacker)        
        self.shout('%s %s at the %s for %i damage' % (at_hit_adress, vi_adress, z,dam))
        
    def do_damage(self, act, dam, zone, type=D_GENERIC, source=None):
        return act.do_damage(dam, zone, type)
        
    def wait_for_target(self, point_of_entry):
        self.__await_target = point_of_entry
        self.__player_actions.cursor()

    def do_identify(self):
        self.__player_actions.identify()
        
    def target_choosen(self, pos):
        self.__await_target(pos)
        self.__await_target = None
        
        #self.player.fire(pos)
    
    def get_items_at(self, pos):
        x, y = pos
        #print self.__item_grid[y][x]
        return [item for item in self.__item_grid[y][x] if not item.picked_up]
        #return [item for item in self.__items if item.pos() == pos]
    def game_over(self):
        print 'You failed'
        self.__quit_loop = True
        self.__actors = []
    
    def redraw_map(self):
        self.map.cur_surf = None
    def redraw_stats(self):
        self.__cur_stat_surf = None
    
    def add_to_world_objects(self, obj):
        self.__world_objects[obj] = True
    def add_actor(self, actor, add=True):
        if add: self.__actors.append(actor)
        if self.map != None:
            actor.sc = sc(self.map.map_array)
        self.__world_objects[actor] = True
    def add_item(self, item, add=True):
        if add: self.__items.append(item)
        self.__world_objects[item] = True
    def del_actor(self, actor):
        if actor in self.__actors:
            self.__actors.remove(actor)
        if actor in self.__actors_on_screen:
            self.__actors_on_screen.remove(actor)
        x, y = actor.pos()
        if actor in self.__actor_grid[y][x]:
            self.__actor_grid[y][x].remove(actor)
    def del_item(self, item):
        if item in self.__items:
            self.__items.remove(item)
        x, y = item.pos()
        if item in self.__item_grid[y][x]:
            self.__item_grid[y][x].remove(item)
        #if item.player_symbol != None:
        #    self.free_symbol(item.player_symbol)
            
    def get_id(self):
        return self.__id_gen.next()
    
    def drawGFX(self, gfx):
        self.__gfx = gfx
        self.state = S_GFX
        
        # good or bad??
        while self.__gfx != None:
            self._world_draw()

    
    def _main_loop(self):
        while not self.__quit_loop:
            self.__clock.tick(40)
            self._world_move()
            self._world_draw()
            self._world_input()
#            print self.clock.get_fps()
        self.std_font = None
        self.__clock = None
        self.__cur_stat_surf = None
        self.__last_id = self.__id_gen.next()
        self.__id_gen = None
        self.__clear_surfaces()
        return self.quit_mes
    def _world_input(self):
        for e in pygame.event.get():
            self.__quit_loop = e.type == pygame.QUIT 
            
            if e.type == pygame.KEYDOWN:
                
                if e.key == GAME_SAVE_QUIT:
                    self.__quit_loop = True
                    self.quit_mes = SAVE
                
                # --- cheat keys >>>
                if e.key == pygame.K_F1:
                    for line in self.map.map_array:
                        l = ''
                        for s in line:
                            l = l + str(s[0])
                        print l    
                
                if e.key == pygame.K_F2:
                    self.player.cur_health = 200
                    self.player.cur_endurance = 5000
                    self.player.cur_mind = 5000
                    self.player.cur_strength = 5000
                    
                if e.key == pygame.K_F3:
                    for item in self.__items:
                        print item.name, item.pos()

                if e.key == pygame.K_F4:                        
                    for act in self.__actors:
                        print act.name, act.timer
                              
                if e.key == pygame.K_F5:
                    for S in gc.get_referrers(Surface):
                        if isinstance(S, Surface):
                            print S

                    print gc.get_referrers(Surface)
                if e.key == pygame.K_F6:
                    self.create_humanoid()      
                # <<< cheat keys ---
                
                if not self.state == S_GFX:
                    self.__cur_stat_surf = None
                self.moved = True
                
                if self.state == S_RUN and not self.player.unconscious:
                    if e.key in PLAYER_ACTIONS: 
                        self.__player_actions.__getattribute__(PLAYER_ACTIONS[e.key])()

                elif self.state in STATE_WORKER:
                    self.__state_worker.__getattribute__(STATE_WORKER[self.state])(e.key)

        if self.state == S_PLAYER_CURSOR:
            pygame.event.pump()
            keys = pygame.key.get_pressed()
            for key in MOVES:
                if keys[key]:
                    pygame.time.wait(150)
                    if key == MOVE_WAIT:
                        pos = self.__actors_on_screen[0].pos()
                        if pos == self.cursor.pos():
                            self.__actors_on_screen.append(self.__actors_on_screen.pop(0))
                            pos = self.__actors_on_screen[0].pos()
                        self.cursor.set_pos(pos)
                        self.__actors_on_screen.append(self.__actors_on_screen.pop(0))
                    else:
                        self.cursor.move(key)

        if self.state == S_RUN  and not self.player.unconscious:
            pygame.event.pump()
            keys = pygame.key.get_pressed()
            [self.player.move(key) for key in MOVES if keys[key] and self.player.timer <= 0]
    def _world_move(self):
        if not self.__quit_loop:
            if self.state == S_RUN:
                self.__actors.sort(sort_by_time) #actors with lowest timer first
                diff = self.__actors[0].timer
                self.__timer += diff
                self.__world_time += diff
                
                if self.__timer > 500:             #act-independent issues # one combat-round
                    [act.tick() for act in self.__actors]
                    self.__timer -= 500
                        
                for actor in self.__actors:
                    if actor.timer > 0:
                        actor.timer -= diff
                    else:
                        actor.act()
                
            elif self.state == S_GFX:
               if self.__gfx == None:
                    self.state = S_RUN
    def _world_draw(self):

        if self.__gfx == None or self.__gfx.redraw:

            if self.camera.adjust(self.player.pos()):
                self.map.cur_surf = None
            self.screen.fill((0, 0, 0))
            if not self.player.dazzled:
                self.screen.blit(self.__get_map_surface(), (-self.camera.x * TILESIZE, -self.camera.y * TILESIZE))
                    
            if not self.player.dazzled:
                for item in self.__items: 
                    if not item.picked_up and (self.player.sc.lit(item.x, item.y) or self.player.x == item.x and self.player.y == item.y):
                        try:
                            self.screen.blit(self.__get_item_surface(item), (item.x * TILESIZE - self.camera.x * TILESIZE, item.y * TILESIZE - self.camera.y * TILESIZE))
                        except:
                            print sys.exc_info()
                            print item.name, item.pos(), 'is invalid!!!!'
            
            for act in self.__actors:
                if  act == self.player or self.player.sc.lit(act.x, act.y) and not self.player.dazzled:
                    try:
                        self.screen.blit(self.__get_actor_surface(act), (act.x * TILESIZE - self.camera.x * TILESIZE, act.y * TILESIZE - self.camera.y * TILESIZE))
                        if not act in self.__actors_on_screen:
                            self.__actors_on_screen.append(act)
                    except:
                        print sys.exc_info()
                        print act.name, act.pos(), 'is invalid!!!!'
                else:
                    if act in self.__actors_on_screen:
                        self.__actors_on_screen.remove(act)
                    
            if self.state == S_PLAYER_CURSOR:
                self.screen.blit(self.cursor.get_surf(), (self.cursor.x * TILESIZE - self.camera.x * TILESIZE, self.cursor.y * TILESIZE - self.camera.y * TILESIZE))
            
            self.screen.blit(self.__get_message_surface(), (0, 768 - 128))
            self.screen.blit(self.__get_statblock_surface(), (1024 - 192, 0))
    
        if self.__gfx != None:
            pos = self.__gfx.pos()
            if pos == None:
                self.__gfx = None
                self.__cur_stat_surf = None
            else:
                x, y = pos
                #tiles = __get_affected_tiles(x,y,self.__gfx.get_surf())
                x = x / TILESIZE + self.camera.x
                y = y / TILESIZE + self.camera.y
                #print x,y,self.player.pos(),self.player.sc.lit(x, y)
                if self.player.sc.lit(x, y):
                    self.screen.blit(self.__gfx.get_surf(), pos)
                    self.__gfx.tick()
            
        pygame.display.flip()

    def __get_map_surface(self):
        if self.map.cur_surf == None:
            self.player.sc.do_fov(self.player.x, self.player.y, 15)
            surf_map = pygame.Surface((self.map.width * TILESIZE, self.map.height * TILESIZE))
            cx, cy, cw, ch = self.camera.get_view_port()       
            for x in xrange(max(cx, 0), min(self.map.width, cw + 1)):

                for y in xrange(max(cy, 0), min(self.map.height, ch + 1)):
                    pos = (x, y) == self.player.pos() 
                    lit = self.player.sc.lit(x, y)
                    memo = self.map.map_array[y][x][MT_FLAGS] & F_MEMO
                    if pos or lit or memo:
                        blit_position = ((x) * TILESIZE, (y) * TILESIZE)
                        surf_map.blit(self.map.get_tile_at(x, y), blit_position)
                        
                        if not pos and not lit and memo:
                            surf_map.blit(self.__surf_cache['FOW'], blit_position)
                        
                        if not self.map.map_array[y][x][MT_FLAGS] & F_MEMO:
                            tile = self.map.map_array[y][x]
                            new_tile = tile[0], tile[1], tile[2] ^ F_MEMO
                            self.map.map_array[y][x] = new_tile

            self.map.cur_surf = surf_map
        
        return self.map.cur_surf
    def __get_actor_surface(self, act):
        if act.cur_surf == None:
            surf_act = pygame.Surface((TILESIZE, TILESIZE), pygame.SRCALPHA, 32)
            surf_act.blit(act.get_tile(), (0, 0)) 
            act.cur_surf = surf_act
        return act.cur_surf
    def __get_item_surface(self, item):
        if item.cur_surf == None:
            surf_item = pygame.Surface((TILESIZE, TILESIZE), pygame.SRCALPHA, 32)
            surf_item.blit(item.get_dd_img(), (0, 0)) 
            item.cur_surf = surf_item
        return item.cur_surf
    def __get_message_surface(self):
        surf = pygame.Surface((1024 - 192, 128))
        surf.blit(self.__surf_cache['mes_block'], (0, 0))
        y = 100
        for mes in self.__message_queue:
            self.__render_text(surf, mes, WHITE, (20, y))
            y -= 20
            if y < 10:
                break
        return surf
    def __get_statblock_surface(self):
        if self.__cur_stat_surf == None:
            surf = pygame.Surface((192, 768))
            surf.fill(BLACK)
            #surf.blit(self.__surf_cache['stat_block'], (0, 0))
            
            if self.state in (S_RUN, S_GFX):
                self.__draw_stat_block(surf)    

            if self.state in CHOOSE_STATES:
                self.__draw_item_choose(surf, CHOOSE_STATES[self.state])
            
            self.__cur_stat_surf = surf
            
        return self.__cur_stat_surf
    
    def __set_game_instance(self):
        dungeon.Map.game = self
        Actor.game = self
        Item.game = self
        AI.game = self
        Camera.game = self
        dungeon.Populator.game = self
        magic.Spell.game = self
        GFX.game = self
        att.Att.game = self
        dungeon.SADungeon.game = self
    def __draw_item_choose(self, surf, message):
        self.__render_text(surf, message, WHITE, (16, 20))
        y = 38
        abc = self._items_to_choose.keys()
        #abc.sort(sort_by_type)
        s48 = pygame.Surface.copy(self.__surf_cache['stat_32'])
       
        for key in abc:
            item = self._items_to_choose[key]
            color = WHITE
            if hasattr(item, 'special'):
                if item.special:
                    color = GREEN
            if hasattr(item, 'color'):
                color = item.color    
            if hasattr(item, 'get_name'):
                name = item.get_name()
            else:
                name = item.name
            
            surf.blit(s48, (16, y))
            if hasattr(item, 'cur_surf'):
                surf.blit(self.__get_item_surface(item), (16, y))
            self.__render_text(surf, '%s' % (key), WHITE, (16, y))
            #self.__render_text(surf, '%s -' % (key), WHITE, (16, y))
            self.__render_text(surf, name, color, (64, y)); y += 18
            
            info = item.info()
            for line in info:
                self.__render_text(surf, line, color, (64, y)); y += 18
            if len(info) == 0:
                y += 18
            y += 18
            
    def __draw_stat_block(self, surf):
        
        bt = self.player.get_body_tile()
        bt = pygame.transform.smoothscale(bt, (96, 96))
        bt.set_alpha(122)
        y = 50
        surf.blit(bt, (32, y))
        
        x = 77
        y = 55
        color = WHITE
        if self.player.HP.Head[0] == self.player.HP.Head[1]: color = GREEN
        if self.player.HP.Head[0] == 0: color = YELLOW
        if self.player.HP.Head[0] < 0: color = RED
        self.__render_text(surf, str(self.player.HP.Head[0]), color, (x, y))
        
        y = 75
        color = WHITE
        if self.player.HP.Chest[0] == self.player.HP.Chest[1]: color = GREEN
        if self.player.HP.Chest[0] == 0: color = YELLOW
        if self.player.HP.Chest[0] < 0: color = RED

        self.__render_text(surf, str(self.player.HP.Chest[0]), color, (x, y))
        
        y = 95
        color = WHITE
        if self.player.HP.Abdomen[0] == self.player.HP.Abdomen[1]: color = GREEN
        if self.player.HP.Abdomen[0] == 0: color = YELLOW
        if self.player.HP.Abdomen[0] < 0: color = RED

        self.__render_text(surf, str(self.player.HP.Abdomen[0]), color, (x, y))
        
        x = 40
        y = 85
        color = WHITE
        if self.player.HP.L_Arm[0] == self.player.HP.L_Arm[1]: color = GREEN
        if self.player.HP.L_Arm[0] == 0: color = YELLOW
        if self.player.HP.L_Arm[0] < 0: color = RED

        self.__render_text(surf, str(self.player.HP.L_Arm[0]), color, (x, y))
        
        x = 110
        color = WHITE
        if self.player.HP.R_Arm[0] == self.player.HP.R_Arm[1]: color = GREEN
        if self.player.HP.R_Arm[0] == 0: color = YELLOW
        if self.player.HP.R_Arm[0] < 0: color = RED
        self.__render_text(surf, str(self.player.HP.R_Arm[0]), color, (x, y))
        
        x = 55
        y = 130
        color = WHITE
        if self.player.HP.L_Leg[0] == self.player.HP.L_Leg[1]: color = GREEN
        if self.player.HP.L_Leg[0] == 0: color = YELLOW
        if self.player.HP.L_Leg[0] < 0: color = RED
        self.__render_text(surf, str(self.player.HP.L_Leg[0]), color, (x, y))
        
        x = 95
        color = WHITE
        if self.player.HP.R_Leg[0] == self.player.HP.R_Leg[1]: color = GREEN
        if self.player.HP.R_Leg[0] == 0: color = YELLOW
        if self.player.HP.R_Leg[0] < 0: color = RED
        self.__render_text(surf, str(self.player.HP.R_Leg[0]), color, (x, y))




                    
        stats = [('STR ', self.player.STR, self.player.get_STR()),
                 ('CON ', self.player.CON, self.player.get_CON()),
                 ('DEX ', self.player.DEX, self.player.get_DEX()),
                 ('SIZ ', self.player.SIZ, self.player.get_SIZ()),
                 ('INT ', self.player.INT, self.player.get_INT()),
                 ('POW ', self.player.POW, self.player.get_POW()),
                 ('CHA ', self.player.CHA, self.player.get_CHA()),
                 ('DM  ', self.player.DM[1], self.player.get_DM()[1])]
        y = 245
        for line in stats:
            b = 0
            if len(str(line[1])) > 3:
                b = 18
            self.__render_text(surf, str(line[0]).rjust(2, ' '), WHITE, (16, y)); 
            self.__render_text(surf, str(line[1]).rjust(2, ' '), GREEN, (55, y)); 
            self.__render_text(surf, '/', WHITE, (70 + b, y));
            self.__render_text(surf, str(line[2]).rjust(2, ' '), WHITE, (80 + b, y));
            y += 18

        #pygame.draw.circle(Surface, color, pos, radius, width=0): return Rect
        
       
        
            #r = pygame.Surface((100, 2))
            #r.fill(RED)
            #surf.blit(r, (16, y + 20))
            #size = float(line[2]) / float(line[1]) * 100
            #if size > 0:
            #    g = pygame.Surface((size, 2))
            #    if size > 100:
            #        g.fill(BLUE)
            #    else:
            #        g.fill(GREEN)
            #surf.blit(g, (16, y + 20))
            
            
        
      
        self.__render_text(surf, 'Gold:', WHITE, (16, 510))
        self.__render_text(surf, str(self.player.gold), WHITE, (90, 510))

        self.__render_text(surf, 'XP:', WHITE, (16, 528))
        self.__render_text(surf, str(self.player.xp), WHITE, (90, 528))
        
        i = 0
        y = 600
        count = len(self.player.items)
        s32 = pygame.Surface.copy(self.__surf_cache['stat_32'])
        sgreen = pygame.Surface.copy(self.__surf_cache['sgreen'])
        for h in xrange(3):
            for x in xrange(5):
                pos = (x * TILESIZE + x * 3 + 5, h * TILESIZE + y + h * 3)
                surf.blit(s32, pos)
                if i < count:
                    item = self.player.items[i]
                    if item.equipped:
                        surf.blit(sgreen, pos)
                    surf.blit(self.__get_item_surface(item), pos)
                    self.__render_text(surf, self.player.items[i].get_ps(), WHITE, (pos))
                    i += 1
                #print (x*TILESIZE,h*TILESIZE+y)
        

        self.__render_text(surf, self.dungeon.name, WHITE, (16, 710))
        self.__render_text(surf, 'Level: %i' % (self.map.level), WHITE, (16, 728))
        
        
    def __render_text(self, surf, text, color, pos, font='std'):
        t = self.__font_cache[font].render('%s' % (text), True, color)
        ts = self.__font_cache[font].render('%s' % (text), True, BLACK)
        surf.blit(ts, (pos[0] + 1, pos[1] + 1))
        surf.blit(t, pos)
    def __load_fonts(self):
        #self.std_font = pygame.font.Font(os.path.join('font', 'jesaya.ttf'), 14)
        self.__font_cache = {'std':pygame.font.Font(os.path.join('font', 'alex.ttf'), 17),
                           'big':pygame.font.Font(os.path.join('font', 'alex.ttf'), 25)}
        
    def __save_map(self):
        self.__clear_surfaces()
        self.__actors.remove(self.player)
        data = self.map, self.__actors, self.__items , self.player.pos()
        if os.access('MAP%i.gz' % (self.map.level), os.F_OK):
             os.remove('MAP%i.gz' % (self.map.level))
        FILE = gzip.open('MAP%i.gz' % (self.map.level), 'w')
        pickle.dump(data, FILE, 2)
        FILE.close()
    def __load_map(self, level):
        if os.access('MAP%i.gz' % (level), os.F_OK):
            FILE = gzip.open('MAP%i.gz' % (level), 'r')
            self.map, self.__actors, self.__items, pos = pickle.load(FILE)
            
            self.__set_game_instance()
            FILE.close()
            self.player.set_pos(pos)
            r = self.camera.adjust(self.player.pos())
            while r:
                r = self.camera.adjust(self.player.pos())
            self.__actors.append(self.player)
            for act in self.__actors:
                self.__world_objects[act] = True
                act.sc = sc(self.map.map_array)
            self.player.sc.do_fov(self.player.x, self.player.y, self.player.cur_mind / 20 + 5)
                
            for item in self.__items:
                self.__world_objects[item] = True
                
            self.__world_objects[self.map] = True
            self.__clear_surfaces()
            
            self._world_draw()
            return True
        return False
        self.__quit_loop = True
        self.quit_mes = SAVE
    def __build_surf_cache(self):
        fow_surf = pygame.Surface((TILESIZE, TILESIZE))
        fow_surf.fill(BLACK)
        fow_surf.set_alpha(100)
        sgreen = pygame.Surface((TILESIZE, TILESIZE))
        sgreen.fill(GREEN)
        sgreen.set_alpha(100)
        self.__surf_cache = {'sgreen':sgreen,
                             'FOW': fow_surf,
                             'stat_block': load_image('stat.png'),
                             'stat_48': load_image('48.png'),
                             'stat_32': pygame.transform.smoothscale(load_image('48.png'), (32, 32)),
                             'mes_block':load_image('mes_block.png'),
                             'mes_block2':load_image('mes_block2.png')}
    def __clear_surfaces(self):
        for obj in self.__world_objects.keys():
            obj.clear_surfaces()
        self.cursor.cursor_surf = None
    def __gen_id(self):
        for x in xrange(self.__last_id, 9999999):
            yield x
class Engine(object):

    __world_objects = WeakKeyDictionary()

    def __init__(self):
        self.__message_queue = []
        self.__cur_stat_surf = None
        self.__actors = []
        self.__items = []
        self.__gfx = None
        self.__quit_loop = False
        self.__last_id = 0
        self.__id_gen = self.__gen_id()
        self.__actors_on_screen = []
        self.__timer = 0
        self.__world_time = 0
        self.__load_fonts()
        self.__build_surf_cache()
        self.__set_game_instance()
        self.__player_actions = PlayerActions(self)
        self.__state_worker = StateWorker(self)

        self.__actor_grid = []
        self.__item_grid = []
        for y in xrange(200):
            aline = []
            iline = []
            for x in xrange(200):
                aline.append([])
                iline.append([])
            self.__actor_grid.append(aline)
            self.__item_grid.append(iline)

        self.map = None
        self.dungeon = dungeon.DungeonsOfGogadan()
        self.quit_mes = QUIT

        self.stats = [
            att.Att('Strength', 'Important for Melee-Fighter', 20),
            att.Att('Endurance', 'Important for Melee-Fighter'),
            att.Att('Mind', 'Important for Spellcaster'),
            att.Att('Health', 'How much can you take?', 45)
        ]

        self.camera = Camera(20, 26)
        self.state = S_RUN
        self.__await_target = None

        Debug.init_debug(self)
        Debug.debug('init pygame')

        pygame.init()
        self.screen = pygame.display.set_mode((1024, 768))
        self.__clock = pygame.time.Clock()
        self.item_to_throw = None
        self.cursor = Cursor(self)
        self._items_to_choose = {}
        self._symbols = []
        c = 'abcdefghijklmonpqrstuvwxyz'
        for s in c:
            self._symbols.append(s)

    def call_pl_item_throw(self):
        self.__player_actions.throw_fire()

    def re_init(self):
        Debug.debug('re_init')
        self.__quit_loop = False
        self.quit_mes = QUIT

        self.__load_fonts()
        self.__build_surf_cache()
        self.__set_game_instance()
        self.__clock = pygame.time.Clock()
        self.__id_gen = self.__gen_id()
        for act in self.__actors:
            self.__world_objects[act] = True
        for item in self.__items:
            self.__world_objects[item] = True
        self.__world_objects[self.map] = True
        self.__clear_surfaces()
        if hasattr(self, 'map'):
            self.redraw_map()

    def get_symbol(self):
        s = self._symbols.pop(0)
        return s

    def free_symbol(self, s):
        if s == None: return
        self._symbols.append(s)
        self._symbols.sort()

    def get_actor_at(self, pos):
        x, y = pos
        act = self.__actor_grid[y][x]
        return act[0] if len(act) > 0 else None
        #for actor in self.__actors:
        #    if actor.pos() == pos:
        #        return actor
        #return None
    def get_all_srd_actors(self, pos, radius=1, null_pos=False):
        mo = []
        for x in xrange(-radius, radius + 1):
            for y in xrange(-radius, radius + 1):
                mo.append((x, y))

        if not null_pos:
            mo.remove((0, 0))

        poss = []
        for m in mo:
            poss.append((m[0] + pos[0], m[1] + pos[1]))

        actors = []
        for p in poss:
            acts = self.get_actor_at(p)
            if not acts == None:
                actors.append(acts)
        return actors
        #for act in self.__actors:
        #    if act.pos() in poss:
        #        actors.append(act)
        #return actors
    def get_free_adj(self, pos):
        new_pos = None
        mo = [(-1, -1), (-1, 0), (-1, 1), (1, -1), (1, 0), (1, 1), (0, -1),
              (0, 1)]

        random.shuffle(mo)
        while new_pos == None and len(mo) > 0:
            t = mo.pop()
            new_pos = pos[0] + t[0], pos[1] + t[1]

            if not self.map.map_array[new_pos[1]][
                    new_pos[0]][MT_FLAGS] & F_WALKABLE:
                new_pos = None
            else:
                for actor in self.__actors:
                    if actor.pos() == new_pos:
                        new_pos = None
                        break

        return new_pos

    def get_sc_up_pos(self):
        y = 0
        x = 0
        pos = None
        for line in self.map.map_array:
            x = 0
            for t in line:
                if t == MAP_TILE_up:
                    pos = x, y
                x += 1
            y += 1
        return pos if pos != None else self.map.get_random_pos()

    def get_sc_down_pos(self):
        y = 0
        x = 0
        pos = None
        for line in self.map.map_array:
            x = 0
            for t in line:
                if t == MAP_TILE_down:
                    pos = x, y
                x += 1
            y += 1
        return pos if pos != None else self.map.get_random_pos()

    def get_actor_by_id(self, id):
        for act in self.__actors:
            if act.id == id:
                return act

    def shout(self, text):
        self.__message_queue.insert(0, text)
        print text

    def change_map(self, down=True):

        if self.map == None:
            level = 1
        elif down:
            level = self.map.level + 1
            self.__save_map()
        else:
            level = self.map.level - 1
            self.__save_map()

        if self.__load_map(level):
            return
        if level == 0:
            self.game_over()

        self.__actors = []
        self.__actors.append(self.player)
        self.__items = []
        self.map = self.dungeon.get_map(level)

        for act in self.__actors:
            act.sc = sc(self.map.map_array)

        if down:
            pos = self.get_sc_up_pos()
        else:
            pos = self.get_sc_down_pos()
        self.player.set_pos(pos)

        r = self.camera.adjust(self.player.pos())
        while r:
            r = self.camera.adjust(self.player.pos())

    def create_gold(self, amount, pos):
        gold = dungeon.Populator.create_item('Gold', 'basic_stuff', 0)
        gold.amount = amount
        gold.set_pos(pos)
        self.add_item(gold)

    def summon_monster(self, caster, name, file, pos):
        mon = dungeon.Populator.create_creature(name, file)
        mon.ai = henchmanAI.HenchmanAI(mon)
        mon.ai.friends.add(caster.id)
        caster.ai.friends.add(mon.id)
        mon.set_pos(pos)
        mon.sc = sc(self.map.map_array)
        self.add_actor(mon, True)

    def create_humanoid(self):
        #man = races[1][3](True, 1)
        man = races[2][3](True, 1)
        #man.classkit = classkits[2][1](man)
        man.classkit = classkits[0][1](man)
        from ai import simpleai
        man.ai = henchmanAI.HenchmanAI(man)
        man.name = 'Nigg Yshur'
        pos = self.get_free_adj(self.player.pos())
        man.set_pos(pos)
        man.ai.friends.add(self.player.id)
        self.player.ai.friends.add(man.id)
        man.sc = sc(self.map.map_array)
        #self.player = races[race][3](True, gender)
        #self.player.classkit = classkits[classkit][1](self.player)
        #self.player.classkit = classkits[classkit][1](self.player)
        #b = dungeon.Populator.create_item('TomeOfVileUmbrages','tovu',100)
        #self.player.pick_up(b)

    def create_character(self):
        c_res = Res('dc-pl.png', TILESIZE)
        g = 'female', 'male'
        gender = 1
        race = 0
        classkit = 0
        OK = False
        name = ''
        title = 'He'
        title2 = 'His'
        story = [
            'This is the incredible story of our hero',
            'Long time ago, there was a hero named',
            'There once was a time, long ago, when'
        ]

        s = random.choice(story)

        while not OK:
            self.screen.fill(BLACK)

            self.__render_text(self.screen, 'Build your character:', WHITE,
                               ((35, 30)), 'big')

            self.screen.blit(self.__surf_cache['mes_block2'], (60, 65))

            img = pygame.transform.smoothscale(
                c_res.get(races[race][1 + gender]),
                (TILESIZE * 2, TILESIZE * 2))
            self.screen.blit(img, (75, 100))

            self.__render_text(self.screen, name, WHITE, ((73, 170)))

            y = 100
            self.__render_text(self.screen, s, WHITE, ((145, y)))
            self.__render_text(self.screen, name, GREEN, ((395, y)))
            y += 20
            self.__render_text(self.screen, 'the', WHITE, ((145, y)))
            self.__render_text(
                self.screen, g[gender] + ' ' + races[race][0] + ' ' +
                classkits[classkit][0] + '.', GREEN, ((167, y)))
            y += 20
            self.__render_text(self.screen, title + ' ' + races[race][3].desc,
                               WHITE, ((145, y)))
            y += 20
            self.__render_text(
                self.screen, classkits[classkit][1].desc.replace(
                    '$$$', title.lower()).replace('%%%', title2.lower()),
                WHITE, ((145, y)))

            self.__render_text(self.screen, 'Type in your name', WHITE,
                               ((600, 100)))
            self.__render_text(self.screen, 'press F1 / F2 to change race',
                               WHITE, ((600, 120)))
            self.__render_text(self.screen, 'press F3 / F4 to change class',
                               WHITE, ((600, 140)))
            self.__render_text(self.screen, 'press F5 to change gender', WHITE,
                               ((600, 160)))
            self.__render_text(self.screen, 'press Enter to start', WHITE,
                               ((600, 180)))

            pygame.display.flip()
            for e in pygame.event.get():
                if e.type == pygame.QUIT:
                    sys.exit()
                if e.type == pygame.KEYDOWN:
                    if e.key == pygame.K_F5:
                        if gender == 1:
                            gender -= 1
                            title = 'She'
                            title2 = 'Her'
                        else:
                            gender += 1
                            title = 'He'
                            title2 = 'His'
                    if e.key == pygame.K_F3:
                        classkit += 1
                        if classkit >= len(classkits):
                            classkit = 0
                    elif e.key == pygame.K_F4:
                        classkit -= 1
                        if classkit < 0:
                            classkit = len(classkits) - 1

                    elif e.key == pygame.K_F1:
                        race += 1
                        if race >= len(races):
                            race = 0
                    elif e.key == pygame.K_F2:
                        race -= 1
                        if race < 0:
                            race = len(races) - 1

                    elif e.key == pygame.K_RETURN:
                        self.player = races[race][3](True, gender)
                        self.player.classkit = classkits[classkit][1](
                            self.player)
                        b = dungeon.Populator.create_item(
                            'TomeOfVileUmbrages', 'tovu', 100)
                        self.player.pick_up(b)
                        self.player.clear_surfaces()
                        self.player.timer = 0
                        OK = True
                    elif e.key == pygame.K_BACKSPACE:
                        if len(name) > 0:
                            name = name[:-1]
                    else:
                        kn = pygame.key.name(e.key)
                        if kn in 'abcdefghijklmnopqrstuvwxyz' and len(
                                name) < 10:
                            if len(name) > 0:
                                name += pygame.key.name(e.key)
                            else:
                                name += pygame.key.name(e.key).upper()

    def start(self, ts):

        if ts:
            self.create_character()
            self.change_map()
            self.create_humanoid()

        Debug.debug('starting mainloop')
        return self._main_loop()

    def is_inside_map(self, pos):
        return not (pos[0] < 0 or pos[1] < 0 or pos[0] >= self.map.width
                    or pos[1] >= self.map.height)

    def update_item_pos(self, item, new_pos):
        x, y = item.pos()
        nx, ny = new_pos
        if item in self.__item_grid[y][x]:
            self.__item_grid[y][x].remove(item)
        self.__item_grid[ny][nx].append(item)

    def update_actor_pos(self, act, new_pos):
        x, y = act.pos()
        nx, ny = new_pos
        if act in self.__actor_grid[y][x]:
            self.__actor_grid[y][x].remove(act)
        self.__actor_grid[ny][nx].append(act)

    def is_move_valid(self, actor, new_pos):
        if not self.is_inside_map(new_pos):
            return False

        nx, ny = new_pos
        act = self.__actor_grid[ny][nx]
        if len(act) > 0:
            return act[0]

        valid = self.map.can_enter(new_pos, actor.move_mode)
        if valid and actor == self.player:
            self.map.cur_surf = None
            items = self.get_items_at(new_pos)
            if len(items) == 1:
                self.shout('You see a %s' % (items[0].get_name()))
            if len(items) > 1:
                self.shout('You see several items here')

        return valid

    def get_range_target(self, cpos, tpos):
        return self.get_actor_at(tpos)
#        if cpos != tpos:
#            poss = line(cpos[0], cpos[1], tpos[0], tpos[1])
#            poss.pop(0)
#            for pos in poss:
#                actor = self.get_actor_at(pos)
#                if actor != None:
#                    return actor
#        else:
#            return self.caster
#        return None

    def throw_item(self, attacker, item, target_pos):
        t_pos = target_pos
        s_pos = attacker.pos()
        victim = self.get_range_target(attacker.pos(), target_pos)
        if victim != None:
            t_pos = victim.pos()
        dir = attacker.locateDirection(t_pos)
        gfx = throw.ThrowFX(dir, s_pos, t_pos, item)
        self.drawGFX(gfx)
        item.set_pos(t_pos)

    def range_attack(self, attacker, target_pos):
        t_pos = target_pos
        s_pos = attacker.pos()
        victim = self.get_range_target(attacker.pos(), target_pos)

        if victim != None:
            t_pos = victim.pos()

        dir = attacker.locateDirection(t_pos)
        gfx = projectile.ProjectileFX(dir, s_pos, t_pos)
        self.drawGFX(gfx)

        #while self.__gfx != None:
        #    self._world_draw()

        if victim != None:
            self.attack(attacker, victim, True)

    def __c_end_friendship(self, attacker, victim):
        victim.ai.friends.discard(attacker.id)
        victim.ai.hostile.add(attacker.id)
        attacker.ai.friends.discard(victim.id)
        attacker.ai.hostile.add(victim.id)
        todel = []
        for id in victim.ai.friends:
            act = self.get_actor_by_id(id)
            if act == None:
                todel.append(id)
            else:
                act.ai.friends.discard(attacker.id)
                act.ai.hostile.add(attacker.id)
        for id in todel:
            victim.ai.friends.discard(id)

        todel = []
        for id in attacker.ai.friends:
            act = self.get_actor_by_id(id)
            if act == None:
                todel.append(id)
            else:
                act.ai.friends.discard(victim.id)
                act.ai.hostile.add(victim.id)
        for id in todel:
            attacker.ai.friends.discard(id)

    def __c_apply_effects(self, attacker, victim):
        for fx in attacker.get_av_fx():
            if d(100) <= fx[1]:
                Debug.debug('Applied effect %s to %s by %s' %
                            (fx[0], victim, attacker))
                f = fx[0](victim, attacker)
                f.tick()

        for fx in victim.get_dv_fx():
            skip = False
            for nt in fx[0].notrigger:
                if attacker.slot.weapon.flags & nt:
                    skip = True

            if not skip and d(100) <= fx[1]:
                Debug.debug('Applied effect %s to %s by %s' %
                            (fx[0], attacker, victim))
                f = fx[0](attacker, victim)
                f.tick()

    def attack(self, attacker, victim, ranged=False):

        self.__c_end_friendship(attacker, victim)

        vi_adress = (victim == self.player and ['you']
                     or ['the ' + victim.name])[0]
        at_miss_adress = (attacker == self.player and ['You miss']
                          or [attacker.name + ' misses'])[0]
        at_hit_adress = (attacker == self.player and ['You hit']
                         or ['The ' + attacker.name + ' hits'])[0]
        at_kill_adress = (attacker == self.player and ['You killed']
                          or ['The ' + attacker.name + ' killed'])[0]

        wpn = attacker.weapon
        if wpn == None:
            wpn = attacker.unarmed_weapon

        highest_skill = None, 0
        for s in wpn.skills:
            v = getattr(attacker.skills, s)
            if v > highest_skill[1]:
                highest_skill = s, v

        attack_roll = d(100)

        if attack_roll > highest_skill:
            self.shout('%s %s.' % (at_miss_adress, vi_adress))
            Debug.debug('Miss!')
            return

        if victim.get_RA() > 0 and not victim.unconscious:
            victim.RA -= 1
            dodge_skill = victim.skills.Dodge
            dodge_roll = d(100)
            if dodge_roll <= dodge_skill:
                self.shout('%s dodged the attack' % (vi_adress))
                return

        self.__c_apply_effects(attacker, victim)

        hit_zone = victim.HP.get_random_zone()
        z = getattr(victim.HP, hit_zone)[2]

        if wpn.type != I_VOID:
            damage = wpn.damage[1]() + attacker.get_DM()[0]()
        else:
            damage = d(3) + attacker.get_DM()[0]()
        dam = self.do_damage(victim, damage, hit_zone, source=attacker)
        self.shout('%s %s at the %s for %i damage' %
                   (at_hit_adress, vi_adress, z, dam))

    def do_damage(self, act, dam, zone, type=D_GENERIC, source=None):
        return act.do_damage(dam, zone, type)

    def wait_for_target(self, point_of_entry):
        self.__await_target = point_of_entry
        self.__player_actions.cursor()

    def do_identify(self):
        self.__player_actions.identify()

    def target_choosen(self, pos):
        self.__await_target(pos)
        self.__await_target = None

        #self.player.fire(pos)

    def get_items_at(self, pos):
        x, y = pos
        #print self.__item_grid[y][x]
        return [item for item in self.__item_grid[y][x] if not item.picked_up]
        #return [item for item in self.__items if item.pos() == pos]
    def game_over(self):
        print 'You failed'
        self.__quit_loop = True
        self.__actors = []

    def redraw_map(self):
        self.map.cur_surf = None

    def redraw_stats(self):
        self.__cur_stat_surf = None

    def add_to_world_objects(self, obj):
        self.__world_objects[obj] = True

    def add_actor(self, actor, add=True):
        if add: self.__actors.append(actor)
        if self.map != None:
            actor.sc = sc(self.map.map_array)
        self.__world_objects[actor] = True

    def add_item(self, item, add=True):
        if add: self.__items.append(item)
        self.__world_objects[item] = True

    def del_actor(self, actor):
        if actor in self.__actors:
            self.__actors.remove(actor)
        if actor in self.__actors_on_screen:
            self.__actors_on_screen.remove(actor)
        x, y = actor.pos()
        if actor in self.__actor_grid[y][x]:
            self.__actor_grid[y][x].remove(actor)

    def del_item(self, item):
        if item in self.__items:
            self.__items.remove(item)
        x, y = item.pos()
        if item in self.__item_grid[y][x]:
            self.__item_grid[y][x].remove(item)
        #if item.player_symbol != None:
        #    self.free_symbol(item.player_symbol)

    def get_id(self):
        return self.__id_gen.next()

    def drawGFX(self, gfx):
        self.__gfx = gfx
        self.state = S_GFX

        # good or bad??
        while self.__gfx != None:
            self._world_draw()

    def _main_loop(self):
        while not self.__quit_loop:
            self.__clock.tick(40)
            self._world_move()
            self._world_draw()
            self._world_input()
#            print self.clock.get_fps()
        self.std_font = None
        self.__clock = None
        self.__cur_stat_surf = None
        self.__last_id = self.__id_gen.next()
        self.__id_gen = None
        self.__clear_surfaces()
        return self.quit_mes

    def _world_input(self):
        for e in pygame.event.get():
            self.__quit_loop = e.type == pygame.QUIT

            if e.type == pygame.KEYDOWN:

                if e.key == GAME_SAVE_QUIT:
                    self.__quit_loop = True
                    self.quit_mes = SAVE

                # --- cheat keys >>>
                if e.key == pygame.K_F1:
                    for line in self.map.map_array:
                        l = ''
                        for s in line:
                            l = l + str(s[0])
                        print l

                if e.key == pygame.K_F2:
                    self.player.cur_health = 200
                    self.player.cur_endurance = 5000
                    self.player.cur_mind = 5000
                    self.player.cur_strength = 5000

                if e.key == pygame.K_F3:
                    for item in self.__items:
                        print item.name, item.pos()

                if e.key == pygame.K_F4:
                    for act in self.__actors:
                        print act.name, act.timer

                if e.key == pygame.K_F5:
                    for S in gc.get_referrers(Surface):
                        if isinstance(S, Surface):
                            print S

                    print gc.get_referrers(Surface)
                if e.key == pygame.K_F6:
                    self.create_humanoid()
                # <<< cheat keys ---

                if not self.state == S_GFX:
                    self.__cur_stat_surf = None
                self.moved = True

                if self.state == S_RUN and not self.player.unconscious:
                    if e.key in PLAYER_ACTIONS:
                        self.__player_actions.__getattribute__(
                            PLAYER_ACTIONS[e.key])()

                elif self.state in STATE_WORKER:
                    self.__state_worker.__getattribute__(
                        STATE_WORKER[self.state])(e.key)

        if self.state == S_PLAYER_CURSOR:
            pygame.event.pump()
            keys = pygame.key.get_pressed()
            for key in MOVES:
                if keys[key]:
                    pygame.time.wait(150)
                    if key == MOVE_WAIT:
                        pos = self.__actors_on_screen[0].pos()
                        if pos == self.cursor.pos():
                            self.__actors_on_screen.append(
                                self.__actors_on_screen.pop(0))
                            pos = self.__actors_on_screen[0].pos()
                        self.cursor.set_pos(pos)
                        self.__actors_on_screen.append(
                            self.__actors_on_screen.pop(0))
                    else:
                        self.cursor.move(key)

        if self.state == S_RUN and not self.player.unconscious:
            pygame.event.pump()
            keys = pygame.key.get_pressed()
            [
                self.player.move(key) for key in MOVES
                if keys[key] and self.player.timer <= 0
            ]

    def _world_move(self):
        if not self.__quit_loop:
            if self.state == S_RUN:
                self.__actors.sort(
                    sort_by_time)  #actors with lowest timer first
                diff = self.__actors[0].timer
                self.__timer += diff
                self.__world_time += diff

                if self.__timer > 500:  #act-independent issues # one combat-round
                    [act.tick() for act in self.__actors]
                    self.__timer -= 500

                for actor in self.__actors:
                    if actor.timer > 0:
                        actor.timer -= diff
                    else:
                        actor.act()

            elif self.state == S_GFX:
                if self.__gfx == None:
                    self.state = S_RUN

    def _world_draw(self):

        if self.__gfx == None or self.__gfx.redraw:

            if self.camera.adjust(self.player.pos()):
                self.map.cur_surf = None
            self.screen.fill((0, 0, 0))
            if not self.player.dazzled:
                self.screen.blit(
                    self.__get_map_surface(),
                    (-self.camera.x * TILESIZE, -self.camera.y * TILESIZE))

            if not self.player.dazzled:
                for item in self.__items:
                    if not item.picked_up and (self.player.sc.lit(
                            item.x, item.y) or self.player.x == item.x
                                               and self.player.y == item.y):
                        try:
                            self.screen.blit(
                                self.__get_item_surface(item),
                                (item.x * TILESIZE - self.camera.x * TILESIZE,
                                 item.y * TILESIZE - self.camera.y * TILESIZE))
                        except:
                            print sys.exc_info()
                            print item.name, item.pos(), 'is invalid!!!!'

            for act in self.__actors:
                if act == self.player or self.player.sc.lit(
                        act.x, act.y) and not self.player.dazzled:
                    try:
                        self.screen.blit(
                            self.__get_actor_surface(act),
                            (act.x * TILESIZE - self.camera.x * TILESIZE,
                             act.y * TILESIZE - self.camera.y * TILESIZE))
                        if not act in self.__actors_on_screen:
                            self.__actors_on_screen.append(act)
                    except:
                        print sys.exc_info()
                        print act.name, act.pos(), 'is invalid!!!!'
                else:
                    if act in self.__actors_on_screen:
                        self.__actors_on_screen.remove(act)

            if self.state == S_PLAYER_CURSOR:
                self.screen.blit(
                    self.cursor.get_surf(),
                    (self.cursor.x * TILESIZE - self.camera.x * TILESIZE,
                     self.cursor.y * TILESIZE - self.camera.y * TILESIZE))

            self.screen.blit(self.__get_message_surface(), (0, 768 - 128))
            self.screen.blit(self.__get_statblock_surface(), (1024 - 192, 0))

        if self.__gfx != None:
            pos = self.__gfx.pos()
            if pos == None:
                self.__gfx = None
                self.__cur_stat_surf = None
            else:
                x, y = pos
                #tiles = __get_affected_tiles(x,y,self.__gfx.get_surf())
                x = x / TILESIZE + self.camera.x
                y = y / TILESIZE + self.camera.y
                #print x,y,self.player.pos(),self.player.sc.lit(x, y)
                if self.player.sc.lit(x, y):
                    self.screen.blit(self.__gfx.get_surf(), pos)
                    self.__gfx.tick()

        pygame.display.flip()

    def __get_map_surface(self):
        if self.map.cur_surf == None:
            self.player.sc.do_fov(self.player.x, self.player.y, 15)
            surf_map = pygame.Surface(
                (self.map.width * TILESIZE, self.map.height * TILESIZE))
            cx, cy, cw, ch = self.camera.get_view_port()
            for x in xrange(max(cx, 0), min(self.map.width, cw + 1)):

                for y in xrange(max(cy, 0), min(self.map.height, ch + 1)):
                    pos = (x, y) == self.player.pos()
                    lit = self.player.sc.lit(x, y)
                    memo = self.map.map_array[y][x][MT_FLAGS] & F_MEMO
                    if pos or lit or memo:
                        blit_position = ((x) * TILESIZE, (y) * TILESIZE)
                        surf_map.blit(self.map.get_tile_at(x, y),
                                      blit_position)

                        if not pos and not lit and memo:
                            surf_map.blit(self.__surf_cache['FOW'],
                                          blit_position)

                        if not self.map.map_array[y][x][MT_FLAGS] & F_MEMO:
                            tile = self.map.map_array[y][x]
                            new_tile = tile[0], tile[1], tile[2] ^ F_MEMO
                            self.map.map_array[y][x] = new_tile

            self.map.cur_surf = surf_map

        return self.map.cur_surf

    def __get_actor_surface(self, act):
        if act.cur_surf == None:
            surf_act = pygame.Surface((TILESIZE, TILESIZE), pygame.SRCALPHA,
                                      32)
            surf_act.blit(act.get_tile(), (0, 0))
            act.cur_surf = surf_act
        return act.cur_surf

    def __get_item_surface(self, item):
        if item.cur_surf == None:
            surf_item = pygame.Surface((TILESIZE, TILESIZE), pygame.SRCALPHA,
                                       32)
            surf_item.blit(item.get_dd_img(), (0, 0))
            item.cur_surf = surf_item
        return item.cur_surf

    def __get_message_surface(self):
        surf = pygame.Surface((1024 - 192, 128))
        surf.blit(self.__surf_cache['mes_block'], (0, 0))
        y = 100
        for mes in self.__message_queue:
            self.__render_text(surf, mes, WHITE, (20, y))
            y -= 20
            if y < 10:
                break
        return surf

    def __get_statblock_surface(self):
        if self.__cur_stat_surf == None:
            surf = pygame.Surface((192, 768))
            surf.fill(BLACK)
            #surf.blit(self.__surf_cache['stat_block'], (0, 0))

            if self.state in (S_RUN, S_GFX):
                self.__draw_stat_block(surf)

            if self.state in CHOOSE_STATES:
                self.__draw_item_choose(surf, CHOOSE_STATES[self.state])

            self.__cur_stat_surf = surf

        return self.__cur_stat_surf

    def __set_game_instance(self):
        dungeon.Map.game = self
        Actor.game = self
        Item.game = self
        AI.game = self
        Camera.game = self
        dungeon.Populator.game = self
        magic.Spell.game = self
        GFX.game = self
        att.Att.game = self
        dungeon.SADungeon.game = self

    def __draw_item_choose(self, surf, message):
        self.__render_text(surf, message, WHITE, (16, 20))
        y = 38
        abc = self._items_to_choose.keys()
        #abc.sort(sort_by_type)
        s48 = pygame.Surface.copy(self.__surf_cache['stat_32'])

        for key in abc:
            item = self._items_to_choose[key]
            color = WHITE
            if hasattr(item, 'special'):
                if item.special:
                    color = GREEN
            if hasattr(item, 'color'):
                color = item.color
            if hasattr(item, 'get_name'):
                name = item.get_name()
            else:
                name = item.name

            surf.blit(s48, (16, y))
            if hasattr(item, 'cur_surf'):
                surf.blit(self.__get_item_surface(item), (16, y))
            self.__render_text(surf, '%s' % (key), WHITE, (16, y))
            #self.__render_text(surf, '%s -' % (key), WHITE, (16, y))
            self.__render_text(surf, name, color, (64, y))
            y += 18

            info = item.info()
            for line in info:
                self.__render_text(surf, line, color, (64, y))
                y += 18
            if len(info) == 0:
                y += 18
            y += 18

    def __draw_stat_block(self, surf):

        bt = self.player.get_body_tile()
        bt = pygame.transform.smoothscale(bt, (96, 96))
        bt.set_alpha(122)
        y = 50
        surf.blit(bt, (32, y))

        x = 77
        y = 55
        color = WHITE
        if self.player.HP.Head[0] == self.player.HP.Head[1]: color = GREEN
        if self.player.HP.Head[0] == 0: color = YELLOW
        if self.player.HP.Head[0] < 0: color = RED
        self.__render_text(surf, str(self.player.HP.Head[0]), color, (x, y))

        y = 75
        color = WHITE
        if self.player.HP.Chest[0] == self.player.HP.Chest[1]: color = GREEN
        if self.player.HP.Chest[0] == 0: color = YELLOW
        if self.player.HP.Chest[0] < 0: color = RED

        self.__render_text(surf, str(self.player.HP.Chest[0]), color, (x, y))

        y = 95
        color = WHITE
        if self.player.HP.Abdomen[0] == self.player.HP.Abdomen[1]:
            color = GREEN
        if self.player.HP.Abdomen[0] == 0: color = YELLOW
        if self.player.HP.Abdomen[0] < 0: color = RED

        self.__render_text(surf, str(self.player.HP.Abdomen[0]), color, (x, y))

        x = 40
        y = 85
        color = WHITE
        if self.player.HP.L_Arm[0] == self.player.HP.L_Arm[1]: color = GREEN
        if self.player.HP.L_Arm[0] == 0: color = YELLOW
        if self.player.HP.L_Arm[0] < 0: color = RED

        self.__render_text(surf, str(self.player.HP.L_Arm[0]), color, (x, y))

        x = 110
        color = WHITE
        if self.player.HP.R_Arm[0] == self.player.HP.R_Arm[1]: color = GREEN
        if self.player.HP.R_Arm[0] == 0: color = YELLOW
        if self.player.HP.R_Arm[0] < 0: color = RED
        self.__render_text(surf, str(self.player.HP.R_Arm[0]), color, (x, y))

        x = 55
        y = 130
        color = WHITE
        if self.player.HP.L_Leg[0] == self.player.HP.L_Leg[1]: color = GREEN
        if self.player.HP.L_Leg[0] == 0: color = YELLOW
        if self.player.HP.L_Leg[0] < 0: color = RED
        self.__render_text(surf, str(self.player.HP.L_Leg[0]), color, (x, y))

        x = 95
        color = WHITE
        if self.player.HP.R_Leg[0] == self.player.HP.R_Leg[1]: color = GREEN
        if self.player.HP.R_Leg[0] == 0: color = YELLOW
        if self.player.HP.R_Leg[0] < 0: color = RED
        self.__render_text(surf, str(self.player.HP.R_Leg[0]), color, (x, y))

        stats = [('STR ', self.player.STR, self.player.get_STR()),
                 ('CON ', self.player.CON, self.player.get_CON()),
                 ('DEX ', self.player.DEX, self.player.get_DEX()),
                 ('SIZ ', self.player.SIZ, self.player.get_SIZ()),
                 ('INT ', self.player.INT, self.player.get_INT()),
                 ('POW ', self.player.POW, self.player.get_POW()),
                 ('CHA ', self.player.CHA, self.player.get_CHA()),
                 ('DM  ', self.player.DM[1], self.player.get_DM()[1])]
        y = 245
        for line in stats:
            b = 0
            if len(str(line[1])) > 3:
                b = 18
            self.__render_text(surf,
                               str(line[0]).rjust(2, ' '), WHITE, (16, y))
            self.__render_text(surf,
                               str(line[1]).rjust(2, ' '), GREEN, (55, y))
            self.__render_text(surf, '/', WHITE, (70 + b, y))
            self.__render_text(surf,
                               str(line[2]).rjust(2, ' '), WHITE, (80 + b, y))
            y += 18

        #pygame.draw.circle(Surface, color, pos, radius, width=0): return Rect

        #r = pygame.Surface((100, 2))
        #r.fill(RED)
        #surf.blit(r, (16, y + 20))
        #size = float(line[2]) / float(line[1]) * 100
        #if size > 0:
        #    g = pygame.Surface((size, 2))
        #    if size > 100:
        #        g.fill(BLUE)
        #    else:
        #        g.fill(GREEN)
        #surf.blit(g, (16, y + 20))

        self.__render_text(surf, 'Gold:', WHITE, (16, 510))
        self.__render_text(surf, str(self.player.gold), WHITE, (90, 510))

        self.__render_text(surf, 'XP:', WHITE, (16, 528))
        self.__render_text(surf, str(self.player.xp), WHITE, (90, 528))

        i = 0
        y = 600
        count = len(self.player.items)
        s32 = pygame.Surface.copy(self.__surf_cache['stat_32'])
        sgreen = pygame.Surface.copy(self.__surf_cache['sgreen'])
        for h in xrange(3):
            for x in xrange(5):
                pos = (x * TILESIZE + x * 3 + 5, h * TILESIZE + y + h * 3)
                surf.blit(s32, pos)
                if i < count:
                    item = self.player.items[i]
                    if item.equipped:
                        surf.blit(sgreen, pos)
                    surf.blit(self.__get_item_surface(item), pos)
                    self.__render_text(surf, self.player.items[i].get_ps(),
                                       WHITE, (pos))
                    i += 1
                #print (x*TILESIZE,h*TILESIZE+y)

        self.__render_text(surf, self.dungeon.name, WHITE, (16, 710))
        self.__render_text(surf, 'Level: %i' % (self.map.level), WHITE,
                           (16, 728))

    def __render_text(self, surf, text, color, pos, font='std'):
        t = self.__font_cache[font].render('%s' % (text), True, color)
        ts = self.__font_cache[font].render('%s' % (text), True, BLACK)
        surf.blit(ts, (pos[0] + 1, pos[1] + 1))
        surf.blit(t, pos)

    def __load_fonts(self):
        #self.std_font = pygame.font.Font(os.path.join('font', 'jesaya.ttf'), 14)
        self.__font_cache = {
            'std': pygame.font.Font(os.path.join('font', 'alex.ttf'), 17),
            'big': pygame.font.Font(os.path.join('font', 'alex.ttf'), 25)
        }

    def __save_map(self):
        self.__clear_surfaces()
        self.__actors.remove(self.player)
        data = self.map, self.__actors, self.__items, self.player.pos()
        if os.access('MAP%i.gz' % (self.map.level), os.F_OK):
            os.remove('MAP%i.gz' % (self.map.level))
        FILE = gzip.open('MAP%i.gz' % (self.map.level), 'w')
        pickle.dump(data, FILE, 2)
        FILE.close()

    def __load_map(self, level):
        if os.access('MAP%i.gz' % (level), os.F_OK):
            FILE = gzip.open('MAP%i.gz' % (level), 'r')
            self.map, self.__actors, self.__items, pos = pickle.load(FILE)

            self.__set_game_instance()
            FILE.close()
            self.player.set_pos(pos)
            r = self.camera.adjust(self.player.pos())
            while r:
                r = self.camera.adjust(self.player.pos())
            self.__actors.append(self.player)
            for act in self.__actors:
                self.__world_objects[act] = True
                act.sc = sc(self.map.map_array)
            self.player.sc.do_fov(self.player.x, self.player.y,
                                  self.player.cur_mind / 20 + 5)

            for item in self.__items:
                self.__world_objects[item] = True

            self.__world_objects[self.map] = True
            self.__clear_surfaces()

            self._world_draw()
            return True
        return False
        self.__quit_loop = True
        self.quit_mes = SAVE

    def __build_surf_cache(self):
        fow_surf = pygame.Surface((TILESIZE, TILESIZE))
        fow_surf.fill(BLACK)
        fow_surf.set_alpha(100)
        sgreen = pygame.Surface((TILESIZE, TILESIZE))
        sgreen.fill(GREEN)
        sgreen.set_alpha(100)
        self.__surf_cache = {
            'sgreen': sgreen,
            'FOW': fow_surf,
            'stat_block': load_image('stat.png'),
            'stat_48': load_image('48.png'),
            'stat_32': pygame.transform.smoothscale(load_image('48.png'),
                                                    (32, 32)),
            'mes_block': load_image('mes_block.png'),
            'mes_block2': load_image('mes_block2.png')
        }

    def __clear_surfaces(self):
        for obj in self.__world_objects.keys():
            obj.clear_surfaces()
        self.cursor.cursor_surf = None

    def __gen_id(self):
        for x in xrange(self.__last_id, 9999999):
            yield x