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

driver_3d.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
import soya, soya.gui, soya.opengl as opengl, soya.label3d as label3d
import soya.sdlconst as sdlconst
import random

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


soya.path.append(globdef.APPDIR)

SHADER_DEFAULT_MATERIAL = None

def init():
  if globdef.SOUND_VOLUME: need_sound = 1
  else:                    need_sound = 0
  soya.init("Balazar III", globdef.SCREEN_WIDTH, globdef.SCREEN_HEIGHT, sound = need_sound, fullscreen = globdef.FULLSCREEN)
  soya.set_use_unicode(1)
  soya.set_quality(globdef.QUALITY)
  
  soya.AUTO_EXPORTERS_ENABLED = os.path.exists(os.path.join(globdef.APPDIR, ".svn"))
  
  global SHADER_DEFAULT_MATERIAL
  SHADER_DEFAULT_MATERIAL = soya.Material.get("shader")
  SHADER_DEFAULT_MATERIAL.filename = "__SHADER_DEFAULT_MATERIAL__"
  soya._soya._set_shader_default_material(SHADER_DEFAULT_MATERIAL)


class MainLoop(BaseMainLoop):
  def __init__(self):
    global human_controller
    human_controller = HumanController()
    
    BaseMainLoop.__init__(self)
    
    self.scenes[0].atmosphere = soya.Atmosphere()
    self.music = None
    self.play_music("oceane.ogg")
    
  def init_interface(self): pass

  def render(self):
    soya.MainLoop.render(self)

  def play_sound(self, name):
    if globdef.SOUND_VOLUME > 0:
      soya.SoundPlayer(self.scenes[0], soya.Sound.get(name), 0, 0, gain = globdef.SOUND_VOLUME)
      
  def play_music(self, name):
    if globdef.SOUND_VOLUME > 0 and globdef.MUSIC:
      if self.music: self.scenes[0].remove(self.music)
      self.music = soya.SoundPlayer(self.scenes[0], soya.Sound.get(name), 1, 0, gain = globdef.SOUND_VOLUME)
      
  def main_loop(self):
    try:
      BaseMainLoop.main_loop(self)
    finally:
      if self.music: self.scenes[0].remove(self.music)
      human_controller.character = None
      human_controller.interface.entries = []
      

def build_level_world(self, room):
  self.set_model(soya.Model.get(room.get_model_name()))
  self.atmosphere = soya.Atmosphere()
  self.atmosphere.fog = 1
  self.atmosphere.fog_color = (0.0, 0.0, 0.0, 1.0)
  self.atmosphere.fog_start =  5.0
  self.atmosphere.fog_end   = 10.0
  self.light = soya.Light(self)
  self.light.set_xyz(-2.0, 4.0, 1.0)
  
  
class Level(BaseLevel):
  def _init(self):
    build_level_world(self, self.room)
    self.set_xyz(self.I * 6.0, 0.0, -self.J * 6.0)
    
  def __init__(self, filename):
    BaseLevel.__init__(self, filename)
    
    self._init()
    
  def __setstate__(self, state):
    BaseLevel.__setstate__(self, state)
    
  def set_active(self, active):
    if   active and not self.active:
      scene = soya.MAIN_LOOP.scenes[0]
      scene.add(self)
      if human_controller.camera.parent:
        human_controller.camera.unfade_level(self)
        
    elif (not active) and self.active:
      human_controller.camera.fade_level(self)
      
    BaseLevel.set_active(self, active)
    
  def loaded(self):
    super(Level, self).loaded()
    self._init()


class Animable(object):
  animation_name = ""
  angle          = 180.0
  def get_model_name(self): return self.model.filename
  def set_model_name(self, name):
    self.set_model(soya.AnimatedModel.get(name))
    #self.set_animation_name("attente")
    
  def set_animation_name(self, name):
    if self.animation_name:
      self.animate_clear_cycle(self.animation_name)
    self.animation_name = name
    self.animation_pos  = 0
    self.animate_reset()
    self.animate_blend_cycle(name)
    
  def begin_round(self):
    super(Animable, self).begin_round()
    
    self.animation_pos += 1
    
  def advance_time(self, proportion):
    super(Animable, self).advance_time(proportion)
    
    self.set_identity()
    self.set_xyz(self.i - 2.5, 0.0, 2.5 - self.j)
    self.scale(0.45, 0.45, 0.45)
    self.rotate_lateral(self.angle)

  

