Logo Search packages:      
Sourcecode: balazar3 version File versions  Download package

driver_2d.py

# -*- coding: utf-8 -*-
# Balazar in the Rancid Skull Dungeon
# Copyright (C) 2008 Jean-Baptiste LAMY
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import os, os.path, time, weakref
import pygame, pygame.image, pygame.color, pygame.mixer
from pygame.locals import *

from balazar3.base import *
import balazar3.globdef as globdef


BACKBUFFER      = None
SCREEN_RECT     = None
DIRTY_RECTS     = []
WHITE           = None
BARRE           = None
BARRE2          = None
MISK_MODEL      = None
FONT_MODEL      = None
ITEM_MODEL      = None

def init(screen_width = 640, screen_height = 480):
  global BACKBUFFER, SCREEN_RECT, WHITE, BARRE, BARRE2, MISK_MODEL, FONT_MODEL, ITEM_MODEL, LOG
  
  pygame.init()
  pygame.display.set_caption("Balazar III")
  SCREEN_RECT = Rect(0, 0, screen_width, screen_height)
  winstyle    = 0
  bestdepth   = pygame.display.mode_ok (SCREEN_RECT.size, winstyle, 32)
  BACKBUFFER  = pygame.display.set_mode(SCREEN_RECT.size, winstyle, bestdepth)
  WHITE       = pygame.color.Color('white')
  
  def load_image(file):
    image = pygame.image.load(os.path.join(globdef.APPDIR, "sprites", file))
    return image.convert()
  
  BARRE       = load_image("barre.png")
  BARRE2      = load_image("barre_interieur.png")
  
  #t = time.time()
  #get_model("balazar_gourdin")
  #print time.time() - t
  #import sys; sys.exit()
  
  MISK_MODEL  = get_model("divers")
  FONT_MODEL  = FontModel("font")
  ITEM_MODEL  = get_model("item")
  
  LOG = Log()
  

SOUND_DIR = os.path.join(globdef.APPDIR, "sounds")
SOUNDS = {}
class MainLoop(BaseMainLoop):
  def __init__(self):
    global human_controller
    human_controller = HumanController()
    
    BaseMainLoop.__init__(self)
    self.music = None
    self.play_music("oceane.ogg")
    
  def init_interface(self): pass
  
  def render(self):
    BaseMainLoop.render(self)
    
    for level in self.levels: level.render()
    
    global DIRTY_RECTS
    pygame.display.update(DIRTY_RECTS)
    DIRTY_RECTS = []
    
  def play_sound(self, name):
    sound = SOUNDS.get(name)
    if not sound: sound = SOUNDS[name] = pygame.mixer.Sound(os.path.join(SOUND_DIR, name))
    sound.play()
    
  def play_music(self, name):
    if globdef.ZAURUS: return # No Ogg support yet
    if self.music: self.music.stop()
    self.music = pygame.mixer.Sound(os.path.join(SOUND_DIR, name))
    self.music.play(1)

  def main_loop(self):
    try:
      BaseMainLoop.main_loop(self)
    finally:
      if self.music: self.music.stop()
      human_controller.character = None
      LOG.entries = []
      
      
def j_sorter(a, b):
  if a.life <= 0.0:
    if b.life <= 0.0: return cmp(b.j, a.j)
    return -1
  if b.life <= 0.0: return  1
  return cmp(b.j, a.j)
  
class Level(BaseLevel):
  shot = None
  
  def __init__(self, filename):
    self.effects     = []
    self.renderables = []
    BaseLevel.__init__(self, filename)
    
    self.shot = pygame.image.load(self.room.get_shot_filename()).convert()
    
  def __setstate__(self, state):
    self.effects      = []
    self.renderables  = []
    BaseLevel.__setstate__(self, state)
    self.renderables += self.mobiles
    
    self.shot = pygame.image.load(self.room.get_shot_filename()).convert()
    
  def add_mobile(self, mobile, _send_later = 0):
    BaseLevel.add_mobile(self, mobile, _send_later)
    
    self.renderables.append(mobile)
    
  def remove_mobile(self, mobile):
    BaseLevel.remove_mobile(self, mobile)
    
    self.renderables.remove(mobile)
    
  def set_active(self, active):
    if active and not self.active:
      rect = BACKBUFFER.blit(self.shot, (0, 0))
      if human_controller.character:
        human_controller.character.update_life()
        human_controller.character.update_experience_curse()
      global DIRTY_RECTS
      DIRTY_RECTS = [rect] # whole screen
      LOG.need_render = 7
      LOG.rect = LOG.input_rect = LOG.item_rect = Rect(0, 0, 0, 0) # No need to clear, the background is fresh!
      
    BaseLevel.set_active(self, active)
    
  def begin_round(self):
    BaseLevel.begin_round(self)
    #for effect in self.effects[:]: effect.begin_round()
    for effect in self.effects: effect.begin_round()
    LOG.begin_round()
    
  def render(self):
    self.renderables.sort(j_sorter)
    
    for character in self.renderables: character.before_render()
    
    rects = [renderable.dirty_rect for renderable in self.renderables if renderable.can_skip_render <= 1]
    
    for character in self.renderables: character.render(rects)
    
    if LOG.need_render: LOG.render(self.shot)
    

