# 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

import tofu, soya, soya.tofu4soya, soya.sdlconst as sdlconst
from soya.ray import Ray, HalfRay

import balazar, balazar.base as base

class KillRay(soya.Point):
  def __init__(self, parent = None, x = 0.0, y = 0.0, z = 0.0, dir_x = 0.0, dir_y = 0.0, dir_z = -1.0):
    soya.Point.__init__(self, parent, x, y, z)
    self.dir    = soya.Vector(parent, dir_x, dir_y, dir_z)
    self.length = self.dir.length()
    
    #soya.Face(parent, [soya.Vertex(parent, x, y, z), soya.Vertex(parent, x + dir_x, y + dir_y, z + dir_z)])
    
UP   = soya.Vector(None, 0.0,  1.0, 0.0)

class Item(soya.World, base.Entity, base.Discutable, tofu.Unique):
  blessed    = 0
  price      = 0
  can_rotate = 1
  equiped    = 0
  discussion_radius = 2.0
  
  def __deepcopy__(self, memo):
    memo[id(self.owner)] = None
    return soya.World.__deepcopy__(self, memo)
  
  def __init__(self, parent = None):
    tofu.Unique.__init__(self)
    soya.World.__init__(self, parent)
    
    self.owner    = None
    self.duration = 50
    
  def start_discussion(self, hero):
    from balazar.character      import ItemAction, ACTION_GRAB_ITEM
    from balazar.game_interface import Discussion
    Discussion(hero, u"", _(u"Objet au sol : %s") % base.display_name(self),
               check   = lambda : hero.distance_to(self) < self.discussion_radius,
               source  = self,
               choices = [
      Discussion(hero, _(u"Ramasser"),
                 action = lambda : hero.doer.do_action(ItemAction(ACTION_GRAB_ITEM, self)),
                 ),
      ]).activate(1)
    
  def kesako(self): return _("__kesako__%s" % self.__class__.__name__)
  def display_name(self): return _("__item__%s" % self.__class__.__name__)
    
  def incompatible_with(self, other): return 0
  
  def set_owner(self, character):
    self.owner    = character
    self.visible  = 1
    self.duration = 25 + abs(self.blessed) * 50
    
  def fighting_bonus  (self, target): return 0.0
  def resistance_bonus(self, target): return 0.0
  
  def zap(self): pass
  
  def set_inventory_pos(self):
    """Set the item in the right position / orientation, when it is displayed in the inventory"""
    self.set_identity()
    self.rotate_vertical(90.0)
    self.rotate_lateral (90.0)
    min, max = self.get_box()
    self.y = -(min.y + max.y) / 2.0
    
  def set_ground_pos(self, ground = None, dir = None, ground_normal = None):
    """Set the item in the right position / orientation, when it is on ground"""
    #self.set_identity()
    if ground: 
      self.move(ground)
      self.y += 0.05
    if dir: self.look_at(dir)
    self.look_at_y(ground_normal or UP)
    if self.can_rotate: self.turn_incline(90.0)
    self.zap()
    
  def loaded(self):
    tofu.Unique.loaded(self)
    soya.World .loaded(self)
    
  def big_round(self):
    self.duration -= 1
    if self.duration < 1: soya.IDLER.next_round_tasks.append(self.destroy)
    
  def begin_round(self):
    if self.duration < 6:
      self.visible = soya.IDLER.big_round_count % 2
      
  def destroy(self):
    if self.parent:
      self.parent.remove(self)
      
      
class UseableItem(Item):
  def used_by(self, user):
    pass
  
  
class EquipableItem(Item):
  def __init__(self, parent):
    Item.__init__(self, parent)
    self.equiped = 0
    
  def unequip_incompatible_items(self):
    for item in self.owner.items:
      if isinstance(item, EquipableItem) and item.equiped and item.incompatible_with(self):
        item.owner.doer.do_action(balazar.character.ItemAction(balazar.character.ACTION_UNEQUIP_ITEM, item))
        
  def set_equiped(self, equiped):
    self.equiped = equiped

  def set_owner(self, owner):
    if self.equiped: self.set_equiped(0)
    
    Item.set_owner(self, owner)
    