class Character(Animable, BaseCharacter):
  label = None


  def _init(self):
    self.right_hand  = soya.World(self)
    self.head        = soya.World(self)
    self.weapon_body = soya.Body(self.right_hand)
    self.eyes        = soya.Body(self.head)

    self.chest_anchor = soya.Point(self, 0.0, 1.0, -0.2)
    
    BaseCharacter._init(self)
    
    if self.level and not self in self.level:
      self.level.add(self)
      
    self.scale(0.45, 0.45, 0.45)
    self.rotate_lateral(self.angle)
    
    self.weapon_body.rotate_x(-90.0)
    self.weapon_body.turn_z(180.0)
    self.weapon_body.set_xyz(0.05, 0.08, -0.08)
    
  def set_model_name(self, name):
    Animable.set_model_name(self, name)
    self.attach_to_bone(self.right_hand, "mainD")
    self.attach_to_bone(self.head      , "tete")
    
    if   self.model.filename == "echassien":
      self.weapon_body.model = soya.Model.get("epee")
      self.eyes.model = soya.Model.get("yeux")
      self.eyes.set_identity()
      self.eyes.rotate_vertical(-20.0)
      self.eyes.set_xyz(0.0, 0.0, -0.4)
      
    elif self.model.filename == "balazar":
      self.eyes.model = soya.Model.get("lunnettes")
      self.eyes.set_identity()
      self.eyes.rotate_vertical(-90.0)
      self.eyes.set_xyz(0.0, -0.26, -0.2)
      
  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 = label3d.Label3D(soya.MAIN_LOOP.scenes[0], name, size = 0.005)
      self.label.lit = 0
    else:
      if self.label:
        self.label.parent.remove(self.label)
        del self.label
        
  def discard(self):
    super(Character, self).discard()
    
    if self.label and self.label.parent:
      self.label.parent.remove(self.label)
      
  def advance_time(self, proportion):
    super(Character, self).advance_time(proportion)
    
    if self.label: self.label.move(soya.Point(self, 0.0, 3.1, 0.0))
    
  def do_physics(self):
    BaseCharacter.do_physics(self)
    if   self.current_action == ACTION_TURN_LEFT:
      if self.animation_name != "tourneG": self.set_animation_name("tourneG")
      
    elif self.current_action == ACTION_TURN_RIGHT:
      if self.animation_name != "tourneD": self.set_animation_name("tourneD")
      
    elif (self.current_action == ACTION_STRIKE_ONE) or (self.current_action == ACTION_STRIKE):
      if   self.animation_pos == 10:
        if self.damages[0][0] == ATTACK_ICE:
          i, j = angle2vector(self.angle, 1.1)
          i += self.i
          j += self.j
          Ice(soya.MAIN_LOOP.scenes[0], radius = 6.0).move(soya.Point(self.parent, i - 2.5, 0.2, 2.5 - j))
          
      elif (self.current_action == ACTION_STRIKE) and (self.animation_pos == 20):
        self.animation_pos = 0
        self.animate_clear_cycle(self.animation_name)
        self.animate_reset()
        self.animate_blend_cycle(self.animation_name)
        
  def create_effect(self, attack):
    if   attack == ATTACK_FIRE: effect = Fire (soya.MAIN_LOOP.scenes[0])
    elif attack == ATTACK_ICE : effect = Ice  (soya.MAIN_LOOP.scenes[0])
    else:                       effect = Blood(soya.MAIN_LOOP.scenes[0])
    effect.move(self.chest_anchor)
    
  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"))
      human_controller.interface.add_log(LogEntry(message))
    else:
      super(Character, self).do_message(message)
    
    
class Monster(Character, BaseMonster):
  pass


