# Balazar
# Copyright (C) 2003-2005 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 2 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, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

import random, math

import tofu, soya, soya.tofu4soya

import balazar, balazar.sound, balazar.base as base, balazar.character as character
from balazar.character import noop, FallingItem, NewItemState, ACTION_HURTED, ACTION_KILLED, ACTION_WALKED_ON, _P, _V

UP   = soya.Vector(None, 0.0,  1.0, 0.0)
DOWN = soya.Vector(None, 0.0, -1.0, 0.0)

ACTION_GROW_1 = 151
ACTION_GROW_2 = 152
ACTION_GROW_3 = 153
ACTION_GROW_4 = 154
ACTION_GROW_5 = 155
ACTION_GROW_6 = 156

class VegetalAction(tofu.Action):
  def __init__(self, action):
    tofu.Action.__init__(self)
    
    self.action = action

  def is_crucial(self): return 1
  
  
class VegetalState(soya.tofu4soya.CoordSystState):
  def __init__(self, mobile):
    soya.tofu4soya.CoordSystState.__init__(self, mobile)


class SpcState(tofu.State):
  def __init__(self, mobile, spc):
    tofu.State.__init__(self)
    
    self.spc = spc
    
    
class HurtState(SpcState):
  def __init__(self, mobile, spc, dir_x, dir_y, dir_z):
    SpcState.__init__(self, mobile, spc)
    
    self.dir_x = dir_x
    self.dir_y = dir_y
    self.dir_z = dir_z
    
  def is_crucial(self): return 1

    
class Vegetal(base.Entity):
  pass


MUSHROOM_SHAPES = (soya.Shape.get("champignon@pos1"), soya.Shape.get("champignon@pos2"), soya.Shape.get("champignon@pos3"))
MUSHROOM_CAL3D_SHAPE = soya.Cal3dShape.get("champignon")