class RightHandItem(EquipableItem):
  def incompatible_with(self, other): return isinstance(other, RightHandItem)
  
  def set_equiped(self, equiped):
    if equiped == self.equiped: return
    
    EquipableItem.set_equiped(self, equiped)
    
    if equiped:
      self.owner.right_hand_item = self
      self.owner.right_hand = soya.World(self.owner)
      self.owner.perso.attach_to_bone(self.owner.right_hand, "mainD")
      self.owner.right_hand.add(self)
      
      self.set_identity()
      self.set_equiped_pos()
      self.zap()
    else:
      self.owner.right_hand_item = None
      
      #print self.parent, self.parent.parent
      
      self.parent.remove(self)
      self.owner.perso.detach_from_bone(self.owner.right_hand)
      self.owner.remove(self.owner.right_hand)
      del self.owner.right_hand
      
  def set_equiped_pos(self):
    self.rotate_incline(180.0)
    self.set_xyz(0.05, 0.1, 0.0)
    
    
class LeftHandItem(EquipableItem):
  def incompatible_with(self, other): return isinstance(other, LeftHandItem)
  
  def set_equiped(self, equiped):
    EquipableItem.set_equiped(self, equiped)
    
    if equiped:
      self.owner.left_hand_item = self
      self.owner.left_hand = soya.World(self.owner)
      self.owner.perso.attach_to_bone(self.owner.left_hand, "mainG")
      self.owner.left_hand.add(self)
      
      self.set_identity()
      self.set_equiped_pos()
      self.zap()
    else:
      self.owner.left_hand_item = None
      self.parent.remove(self)
      self.owner.perso.detach_from_bone(self.owner.left_hand)
      self.owner.remove(self.owner.left_hand)
      
  def set_equiped_pos(self):
    self.set_xyz(0.0, 0.1, 0.0)
    
    
class Weapon(RightHandItem):
  range       = 1.0 # Used by AI
  hurt_weapon = 1
  
  def set_equiped(self, equiped):
    RightHandItem.set_equiped(self, equiped)
    if equiped:
      self.owner.weapon    = self
      self.owner.kill_rays = self.kill_rays
    else:
      self.owner.weapon    = None
      self.owner.kill_rays = ()
      
  def display_name(self):
    self.equiped = 1
    r = _(u"__item__%s" % self.__class__.__name__) + u" (+%s)" % int(round(self.fighting_bonus(None)))
    self.equiped = 0
    return r
  
class Camera(LeftHandItem):
  price = 50
  def __init__(self, parent = None):
    LeftHandItem.__init__(self, parent)
    self.set_shape(soya.Shape.get("camera"))
    
  def set_equiped_pos(self):
    self.rotate_lateral(25.0)
    self.set_xyz(-0.05, 0.34, 0.0)
    
    
photo_model = soya.World()
soya.Face(photo_model, [
  soya.Vertex(photo_model, -0.5333,  0.36, 0.0, 0.005, 0.005),
  soya.Vertex(photo_model,  0.5333,  0.36, 0.0, 0.995, 0.005),
  soya.Vertex(photo_model,  0.5333, -0.36, 0.0, 0.995, 0.995),
  soya.Vertex(photo_model, -0.5333, -0.36, 0.0, 0.005, 0.995),
  ]).double_sided = 1