HERO_MODEL_NAMES = set(["balazar"])
class Hero(Character, BaseHero):
  weapon_extra = None
  
  def set_model_name(self, name):
    Character.set_model_name(self, name)
    
    self.define_weapon_model()
    
  def define_weapon_model(self):
    if self.weapon_extra:
      self.weapon_extra.parent.remove(self.weapon_extra)
      del self.weapon_extra
      
    if self.get_model_name() in HERO_MODEL_NAMES:
      for item in self.items:
        if isinstance(item, Weapon) and item.equiped:
          self.weapon_body.model = soya.Model.get(item.model_name)
          if   item.model_name == "baguette_feu":
            self.weapon_extra = Torch(self.right_hand)
            self.weapon_extra.set_xyz(0.15, -0.92, -0.08)
            #self.weapon_extra = soya.Sprite(self.right_hand, soya.Material.get("x_solar_2"))
            #self.weapon_extra.width = self.weapon_extra.height = 0.15
            #self.weapon_extra.color = (0.9, 0.7, 0.3, 1.0)
            #self.weapon_extra.lit = 0
            #self.weapon_extra.set_xyz(0.15, -0.92, -0.08)
          elif item.model_name == "baton_feu":
            self.weapon_extra = Torch(self.right_hand)
            self.weapon_extra.set_xyz(0.30, -0.92, -0.08)
          elif item.model_name == "baguette_glace":
            self.weapon_extra = IceTorch(self.right_hand)
            self.weapon_extra.set_xyz(0.15, -0.92, -0.08)
          elif item.model_name == "baton_glace":
            self.weapon_extra = IceTorch(self.right_hand)
            self.weapon_extra.set_xyz(0.30, -0.92, -0.08)
          break
        
  def control_owned(self):
    BaseCharacter.control_owned(self)
    
    self.set_controller(human_controller)
    human_controller.current_action = ""
    
  def control_lost(self):
    BaseCharacter.control_lost(self)
    
    self.set_controller(None)
    
  def set_controller(self, controller):
    super(Hero, self).set_controller(controller)
    
    if controller is human_controller:
      soya.MAIN_LOOP.scenes[0].add(controller.camera)
      soya.set_root_widget(controller.widget)
      controller.camera.target = self
      controller.indicators.character = self
      controller.interface .character = self
      controller.interface.build_inventory()
      
  def update_life(self):
    if self.local: human_controller.indicators.update()
    
  def update_experience_curse(self):
    if self.local: human_controller.indicators.update()
    
  def do_message(self, message):
    type = message[0]
    super(Hero, self).do_message(message)
    
    if   type == MESSAGE_EQUIPED:
      item = tofu.Unique.undumpsuid(message[1:])
      if isinstance(item, Weapon): self.define_weapon_model()
      if self.local: human_controller.interface.build_inventory()
    elif type == MESSAGE_UNEQUIPED:
      if self.local: human_controller.interface.build_inventory()
    elif type == MESSAGE_USED:
      if self.local: human_controller.interface.build_inventory()
    elif type == MESSAGE_DROPPED:
      if self.local: human_controller.interface.build_inventory()
    elif type == MESSAGE_GRABBED:
      if self.local: human_controller.interface.build_inventory()
    elif type == MESSAGE_GAINED:
      if self.local: human_controller.interface.build_inventory()

  
class Chest(Animable, BaseChest):
  def __init__(self):
    super(Chest, self).__init__()
    
  def _init(self):
    BaseChest._init(self)
    
    if self.level and not self in self.level:
      self.level.add(self)
      
    self.scale(0.45, 0.45, 0.45)
    
    
class ItemOnGround(BaseItemOnGround):
  def __init__(self, item, i, j):
    BaseItemOnGround.__init__(self, item, i, j)
    
    self.set_xyz(i - 2.5, 0.1, 2.5 - j)
    self.model = soya.Model.get(item.model_name)
    self.scale(0.45, 0.45, 0.45)
    self.rotate_lateral(-90.0)
    
  def loaded(self):
    BaseItemOnGround.loaded(self)
    
    self.set_xyz(self.i - 2.5, 0.1, 2.5 - self.j)
    if self.item: self.model = soya.Model.get(self.item.model_name)
    self.scale(0.45, 0.45, 0.45)
    self.rotate_lateral(-90.0)
    
    if self.level and not self in self.level:
      self.level.add(self)
    

class Blood(soya.Particles):
  def __init__(self, parent = None, nb_particles = 30):
    soya.Particles.__init__(self, parent, soya.PARTICLE_DEFAULT_MATERIAL, nb_particles)
    self.auto_generate_particle = 1
    self.set_colors((0.2, 0.0, 0.0, 1.0), (0.2, 0.04, 0.04, 1.0), (0.2, 0.01, 0.01, 1.0), (0.0, 0.0, 0.0, 1.0))
    self.set_sizes((0.25, 0.25))
    self.max_particles_per_round = 4
    self.life = 15
    self.lit  = 0
    
  def generate(self, index):
    angle = random.uniform(0.0, 6.2834)
    sx = math.cos(angle)
    sz = math.sin(angle)
    l = 0.03 / math.sqrt(sx * sx + 0.2 + sz * sz)
    self.set_particle(index, random.uniform(0.5, 1.5), sx * l, 5 * l, sz * l, 0.0, -0.01, 0.0)
    
  def begin_round(self):
    soya.Particles.begin_round(self)
    if self.life == 1:
      self.auto_generate_particle = 0
      self.removable = 1
      self.life = 0
    else: self.life -= 1