class Mushroom(soya.tofu4soya.Mobile, Vegetal, base.Strikeable, base.Photographiable):
  radius    = 1.0
  map_color = 198, 196, 128, 255
  
  def __init__(self, state = -0.8, grow = 1.0, size = 1.0):
    soya.tofu4soya.Mobile.__init__(self)
    self.volume          = soya.Volume(self, MUSHROOM_SHAPES[int(state)])
    self.volume.name     = "mushroom.volume"
    self.state           = state
    self.grow            = grow
    self.animating       = 0
    self.sub_mushrooms   = []
    self.parent_mushroom = None
    self.center          = soya.Point(self, 0.0, 1.2, 0.0)
    self.bot             = 1
    
    self.scale(0.2176291357901485, 0.2176291357901485, 0.2176291357901485)
    self.scale(size, size, size)
    
  def kesako(self): return _("__kesako__champignon")
  
  def begin_round(self):
    soya.tofu4soya.Mobile.begin_round(self)
    
    if self.animating < 0: # Disappear
      self.animating -= 1
      self.scale(0.9, 0.9, 0.9)
      if self.animating < -15:
        if not self.doer.remote:
          self.parent.remove_mobile(self)
    elif self.state < 0.0: # Appear
      self.state += 0.05
      self.scale(1.1, 1.1, 1.1)
      
  def set_state(self, state):
    if   isinstance(state, SpcState):
      getattr(self, "set_state_%s" % state.spc, noop)(state)
      
    elif isinstance(state, NewItemState):
      f = FallingItem(self.level, state.item, soya.Vector(None, 0.0, 0.3, 0.0))
      f.move(soya.Point(self, 0.0, 2.0, 0.0))
      
  def set_state_151(self, state):
    self.state = 1.01
    self.animating = 2
    self.perso = soya.Cal3dVolume(self, MUSHROOM_CAL3D_SHAPE)
    self.perso.animate_execute_action("pousse1", 0.0, 0.0)
    self.volume.visible = self.volume.solid = 0
    
  def set_state_152(self, state):
    self.state = 2.01
    self.animating = 2
    self.perso = soya.Cal3dVolume(self, MUSHROOM_CAL3D_SHAPE)
    self.perso.animate_execute_action("pousse2", 0.0, 0.0)
    self.volume.visible = self.volume.solid = 0
    
  def set_state_153(self, state):
    self.state = 3.01
    self.animating = -1
    
    if self.parent_mushroom:
      self.parent_mushroom.sub_mushrooms.remove(self)
      if not self.parent_mushroom.sub_mushrooms: self.parent_mushroom.grow = 1.0
      
    balazar.sound.play("morfle1.wav", self, gain = 4.0)
    spore = Spore(self.parent)
    spore.move(self)
    
  def big_round(self):
    if self.state < 0.0: return
    if not self.level: return
    
    if self.animating > 0: # Playing animation
      self.animating -= 1
      if self.animating == 0:
        self.remove(self.perso)
        self.volume.set_shape(MUSHROOM_SHAPES[int(self.state)])
        self.volume.visible = self.volume.solid = 1
      return
      
    if not self.doer.remote:
      old_state = self.state
      self.state += self.grow
      
      if   old_state < 1.0 <= self.state: self.doer.action_done(SpcState(self, ACTION_GROW_1))
      elif old_state < 2.0 <= self.state: self.doer.action_done(SpcState(self, ACTION_GROW_2))
      elif old_state < 3.0 <= self.state:
        self.doer.action_done(SpcState(self, ACTION_GROW_3))
        
        parent = self.parent
        if parent.nb_mushroom < self.level.nb_max_mushroom:
          self.solid = 0
          context = parent.get_root().RaypickContext(self, 10.0)
          self.solid = 1
          if self.scale_x > 2.0:
            for obj in self.parent:
              if isinstance(obj, GiantMushroom): break # Only ONE giant mushroom per level
            else:
              child = GiantMushroom()
              child.rotate_lateral(random.uniform(0.0, 360.0))
              child.set_xyz(
                self.x + random.uniform(-5.0, 5.0),
                self.y + 10.0,
                self.z + random.uniform(-5.0, 5.0),
                )
              parent.add(child)
              r = context.raypick(child, DOWN, -1.0, 1, 1)
              if r:
                r[0].convert_to(parent)
                child.y = r[0].y - 1.0
              else: parent.remove(child)
              
          else:
            for i in range(random.randint(1, 3)):
              child = Mushroom(grow = random.uniform(0.01, 0.1), size = max(0.4, self.scale_x + random.uniform(-0.3, 0.3)))
              child.rotate_lateral(random.uniform(0.0, 360.0))
              child.set_xyz(
                self.x + random.uniform(-5.0, 5.0),
                self.y + 5.0,
                self.z + random.uniform(-5.0, 5.0),
                )
              parent.add(child)
              r = context.raypick(child, DOWN, -1.0, 1, 1)
              if r:
                if   isinstance(r[0].parent.parent, character.Character): continue
                elif isinstance(r[0].parent.parent, TreePompon): continue
                elif isinstance(r[0].parent.parent, Mushroom):
                  child.parent_mushroom = r[0].parent.parent
                  child.parent_mushroom.grow = 0.0
                  child.parent_mushroom.sub_mushrooms.append(child)
                elif getattr(r[0].parent, "name", "") == "giant_mushroom.volume": continue
                r[0].convert_to(parent)
                child.y = r[0].y
                
                self.level.add_mobile(child)
                
              else: self.level.remove(child)
              
  def hurt(self, caster, damage):
    if isinstance(self.parent, base.EjectedBody): return 1
    if isinstance(self.parent, base.HurtedBody ): return 0
    if self.state >= 3.0: return
    
    if damage > -5.0:
      if isinstance(self.parent, base.EjectedBody): return
      if self.state >= 3.0: return
      
      for mushroom in self.sub_mushrooms[:]: mushroom.hurt(caster, damage)
      
      if not self.parent_mushroom:
        h = random.random()
        if   h < 0.2: self.doer.action_done(NewItemState(balazar.item.MushroomFood()))
        elif h < 0.4: self.doer.action_done(NewItemState(balazar.item.OldMushroomFood()))
        
        
      _V.set_start_end(caster, self)
      _V.convert_to(self.parent)
      self.doer.action_done(HurtState(self, ACTION_KILLED, _V.x, _V.y, _V.z))
      
      return 1
    else:
      for mushroom in self.sub_mushrooms: mushroom.hurt(caster, damage)
      
      _V.set_start_end(caster, self)
      _V.convert_to(self.parent)
      self.doer.action_done(HurtState(self, ACTION_HURTED, _V.x, _V.y, _V.z))
      
      return 0
    
  def set_state_11(self, state): # ACTION_HURTED
    balazar.sound.play("morfle1.wav", self, gain = 4.0)
    Spore(self.parent, 10).move(self)
    base.HurtedBody(self.parent, self, soya.Vector(self.parent, state.dir_x, state.dir_y, state.dir_z))
    
  def set_state_12(self, state): # ACTION_KILLED
    if self.parent_mushroom:
      self.parent_mushroom.sub_mushrooms.remove(self)
      if not self.parent_mushroom.sub_mushrooms: self.parent_mushroom.grow = 1.0
      
    level = self.level
    if not self.doer.remote: self.level.remove_mobile(self)
    Spore(level, 16).move(self)
    base.EjectedBody(level, self, soya.Vector(level, state.dir_x, state.dir_y, state.dir_z))
    balazar.sound.play("morfle1.wav", self, gain = 4.0)
        