class Photo(UseableItem):
  price = 1
  def __init__(self, parent = None, image = None, content = []):
    UseableItem.__init__(self, parent)
    self.image   = image
    self.content = content
    
    photo_model.children[0].material = material = soya.Material(soya.image_from_pil(image.resize((64, 64))))
    material.clamp = 1
    self.shape = photo_model.shapify()
    photo_model.children[0].material = soya.DEFAULT_MATERIAL
    
    self.life = 250
    
  def __deepcopy__(self, memo):
    memo[id(self.image)] = self.image
    memo[id(self.shape)] = self.shape
    return UseableItem.__deepcopy__(self, memo)
  
  def set_owner(self, character):
    UseableItem.set_owner(self, character)
    if not character: self.life = 250
    
  def begin_round(self):
    self.life -= 1
    if self.life == 0: soya.IDLER.next_round_tasks.append(self.destroy)
    
  def destroy(self):
    if self.parent:
      self.parent.remove(self)
      
  def set_inventory_pos(self):
    self.set_identity()
    
  def used_by(self, user):
    if user.controller.local and user.player:
      import balazar.discussion
      import balazar.inventory
      balazar.discussion.Bubble(soya.root_widget, user, balazar.inventory.PhotoViewer(self)).set_focus(1)
      
  def display_name(self):
    if len(self.content) < 3: content = self.content
    else:                     content = self.content[:2] + ["..."]
    return _("__item__%s" % self.__class__.__name__) + u" (%s : %s)" % (self.score, ", ".join(content))
  
  def full_display_name(self):
    return _("__item__%s" % self.__class__.__name__) + u" (%s : %s)" % (self.score, ", ".join(self.content))
  
  def kesako(self):
    return _("__kesako__%s" % self.__class__.__name__) + u"\n%s" % ", ".join(self.content)

  
class Sword(Weapon):
  price   = 100
  range   = 1.9
  
  def __init__(self, parent = None):
    Weapon.__init__(self, parent)
    self.set_shape(soya.Shape.get("epee"))
    
    #m = soya.Material()
    #m.diffuse = [168 / 255.0, 165 / 255.0, 108 / 255.0, 1.0]
    #m.separate_specular = 1
    #m.filename = "epee_rayon"
    #m.save()
    m = soya.Material.get("epee_rayon")
    
    self.ray = HalfRay(self, soya.Material.get("epee_rayon"))
    self.ray.z = -0.16
    self.ray.endpoint.z = -1.7
    self.ray.length = 5
    
    self.kill_rays = [
      KillRay(self, 0.0,  0.2, 0.0, 0.0, 0.0, -1.9),
      KillRay(self, 0.0, -0.2, 0.0, 0.0, 0.0, -1.9),
      ]
    
    self.prot = soya.Volume(self, soya.Shape.get("epee_prot"))
    self.prot.visible = self.prot.solid = 0
    
  def fighting_bonus(self, target):
    if self.equiped: return 2.0
    return 0.0
  
  def zap(self): self.ray.zap()

  
class LargeSword(Weapon):
  price   = 100
  range   = 1.2
  
  def __init__(self, parent = None):
    Weapon.__init__(self, parent)
    self.set_shape(soya.Shape.get("epee_large"))
    
    #m = soya.Material()
    #m.diffuse = [168 / 255.0, 165 / 255.0, 108 / 255.0, 1.0]
    #m.separate_specular = 1
    #m.filename = "epee_rayon"
    #m.save()
    m = soya.Material.get("epee_rayon")
    
    self.ray = HalfRay(self, soya.Material.get("epee_rayon"))
    self.ray.z = -0.16
    self.ray.endpoint.z = -1.0
    self.ray.length = 5
    
    self.kill_rays = [
      KillRay(self, 0.0,  0.2, 0.0, 0.0, 0.0, -1.2),
      KillRay(self, 0.0, -0.2, 0.0, 0.0, 0.0, -1.2),
      ]
    
    self.prot = soya.Volume(self, soya.Shape.get("epee_prot"))
    self.prot.visible = self.prot.solid = 0
    
  def fighting_bonus(self, target):
    if self.equiped: return 4.0
    return 0.0
  
  def zap(self): self.ray.zap()

  