MODELS = weakref.WeakValueDictionary()
LAST_MODELS = [None] * 3

def get_model(name):
  model = MODELS.get(name)
  if not model:
    model = MODELS[name] = SpriteModel(name)
    LAST_MODELS.append(model)
    del LAST_MODELS[0]
  return model
  
class SpriteModel(object):
  def __init__(self, name):
    t = time.time()
    self.name         = name
    self.image        = pygame.image.load(os.path.join(globdef.APPDIR, "sprites", "%s.png" % name))
    t2 = time.time() - t
    sprite_module = __import__("balazar3.sprites.%s" % name, fromlist = ["animation_nb", "coords"])
    self.animation_nb = sprite_module.animation_nb
    self.coords       = sprite_module.coords
    t3 = time.time() - t
    print "* Balazar 3 * Animation %s loaded in %s s (graphics: %s s)." % (name, t3, t2)
    
class FontModel(SpriteModel):
  def __init__(self, name):
    SpriteModel.__init__(self, name)
    self._red = (255, 0, 0, 255)
    
  def render_to_image(self, text):
    x = y = width = 0
    for char in text:
      if   char == u" ":
        x += 6
      elif char == u"\n":
        if x > width: width = x
        x  = 0
        y += 20
      else:
        ix, iy, iw, ih, x0, y0 = self.coords[char, 0, 0]
        x += iw
    if x > width: width = x
    height = y + 25
    image = pygame.Surface((width + 1, height))
    image.fill(self._red)
    self.render(text, 0, 0, image)
    image.set_colorkey(self._red, RLEACCEL)
    return image
  
  def render(self, text, x, y, surface):
    start_x = x
    start_y = y
    width   = 0
    for char in text:
      if   char == u" ":
        x += 6
      elif char == u"\n":
        if x - start_x > width: width = x - start_x
        x  = start_x
        y += 20
      else:
        ix, iy, iw, ih, x0, y0 = self.coords[char, 0, 0]
        surface.blit(self.image, (x + x0, y + y0), (ix, iy, iw, ih))
        x += iw
    if x - start_x > width: width = x - start_x
    return width + 1, y - start_y + 25
  
  def wrap_text(self, text, max_width):
    i   = 0
    x   = 0
    y   = 0
    pos = 0
    last_word = 0
    last_word_width = 0
    new_text = []
    width = 0
    while i < len(text):
      char = text[i]
      if   char == u" ":
        last_word = i
        last_word_width = x
        x += 6
      elif char == u"\n":
        x  = 0
        y += 20
      else:
        ix, iy, iw, ih, x0, y0 = self.coords[char, 0, 0]
        x += iw
        if x > max_width:
          if last_word_width > width: width = last_word_width
          i  = last_word
          x  = 0
          y += 20
          new_text.append(text[pos:i])
          pos = i + 1
      i += 1
    if x > width: width = x
    new_text.append(text[pos:])
    return u"\n".join(new_text), width + 1, y + 25
    
    
class LogEntry(object):
  wrap_width = 512
  def __init__(self, text):
    self.duration = 100 + 2 * len(text)
    self.text, self.width, self.height = FONT_MODEL.wrap_text(text, self.wrap_width)
    self.height -= 5
    
