# Balazar
# Copyright (C) 2004-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 soya, tofu
from soya import Point, Vector

from balazar.controller import StackController, Goto, GotoXZ, Gotoward, LookAt, GrabItem, Fly, Wait, Wander
from balazar.character  import *
from balazar.character  import _P, _V
import balazar.globdef  as globdef

import balazar.base  as base
import balazar.sound

import balazar.npc as npc


class AIController(npc.AIController):
  aggressivity  = 0.4
  dodge         = 0.9
  
  def __init__(self, mobile):
    npc.AIController.__init__(self, mobile)
    
  def attack(self, enemy):
    h = random.random()
    if   h < 0.40: yield self.attack_basic     (enemy)
    elif h < 0.50: yield self.attack_jump      (enemy)
    elif h < 0.80: yield self.attack_jump_over (enemy)
    elif h < 0.95: yield self.attack_side      (enemy)
    else:          yield self.attack_back      (enemy)
    
  def do_nothing(self):
    if random.random() < 0.06: yield Wait  (self.mobile, 200)
    else:                     yield Wander(self.mobile, 200)
    
    
class FixedAIController(AIController):
  def do_nothing(self): yield Wait(self.mobile, 400)
  def interest(self, o, d = None):
    if isinstance(o, Item): return 0.0
    return AIController.interest(self, o, d)
    
class Echassian(npc.Monster, base.Discutable):
  Controller = AIController
  
  race           = "echassian"
  die_duration1  = 114
  die_duration2  = 155
  move_speed     =  1.05
  couic_sound    = "couic_morkul.wav"
  die_sound      = "grogne2.wav"
  
  def __init__(self):
    npc.Monster.__init__(self)
    
    self.perso = soya.Cal3dVolume(self, soya.Cal3dShape.get("echassien2"), ["perso"])
    
    self.fighting   = 1.2
    self.resistance = 1.5
    
    if random.random() < 0.3: self.add_item(balazar.item.Knife(), 0)
    else:                     self.add_item(balazar.item.Sword(), 0)
    if random.random() < 0.3: self.add_item(random.choice(balazar.item.SMALL_TREASURE)(), 0)
    self.auto_equip_items()

    self.relations["Echassian"] = 1.0
    
  def discussion_action___echassien_0__0_1_0(self, hero):
    self.set_relation(hero, -1.0)
    
  def start_discussion(self, hero):
    if not self.is_enemy(hero):
      from balazar.game_interface import AutoDiscussion
      AutoDiscussion(hero, self, u"", u"__echassien_0__0").activate(0)
      
  def strike_left(self, target):
    yield FightAction(ACTION_FIGHT_LEFT, target)
    yield Action(ACTION_WAIT)
    for i in range(26): yield None
    
  def strike_right(self, target):
    yield FightAction(ACTION_FIGHT_RIGHT, target)
    yield Action(ACTION_WAIT)
    for i in range(26): yield None
    
  def strike_charge(self, target):
    yield FightAction(ACTION_FIGHT_CHARGE, target)
    yield Action(ACTION_WAIT)
    for i in range(26): yield None
    
  def strike_sagittal(self, target):
    yield FightAction(ACTION_FIGHT_SAGITTAL, target)
    yield Action(ACTION_WAIT)
    for i in range(29): yield None
    
  def do_action_31(self, action, state): # ACTION_FIGHT_LEFT
    state.animation = "combat1"
    target = (action.target_uid and tofu.Unique.hasuid(action.target_uid) and tofu.Unique.getbyuid(action.target_uid)) or None
    if target:
      _P.clone(target)
      _P.convert_to(self)
      if _P.x > 0.1: state.rotate_lateral(-2.0)
      else: state.rotate_lateral(4.0)
    else: state.rotate_lateral(4.0)
    
    if   action.duration == 0: self.kill_rays = self.weapon.kill_rays
    elif action.duration == 10: self.striking = 1; self.doer.action_done(SoundState(self, "strike2.wav", 4.0))
    action.duration += 1
    if action.duration > 27: self.striking = 0; self.current_action = None
    
  def do_action_32(self, action, state): # ACTION_FIGHT_RIGHT
    state.animation = "combat0"
    target = (action.target_uid and tofu.Unique.hasuid(action.target_uid) and tofu.Unique.getbyuid(action.target_uid)) or None
    if target:
      _P.clone(target)
      _P.convert_to(self)
      if _P.x > -0.1: state.rotate_lateral(-4.0)
      else: state.rotate_lateral(2.0)
    else: state.rotate_lateral(-4.0)

    if   action.duration == 0: self.kill_rays = self.weapon.kill_rays
    elif action.duration == 10: self.striking = 1; self.doer.action_done(SoundState(self, "strike2.wav", 4.0))
    action.duration += 1
    if action.duration > 27: self.striking = 0; self.current_action = None
    
  def do_action_33(self, action, state): # ACTION_FIGHT_SAGITTAL
    state.animation = "combat3"
    target = (action.target_uid and tofu.Unique.hasuid(action.target_uid) and tofu.Unique.getbyuid(action.target_uid)) or None
    if target: self.strike_rot(target, state)
    
    if   action.duration ==  0: self.kill_rays = self.weapon.kill_rays
    elif action.duration == 10: self.striking = 1; self.doer.action_done(SoundState(self, "strike3.wav", 4.0))
    action.duration += 1
    if action.duration > 30: self.striking = 0; self.current_action = None
    
  def do_action_34(self, action, state): # ACTION_FIGHT_CHARGE
    state.animation = "combat2"
    target = (action.target_uid and tofu.Unique.hasuid(action.target_uid) and tofu.Unique.getbyuid(action.target_uid)) or None
    if target: self.strike_rot(target, state)
    
    if        action.duration == 10: self.striking = 1; self.doer.action_done(SoundState(self, "strike2.wav", 2.0))
    
    max_dz = 0.4 - math.sqrt((self.x - state.x) ** 2 + (self.z - state.z) ** 2)
    
    if 10 < action.duration < 20: state.shift(0.0, 0.0, -min(0.4 , max_dz))
    else                        : state.shift(0.0, 0.0, -min(0.15, max_dz))
    action.duration += 1
    if action.duration > 30: self.striking = 0; self.current_action = None
    
    