class TuryleSword(Weapon):
  blessed = 2
  price   = 100
  range   = 2.0
  
  def __init__(self, parent = None):
    Weapon.__init__(self, parent)
    self.set_shape(soya.Shape.get("epee_turyle"))
    
    self.ray = HalfRay(self, soya.Material.get("epee_turyle_rayon"))
    self.ray.z = -0.16
    self.ray.endpoint.z = -1.8
    self.ray.length = 5
    
    self.kill_rays = [
      KillRay(self, 0.0,  0.2, 0.0, 0.0, 0.0, -2.0),
      KillRay(self, 0.0, -0.2, 0.0, 0.0, 0.0, -2.0),
      ]
    
    self.prot = soya.Volume(self, soya.Shape.get("epee_turyle_prot"))
    self.prot.visible = self.prot.solid = 0
    
#     import soya.sphere as sphere
#     m = soya.Material()
#     m.diffuse = 1.0, 0.0, 0.0, 0.5
#     m.
#     w = sphere.Sphere(None, m)
#     w.scale(1.3, 1.3, 1.3)
#     soya.Volume(self, w.shapify()).move(self.kill_point)

    # Halo (ideal pour un baton magique)
#     import soya.sphere as sphere
#     m = soya.Material()
#     m.diffuse = 0.15, 0.1, 0.2, 1.0
#     m.diffuse = 0.5, 0.4, 0.8, 1.0
#     m.additive_blending = 1
#     w = sphere.Sphere(None, m)
#     w.scale(1.3, 1.3, 1.3)
#     soya.Volume(self, w.shapify()).move(self.kill_point)
    
  def fighting_bonus(self, target):
    if self.equiped: return 5.0
    return 0.0
  
  def zap(self): self.ray.zap()
    
    
class Axe(Weapon):
  range   = 1.7
  price   = 30
  
  def __init__(self, parent = None):
    Weapon.__init__(self, parent)
    self.set_shape(soya.Shape.get("hache"))
    
    self.ray = HalfRay(self, soya.Material.get("couteau_rayon"))
    self.ray.         set_xyz(0.0, -0.5, -0.4)
    self.ray.endpoint.set_xyz(0.0,  0.0, -1.1)
    self.ray.length = 5
    
    self.kill_rays = [
      KillRay(self, 0.0,   0.15,  0.0,  0.0,  0.0, -1.5),
      KillRay(self, 0.0,  -0.15,  0.0,  0.0, -0.6,  -1.2),
      ]
    
    self.prot = soya.Volume(self, soya.Shape.get("hache_prot"))
    self.prot.visible = self.prot.solid = 0
    
  def fighting_bonus(self, target):
    if self.equiped: return 2.0
    return 0.0
  
  def zap(self): self.ray.zap()
  
  
class HalfVisibleAxe(Axe):
  price       = 80
  blessed     = 1
  hurt_weapon = 0
  def __init__(self, parent = None):
    Axe.__init__(self, parent)
    
  def begin_round(self):
    self.visible       = soya.IDLER.big_round_count % 4
    

class Scepter(object):
  pass

class PomponScepter(LeftHandItem, Scepter):
  price   = 200
  blessed = 3
  
  def __init__(self, parent = None):
    LeftHandItem.__init__(self, parent)
    
    self.set_shape(soya.Shape.get("sceptre_pompon"))
    self.nb_round = 0
    
  def begin_round(self):
    if self.owner:
      self.nb_round += 1
      if self.nb_round == 200:
        self.nb_round         = 0
        self.owner.invincible = 120
        
  def set_equiped_pos(self):
    self.rotate_incline(180.0)
    self.set_xyz(-0.05, 0.1, 0.0)
    
    