class Fire(soya.Particles):
  def __init__(self, parent = None, nb_particles = 30):
    #m = soya.Material.get("x_solar_2")
    #m.additive_blending = 1
    #m.save()
    soya.Particles.__init__(self, parent, soya.Material.get("x_solar_2"), nb_particles)
    self.auto_generate_particle = 1
    self.set_colors((0.2, 0.2, 0.1, 1.0), (0.2, 0.1, 0.0, 1.0), (0.3, 0.0, 0.0, 1.0), (0.0, 0.0, 0.0, 1.0))
    self.set_sizes((0.25, 0.25))
    self.max_particles_per_round = 4
    self.life = 15
    self.lit  = 0
    
  def generate(self, index):
    angle = random.uniform(0.0, 6.2834)
    sx = math.cos(angle)
    sz = math.sin(angle)
    l = 0.02 / math.sqrt(sx * sx + 0.2 + sz * sz)
    self.set_particle(index, random.uniform(0.5, 1.5), sx * l, -l, sz * l, 0.0, 0.003, 0.0)
    
  def begin_round(self):
    soya.Particles.begin_round(self)
    if self.life == 1:
      self.auto_generate_particle = 0
      self.removable = 1
      self.life = 0
    else: self.life -= 1

    
class Ice(soya.Particles):
  def __init__(self, parent = None, nb_particles = 30, radius=1):
    soya.Particles.__init__(self, parent, soya.Material.get("x_cristal_1"), nb_particles)
    self.auto_generate_particle = 1
    self.set_colors((0.4, 0.4, 0.5, 1.0), (0.2, 0.4, 0.5, 1.0), (0.1, 0.2, 0.4, 1.0), (0.0, 0.0, 0.0, 1.0))
    self.set_sizes((0.2, 0.2))
    self.max_particles_per_round = 40
    self.life = 15
    self.lit  = 0
    self.radius = radius
    
  def generate(self, index):
    angle = 6.2834 * index / 30.0
    sx = math.cos(angle)
    sz = math.sin(angle)
    l = 0.05
    self.set_particle(index, 1.0 * self.radius, sx * l, 0.0, sz * l, 0.0, 0.0, 0.0)
    
  def begin_round(self):
    soya.Particles.begin_round(self)
    if self.life == 1:
      self.auto_generate_particle = 0
      self.removable = 1
      self.life = 0
    else: self.life -= 1


class Torch(soya.Particles):
  def __init__(self, parent = None, nb_particles = 65, make_sprite = 1):
    soya.Particles.__init__(self, parent, soya.Material.get("x_solar_2"), nb_particles)
    self.auto_generate_particle = 1
    self.set_colors((0.4, 0.4, 0.4, 1.0), (0.6, 0.3, 0.0, 1.0), (0.6, 0.0, 0.0, 1.0), (0.0, 0.0, 0.0, 1.0))
    self.set_sizes((0.1, 0.1))
    self.max_particles_per_round = 5
    self.lit  = 0
    self.make_sprite = make_sprite
    
  def generate(self, index):
    angle = random.uniform(0.0, 6.2834)
    sx = math.cos(angle)
    sz = math.sin(angle)
    l1 =  0.05 / math.sqrt(sx * sx + 0.2 + sz * sz)
    l2 =  0.4 * l1
    l3 = -0.1 * l2
    p = self % soya.MAIN_LOOP.scenes[0]
    if self.make_sprite:
      l2 *= 2.0
      l3 *= 3.0
      self.set_particle2(index, random.uniform(0.4, 0.6), p.x + sx * l1, p.y, p.z + sz * l1, sx * l2, 0.0, sz * l2, sx * l3, 0.007, sz * l3)
    else:
      self.set_particle2(index, random.uniform(0.5, 1.0), p.x + sx * l1, p.y, p.z + sz * l1, sx * l2, 0.0, sz * l2, sx * l3, 0.002, sz * l3)


class IceTorch(soya.Particles):
  def __init__(self, parent = None, nb_particles = 65):
    soya.Particles.__init__(self, parent, soya.Material.get("x_cristal_1"), nb_particles)
    self.auto_generate_particle = 1
    self.set_colors((0.4, 0.4, 0.5, 1.0), (0.2, 0.4, 0.5, 1.0), (0.1, 0.2, 0.4, 1.0), (0.0, 0.0, 0.0, 1.0))
    self.set_sizes((0.1, 0.1))
    self.max_particles_per_round = 5
    self.lit  = 0
    
  def generate(self, index):
    sx = random.uniform(-1.0, 1.0)
    sy = random.uniform(-1.0, 1.0)
    sz = random.uniform(-1.0, 1.0)
    l  =  0.04 / math.sqrt(sx * sx + sy * sy + sz * sz)
    self.set_particle(index, random.uniform(0.5, 1.0), sx * l, sy * l, sz * l, 0.0, 0.0, 0.0)