class EchassianMiller(Echassian):
  Controller = FixedAIController
  
  discussion_radius = 2.5
  
  def discussion_is_valid___echassien_meunier__0_1_0(self, hero):
    return hero.get_item(balazar.item.MushroomFood) and not hero.level.wind
  
  def discussion_is_valid___echassien_meunier__0_1_1(self, hero):
    return hero.get_item(balazar.item.MushroomFood) and hero.level.wind
  
  def discussion_action___echassien_meunier__0_1_1(self, hero):
    item = hero.get_item(balazar.item.MushroomFood)
    if item:
      hero.doer.do_action(ItemAction(ACTION_DROP_ITEM, item))
      
      
  def discussion_is_valid___echassien_meunier__0_2_0(self, hero):
    return not hero.level.wind
  
  
  def start_discussion(self, hero):
    if not self.is_enemy(hero):
      from balazar.game_interface import AutoDiscussion
      AutoDiscussion(hero, self, u"", u"__echassien_meunier__0").activate(0)
      
      
class EchassianCartograph(Echassian):
  Controller = FixedAIController
  
  discussion_radius = 3.0
  
  def __init__(self):
    Echassian.__init__(self)
    
    self.perso.set_shape(soya.Cal3dShape.get("echassien2@vert"), ["perso"])
    
    self.add_item(balazar.item.Map("foret_pompon"), 0)
    
  def discussion_action___echassien_cartographe__0_1_0(self, hero):
    import balazar.trap
    self.level.get_by_class(balazar.trap.IndicatorPlatForm).rotate_toward(1.0, 0.0)
    
  def discussion_action___echassien_cartographe__0_2_0(self, hero):
    import balazar.trap
    self.level.get_by_class(balazar.trap.IndicatorPlatForm).rotate_toward(0.0, -1.0)
    
  def start_discussion(self, hero):
    if not self.is_enemy(hero):
      from balazar.game_interface import AutoDiscussion
      AutoDiscussion(hero, self, u"", u"__echassien_cartographe__0").activate(0)
      