class Knife(Weapon):
  range = 1.3
  price = 5
  def __init__(self, parent = None):
    Weapon.__init__(self, parent)
    self.set_shape(soya.Shape.get("couteau"))
    
    self.ray = HalfRay(self, soya.Material.get("couteau_rayon"))
    self.ray.z = -0.16
    self.ray.endpoint.z = -0.85
    self.ray.length = 5
    
    self.kill_rays = [
      KillRay(self, 0.0,  0.15, 0.2, 0.0, 0.0, -1.4),
      KillRay(self, 0.0, -0.15, 0.2, 0.0, 0.0, -1.4),
      ]
    
    self.prot = soya.Volume(self, soya.Shape.get("couteau_prot"))
    self.prot.visible = self.prot.solid = 0
    
  def fighting_bonus(self, target):
    if self.equiped: return 1.0
    return 0.0
  
  def zap(self):
    self.ray.zap()
    
    
class MorkulKillerKnife(Knife):
  price = 70
  
  def fighting_bonus(self, target):
    if self.equiped:
      import balazar.morkul
      if isinstance(target, balazar.morkul.Morkul):
        #if tofu.GAME_INTERFACE:
        #  e = KillerEffect(self.owner.level)
        #  e.move(self)
        #  e.regenerate()
        return 8.0
      return 1.0
    return 0.0
  
  def zap(self):
    self.ray.zap()
    
    
class KillerEffect(soya.Particles):
  def __init__(self, parent = None, nb_particles = 6):
    soya.Particles.__init__(self, parent, soya.Material.get("x_lumiere_1"), nb_particles)
    self.set_colors((0.3, 0.1, 1.0, 1.0), (0.3, 0.1, 1.0, 1.0), (0.3, 0.1, 1.0, 1.0), (0.0, 0.0, 0.1, 1.0))
    #self.set_sizes((0.4, 0.4))
    self.set_sizes((2.0, 2.0), (0.1, 0.1))
    self.lit        = 0
    self.removeable = 1
    self.auto_generate_particle  = 0
    self.max_particles_per_round = 200
    
  def generate(self, index):
    sx = random.uniform(-0.01, 0.01)
    sy = random.uniform(-0.0 , 0.01)
    sz = random.uniform(-0.01, 0.01)
    self.set_particle(index, random.uniform(2.0 , 3.0), 10.0 * sx, 10.0 * sy, 10.0 * sz, sx, sy, sz)
    


class Key(Item):
  price = 5
  def __init__(self, parent = None):
    Item.__init__(self, parent)
    self.set_shape(soya.Shape.get("clef"))
    
    
class LeftHandHorizontalItem(LeftHandItem):
  def equip(self, character):
    Item.equip(self, character)
    
    character.left_hand = soya.World(character)
    character.perso.attach_to_bone(character.left_hand, "mainG")
    character.add(self)
    self.set_identity()
    self.rotate_incline(180.0)
    self.set_xyz(-0.05, 0.1, 0.0)
    
  def unequip(self):
    self.owner.perso.detach_from_bone(self.owner.left_hand)
    self.owner.remove(self.owner.left_hand)
    self.owner.remove(self)
    
    Item.unequip(self)
    
  def advance_time(self, proportion):
    if self.owner:
      xyz = self.owner.left_hand.x, self.owner.left_hand.y, self.owner.left_hand.z
      self.set_identity()
      self.set_xyz(*xyz)
      
  def set_inventory_pos(self):
    """Set the item in the right position / orientation, when it is displayed
in the inventory"""
    self.set_identity()
    min, max = self.get_box()
    self.y = -(min.y + max.y) / 2.0
    
    
class Food(UseableItem):
  can_rotate = 0
  
  def set_inventory_pos(self):
    self.set_identity()
    min, max = self.get_box()
    self.y = -(min.y + max.y) / 2.0
    
    
class MushroomFood(Food):
  price = 9
  def __init__(self, parent = None):
    Food.__init__(self, parent)
    self.set_shape(soya.Shape.get("nourriture_champignon"))
    
  def used_by(self, user):
    if not user.doer.remote:
      user.doer.action_done(balazar.character.ValueState(user, balazar.character.STATE_HEAL_LIFE, 0.4))
      
    self.owner.items.remove(self)
    
    