class Log(object):
  nb_item = 3
  def __init__(self):
    self.entries       = [LogEntry(_(u"__welcome__"))]
    self.rect          = Rect(0, 0, 0, 0)
    self.need_render   = 0
    self.max_height    = 25 * 3
    self.input         = u""
    self.input_rect    = Rect(0, 0, 0, 0)
    self.item_rect     = Rect(0, 0, 0, 0)
    self.inventory     = 0
    self.first_item    = 0
    self.selected_item = 0
    self.item_entry    = None
    self.inventory_item_on_ground = None
    
  def set_character(self, character):
    self.character = character
    
  def start_input(self):
    self.input = u"- "
    self.need_render |= 2
    
  def add_input(self, s):
    if FONT_MODEL.animation_nb.has_key(s) or (s == u" "):
      self.input += s
      self.need_render |= 2
      
  def backspace_input(self):
    if len(self.input) > 2: self.input = self.input[:-1]
    self.need_render |= 2
    
  def end_input(self, ok = 1):
    if ok and self.input[2:]:
      self.character.chat(self.input[2:])
    self.input = u""
    self.need_render |= 2

  def start_inventory(self):
    self.inventory = 1
    self.need_render |= 4
    
  def end_inventory(self):
    self.inventory = 0
    self.need_render |= 4
    if self.item_entry in self.entries: self.remove(self.item_entry)
    
  def inventory_use(self):
    item = self.character.items[self.selected_item]
    if   isinstance(item, Equipable):
      if item.equiped: self.character.unequip(item)
      else:            self.character.equip  (item)
    elif isinstance(item, Usable   ):
      self.character.use(item)
      
  def inventory_drop(self):
    item = self.character.items[self.selected_item]
    if isinstance(item, Dropable):
      self.character.drop(item)
    
  def inventory_grab(self, item_on_ground):
    if (self.inventory_item_on_ground is item_on_ground) and (self.item_entry in self.entries):
      self.character.grab(item_on_ground)
      
    else:
      self.inventory_item_on_ground = item_on_ground
      
      if self.item_entry in self.entries: self.remove(self.item_entry)
      self.item_entry = LogEntry(item_on_ground.item.description() + u"\n  " + _(u"__item__grab__"))
      self.add(self.item_entry)
      
  def set_first_item(self, i, relative = 0):
      if relative: self.first_item += i
      else:        self.first_item  = i
      self.need_render   |= 4
      
      if   self.first_item < 0: self.first_item = 0
      elif self.first_item > len(self.character.items) - self.nb_item:
        self.first_item = max(0, len(self.character.items) - self.nb_item)

      if   self.selected_item < self.first_item                   : self.selected_item = self.first_item
      elif self.selected_item > self.first_item + self.nb_item - 1: self.selected_item = self.first_item + self.nb_item - 1
      
  def select_item(self, i, relative = 0):
    if not self.inventory: self.start_inventory()
    else:
      if relative: self.selected_item += i
      else:        self.selected_item  = i
      self.need_render   |= 4
      
    if   self.selected_item < 0                            : self.selected_item = 0
    elif self.selected_item > len(self.character.items) - 1: self.selected_item = len(self.character.items) - 1
    
    item    = self.character.items[self.selected_item]
    actions = []
    if isinstance(item, Equipable):
      if not item.equiped: actions.append(_(u"__item_equip__"))
      #actions.append(_(u"__item_quick_key__"))
    if isinstance(item, Usable   ):
      actions.append(_(u"__item_use__"))
      #actions.append(_(u"__item_quick_key__"))
    if isinstance(item, Dropable ):
      actions.append(_(u"__item_drop__"))
    if self.item_entry in self.entries: self.remove(self.item_entry)
    if actions: actions = u"\n  " + u", ".join(actions)
    else:       actions = u""
    self.item_entry = LogEntry(item.description() + actions)
    self.add(self.item_entry)
    
  def begin_round(self):
    if self.entries:
      self.entries[0].duration -= 1
      if self.entries[0].duration == 0:
        del self.entries[0]
        self.need_render |= 1
        
  def add(self, entry):
    entries = [entry]
    height = entry.height
    self.entries.reverse()
    for entry in self.entries:
      height += entry.height
      if height > self.max_height: break
      entries.insert(0, entry)
    self.entries = entries
    self.need_render |= 1
    
  def remove(self, entry):
    self.entries.remove(entry)
    self.need_render |= 1
    
  def render(self, shot):
    if self.need_render & 1:
      old_rect  = BACKBUFFER.blit(shot, self.rect, self.rect)
      y = 0
      total_width  = 0
      total_height = 0
      for entry in self.entries:
        width, height = FONT_MODEL.render(entry.text, 64, y, BACKBUFFER)
        if width > total_width: total_width = width
        total_height += height
        y += entry.height
      self.rect = Rect(64, 0, total_width, total_height)
      DIRTY_RECTS.append(self.rect.union(old_rect))
      
    if self.need_render & 2:
      old_rect  = BACKBUFFER.blit(shot, self.input_rect, self.input_rect)
      if self.input:
        width, height = FONT_MODEL.render(self.input, 0, 480 - 25, BACKBUFFER)
        self.input_rect = Rect(0, 480 - 25, width, height)
      else:
        self.input_rect = Rect(0, 0, 0, 0)
      DIRTY_RECTS.append(self.input_rect.union(old_rect))
      
    if self.need_render & 4:
      old_rect  = BACKBUFFER.blit(shot, self.item_rect, self.item_rect)
      
      if   self.selected_item < 0                            : self.selected_item = 0
      elif self.selected_item > len(self.character.items) - 1: self.selected_item = len(self.character.items) - 1
      if   self.first_item <= self.selected_item - self.nb_item:
        self.first_item = self.selected_item - self.nb_item + 1
      elif self.first_item > self.selected_item:
        self.first_item = self.selected_item
      elif self.first_item > len(self.character.items) - self.nb_item:
        self.first_item = max(0, len(self.character.items) - self.nb_item)
        
      if self.first_item > 0:
        ix, iy, iw, ih, x0, y0 = ITEM_MODEL.coords[u"^", 0, 0]
        BACKBUFFER.blit(ITEM_MODEL.image, (SCREEN_RECT.width - 32 + x0, 25 + y0), (ix, iy, iw, ih))
        
      if self.inventory:
        ix, iy, iw, ih, x0, y0 = ITEM_MODEL.coords["cadre", 0, 0]
        BACKBUFFER.blit(ITEM_MODEL.image, (SCREEN_RECT.width - 32 + x0, 64 * (self.selected_item - self.first_item) + 32 + 27 + y0), (ix, iy, iw, ih))
        
      y = 27
      for i in range(self.first_item, min(self.first_item + self.nb_item, len(self.character.items))):
        item = self.character.items[i]
        ix, iy, iw, ih, x0, y0 = ITEM_MODEL.coords[item.model_name, 0, 0]
        BACKBUFFER.blit(ITEM_MODEL.image, (SCREEN_RECT.width - 32 + x0, y + 32 + y0), (ix, iy, iw, ih))
        if isinstance(item, Equipable) and item.equiped:
          ix, iy, iw, ih, x0, y0 = FONT_MODEL.coords[u"x", 0, 0]
          BACKBUFFER.blit(FONT_MODEL.image, (SCREEN_RECT.width + x0 - 18, y + 64 + y0 - 26), (ix, iy, iw, ih))
        if isinstance(item, Limited):
          FONT_MODEL.render(unicode(item.nb_use), SCREEN_RECT.width - 60, y + 64 - 24, BACKBUFFER)
        y += 64
        
      if self.first_item < len(self.character.items) - self.nb_item:
        ix, iy, iw, ih, x0, y0 = ITEM_MODEL.coords[u"v", 0, 0]
        BACKBUFFER.blit(ITEM_MODEL.image, (SCREEN_RECT.width - 32 + x0, y + 2 + y0), (ix, iy, iw, ih))
      self.item_rect = Rect(SCREEN_RECT.width - 64, 0, 64, y + 27)
      DIRTY_RECTS.append(self.item_rect.union(old_rect))
    self.need_render = 0
    