class Camera(soya.Camera):
  def __init__(self, parent = None):
    soya.Camera.__init__(self, parent)
    
    self.target           = None
    self.fading_levels    = []
    self.unfading_levels  = []
    f = 0.9
    self.look_at(soya.Vector(None, 0.0, -3.5, -4.5))
    self.set_xyz(0.0, 3.5 * f, 4.5 * f)

  def fade_level(self, level):
    level.atmosphere.fog = 1
    level.atmosphere.fog_color = (0.0, 0.0, 0.0, 1.0)
    level.atmosphere.fog_start = 1.0
    level.atmosphere.fog_end   = 12.0
    self.fading_levels.append(level)
    
  def unfade_level(self, level):
    level.atmosphere.fog = 1
    level.atmosphere.fog_color = (0.0, 0.0, 0.0, 1.0)
    level.atmosphere.fog_start = 1.0
    level.atmosphere.fog_end   = 4.0
    self.unfading_levels.append(level)
    
  def advance_time(self, proportion):
    if self.target and self.target.level:
      x = self.target.level.x + (self.target.i - 2.5) * 0.3
      z = self.target.level.z + 4.5 * 0.9
      
      for level in self.fading_levels[:]:
        level.atmosphere.fog_end -= 0.1
        if level.atmosphere.fog_end <= 1.0:
          level.parent.remove(level)
          self.fading_levels.remove(level)
          
      for level in self.unfading_levels[:]:
        level.atmosphere.fog_start += 0.2
        level.atmosphere.fog_end   += 0.4
        if level.atmosphere.fog_end >= 12.0:
          self.unfading_levels.remove(level)
          
      if not(-0.05 < self.x - x < 0.05):
        self.x = (20.0 * self.x + x) / 21.0
      
      self.z = (20.0 * self.z + z) / 21.0
      
    soya.Camera.advance_time(self, proportion)
    

class Indicators(soya.gui.Layer):
  def __init__(self, parent):
    soya.gui.Layer.__init__(self, parent)

    self.life       = soya.gui.Image(self, soya.Material.get("vie"))
    self.experience = soya.gui.Image(self, soya.Material.get("experience"))
    self.curse      = soya.gui.Image(self, soya.Material.get("malediction"))
    self.barre      = soya.gui.Image(self, soya.Material.get("barre"))

    self.character = None
    
  def allocate(self, x, y, width, height):
    self.u = soya.get_screen_width() // 10
    self.barre.allocate(0, 0, self.u, 4 * self.u)
    self.update()
    
  def update(self):
    if self.character:
      r = self.u / 64.0
      self.life      .allocate(int( 9 * r), int((47 + 191 * (1.0 -  self.character.life           )) * r), int(16 * r), int(191 *  self.character.life            * r))
      self.experience.allocate(int(24 * r), int((47 + 191 * (1.0 - (self.character.experience % 1))) * r), int(16 * r), int(191 * (self.character.experience % 1) * r))
      self.curse     .allocate(int(39 * r), int((47 + 191 * (1.0 - (self.character.curse      % 1))) * r), int(16 * r), int(191 * (self.character.curse      % 1) * r))
    
      self.life      .tex_y2 = self.character.life
      self.experience.tex_y2 = self.character.experience % 1.0
      self.curse     .tex_y2 = self.character.curse      % 1.0

class LogEntry(object):
  def __init__(self, text):
    self.duration = 100 + 2 * len(text)
    self.width, self.height, self.text = soya.gui.widgets.STYLE.font.wordwrap(text, int(soya.get_screen_width() * 0.75))

class GrayedBackground(soya.gui.Widget):
  def render(self):
    self.height = human_controller.interface.log.ideal_height
    if human_controller.interface.entries:
      opengl.glEnable(opengl.GL_BLEND)
      opengl.glColor4f(0.0, 0.0, 0.0, 0.6)
      opengl.glBegin(opengl.GL_QUADS)
      opengl.glVertex2f(self.x             , self.y              )
      opengl.glVertex2f(self.x             , self.y + self.height)
      opengl.glVertex2f(self.x + self.width, self.y + self.height)
      opengl.glVertex2f(self.x + self.width, self.y              )

      opengl.glVertex2f(self.x + self.width, self.y + self.height     )
      opengl.glVertex2f(self.x             , self.y + self.height     )
      opengl.glColor4f(0.0, 0.0, 0.0, 0.0)
      opengl.glVertex2f(self.x              - 20, self.y + self.height + 20)
      opengl.glVertex2f(self.x + self.width + 20, self.y + self.height + 20)
      
      opengl.glVertex2f(self.x - 20, self.y                   )
      opengl.glVertex2f(self.x - 20, self.y + self.height + 20)
      opengl.glColor4f(0.0, 0.0, 0.0, 0.6)
      opengl.glVertex2f(self.x     , self.y + self.height     )
      opengl.glVertex2f(self.x     , self.y                   )
      
      opengl.glVertex2f(self.x + self.width     , self.y                   )
      opengl.glVertex2f(self.x + self.width     , self.y + self.height     )
      opengl.glColor4f(0.0, 0.0, 0.0, 0.0)
      opengl.glVertex2f(self.x + self.width + 20, self.y + self.height + 20)
      opengl.glVertex2f(self.x + self.width + 20, self.y                   )
      
      opengl.glEnd()
      opengl.glDisable(opengl.GL_BLEND)
  
  