class OldMushroomFood(Food):
  price = 2
  def __init__(self, parent = None):
    Food.__init__(self, parent)
    self.set_shape(soya.Shape.get("nourriture_champignon_blet"))
    #self.volume = soya.Volume(self, soya.Shape.get("nourriture_champignon_blet"))
    
  def used_by(self, user):
    if not user.doer.remote:
      user.hurt(0.2, user, can_resist = 0)
      
    self.owner.items.remove(self)
    
class TelekinesyCursedItem(Item):
  price   =  3
  blessed = -1
  
  def __init__(self, parent = None):
    super(TelekinesyCursedItem, self).__init__(parent)
    self.cursed_owner = None
    
    self.kill_point = KillPoint(self, 0.0, 0.0, -1.5)
    
  def set_owner(self, character):
    if self.owner and (self.owner.life <= 0.0):
      # Free the curse if the previous owner is dead
      self.cursed_owner = None
    super(TelekinesyCursedItem, self).set_owner(character)
    
    if not self.cursed_owner: self.cursed_owner = character
    elif character and (not self.cursed_owner is character):
      # Cursed items cannot be given to someone else
      character.drop_item(self)
      
    if self.owner: self.set_equiped(1)
    
  def set_equiped(self, equiped):
    super(TelekinesyCursedItem, self).set_equiped(equiped)
    if (not equiped) and (self.owner.life > 0.0): self.owner.drop_item(self) # => will activate the curse !
    
  def big_round(self):
    if self.cursed_owner and (not self.owner):
      if self.distance_to(self.cursed_owner) > 1.0:
        from balazar.activity import TelekinesyEffect
        TelekinesyEffect(self.parent, self.cursed_owner, self).move(self)
        self.cursed_owner = None
        
  def reset_curse(self): self.cursed_owner = None
  
  
class CursedSword(TelekinesyCursedItem, Weapon):
  range = 1.6
  
  def __init__(self, parent = None):
    TelekinesyCursedItem.__init__(self, parent)
    self.set_shape(soya.Shape.get("epee_maudite"))
    
    self.ray = HalfRay(self, soya.Material.get("epee_maudite_rayon"))
    self.ray.z = -0.16
    self.ray.endpoint.set_xyz(0.0, 0.27, -1.38)
    self.ray.length = 5
    
    self.kill_rays = [
      KillRay(self, 0.0,  0.15, 0.0, 0.0, 0.2, -1.6),
      KillRay(self, 0.0, -0.15, 0.0, 0.0, 0.2, -1.6),
      ]
    
    self.prot = soya.Volume(self, soya.Shape.get("epee_maudite_prot"))
    self.prot.visible = self.prot.solid = 0
    
  def fighting_bonus(self, target):
    if self.equiped: return -1.0
    return 0.0
  
  def zap(self): self.ray.zap()
  
  
class Map(Item):
  can_rotate = 0
  blessed    = 1
  
  def __init__(self, country_name):
    Item.__init__(self)
    
    self.set_shape(soya.Shape.get("item_map"))
    
    self.country_name = country_name

  def display_name(self): return _("__item__Map_%s" % self.country_name)
  
  def set_inventory_pos(self):
    self.set_identity()
    min, max = self.get_box()
    self.y = -(min.y + max.y) / 2.0
    
  def set_ground_pos(self, ground = None, dir = None, ground_normal = None):
    Item.set_ground_pos(self, ground, dir, ground_normal)
    self.turn_vertical(90.0)
    self.y += 0.05
    
# Tables for random item choice

SMALL_TREASURE = [
  MushroomFood,
  MushroomFood,
  Key,
  Key,
  Knife,
  Knife,
  Axe,
  Sword,
  lambda : Map("foret_pompon"),
#  CursedSword,
  ]

BIG_TREASURE = [
  Key,
  TuryleSword,
  HalfVisibleAxe,
#  CursedSword,
  ]