class GiantMushroom(soya.tofu4soya.Mobile, Vegetal, base.Photographiable):
  radius    = 10.0
  map_color = 198, 196, 128, 255
  
  def __init__(self, state = -0.8):
    soya.tofu4soya.Mobile.__init__(self)
    if state < 1.0: self.volume = soya.Volume(self, soya.Shape.get("champignon_geant@pos1"))
    else:           self.volume = soya.Volume(self, soya.Shape.get("champignon_geant@pos2"))
    self.volume.name = "giant_mushroom.volume"
    self.state     = state
    self.scale(0.2176291357901485, 0.2176291357901485, 0.2176291357901485)
    self.animating = 0
    self.bot = 1
    
  def kesako(self): return _("__kesako__champignon_geant")
    
  def set_state(self, state):
    if isinstance(state, SpcState):
      getattr(self, "set_state_%s" % state.spc, noop)(state)
      
  def set_state_151(self, state):
    self.state = 1.01
    self.animating = 35
    self.perso = soya.Cal3dVolume(self, soya.Cal3dShape.get("champignon_geant"))
    self.perso.animate_execute_action("pousse1", 0.0, 0.0)
    self.volume.visible = self.volume.solid = 0
    
  def begin_round(self):
    soya.tofu4soya.Mobile.begin_round(self)
    
    if self.state < 0.0: # Appear
      self.state += 0.05
      self.scale(1.1, 1.1, 1.1)
    else:
      old_state = self.state
      
      if self.state < 3.0:
        if self.animating > 0: # Playing animation
          self.animating -= 1
          if self.animating == 0:
            self.remove(self.perso)
            self.volume.set_shape(soya.Shape.get("champignon_geant@pos2"))
            self.volume.visible = self.volume.solid = 1
          return
        
        if not self.doer.remote:
          self.state += 0.001
          
          if old_state < 1.0 <= self.state: self.doer.action_done(SpcState(self, ACTION_GROW_1))
          
  def advance_time(self, proportion):
    soya.tofu4soya.Mobile.advance_time(self, proportion)
    
    if (not self.animating) and (0.0 <= self.state < 3.0):
      self.scale_x = self.scale_y = self.scale_z = self.scale_x + proportion * 0.002

      

#import os, os.path, cPickle as pickle
#SPORE_MATERIAL = pickle.load(open(os.path.join(soya.DATADIR, "fx.data"), "rb"))
SPORE_MATERIAL = soya.PARTICLE_DEFAULT_MATERIAL