class Interface(soya.gui.Layer):
  nb_item = 3
  def __init__(self, parent):
    soya.gui.Layer.__init__(self, parent)
    
    self.character  = None
    self.entries    = []
    self.input      = u""
    self.max_height = 100
    self.grayed_log = GrayedBackground(self)
    self.log        = soya.gui.Label(self, color = (1.0, 1.0, 1.0, 1.0))
    self.log.extra_height = 0
    
    self.input = 0
    self.input_box = soya.gui.Input()
    
    self.inventory       = 0
    self.first_item      = 0
    self.selected_item   = 0
    self.item_entry      = None
    self.inventory_world = soya.World()
    self.inventory_world.atmosphere = soya.NoBackgroundAtmosphere()
    self.inventory_labels = []
    
    light = soya.Light(self.inventory_world)
    light.set_xyz(-2.0, -1.0, 1.0)
    
    self.inventory_camera = soya.Camera(self.inventory_world)
    self.inventory_camera.set_xyz(0.3, 0.0, 10.0)
    self.inventory_camera.fov = 25
    
    self.inventory_viewport = soya.gui.CameraViewport(self, self.inventory_camera)
    
    self.inventory_bodies = []
    y = 1.5 * ((self.nb_item - 1) / 2.0)
    for i in range(self.nb_item):
      body = soya.Body(self.inventory_world)
      body.rotate_lateral(-90.0)
      body.rotate_incline( 10.0)
      body.y = y
      self.inventory_bodies.append(body)
      y -= 1.5
      
    self.frame_material = soya.Material.get("cadre")
    self.frame_material.clamp = 1
    self.arrow_material = soya.Material.get("fleche")
    self.arrow_material.clamp = 1
    
    self.inventory_item_on_ground = None
    
    self.add_log(LogEntry(_(u"__welcome__")))
    
  def begin_round(self):
    if self.entries:
      self.entries[0].duration -= 1
      if self.entries[0].duration == 0:
        del self.entries[0]
        self.log_changed()
        
  def add_log(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.log_changed()
    
  def remove_log(self, entry):
    self.entries.remove(entry)
    self.log_changed()
    
  def log_changed(self):
    self.log.text = u"\n".join([entry.text for entry in self.entries])
    self.log.allocate(int(soya.get_screen_width() * 0.1) + 6, 5, int(soya.get_screen_width() * 0.75) - 12, self.max_height)
    
  def start_input(self):
    self.input = 1
    self.add(self.input_box)
    self.input_box.set_focus(1)
    
  def end_input(self, ok = 1):
    self.input = 0
    if ok and self.input_box.text:
      self.character.chat(self.input_box.text)
    self.input_box.text = u""
    self.input_box.set_focus(0)
    self.remove(self.input_box)
    
  def start_inventory(self):
    self.inventory = 1
    
  def end_inventory(self):
    self.inventory = 0
    if self.item_entry in self.entries: self.remove_log(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_log(self.item_entry)
      self.item_entry = LogEntry(item_on_ground.item.description() + u"\n  " + _(u"__item__grab__"))
      self.add_log(self.item_entry)
      
  def set_first_item(self, i, relative = 0):
      if relative: self.first_item += i
      else:        self.first_item  = i
      
      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
      
      self.build_inventory()
      
  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
      
    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_log(self.item_entry)
    if actions: actions = u"\n  " + u", ".join(actions)
    else:       actions = u""
    self.item_entry = LogEntry(item.description() + actions)
    self.add_log(self.item_entry)
    
    self.build_inventory()
    
  def build_inventory(self):
    for label in self.inventory_labels: self.inventory_world.remove(label)
    self.inventory_labels = []
    
    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)
      
    y = 1.5 * ((self.nb_item - 1) / 2.0)
    for i in range(self.first_item, min(self.first_item + self.nb_item, len(self.character.items))):
      item = self.character.items[i]
      body = self.inventory_bodies[i - self.first_item]
      body.model = soya.Model.get(item.model_name)
      body.set_identity()
      if isinstance(item, Weapon):
        body.rotate_lateral(-90.0)
        body.rotate_incline( 10.0)
        body.y = y
      else:
        body.x = 0.5
        body.y = y - 0.3
      if isinstance(item, Equipable) and item.equiped:
        label = label3d.Label3D(self.inventory_world, u"X", size = 0.015)
        label.set_xyz(0.95, y * 0.89 - 0.18, 1.0)
        label.lit = 0
        self.inventory_labels.append(label)
      if isinstance(item, Limited):
        label = label3d.Label3D(self.inventory_world, unicode(item.nb_use), size = 0.015)
        label.set_xyz(-0.0, y * 0.89 - 0.17, 1.0)
        label.lit = 0
        self.inventory_labels.append(label)
      y -= 1.5
      
    for i in range(i - self.first_item + 1, self.nb_item):
      self.inventory_bodies[i].model = None
      
  def allocate(self, x, y, width, height):
    self.log               .allocate(int(soya.get_screen_width() * 0.1) + 6, 5, int(soya.get_screen_width() * 0.75) - 12, self.max_height)
    self.grayed_log        .allocate(int(soya.get_screen_width() * 0.1), 0, int(soya.get_screen_width() * 0.75), self.max_height)
    self.input_box         .allocate(6, soya.get_screen_height() - 36, soya.get_screen_width() - 12, 30)
    self.inventory_viewport.allocate(int(soya.get_screen_width() * 0.85), int(soya.get_screen_width() * 0.06), int(soya.get_screen_width() * 0.15), int(soya.get_screen_width() * 0.1) * self.nb_item)
    
  def render(self):
    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)
      
    x2 = soya.get_screen_width()
    x1 = int(x2 * 0.88)
    
    opengl.glEnable(opengl.GL_BLEND)
    if self.first_item > 0:
      y1 = 0
      y2 = int(x2 * 0.05)
      self.arrow_material.activate()
      opengl.glBegin(opengl.GL_QUADS)
      opengl.glTexCoord2f(0.0, 0.0); opengl.glVertex2i(x1, y1)
      opengl.glTexCoord2f(0.0, 0.5); opengl.glVertex2i(x1, y2)
      opengl.glTexCoord2f(1.0, 0.5); opengl.glVertex2i(x2, y2)
      opengl.glTexCoord2f(1.0, 0.0); opengl.glVertex2i(x2, y1)
      opengl.glEnd()
    if self.first_item < len(self.character.items) - self.nb_item:
      y1 = int(soya.get_screen_width() * 0.05) + int(x2 * 0.1) * self.nb_item
      y2 = y1 + int(x2 * 0.05)
      self.arrow_material.activate()
      opengl.glBegin(opengl.GL_QUADS)
      opengl.glTexCoord2f(0.0, 0.5); opengl.glVertex2i(x1, y1)
      opengl.glTexCoord2f(0.0, 1.0); opengl.glVertex2i(x1, y2)
      opengl.glTexCoord2f(1.0, 1.0); opengl.glVertex2i(x2, y2)
      opengl.glTexCoord2f(1.0, 0.5); opengl.glVertex2i(x2, y1)
      opengl.glEnd()
      
    if self.inventory:
      y1 = int(soya.get_screen_width() * 0.05) + int(x2 * 0.1) * (self.selected_item - self.first_item)
      y2 = y1 + int(x2 * 0.1)
      self.frame_material.activate()
      opengl.glBegin(opengl.GL_QUADS)
      opengl.glTexCoord2f(0.0, 0.0); opengl.glVertex2i(x1, y1)
      opengl.glTexCoord2f(0.0, 1.0); opengl.glVertex2i(x1, y2)
      opengl.glTexCoord2f(1.0, 1.0); opengl.glVertex2i(x2, y2)
      opengl.glTexCoord2f(1.0, 0.0); opengl.glVertex2i(x2, y1)
      opengl.glEnd()
      
    opengl.glDisable(opengl.GL_BLEND)
    soya.DEFAULT_MATERIAL.activate()
    
    soya.gui.Layer.render(self)