LOG = None


class Effect(object):
  life = 1.0 # for j_sorter()
  can_skip_render = 0
  def __init__(self, level, animation_name, i, j, dy = 0, dj = 0.0):
    self.level           = level
    self.animation_name  = animation_name
    self.animation_pos   = 0
    self.i               = i
    self.j               = j + dj
    self.x, self.y       = self.level.room.ij2xy(i, j)
    self.y              += dy
    self.dirty_rect      = self.rect = Rect(self.x, self.y, 0, 0)
    self.nb              = MISK_MODEL.animation_nb[animation_name]
    level.effects    .append(self)
    level.renderables.append(self)
    
  #def begin_round(self):
  #  self.animation_pos += 1
  #  if self.animation_pos == MISK_MODEL.animation_nb[self.animation_name]:
  #    self.level.effects    .remove(self)
  #    self.level.renderables.remove(self)
  #    DIRTY_RECTS.append(BACKBUFFER.blit(self.level.shot, self.rect, self.rect))

  def begin_round(self):
    if self.animation_pos < self.nb: self.animation_pos += 1
      
  def delete(self):
    if self in self.level.effects:
      self.level.effects    .remove(self)
      self.level.renderables.remove(self)
      
  def before_render(self):
    if self.animation_pos < self.nb:
      BACKBUFFER.blit(self.level.shot, self.rect, self.rect)
    else:
      tofu.MAIN_LOOP.next_round_tasks.append(self.delete)
      DIRTY_RECTS.append(BACKBUFFER.blit(self.level.shot, self.rect, self.rect))
      
  def render(self, rects):
    if self.animation_pos < self.nb:
      ix, iy, iw, ih, x0, y0 = MISK_MODEL.coords[self.animation_name, 0, self.animation_pos]
      self.dirty_rect = self.rect
      self.rect = BACKBUFFER.blit(MISK_MODEL.image, (self.x + x0, self.y + y0), (ix, iy, iw, ih))
      self.dirty_rect.union_ip(self.rect)
      DIRTY_RECTS.append(self.dirty_rect)
    
    