class Spore(soya.Particles):
  def __init__(self, parent = None, nb_particles = 15):
    #soya.Particles.__init__(self, parent, particle._default() or SPORE_MATERIAL, nb_particles)
    soya.Particles.__init__(self, parent, SPORE_MATERIAL, nb_particles)
    self.auto_generate_particle = 1
    self.removable = 1
    self.set_colors((0.5, 0.95, 0.05, 0.5), (0.5, 0.95, 0.05, 1.0), (0.0, 0.7, 0.0, 0.2))
    self.set_sizes((2.0, 2.0))
    self.life = 25
    
  def generate(self, index):
    angle = random.random() * 3.1417
    sx = math.cos(angle)
    sy = math.sqrt(random.random() * 3.0)
    sz = math.sin(angle)
    l = (0.06 * (1.5 + random.random())) / math.sqrt(sx * sx + sy * sy + sz * sz)
    self.set_particle(index, 0.5 + random.random(), sx * l, sy * l, sz * l, 0.0, 0.01, 0.0)
    
  def begin_round(self):
    soya.Particles.begin_round(self)
    if   self.life >  0: self.life -= 1
    elif self.life == 0:
      self.auto_generate_particle = 0
      self.life = -1





class PlantElement(object):
  pass
    
    
FASME_SHAPE_NAMES = ["fasme@var1", "fasme@var2"]

class Fasme(soya.tofu4soya.Mobile, Vegetal, base.Photographiable):
  map_color = 128, 196, 128, 255
  radius    = 5.0
  def __init__(self, size = 8):
    soya.tofu4soya.Mobile.__init__(self)
    self.shape = soya.Shape.get(random.choice(FASME_SHAPE_NAMES))
    self.oscille_angle = 0.0
    self.oscille_vx    = 1.0
    self.oscille_vy    = 0.0
    self.oscille_vz    = 0.0
    self.size = size
    self.bot = 1
    
  def placed(self):
    if self.size:
      previous = None
      
      child = Fasme(self.size - 1)
      child.solid = 0
      
      _P.parent = self.parent
      
      for j in range(8):
        _P.set_xyz(self.x + random.uniform(-3.0, 3.0), 10000.0, self.z + random.uniform(-3.0, 3.0))
        r = self.parent.raypick(_P, DOWN, -1.0, 1, 1)
        if r and r[0].parent is self: break
      else: return
      child.solid = 1
      
      r[0].parent.add(child)
      child.move(r[0])
      child.rotate_lateral(random.uniform(0.0, 360.0))
      
      child.oscille_vx, child.oscille_vy, child.oscille_vz = child.parent.transform_vector(self.oscille_vx, self.oscille_vy, self.oscille_vz, self.parent)
      child.placed()
      
  def kesako(self): return _("__kesako__fasme")
  
  def begin_round(self):
    soya.tofu4soya.Mobile.begin_round(self)
    
    self.oscille_angle  += 0.01 
    
  def advance_time(self, proportion):
    soya.tofu4soya.Mobile.advance_time(self, proportion)
    
    self.rotate_axe_xyz(proportion * 0.1 * math.cos(self.oscille_angle), self.oscille_vx, self.oscille_vy, self.oscille_vz)
    
  def set_state(self, state):
    soya.tofu4soya.Mobile.set_state(self, state)
    
  def big_round(self):
    if not self.doer.remote:
      self.doer.action_done(VegetalState(self))
      
      

    



# class TreePompon(soya.tofu4soya.Mobile, Vegetal, base.Strikeable, base.Photographiable, base.Terraformer):
#   radius           = 5.5
#   terraform_radius = 3.0
#   map_color        = 128, 64, 128, 255
  
#   def __init__(self):
#     soya.tofu4soya.Mobile.__init__(self)
#     self.shape = soya.Shape.get("scn-tree5")
#     self.bot = 1
    
#   def set_state(self, state):
#     if isinstance(state, SpcState):
#       getattr(self, "set_state_%s" % state.spc, noop)(state)
    