class RootWidget(soya.gui.RootLayer):
  def process_event(self, events):
    pass
    
class HumanController(BaseController):

  # XXX rewrite this
  _action_dict = {
     sdlconst.K_LEFT: ACTION_MOVE_LEFT,
     sdlconst.K_RIGHT: ACTION_MOVE_RIGHT,
     sdlconst.K_UP: ACTION_MOVE_UP,
     sdlconst.K_DOWN: ACTION_MOVE_DOWN,
     sdlconst.K_KP4: ACTION_MOVE_LEFT,
     sdlconst.K_KP1: ACTION_MOVE_LEFT_DOWN,
     sdlconst.K_KP7: ACTION_MOVE_LEFT_UP,
     sdlconst.K_KP6: ACTION_MOVE_RIGHT,
     sdlconst.K_KP3: ACTION_MOVE_RIGHT_DOWN,
     sdlconst.K_KP9: ACTION_MOVE_RIGHT_UP,
     sdlconst.K_KP8: ACTION_MOVE_UP,
     sdlconst.K_KP2: ACTION_MOVE_DOWN,
     sdlconst.K_KP0: ACTION_STRIKE,
     sdlconst.K_LSHIFT: ACTION_STRIKE,
     sdlconst.K_RSHIFT: ACTION_STRIKE,
  }

  _composite_dict = {
    (ACTION_MOVE_LEFT, ACTION_MOVE_DOWN) : ACTION_MOVE_LEFT_DOWN,
    (ACTION_MOVE_LEFT, ACTION_MOVE_UP) : ACTION_MOVE_LEFT_UP,
    (ACTION_MOVE_RIGHT, ACTION_MOVE_DOWN) : ACTION_MOVE_RIGHT_DOWN,
    (ACTION_MOVE_RIGHT, ACTION_MOVE_UP) : ACTION_MOVE_RIGHT_UP,
  }

  _composite_candidates = set(candidate for keys in _composite_dict for candidate in keys)

  def __init__(self):
    BaseController.__init__(self)
    self.current_action = ACTION_STOP_MOVING
    self.character      = None
    self.camera         = Camera()
    self.widget         = RootWidget(None)
    soya.gui.CameraViewport(self.widget, self.camera)
    
    self.indicators = Indicators(self.widget)
    
    self.interface  = Interface(self.widget)
    
    self.next_help  = 0

    self._action_stack = []
    
  # XXX rewrite me
  def _get_composite(self, action):
    if action in self._composite_candidates:
      for keys in self._composite_dict:
        if action in keys:
          first, other = keys
          if other == action:
            other = first
          if other in self._action_stack:
            return self._composite_dict[keys]
    return None

  def _add_action(self, action):
    if action not in self._action_stack:
        composite = self._get_composite(action)
        self._action_stack.append(action)
        self.character.send_action(action)
        if composite is not None:
          self._add_action(composite)
  
  def _cancel_action(self, action):
    if action in self._action_stack:
        change = False
        if self._action_stack[-1] == action:
            change = True
        self._action_stack.remove(action)
        composite = self._get_composite(action)
        if composite is not None:
          self._cancel_action(composite)
        if change:
            if len(self._action_stack):
                self.character.send_action(self._action_stack[-1])
            else:
                self.character.send_action(ACTION_STOP_MOVING)

  def show_info(self): self.interface.add_log(LogEntry(self.character.get_info()))
  
  def generate_actions(self):
    if not self.character:
      for event in soya.process_event():
        if   event[0] == sdlconst.KEYDOWN:
          if   event[1] == sdlconst.K_ESCAPE: soya.MAIN_LOOP.stop()
    else:
      for event in soya.process_event():
        if   event[0] == sdlconst.KEYDOWN:
          key = event[1]
          if   self.interface.input:
            if   key == sdlconst.K_RETURN   : self.interface.end_input(1)
            elif key == sdlconst.K_ESCAPE   : self.interface.end_input(0)
            #else:                             self.interface.input_box.on_key_pressed(*event[1:])
            #elif key == sdlconst.K_SPACE    : self.interface.add_input(u" ")
            #elif key == sdlconst.K_BACKSPACE: self.interface.backspace_input()
            #elif event.unicode     : self.interface.add_input(event.unicode)
          else:
            if   key == sdlconst.K_ESCAPE: soya.MAIN_LOOP.stop()
            elif (key == sdlconst.K_LSHIFT) or (key == sdlconst.K_RSHIFT):
              if self.interface.inventory: self.interface.inventory_use(); self.interface.end_inventory()
              else: self._add_action(ACTION_STRIKE)
            elif key in self._action_dict:
              if self.interface.inventory: self.interface.end_inventory()
              self._add_action(self._action_dict[key])
            elif key == sdlconst.K_i     : self.show_info()
            elif key == sdlconst.K_h     :
             self.interface.add_log(LogEntry(_(u"__help%s__" % self.next_help)))
             self.next_help += 1
             if self.next_help == 9: self.next_help = 0
            elif key == sdlconst.K_RETURN:
                if self.interface.inventory: self.interface.inventory_use(); self.interface.end_inventory()
                else:                        self.interface.start_input()
            elif key == sdlconst.K_PAGEUP   : self.interface.select_item(-1, 1)
            elif key == sdlconst.K_PAGEDOWN : self.interface.select_item( 1, 1)
            elif (key == sdlconst.K_BACKSPACE) or (key == sdlconst.K_DELETE):
              if self.interface.inventory: self.interface.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):
                      self.interface.inventory_grab(item_on_ground)
                      break
            elif key == sdlconst.K_PRINT: soya.screenshot().save("/tmp/3d.png")
        elif event[0] == sdlconst.KEYUP:
          key = event[1]
          if  key in self._action_dict:
            self._cancel_action(self._action_dict[key])
    return


human_controller = None



Generated by  Doxygen 1.6.0   Back to index