class Animable(object):
  label = None
  
  speed_i = 0
  speed_j = 0
  
  def _init(self):
    self.can_skip_render = 0
    if self.level: x, y = self.level.room.ij2xy(self.i, self.j)
    else:          x = y = 0
    self.dirty_rect  = self.rect = Rect(x, y, 0, 0)
    
  def change_room(self):
    r = super(Animable, self).change_room()
    if r:
      x , y = self.level.room.ij2xy(self.i, self.j)
      self.dirty_rect  = self.rect = Rect(x, y, 0, 0)
    return r
    
  def exit_from_dungeon(self):
    r = super(Animable, self).exit_from_dungeon()
    if r:
      x , y = self.level.room.ij2xy(self.i, self.j)
      self.dirty_rect  = self.rect = Rect(x, y, 0, 0)
    return r
  
  def get_model_name(self): return self.model.name
  def set_model_name(self, name):
    self.model = get_model(name)
    self.can_skip_render = 0
    
  def set_animation_name(self, name):
    self.animation_name = name
    self.animation_pos  = 0
    self.animation_nb   = self.model.animation_nb[name]
    self.can_skip_render = 0
    
  def begin_round(self):
    super(Animable, self).begin_round()
    
    self.animation_pos += 1
    if self.animation_pos == self.animation_nb: self.animation_pos = 0
        
  #def render(self):
  #  x , y    = self.level.room.ij2xy(self.i, self.j)
  #  ix, iy, iw, ih, x0, y0 = self.model.coords[self.animation_name, self.animation_angle, self.animation_pos]
  #  self.dirty_rect = self.rect
  #  self.rect = BACKBUFFER.blit(self.model.image, (x + x0, y + y0), (ix, iy, iw, ih))
  #  if self.label:
  #    #self.rect = self.rect.union(BACKBUFFER.blit(self.label, (x - self.label.get_width() // 2, y - 90)))
  #    self.rect.union_ip(BACKBUFFER.blit(self.label, (x - self.label.get_width() // 2, y - 90)))
  #  self.dirty_rect.union_ip(self.rect)
  #  DIRTY_RECTS.append(self.dirty_rect)
    
    
  def before_render(self):
    if self.can_skip_render == 0:
      self.can_skip_render = 1
      BACKBUFFER.blit(self.level.shot, self.rect, self.rect)
    elif (self.animation_nb == 1) and (self.speed_i == 0) and (self.speed_j == 0):
      self.can_skip_render = 2
    else:
      BACKBUFFER.blit(self.level.shot, self.rect, self.rect)
      
  def render(self, rects):
    if self.can_skip_render == 2:
      for rect in rects:
        if self.rect.colliderect(rect): break
      else: return
      
    x, y = self.level.room.ij2xy(self.i, self.j)
    ix, iy, iw, ih, x0, y0 = self.model.coords[self.animation_name, self.animation_angle, self.animation_pos]
    
    if self.can_skip_render == 2:
      BACKBUFFER.blit(self.model.image, (x + x0, y + y0), (ix, iy, iw, ih))
      if self.label:
        BACKBUFFER.blit(self.label, (x - self.label.get_width() // 2, y - 90))
        
    else:
      self.dirty_rect = self.rect
      self.rect = BACKBUFFER.blit(self.model.image, (x + x0, y + y0), (ix, iy, iw, ih))
      if self.label:
        self.rect.union_ip(BACKBUFFER.blit(self.label, (x - self.label.get_width() // 2, y - 90)))
        
      self.dirty_rect.union_ip(self.rect)
      
      DIRTY_RECTS.append(self.dirty_rect)

  def added_into_level(self, level):
    super(Animable, self).added_into_level(level)
    
    if (not level) and self.level:
      # Clean the screen
      DIRTY_RECTS.append(BACKBUFFER.blit(self.level.shot, self.rect, self.rect))
      # Mark all overlapping mobiles so as they don't skip the next rendering
      for mobile in self.level.mobiles:
        if isinstance(mobile, Animable):
          if self.dirty_rect.colliderect(mobile.dirty_rect): mobile.can_skip_render = 0
          
ATTACK_2_EFFECT = { ATTACK_SHARP : "blood", ATTACK_BASH : "blood", ATTACK_FIRE : "fire", ATTACK_ICE : "ice", ATTACK_HEAL : "blood" }
        


class Character(Animable, BaseCharacter):
  def _init(self):
    BaseCharacter._init(self)
    Animable._init(self)
    self.effect = None
    
  def set_model_name(self, name):
    Animable.set_model_name(self, name)
    self.set_animation_name("attente")
    
  def set_display_name(self, name):
    BaseCharacter.set_display_name(self, name)
    
    if name and not(isinstance(self, Hero) and tofu.has_side("single")):
      self.label = FONT_MODEL.render_to_image(name)
    else:
      self.label = None
      
  def set_animation_name(self, name):
    Animable.set_animation_name(self, name)
    
    if name == "attente": self.animation_angle = (int(round((self.angle) / 45.0)) * 45) % 360
    else:                 self.animation_angle = (int(round((self.angle) / 90.0)) * 90) % 360
    
  def advance_time(self, proportion):
    BaseCharacter.advance_time(self, proportion)
    
    if self.speed_angle:
      if self.animation_name == "attente":
        self.animation_angle = (int(round((self.angle) / 45.0)) * 45) % 360
        self.can_skip_render = 0
      else:
        self.animation_angle = (int(round((self.angle) / 90.0)) * 90) % 360
        self.can_skip_render = 0
        
  def create_effect(self, attack):
    if self.effect and (self.effect in self.level.effects):
      self.effect.delete()
    self.effect = Effect(self.level, ATTACK_2_EFFECT[attack] , self.i, self.j, -30, -0.5)
    
  def do_message(self, message):
    if message[0] == MESSAGE_CHAT:
      tofu.MAIN_LOOP.play_sound("menu2.wav")
      message = _(u"__chat__") % (self.player_name or self.display_name, message[1:].decode("utf8"))
      LOG.add(LogEntry(message))
    else:
      super(Character, self).do_message(message)


class Monster(Character, BaseMonster):
  pass


class Chest(Animable, BaseChest):
  animation_angle = 180
  def _init(self):
    BaseChest._init(self)
    Animable ._init(self)
    if self.level: self.x , self.y = self.level.room.ij2xy(self.i, self.j)
    
  def loaded(self):
    BaseChest.loaded(self)
    Animable ._init(self)
    
  def added_into_level(self, level):
    super(Chest, self).added_into_level(level)
    if self.level: self.x , self.y = self.level.room.ij2xy(self.i, self.j)
    
    
class ItemOnGround(Animable, BaseItemOnGround):
  life = 1.0
  animation_angle = 0
  animation_nb    = 0
  animation_pos   = 0
  def __init__(self, item, i, j):
    BaseItemOnGround.__init__(self, item, i, j)
    Animable        ._init(self)
    self.set_model_name("item")
    self.animation_name  = self.item.model_name
    
  def loaded(self):
    BaseItemOnGround.loaded(self)
    Animable        ._init(self)
    self.x, self.y = self.level.room.ij2xy(self.i, self.j)
    self.set_model_name("item")
    self.animation_name  = self.item.model_name
    
  def begin_round(self):
    BaseItemOnGround.begin_round(self)
    
  def added_into_level(self, level):
    super(ItemOnGround, self).added_into_level(level)
    
    if level:
      self.x, self.y = self.level.room.ij2xy(self.i, self.j)
      
      

WEAPON_MAP = {
#  "gros_gourdin" : "gourdin",
  }

class Hero(Character, BaseHero):
  def get_model_name(self): return self.model.name.split("_")[0]
  def set_model_name(self, name):
    #for item in self.items:
    #  if isinstance(item, Weapon) and item.equiped: break
    if name in ["balazar"]:
      self.model = get_model("%s_%s" % (name, WEAPON_MAP.get(self.weapon.model_name, self.weapon.model_name)))
    else:
      self.model = get_model(name)
    self.set_animation_name("attente")
    self.last_anim = None
        
  def control_owned(self):
    BaseCharacter.control_owned(self)
    
    self.set_controller(human_controller)
    LOG.set_character(self)
    human_controller.current_action = ""
    self.label = None
    
  def control_lost(self):
    BaseCharacter.control_lost(self)
    
    self.set_controller(None)
    
  def update_life(self):
    if self.local:
      DIRTY_RECTS.append(BACKBUFFER.blit(self.level.shot, (0, 0), Rect([0, 0, 64, 64])))
      if self.life > 0.0:
        BACKBUFFER.blit(pygame.transform.scale(HEART, (int(64 * self.life), int(64 * self.life))), (2 + int(32 - 32 * self.life), 2 + int(32 - 32 * self.life)))
        
  def update_experience_curse(self):
    if self.local:
      DIRTY_RECTS.append(BACKBUFFER.blit(self.level.shot, (0, 64), Rect([0, 64, 64, 128])))
      
      v = self.experience - int(self.experience)
      if v > 0.0:
        BACKBUFFER.blit(pygame.transform.scale(BRAIN, (int(64 * v), int(64 * v))), (int(32 - 32 * v), 64 + int(32 - 32 * v)))
      FONT_MODEL.render(str(int(self.experience)), 50, 64 + 45, BACKBUFFER)
      
      v = self.curse - int(self.curse)
      if v > 0.0:
        BACKBUFFER.blit(pygame.transform.scale(CURSE, (int(64 * v), int(64 * v))), (int(32 - 32 * v), 128 + int(32 - 32 * v)))
      FONT_MODEL.render(str(int(self.curse)), 50, 128 + 45, BACKBUFFER)



  def update_life(self):
    if self.local:
      DIRTY_RECTS.append(BACKBUFFER.blit(self.level.shot, (0, 0), Rect([0, 0, 64, 256])))
      BACKBUFFER.blit(BARRE2, (10, 47 + int(191 * (1.0 -  self.life             ))), Rect([ 0, 0, 14, int(round(191 *  self.life             ))]))
      BACKBUFFER.blit(BARRE2, (25, 47 + int(191 * (1.0 - (self.experience % 1.0)))), Rect([15, 0, 14, int(round(191 * (self.experience % 1.0)))]))
      BACKBUFFER.blit(BARRE2, (40, 47 + int(191 * (1.0 - (self.curse      % 1.0)))), Rect([30, 0, 14, int(round(191 * (self.curse      % 1.0)))]))
      BACKBUFFER.blit(BARRE, (0, 0), Rect([0, 0, 64, 256]))
      
      
  def update_experience_curse(self):
    self.update_life()
    
  def do_message(self, message):
    type = message[0]
    super(Hero, self).do_message(message)
    
    if   type == MESSAGE_EQUIPED:
      self.set_model_name(self.get_model_name())
      if self.local: LOG.need_render |= 4
    elif type in UPDATE_MSG and self.local: LOG.need_render |= 4
      
UPDATE_MSG = set([MESSAGE_UNEQUIPED, MESSAGE_USED, MESSAGE_DROPPED, MESSAGE_GRABBED, MESSAGE_GAINED])


class HumanController(BaseController):
  def __init__(self):
    BaseController.__init__(self)
    self.current_action = ACTION_STOP_MOVING
    self.character      = None
    self.click_duration = 0
    self.click_pos      = (0, 0)
    self.mouse_down     = 0
    self.target         = None
    self.next_help      = 0

    self.up_pressed    = 0
    self.left_pressed  = 0
    self.right_pressed = 0
    self.down_pressed  = 0
    
  def show_info(self): LOG.add(LogEntry(self.character.get_info()))

  def arrow_key_changed(self):
    if   self.left_pressed:
      if   self.up_pressed  : self.character.send_action(ACTION_MOVE_LEFT_UP   )
      elif self.down_pressed: self.character.send_action(ACTION_MOVE_LEFT_DOWN )
      else:                   self.character.send_action(ACTION_MOVE_LEFT      )
    elif self.right_pressed:
      if   self.up_pressed  : self.character.send_action(ACTION_MOVE_RIGHT_UP  )
      elif self.down_pressed: self.character.send_action(ACTION_MOVE_RIGHT_DOWN)
      else:                   self.character.send_action(ACTION_MOVE_RIGHT     )
    elif self.up_pressed:     self.character.send_action(ACTION_MOVE_UP        )
    elif self.down_pressed:   self.character.send_action(ACTION_MOVE_DOWN      )
    else:                     self.character.send_action(ACTION_STOP_MOVING    )
      
  def generate_actions(self):
    events = pygame.event.get()
    
    if not self.character:
      for event in events:
        if (event.type == KEYDOWN) and (event.key == K_ESCAPE): tofu.MAIN_LOOP.stop()
    else:
      if self.target:
        if   (self.character.current_action == ACTION_STRIKE) and ((self.target.life <= 0.0) or ((isinstance(self.target, Chest) and self.target.opened))):
          self.character.send_action(ACTION_STOP_MOVING)
          self.target = None
        elif (self.character.current_action == ACTION_STOP_MOVING) and (self.target.life > 0.0) and not isinstance(self.target, Chest):
           self.character.send_action(ACTION_STRIKE)
           
      pos    = None
      pos_up = None
      for event in events:
        if   event.type == KEYDOWN:
          key = event.key
          if   LOG.input:
            if   key == K_ESCAPE   : LOG.end_input(0)
            elif key == K_RETURN   : LOG.end_input(1)
            elif key == K_SPACE    : LOG.add_input(u" ")
            elif key == K_BACKSPACE: LOG.backspace_input()
            elif event.unicode     : LOG.add_input(event.unicode)
          else:
            if   key == K_ESCAPE  :
              if LOG.inventory: LOG.end_inventory()
              else:             tofu.MAIN_LOOP.stop()
            elif key == K_LEFT    :
              if LOG.inventory: LOG.end_inventory()
              self.left_pressed = 1
              self.arrow_key_changed()
            elif key == K_RIGHT   :
              if LOG.inventory: LOG.end_inventory()
              self.right_pressed = 1
              self.arrow_key_changed()
            elif key == K_UP      :
              if LOG.inventory: LOG.end_inventory()
              self.up_pressed = 1
              self.arrow_key_changed()
            elif key == K_DOWN    :
              if LOG.inventory: LOG.end_inventory()
              self.down_pressed = 1
              self.arrow_key_changed()
            elif (key == K_LSHIFT) or (key == K_RSHIFT):
              if LOG.inventory: LOG.inventory_use(); LOG.end_inventory()
              else:             self.character.send_action(ACTION_STRIKE)
            elif key == K_i       : self.show_info()
            elif key == K_h       :
             LOG.add(LogEntry(_(u"__help%s__" % self.next_help)))
             self.next_help += 1
             if self.next_help == 9: self.next_help = 0
            elif key == K_RETURN  :
              if LOG.inventory: LOG.inventory_use(); LOG.end_inventory()
              else:             LOG.start_input()
            elif key == K_PAGEUP   : LOG.select_item(-1, 1)
            elif key == K_PAGEDOWN : LOG.select_item( 1, 1)
            elif (key == K_BACKSPACE) or (key == K_DELETE):
              if LOG.inventory: LOG.inventory_drop()
              else:
                for item_on_ground in self.character.level.mobiles:
                  if isinstance(item_on_ground, ItemOnGround):
                    if (abs(item_on_ground.i - self.character.i) < 0.8) and (abs(item_on_ground.j - self.character.j) < 0.8):
                      LOG.inventory_grab(item_on_ground)
                      break
        elif event.type == KEYUP:
          key = event.key
          if not LOG.input:
            if   key == K_LEFT    :
              self.left_pressed = 0
              self.arrow_key_changed()
            elif key == K_RIGHT   :
              self.right_pressed = 0
              self.arrow_key_changed()
            elif key == K_UP      :
              self.up_pressed = 0
              self.arrow_key_changed()
            elif key == K_DOWN    :
              self.down_pressed = 0
              self.arrow_key_changed()
            elif (key == K_LSHIFT) or (key == K_RSHIFT):
              self.character.send_action(ACTION_STOP_MOVING)
        elif event.type == MOUSEBUTTONDOWN:
          self.mouse_down     = 1
          self.click_duration = 1
          if event.button == 3:
            self.character.send_action(ACTION_STRIKE)
          else:
            self.click_pos = pos = event.pos
        elif event.type == MOUSEBUTTONUP:
          if (event.button == 1) and (self.click_duration > 10):
            self.character.send_action(ACTION_STOP_MOVING)
          if event.button == 3:
            self.character.send_action(ACTION_STOP_MOVING)
          self.click_duration = 0
          pos_up = event.pos
          
      if pos_up and self.mouse_down:
        self.mouse_down = 0
        x, y = pos_up
        if   (x <= 64 ) and (y < 40 ): tofu.MAIN_LOOP.stop()
        elif (x <= 64 ) and (y < 168): self.show_info()
        elif (x >= SCREEN_RECT.width - 64):
          if y < 27: LOG.set_first_item(-1, 1)
          else:
            i = (y - 27) // 64 + LOG.first_item
            if (i >= len(self.character.items)) or (y > 27 + 64 * LOG.nb_item):
              LOG.set_first_item(1, 1)
            else:
              if LOG.inventory and (LOG.selected_item == i):
                LOG.inventory_use()
                pass
              else:
                LOG.start_inventory(); LOG.select_item(i)
                
      if pos:
        x, y = pos
        if   (64 < x < SCREEN_RECT.width - 64):
          if LOG.inventory: LOG.end_inventory()
          i, j  = self.character.level.room.xy2ij(x, y)
          angle = vector2angle(i - self.character.i, j - self.character.j)
          
          for mobile in self.character.level.mobiles:
            if (not mobile is self.character) and isinstance(mobile, Strikeable) and (-0.5 < mobile.i - i < 0.5) and (-1.0 < mobile.j - j < 0.0) and (mobile.life > 0.0):
              self.target = mobile
              self.character.send_action(ACTION_GOTO_STRIKE + struct.pack("!fff", i, j, angle))
              break
          else:
            self.target = None
            for mobile in self.character.level.mobiles:
              if isinstance(mobile, ItemOnGround):
                if (abs(mobile.i - i) < 0.5) and (abs(mobile.j - j) < 0.5):
                  LOG.inventory_grab(mobile)
                  
            self.character.send_action(ACTION_GOTO + struct.pack("!fff", i, j, angle))
            
      if self.click_duration:
        if self.click_duration == 15:
          self.mouse_down = 0
          if   self.click_pos[0] >= SCREEN_RECT.width - 64:
            i = (self.click_pos[1] - 27) // 64 + LOG.first_item
            if LOG.inventory and (LOG.selected_item == i):
              LOG.inventory_drop()
        self.click_duration += 1
        

human_controller = None


Generated by  Doxygen 1.6.0   Back to index