#   def hurt(self, caster, damage):
#     if isinstance(self.parent, base.HurtedBody ): return 0
    
#     _V.set_start_end(caster, self)
#     _V.convert_to(self.parent)
#     self.doer.action_done(HurtState(self, ACTION_HURTED, _V.x, _V.y, _V.z))
    
#     self.solid = 0
    
#     return 0
  
#   def big_round(self):
#     if self.solid == 0: self.solid = 1
  
#   def set_state_11(self, state): # ACTION_HURTED
#     balazar.sound.play("morfle1.wav", self, gain = 4.0)
#     base.HurtedBody(self.parent, self, soya.Vector(self.parent, state.dir_x, state.dir_y, state.dir_z))
        
    
    
class TreePompon(soya.tofu4soya.Mobile, Vegetal, base.Strikeable, base.Photographiable, base.Terraformer, base.SensitiveFloor):
  radius           = 5.0
  terraform_radius = 4.0
  map_color        = 128, 64, 128, 255
  
  def __init__(self):
    soya.tofu4soya.Mobile.__init__(self)
    self.shape  = soya.Shape.get("scn-tree5")
    self.bot    = 1
    self.moving = 0
    self.angle  = 0.0
    
  def set_state(self, state):
    if   isinstance(state, SpcState):
      getattr(self, "set_state_%s" % state.spc, noop)(state)
      
  def character_on(self, character):
    if not self.moving: self.doer.action_done(SpcState(self, ACTION_WALKED_ON))
    else: self.moving = 3
    
  def hurt(self, caster, damage):
    if isinstance(self.parent, base.HurtedBody): return 0
    
    _V.set_start_end(caster, self)
    _V.convert_to(self.parent)
    self.doer.action_done(HurtState(self, ACTION_HURTED, _V.x, _V.y, _V.z))
    
    self.solid = 0
    
    return 0
  
  def big_round(self):
    if self.solid == 0: self.solid = 1
    
  def set_state_11(self, state): # ACTION_HURTED
    balazar.sound.play("morfle1.wav", self, gain = 4.0)
    base.HurtedBody(self.parent, self, soya.Vector(self.parent, state.dir_x, state.dir_y, state.dir_z))
    
  def begin_round(self):
    soya.tofu4soya.Mobile.begin_round(self)
    
    if self.moving:
      self.angle += 0.25
      self.rotate_vertical(math.cos(self.angle) / (1 + (self.angle // 6.2832)))
      
      if not self.doer.remote: self.moving -= 1
      
  def set_state_10(self, state): # ACTION_WAIT
    self.moving = 0
    
  def set_state_14(self, state): # ACTION_WALKED_ON
    self.moving = 3
    
  
class TreasureTreePompon(TreePompon):
  treasure = [balazar.item.Knife, balazar.item.Knife, balazar.item.Knife, balazar.item.Knife, balazar.item.Axe, balazar.item.MushroomFood, balazar.item.OldMushroomFood, lambda : balazar.item.Map("foret_pompon")]
  
  def __init__(self):
    soya.tofu4soya.Mobile.__init__(self)
    self.shape  = soya.Shape.get("scn-tree5")
    self.bot    = 1
    self.moving = 0
    self.angle  = 0.0
    self.treasure_duration = 0
    
  def set_state(self, state):
    if isinstance(state, NewItemState):
      f = FallingItem(self.level, state.item, soya.Vector(None, 0.0, -0.1, 0.0))
      f.move(soya.Point(self, 0.0, 8.0, -6.0))
      
    else: TreePompon.set_state(self, state)
    
  def big_round(self):
    if self.solid == 0: self.solid = 1
    if self.treasure_duration: self.treasure_duration -= 1
    
  def hurt(self, caster, damage):
    r = TreePompon.hurt(self, caster, damage)
    
    if self.treasure and (not self.treasure_duration):
      item = random.choice(self.treasure)()
      self.doer.action_done(NewItemState(item))
      
      self.treasure_duration = 300
      
    return r
  
  
