# -*- coding: latin-1 -*-
# 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.base as base, balazar.character as character, balazar.vegetation as vegetation, balazar.building as building
import balazar.weather
from balazar.character import _P, _V, _P2, _V2
from balazar.base      import DecorativeEntity
from balazar.level     import *
from balazar.trap      import *
from balazar.morkul    import *



def create_level(X, Z):
  level = Level()
  level.filename = "level_%s_%s" % (X, Z)
  level.X = X
  level.Z = Z
  level.x = X * LEVEL_DIM
  level.z = Z * LEVEL_DIM
  level.static_part        = soya.World(level)
  level.buildings          = []
  level.interesting_places = []
  level.down               = soya.Vector(level, 0.0, -1.0, 0.0)
  level.weather_agent      = balazar.weather.WeatherAgent()
  level.add_mobile(level.weather_agent)
  return level
  
def end_level(level):
  del level.buildings
  del level.interesting_places
  del level.down
  level.static_part.children[0].reinit()
  
def add_bridge(level, b_0_1 = 1, b_1_0 = 1, b_1_2 = 1, b_2_1 = 1):
  _P2.parent = level

  if b_0_1:
    y = 0.0
    i = x = 0.0
    while i < LAND_DIM / 2.0:
      i += 1.0
      _P2.set_xyz(i, 1000.0, LAND_DIM / 2.0)

      if level.children[0].raypick_b(_P2, level.down, -1, 1, 0):
        _P2.set_xyz(i + 2.0, 1000.0, LAND_DIM / 2.0)
        if level.children[0].raypick(_P2, level.down, -1, 1, 1, _P, _V):
          _P.convert_to(level)
          y = _P.y - 13.0
          x = i - 8.0
        break

    bridge = level.bridge_0_1 = Bridge(level, -1, 0)
    bridge.set_xyz(x, y, LAND_DIM / 2.0)
    
  if b_2_1:
    y = 0.0
    i = x = LAND_DIM
    while i > LAND_DIM / 2.0:
      i -= 1.0
      _P2.set_xyz(i, 1000.0, LAND_DIM / 2.0)

      if level.children[0].raypick_b(_P2, level.down, -1, 1, 0):
        _P2.set_xyz(i - 2.0, 1000.0, LAND_DIM / 2.0)
        if level.children[0].raypick(_P2, level.down, -1, 1, 1, _P, _V):
          _P.convert_to(level)
          y = _P.y - 13.0
          x = i + 8.0
        break
      
    bridge = level.bridge_2_1 = Bridge(level, 1, 0)
    bridge.rotate_lateral(180.0)
    bridge.set_xyz(x, y, LAND_DIM / 2.0)
    
    
  if b_1_0:
    y = 0.0
    i = x = 0.0
    while i < LAND_DIM / 2.0:
      i += 1.0
      _P2.set_xyz(LAND_DIM / 2.0, 1000.0, i)

      if level.children[0].raypick_b(_P2, level.down, -1, 1, 0):
        _P2.set_xyz(LAND_DIM / 2.0, 1000.0, i + 2.0)
        if level.children[0].raypick(_P2, level.down, -1, 1, 1, _P, _V):
          _P.convert_to(level)
          y = _P.y - 13.0
          x = i - 8.0
        break

    bridge = level.bridge_1_0 = Bridge(level, 0, -1)
    bridge.rotate_lateral(270.0)
    bridge.set_xyz(LAND_DIM / 2.0, y, x)


  if b_1_2:
    y = 0.0
    i = x = LAND_DIM
    while i > LAND_DIM / 2.0:
      i -= 1.0
      _P2.set_xyz(LAND_DIM / 2.0, 1000.0, i)

      if level.children[0].raypick_b(_P2, level.down, -1, 1, 0):
        _P2.set_xyz(LAND_DIM / 2.0, 1000.0, i - 2.0)
        if level.children[0].raypick(_P2, level.down, -1, 1, 1, _P, _V):
          _P.convert_to(level)
          y = _P.y - 13.0
          x = i + 8.0
        break

    bridge = level.bridge_1_2 = Bridge(level, 0, 1)
    bridge.rotate_lateral(90.0)
    bridge.set_xyz(LAND_DIM / 2.0, y, x)


def place_on_bridge(bridge, traps_distances):
  level = bridge.parent
  _V.__init__(bridge, 0.0, 0.0, -1.0)
  
  i = 1.0
  for trap, distance in traps_distances:
    i += distance
    _P.__init__(bridge, -i, 16.0, 0.0)
    _P.convert_to(level)
    trap.move(_P)
    if isinstance(trap, tofu.Mobile): level.add_mobile(trap)
    trap.look_at(_V)
    trap.rotate_lateral(90.0)
    
    
    
def create_land(level):
  land = soya.Land(None, LAND_SIZE)
  land.texture_factor = 0.5
  return land

def random_land(level, land,
                default_min_y = 0.18, default_max_y = 0.23,
                nb_max = 0.0015,
                pic_proba = 0.1, plateau_proba = 0.25, colline_proba = 0.25, cratere1_proba = 0.1, cratere2_proba = 0.1, monticule_proba = 0.1, termitiere_proba = 0.1, 
                ):
  proba = pic_proba + plateau_proba + colline_proba + cratere1_proba + cratere2_proba + monticule_proba + termitiere_proba
  pic_proba        += 0
  plateau_proba    += pic_proba
  colline_proba    += plateau_proba
  cratere1_proba   += colline_proba
  cratere2_proba   += cratere1_proba
  monticule_proba  += cratere2_proba
  termitiere_proba += monticule_proba
  
  # Default height
  for i in range(LAND_SIZE):
    for j in range(LAND_SIZE):
      land.set_height(i, j, random.uniform(default_min_y, default_max_y))
      
  for n in range(int(random.uniform(0.001, nb_max) * LAND_SIZE * LAND_SIZE)):
    t = random.random() * proba
    x = random.randrange(LAND_SIZE)
    y = random.randrange(LAND_SIZE)
    if   t < pic_proba:
      r = random.randrange(5, 20)
      h = random.uniform(0.5, 2.5)
      
      for i in range(max(0, x - r), min(LAND_SIZE - 1, x + r)):
        for j in range(max(0, y - r), min(LAND_SIZE - 1, y + r)):
          d = (r - math.sqrt((i - x) * (i - x) + (j - y) * (j - y))) / float(r)
          if d > 0.0:
            d = d * d
            land.set_height(i, j, min(1.0, d * h + land.get_height(i, j)))
            
    elif t < plateau_proba:
      r1 = random.randrange(10, 20)
      r2 = random.randrange(r1 + 5, 40)
      h  = random.uniform(0.05, 0.3)
      
      for i in range(max(0, x - r2), min(LAND_SIZE - 1, x + r2)):
        for j in range(max(0, y - r2), min(LAND_SIZE - 1, y + r2)):
          d = (r2 - math.sqrt((i - x) * (i - x) + (j - y) * (j - y))) / float(r2 - r1)
          if d > 1.0: d = 1.0
          if d > 0.0:
            d = 0.5 - (math.cos(d * math.pi) / 2.0)
            land.set_height(i, j, min(1.0, d * h + land.get_height(i, j)))
            
    elif t < colline_proba:
      r = random.randrange(5, 35)
      h = random.uniform(0.1, 0.4)
      
      for i in range(max(0, x - r), min(LAND_SIZE - 1, x + r)):
        for j in range(max(0, y - r), min(LAND_SIZE - 1, y + r)):
          d = (r - math.sqrt((i - x) * (i - x) + (j - y) * (j - y))) / float(r)
          if d > 0.0:
            d = 0.5 - (math.cos(d * math.pi) / 2.0)
            land.set_height(i, j, min(1.0, d * h + land.get_height(i, j)))
            
    elif t < cratere1_proba:
      r = random.randrange(15, 20)
      h = random.uniform(0.1, 0.1)
      level.interesting_places.append((x * land.scale_factor, y * land.scale_factor))
      
      for i in range(max(0, x - r), min(LAND_SIZE - 1, x + r)):
        for j in range(max(0, y - r), min(LAND_SIZE - 1, y + r)):
          d = (r - math.sqrt((i - x) * (i - x) + (j - y) * (j - y))) / float(r)
          if d > 0.0:
            d = (math.sin(d * math.pi) / 2.0) + 0.5
            land.set_height(i, j, min(1.0, d * h + land.get_height(i, j)))
            
    elif t < cratere2_proba:
      r = random.randrange(15, 20)
      h = random.uniform(0.1, 0.1)
      level.interesting_places.append((x * land.scale_factor, y * land.scale_factor))
      
      for i in range(max(0, x - r), min(LAND_SIZE - 1, x + r)):
        for j in range(max(0, y - r), min(LAND_SIZE - 1, y + r)):
          d = (r - math.sqrt((i - x) * (i - x) + (j - y) * (j - y))) / float(r)
          if d > 0.0:
            d = (math.cos(d * math.pi) / 2.0) + 0.5
            land.set_height(i, j, min(1.0, d * h + land.get_height(i, j)))
            
    elif t < monticule_proba:
      r = random.randrange(5, 20)
      h = random.uniform(0.5, 1.0)
      level.interesting_places.append((x * land.scale_factor, y * land.scale_factor))
      
      for i in range(max(0, x - r), min(LAND_SIZE - 1, x + r)):
        for j in range(max(0, y - r), min(LAND_SIZE - 1, y + r)):
          d = (r - math.sqrt((i - x) * (i - x) + (j - y) * (j - y))) / float(r)
          if d > 0.0:
            d = math.sqrt(d)
            land.set_height(i, j, min(1.0, d * h + land.get_height(i, j)))
            
    else:
      # Termitière conique XXX une texture spéciale ?
      r = random.randrange(5, 15)
      h = random.uniform(0.1, 0.5)
      
      for i in range(max(0, x - r), min(LAND_SIZE - 1, x + r)):
        for j in range(max(0, y - r), min(LAND_SIZE - 1, y + r)):
          d = (r - math.sqrt((i - x) * (i - x) + (j - y) * (j - y))) / float(r)
          if d > 0.0:
            land.set_height(i, j, min(1.0, d * h + land.get_height(i, j)))
            
def smooth_land(level, land, nb = 2):
  for k in range(nb):
    smoothed = [0.0] * (LAND_SIZE * LAND_SIZE)
    for i in range(1, LAND_SIZE - 1):
      for j in range(1, LAND_SIZE - 1):
        smoothed[i + LAND_SIZE * j] = (land.get_height(i, j) + land.get_height(i - 1, j) + land.get_height(i, j - 1) + land.get_height(i + 1, j) + land.get_height(i, j + 1)) / 5.0
    for i in range(1, LAND_SIZE - 1):
      for j in range(1, LAND_SIZE - 1):
        land.set_height(i, j, smoothed[i + LAND_SIZE * j])
        
def random_land_border(level, land):
  out_of_map_vertex_options = (1, 1)
  
  fall_length  = 2
  afall_length = 0
  for i in range(LAND_SIZE):
    for j in range(fall_length):
      land.set_vertex_option(i, j, *out_of_map_vertex_options)
      #land.set_vertex_color(i, j, (0.0, 0.0, 0.0, 0.0))
      land.set_height(i, j, 0.0)
    land.set_height(i, j + 1, 0.92 * land.get_height(i, j + 1))
    if i % 2:
      fall_length = max(1, fall_length + afall_length)
      if (fall_length == 1) and (afall_length == -1): afall_length = 0
      else: afall_length = max(-1, min(1, afall_length + random.randint(-1, 1)))
      
  fall_length  = 2
  afall_length = 0
  for i in range(LAND_SIZE):
    for j in range(LAND_SIZE - 1, LAND_SIZE - fall_length - 1, -1):
      land.set_vertex_option(i, j, *out_of_map_vertex_options)
      #land.set_vertex_color(i, j, (0.0, 0.0, 0.0, 0.0))
      land.set_height(i, j, 0.0)
    land.set_height(i, j - 1, 0.92 * land.get_height(i, j - 1))
    if i % 2:
      fall_length = max(1, fall_length + afall_length)
      if (fall_length == 1) and (afall_length == -1): afall_length = 0
      else: afall_length = max(-1, min(1, afall_length + random.randint(-1, 1)))

  fall_length  = 2
  afall_length = 0
  for j in range(LAND_SIZE):
    for i in range(fall_length):
      land.set_vertex_option(i, j, *out_of_map_vertex_options)
      #land.set_vertex_color(i, j, (0.0, 0.0, 0.0, 0.0))
      land.set_height(i, j, 0.0)
    land.set_height(i + 1, j, 0.92 * land.get_height(i + 1, j))
    if j % 2:
      fall_length = max(1, fall_length + afall_length)
      if (fall_length == 1) and (afall_length == -1): afall_length = 0
      else: afall_length = max(-1, min(1, afall_length + random.randint(-1, 1)))
      
  fall_length  = 2
  afall_length = 0
  for j in range(LAND_SIZE):
    for i in range(LAND_SIZE - 1, LAND_SIZE - fall_length - 1, -1):
      land.set_vertex_option(i, j, *out_of_map_vertex_options)
      #land.set_vertex_color(i, j, (0.0, 0.0, 0.0, 0.0))
      land.set_height(i, j, 0.0)
    land.set_height(i - 1, j, 0.92 * land.get_height(i - 1, j))
    if j % 2:
      fall_length = max(1, fall_length + afall_length)
      if (fall_length == 1) and (afall_length == -1): afall_length = 0
      else: afall_length = max(-1, min(1, afall_length + random.randint(-1, 1)))


def end_land(level, land, height = 50.0):
  land.multiply_height(height)
  land.scale_factor = LAND_SCALE
  land.map_size = 8
  land.split_factor = 4.0
  
  level.static_part.add(land)


def check_distance(level, land, house, add_in_buildings = 1):
  for building in level.buildings:
    if math.sqrt((house.x - building.x) ** 2 + (house.z - building.z) ** 2) < (getattr(house, "radius", 1.0) + getattr(building, "radius", 1.0)) * 1.8:
      return 0
  if add_in_buildings: level.buildings.append(house)
  return 1

def put_on_ground(level, land, o, orientate = 0, p = None, land_only = 0):
  old_solid = o.solid
  o.solid = 0
  
  r = level.raypick(p or o, level.down, -1.0, 1, 1)
  
  o.solid = old_solid
  if r:
    if land_only:
      if not isinstance(r[0].parent, soya.Land): return
      
    r[0].convert_to(level)
    if r[0].y <= 0.0: return 0
    o.move(r[0])
    if orientate: o.look_at_y(r[1])
    return r
  
def terraform(level, land, terraformer):
  x = int(round(terraformer.x / land.scale_factor))
  y = int(round(terraformer.z / land.scale_factor))
  r1 = terraformer.terraform_radius
  r2 = r1 * 2.0
  h  = terraformer.y
  
  for i in range(max(0, int(x - r2)), min(LAND_SIZE - 1, int(x + r2))):
    for j in range(max(0, int(y - r2)), min(LAND_SIZE - 1, int(y + r2))):
      if not land.get_vertex_option(i, j)[0]:
        d = (r2 - math.sqrt((i - x) * (i - x) + (j - y) * (j - y))) / float(r2 - r1)
        if   d > 1.0: land.set_height(i, j, h)
        elif d > 0.0: land.set_height(i, j, d * h + (1.0 - d) * land.get_height(i, j))
        
        
def place(level, land, obj, orientate = 0, uppon = None, arround = None, arround_radius = 5.0, min_x = 0.0, max_x = 10000.0, min_z = 0.0, max_z = 10000.0, land_only = 0, rotate = 1):
  radius = getattr(obj, "radius", 3.0) * 3.0
  if   uppon:
    for i in range(100):
      uppon_radius = uppon.radius
      _P.parent = level
      _P.set_xyz(uppon.x + random.uniform(-uppon_radius, uppon_radius), 1000.0, uppon.z + random.uniform(-uppon_radius, uppon_radius))
      impact = put_on_ground(level, land, obj, orientate, _P)
      if impact and impact[0].parent.is_inside(uppon): break
      
  elif arround:
    arround = arround % level
    for i in range(100):
      _P.parent = level
      _P.set_xyz(arround.x + random.uniform(-arround_radius, arround_radius), 1000.0, arround.z + random.uniform(-arround_radius, arround_radius))
      if put_on_ground(level, land, obj, orientate, _P): break
      
  else:
    for i in range(100):
      _P.parent = level
      _P.set_xyz(
        min(max_x, max(min_x, random.uniform(radius, LAND_DIM - radius))),
        1000.0,
        min(max_z, max(min_z, random.uniform(radius, LAND_DIM - radius))),
        )
      if put_on_ground(level, land, obj, orientate, _P, land_only = land_only) and check_distance(level, land, obj):
        break
    else:
      print "place failed"
      return 0
    
  if isinstance(obj, tofu.Mobile): level.add_mobile(obj)
  if rotate: obj.rotate_lateral(random.uniform(0.0, 360.0))
  if isinstance(obj, base.Terraformer): terraform(level, land, obj)
  if hasattr(obj, "placed"): obj.placed()
  
  return obj


def place_on_altar(level, obj):
  land = level.static_part.children[0]
  altar = building.Altar(level)
  place(level, land, altar)
  p = altar.position()
  p.y += 1000.0
  put_on_ground(level, land, obj, p = p)
  
  if isinstance(obj, tofu.Mobile): level.add_mobile(obj)
  obj.rotate_lateral(random.uniform(0.0, 360.0))
  if hasattr(obj, "placed"): obj.placed()
  
  return obj

def place_in_line(level, *args):
  """args is [(obj, distance_from_previous, x_offset)]"""
  land = level.static_part.children[0]
  
  previous = obj0 = place(level, land, args[0][0])
  
  args = args[1:]

  for i in range(200):
    previous = obj0
    dir = soya.Vector(level, random.uniform(-1.0, 1.0), 0.0, random.uniform(-1.0, 1.0))
    dir.set_length(1.0)
    
    x = dir.cross_product(soya.Vector(level, 0.0, 1.0, 0.0))
    
    for (obj, d, x_offset) in args:
      p = previous + dir * d + x * x_offset
      p.y += 1000.0
      p.parent = level
      
      if put_on_ground(level, land, obj, 0, p, land_only = 1): # and check_distance(level, land, obj, add_in_buildings = 0):
        pass
      else:
        break
      previous = obj
    else: break
    
  else: print "place_in_line failed"
  
  obj0.look_at(dir)
  for (obj, d, x_offset) in args:
    if isinstance(obj, tofu.Mobile): level.add_mobile(obj)
    elif obj.parent is None: level.add(obj)
    obj.look_at(dir)
    if isinstance(obj, base.Terraformer): terraform(level, land, obj)
    if hasattr(obj, "placed"): obj.placed()
    level.buildings.append(obj)
    
  return obj
    
def add_vigie(level, pos = None, put_uppon = None):
  v = building.Vigie(level.static_part)
  
  if pos: put_on_ground(level, land, v, 0, pos)
  elif pos is None: place(level, level.static_part.children[0], v)
  
  if put_uppon:
    put_uppon.set_xyz(v.x, v.y + 17.0, v.z)
    if isinstance(put_uppon, tofu.Mobile): level.add_mobile(put_uppon)
    if hasattr(put_uppon, "placed"): put_uppon.placed()
    
  return v

def put_uppon_vigie(level, vigie, obj):
  obj.set_xyz(vigie.x, vigie.y + 17.0, vigie.z)
  if isinstance(obj, tofu.Mobile): level.add_mobile(obj)
  if hasattr(obj, "placed"): obj.placed()
  
  
def add_flower(level, land, nb = 40):
  grass2 = soya.Material.get("grass3")
  for i in range(nb):
    x = random.randrange(LAND_SIZE)
    y = random.randrange(LAND_SIZE)
    r = random.randint(1, 3)
    if r == 3: level.interesting_places.append((x * land.scale_factor, y * land.scale_factor))
    
    for i in range(max(0, x - r), min(LAND_SIZE - 1, x + r)):
      for j in range(max(0, y - r), min(LAND_SIZE - 1, y + r)):
        d = (r - math.sqrt((i - x) * (i - x) + (j - y) * (j - y))) / float(r)
        if (d >= 0.0) and land.get_height(i, j) < 30:
          land.set_material(i, j, grass2)
  

def add_random_item(level, land):
  import balazar.item
  item = random.choice(balazar.item.SMALL_TREASURE)(level)
  for i in range(100):
    if level.interesting_places and random.random() < 0.5:
      pos = level.interesting_places.pop()
      item.set_xyz(pos[0], 1000.0, pos[1])
    else: item.set_xyz(random.uniform(10.0, LAND_DIM - 20.0), 1000.0, random.uniform(10.0, LAND_DIM - 20.0))
    if put_on_ground(level, land, item, 1): break
  if item.can_rotate: item.turn_incline(90.0)
  
  

  
def random_level(X, Z):
  
  level = create_level(X, Z)
  import balazar.weather
  level.weather_agent = balazar.weather.WeatherAgent()
  level.add_mobile(level.weather_agent)
  
  land = create_land(level)
  random_land       (level, land)
  smooth_land       (level, land)
  
  random_land_border(level, land)
  end_land          (level, land)
  
  
  place(level, land, building.Market(level.static_part))
  for i in range( 2): place(level, land, building.Farm (level.static_part), max_x = 130.0, max_z = 130.0)
  for i in range( 2): place(level, land, building.Tower(level.static_part))
  #houses = [house for house in level.buildings if isinstance(house, building.Farm)]
  
  land.set_material_layer(soya.Material.get("grass2"), 0.0  * 50.0, 0.24 * 50.0)
  land.set_material_layer(soya.Material.get("grass" ), 0.24 * 50.0, 0.54 * 50.0)
  land.set_material_layer(soya.Material.get("snow"  ), 0.54 * 50.0, 0.6  * 50.0)
  land.set_material_layer(soya.Material.get("ice"   ), 0.6  * 50.0, 1.0  * 50.0)
  level.push_down_level = 0.54 * 50.0
  level.step_level      = 0.54 * 50.0
  
  land.reinit() # XXX in end_land ???
  
  for i in range(6):
    d = DecorativeEntity(level.static_part, soya.Shape.get("dent"))
    place(level, land, d, 1)
    s = random.uniform(0.5, 2)
    d.scale(s, s, s)
  #for i in range(8): place(level, land, vegetation.Mushroom  ())
  for i in range(3): place(level, land, vegetation.Fasme     (random.randint(1, 4)))
  for i in range(2): place(level, land, vegetation.TreePompon())
  
  level.random_monsters = [Morkul, Morkul, MorkulChief]
  
  land.reinit() # XXX in end_land ???
  
  random.shuffle(level.interesting_places)
  #for i in range( 4): add_random_item(level, land)
  
  add_bridge(level)
  
  return level


  

  
def random_level_forest(X, Z, difficulty = 0):
  level = create_level(X, Z)
  if   difficulty <= 0:
    level.random_monsters = [Morkul]
    level.random_monster_proba = 0.03
    level.nb_max_monster = 2
    
  elif difficulty == 1:
    level.random_monsters = [Morkul, Morkul, MorkulChief]
    level.random_monster_proba = 0.08
    level.nb_max_monster = 3
    
  elif difficulty >= 2:
    level.random_monsters = [Morkul, MorkulChief, MorkulChief]
    level.random_monster_proba = 0.15
    level.nb_max_monster = 3
    
  land = create_land(level)
  random_land       (level, land,
                     default_min_y = 0.1, default_max_y = 0.3,
                     nb_max = 0.0003,
                     pic_proba = 0.1, plateau_proba = 0.5, colline_proba = 0.4, cratere1_proba = 0.2, cratere2_proba = 0.2, monticule_proba = 0.1, termitiere_proba = 0.1, 
                     )
  smooth_land       (level, land, nb = 4)
  random_land_border(level, land)
  end_land          (level, land)
  
  land.set_material_layer(soya.Material.get("grass4"), 0.0  * 50.0, 0.24 * 50.0)
  land.set_material_layer(soya.Material.get("feuillesmortes" ), 0.24 * 50.0, 1.0 * 50.0)
  level.push_down_level = 0.24 * 50.0
  level.step_level      = 1000.0
  
  land.reinit() # XXX in end_land ???
  
  return level


def end_random_level_forest(level, nb_tree = 13, nb_tree_treasure = 7):
  land = level.static_part.children[0]
  land.reinit() # XXX in end_land ???
  
  #for i in level.buildings:
  #  if isinstance(i, FallingVigie) or isinstance(i, building.Vigie) or isinstance(i, building.MovingVigie) or isinstance(i, building.RotatingVigie):
  #    i.radius += 17.0
  
  #for i in range( 8): place(level, land, vegetation.Mushroom  ())
  for i in range(nb_tree         ): place(level, land, vegetation.        TreePompon(), land_only = 1)
  for i in range(nb_tree_treasure): place(level, land, vegetation.TreasureTreePompon(), land_only = 1)
  
  land.texture_factor = 0.25
  
  land.reinit() # XXX in end_land ???
  
  random.shuffle(level.interesting_places)
  #for i in range( 4): add_random_item(level, land)
  
  return level


def random_level_hill(X, Z, difficulty = 0):
  level = create_level(X, Z)
  if   difficulty <= 0:
    level.random_monsters = [Morkul]
    level.random_monster_proba = 0.02
    level.nb_max_monster = 2
    
  elif difficulty == 1:
    level.random_monsters = [Morkul, MorkulChief]
    level.random_monster_proba = 0.04
    level.nb_max_monster = 3
    
  elif difficulty >= 2:
    level.random_monsters = [MorkulChief]
    level.random_monster_proba = 0.1
    level.nb_max_monster = 3
    
  land = create_land(level)
  random_land       (level, land,
                     default_min_y = 0.1, default_max_y = 0.2,
                     nb_max = 0.0010,
                     pic_proba        = 0.0,
                     plateau_proba    = 0.0,
                     colline_proba    = 0.1,
                     cratere1_proba   = 0.0,
                     cratere2_proba   = 0.0,
                     monticule_proba  = 0.0,
                     termitiere_proba = 0.0,
                     )
  smooth_land       (level, land, nb = 3)
  random_land_border(level, land)
  end_land          (level, land)
  
  land.set_material_layer(soya.Material.get("grass2"), 0.0  * 50.0, 0.24 * 50.0)
  land.set_material_layer(soya.Material.get("grass" ), 0.24 * 50.0, 0.54 * 50.0)
  land.set_material_layer(soya.Material.get("snow"  ), 0.54 * 50.0, 0.6  * 50.0)
  land.set_material_layer(soya.Material.get("ice"   ), 0.6  * 50.0, 1.0  * 50.0)
  level.push_down_level = 0.54 * 50.0
  level.step_level      = 0.54 * 50.0
  
  land.reinit() # XXX in end_land ???
  
  for i in range(6): place(level, land, vegetation.Mushroom  ())
  for i in range(2): place(level, land, vegetation.TreePompon(), land_only = 1)
  
  land.reinit() # XXX in end_land ???
  
  random.shuffle(level.interesting_places)
  #for i in range( 4): add_random_item(level, land)
  
  add_bridge(level)
  
  return level


def random_level_village(X, Z):
  level = create_level(X, Z)
  level.random_monsters = []
  level.random_monster_proba = 0.0
  level.nb_max_monster = 2
  
  land = create_land(level)
  random_land       (level, land,
                     default_min_y = 0.1, default_max_y = 0.2,
                     nb_max = 0.0010,
                     pic_proba        = 0.1,
                     plateau_proba    = 0.2,
                     colline_proba    = 0.5,
                     cratere1_proba   = 0.0,
                     cratere2_proba   = 0.0,
                     monticule_proba  = 0.1,
                     termitiere_proba = 0.0,
                     )
  smooth_land       (level, land, nb = 3)
  random_land_border(level, land)
  end_land          (level, land)
  
  return level

def end_random_level_village(level, nb_mushroom = 8, nb_farm = 12, nb_tower = 1, nb_echassian = 8):
  land = level.static_part.children[0]
  land.reinit() # XXX in end_land ???
  
  for i in range(nb_mushroom): place(level, land, vegetation.Mushroom(), land_only = 1)
  for i in range(nb_farm    ): place(level, land, building.Farm(level) , land_only = 1)
  for i in range(nb_tower   ): place(level, land, building.Tower(level), land_only = 1)
  
  #v = soya.Volume(level, soya.Shape.get("pont_monte1"))
  #place(level, level,
  #      v,
  #      land_only = 1,
  #      )
  
  for i in range(nb_echassian):
    import balazar.echassian
    character = balazar.echassian.Echassian()
    place(level, level.static_part.children[0], character)
    
  land.reinit() # XXX in end_land ???
  
  random.shuffle(level.interesting_places)
  #for i in range( 4): add_random_item(level, land)
  
  land.set_material_layer(soya.Material.get("grass2"), 0.0  * 50.0, 0.24 * 50.0)
  land.set_material_layer(soya.Material.get("grass" ), 0.24 * 50.0, 0.54 * 50.0)
  land.set_material_layer(soya.Material.get("snow"  ), 0.54 * 50.0, 0.6  * 50.0)
  land.set_material_layer(soya.Material.get("ice"   ), 0.6  * 50.0, 1.0  * 50.0)
  level.push_down_level = 0.54 * 50.0
  level.step_level      = 0.54 * 50.0
  
  land.reinit() # XXX in end_land ???
  
  return level


def random_level_montagne(X, Z):
  level = create_level(X, Z)
  level.random_monsters = []
  level.random_monster_proba = 0.0
  level.nb_max_monster = 2
  
  land = create_land(level)
  random_land       (level, land,
                     default_min_y = 0.4, default_max_y = 0.5,
                     nb_max = 0.0012,
                     pic_proba        = 0.2,
                     plateau_proba    = 0.2,
                     colline_proba    = 0.2,
                     cratere1_proba   = 0.3,
                     cratere2_proba   = 0.3,
                     monticule_proba  = 0.2,
                     termitiere_proba = 0.0,
                     )
  smooth_land       (level, land, nb = 3)
  random_land_border(level, land)
  end_land          (level, land, height = 100.0)
  
  return level

def end_random_level_montagne(level, nb_tower = 4):
  land = level.static_part.children[0]
  land.reinit() # XXX in end_land ???
  
  #for i in range(nb_mushroom): place(level, land, vegetation.Mushroom(), land_only = 1)
  #for i in range(nb_farm    ): place(level, land, building.Farm(level) , land_only = 1)
  for i in range(nb_tower   ): place(level, land, building.Tower(level), land_only = 1)
  
  land.reinit() # XXX in end_land ???
  
  random.shuffle(level.interesting_places)
  #for i in range( 4): add_random_item(level, land)
  
  land.set_material_layer(soya.Material.get("grass4"), 0.0  * 100.0, 0.2 * 100.0)
  land.set_material_layer(soya.Material.get("snow"  ), 0.2  * 100.0, 0.6 * 100.0)
  land.set_material_layer(soya.Material.get("ice"   ), 0.6  * 100.0, 1.0 * 100.0)
  level.push_down_level = 0.2 * 50.0
  level.step_level      = 0.2 * 50.0
  
  land.reinit() # XXX in end_land ???
  
  return level



class Universe(object):
  def __init__(self):
    self.map = {
#      ( 0,  0) : (random_level_hill  , 0),
      
#      ( 1, -1) : (random_level_forest, 0),
#      ( 1,  0) : (random_level_forest, 1),
#      ( 1,  1) : (random_level_forest, 0),
      
#      ( 2, -1) : (random_level_forest, 1),
#      ( 2,  0) : (random_level_forest, 1),
#      ( 2,  1) : (random_level_forest, 1),
      
#      ( 3, -1) : (random_level_forest, 2),
#      ( 3,  0) : (random_level_forest, 2),
#      ( 3,  1) : (random_level_forest, 2),
      }
    
  def create_level(self, X, Z):
    r = getattr(self, "create_level_%s_%s" % (str(X).replace("-", "_"), str(Z).replace("-", "_")), None)
    if r: level = r(X, Z)
    else:
      r = self.map.get((X, Z), (random_level,))
      level = r[0](X, Z, *r[1:])
    end_level(level)
    return level
  
  def create_level__1000_0(self, X, Z):
    level = create_level(X, Z)
    level.random_monsters = []
    level.random_monster_proba = 0.0
    level.nb_max_monster = 2

    land = create_land(level)
    random_land       (level, land,
                       default_min_y = 0.1, default_max_y = 0.2,
                       nb_max = 0.0010,
                       pic_proba        = 0.1,
                       plateau_proba    = 0.2,
                       colline_proba    = 0.5,
                       cratere1_proba   = 0.0,
                       cratere2_proba   = 0.0,
                       monticule_proba  = 0.1,
                       termitiere_proba = 0.0,
                       )
    smooth_land       (level, land, nb = 3)
    random_land_border(level, land)
    end_land          (level, land)

    
    land = level.static_part.children[0]
    land.reinit() # XXX in end_land ???
    random.shuffle(level.interesting_places)
    
    land.set_material_layer(soya.Material.get("grass2"), 0.0  * 50.0, 0.24 * 50.0)
    land.set_material_layer(soya.Material.get("grass" ), 0.24 * 50.0, 0.54 * 50.0)
    land.set_material_layer(soya.Material.get("snow"  ), 0.54 * 50.0, 0.6  * 50.0)
    land.set_material_layer(soya.Material.get("ice"   ), 0.6  * 50.0, 1.0  * 50.0)
    level.push_down_level = 0.54 * 50.0
    level.step_level      = 0.54 * 50.0

    land.reinit() # XXX in end_land ???
    add_bridge(level, 0, 0, 0, 0)
    
    return level
  
  def create_level_0_0(self, X, Z):
    import balazar.wiseman, balazar.echassian
    
    level = random_level_village(X, Z)
    add_bridge(level, 0, 1, 0, 1)
    
    indicator = IndicatorPlatForm(level)
    place(level, level.static_part.children[0], indicator, land_only = 1, min_x = level.bridge_1_0.x - 5.0, max_x = level.bridge_1_0.x + 5.0, min_z = level.bridge_1_0.x - 5.0, max_z = level.bridge_1_0.x + 5.0)
    
    character = balazar.echassian.EchassianCartograph()
    place(level, level.static_part.children[0], character)
    character.move(soya.Point(indicator, 0.0, 8.0, -0.4))
    
    wiseman = balazar.wiseman.Wiseman()
    place(level, level.static_part.children[0], wiseman, arround = Point(level.bridge_2_1, 20.0, 0.0, 0.0))
    

    g1 = Grid(size = "2_4")
    g2 = Grid(size = "2_4")
    place_in_line(level, [g1, 0.0, 0.0], [g2, 8.0, 0.0])
    g2.y = g1.y - 2.5
    v = g1 >> g2; v.y = 0.0
    g1.look_at(v); g1.rotate_lateral(180.0)
    g2.look_at(v)
    
    for gn in (g1, g2):
      for i in range(1, 6):
        if i == 5:
          if gn is g1: g = MiamGrid(size = "4_8")
          else:        g = Grid(size = "4_8", typ = 2)
        else:      g = Grid(size = "2_4")
        level.add_mobile(g)
        g.move(gn)
        g.y += 5.0 * i
        g.look_at(v)
        if gn is g1: g.rotate_lateral(180.0)
        
        if i == 5:
          plateform = soya.Volume(level, soya.Shape.get("plateforme_2_4"))
          plateform.move(soya.Point(g, 0.0, 0.0, -1.0))
          plateform.look_at(soya.Vector(g, 0.0, 0.0, -1.0))
          if gn is g1:
            dalle = GridOpener(2, 1, 0, 5)
            level.add_mobile(dalle)
            dalle.move(soya.Point(plateform, 0.0, 1.0, -10.0)); dalle.look_at(v)
          else:
            chest = Chest()
            level.add_mobile(chest)
            chest.move(soya.Point(plateform, 0.0, 0.82, -10.0))#; chest.look_at(-v)
            
    level = end_random_level_village(level)
    
    house = random.choice(level.get_all_by_class(building.Farm))
    house.add_grid(typ = 3)
    
    chest = Chest(typ = 2)
    level.add_mobile(chest)
    chest.move(house)
    
    return level
  
  def create_level_0__1(self, X, Z):
    level = random_level_village(X, Z)
    add_bridge(level, 0, 1, 1, 0)
    
    mill = Mill(level)
    mill.turn_lateral(45.0)
    place(level, level.static_part.children[0], mill, land_only = 1, rotate = 0, min_x = level.bridge_1_0.x, max_x = level.bridge_1_0.x)
    
    p = soya.Volume(level, soya.Shape.get("pont1_court_volant"))
    p.rotate_lateral(90.0)
    p.set_xyz(mill.x, mill.y + 25.0 * 1.5, mill.z - 20.0 - 18.0)
    
    p = RotatingBridgeTile()
    level.add(p)
    p.rotate_lateral(90.0)
    p.set_xyz(mill.x, mill.y + 25.0 * 1.5, mill.z - 20.0 - 18.0 - 18.0 - 10.0)
    
    p = soya.Volume(level, soya.Shape.get("pont1_court_volant"))
    p.rotate_lateral(90.0)
    p.set_xyz(mill.x, mill.y + 25.0 * 1.5, mill.z - 20.0 - 18.0 - 18.0 - 10.0 - 10.0 - 18.0)
    
    p = RotatingBridgeTile()
    level.add(p)
    p.rotate_lateral(90.0)
    p.set_xyz(mill.x, mill.y + 25.0 * 1.5, mill.z - 20.0 - 18.0 - 18.0 - 10.0 - 10.0 - 18.0 - 18.0 - 10.0)
    
    level.bridge_1_0.set_xyz(mill.x, mill.y + 25.0 * 1.5 - 16.0 + 0.85, mill.z - 20.0 - 18.0 - 18.0 - 10.0 - 10.0 - 18.0 - 18.0 - 10.0 - 12.0 - 10.0)
    
    temple = building.Temple(level)
    temple.add_grid(MiamGrid)
    place(level, level.static_part.children[0], temple, land_only = 1)
    
    #dalle = ManualGridOpener()
    #mill.add(dalle)
    #dalle.move(soya.Point(mill, 0.0, 9 * 1.5, 0.0))
    #dalle.add_grid(mill, 1, None)
    
    import balazar.echassian
    character = balazar.echassian.EchassianMiller()
    place(level, level.static_part.children[0], character, rotate = 0)
    character.move(soya.Point(mill, 0.0, 9 * 1.5, 0.0))
    
    for i in range(2):
      p = building.Vigie(level)
      place(level, level.static_part.children[0], p)
      put_uppon_vigie(level, p, Chest(1))
      
    level = end_random_level_village(level)

    print level.children
    print level.static_part.children
    
    house = random.choice(level.get_all_by_class(building.Farm))
    dalle = GridOpener()
    level.add_mobile(dalle)
    dalle.move(house)
    
    return level
  
  def create_level_0__2(self, X, Z):
    level = random_level_montagne(X, Z)
    add_bridge(level, 0, 0, 1, 0)
    
    return end_random_level_montagne(level)
  
  
  def create_level_1__1(self, X, Z):
    level = random_level_forest(X, Z, 1)
    add_bridge(level, 0, 0, 1, 1)
    
    place_on_bridge(level.bridge_2_1, [(Grid(1, 360), 4.0), (GridOpener(1, 1, 0), 4.0)])
    
    place(level, level.static_part.children[0], GridOpener(1, 1, None))
    
    place(level, level.static_part.children[0], LifeAltar())
    
    return end_random_level_forest(level)
  
  def create_level_1_0(self, X, Z):
    level = random_level_forest(X, Z, 0)
    add_bridge(level, 1, 1, 1, 1)
    place_on_bridge(level.bridge_1_0, [(MiamGrid(), 0.0)])
    place_on_bridge(level.bridge_2_1, [(MiamGrid(), 0.0)])
    
    place(level, level.static_part.children[0], LifeAltar())
    
    return end_random_level_forest(level)
  
  def create_level_1_1(self, X, Z):
    level = random_level_forest(X, Z, 0)
    add_bridge(level, 0, 1, 0, 1)
    place_on_bridge(level.bridge_2_1, [(NoMonsterGrid(), 4.0), (GridOpener(3, 1, None), 4.0)])
    
    v1 = building.MovingVigie(level)
    v2 = building.Vigie(level)
    v3 = building.Vigie(level)
    v4 = building.Vigie(level)
    v5 = building.MovingVigie(level)
    v6 = FallingVigie()
    v7 = building.Vigie(level)
    
    place_in_line(level, [v1, 0.0, 0.0], [v2, 12.0, 0.0], [v3, 12.0, 0.0], [v4, 12.0, 0.0])
    
    put_uppon_vigie(level, v4, v5)
    put_uppon_vigie(level, v3, v6)
    put_uppon_vigie(level, v2, v7)
    put_uppon_vigie(level, v7, Chest(1, [balazar.item.Map]))
    
    #for v in [v1, v2, v3, v4, v5, v6, v7]: v.radius += 18.0
    
    return end_random_level_forest(level, 10, 6)
  
  
  def create_level_2__1(self, X, Z):
    level = random_level_forest(X, Z, 2)
    add_bridge(level, 1, 0, 0, 1)
    place_on_bridge(level.bridge_2_1, [(NoMonsterGrid(), 4.0), (GridOpener(3, 1, None), 4.0)])
    return end_random_level_forest(level)
  
  def create_level_2_0(self, X, Z):
    level = random_level_forest(X, Z, 0)
    add_bridge(level, 1, 0, 1, 0)
    
    # XXX porte, clef
    
    v1 = building.MovingVigie(level)
    v2 = building.Vigie      (level)
    v3 = building.MovingVigie(level)
    v4 = building.Vigie      (level)
    place_in_line(level, [v1, 0.0, 0.0], [v2, 12.0, 0.0])
    put_uppon_vigie(level, v1, v3)
    put_uppon_vigie(level, v2, v4)
    put_uppon_vigie(level, v4, Chest(1))
    
    return end_random_level_forest(level, 12, 6)
  
  def create_level_2_1(self, X, Z):
    level = random_level_forest(X, Z, 1)
    add_bridge(level, 1, 1, 1, 1)
    place_on_bridge(level.bridge_1_2, [(Grid(2), 0.0)]) # XXX grille fermée pour l'instant
    
    place_on_bridge(level.bridge_2_1, [(Grid(1, 360), 4.0), (GridOpener(1, 1, 0), 4.0)])
    
    place(level, level.static_part.children[0], GridOpener(1, 1, None))
    
    for i in range(6): place(level, level.static_part.children[0], vegetation.Mushroom())
    return end_random_level_forest(level, 10, 6)
  
  
  def create_level_3__1(self, X, Z):
    level = random_level_forest(X, Z, 2)
    add_bridge(level, 1, 0, 1, 1)
    
    place_on_bridge(level.bridge_2_1, [(Grid(1, 200), 3.0), (Grid(2, 200), 2.0), (GridOpener(1, 1, 0), 3.0), (GridOpener(2, 1, 0), 2.0)])
    
    place(level, level.static_part.children[0], GridOpener(1, 1, None))
    
    put_uppon_vigie(level, place_in_line(level, [building.MovingVigie(level), 0.0, 0.0], [vegetation.TreePompon(), 20.0, 0.0], [building.Vigie(), 20.0, 0.0]), GridOpener(2, 1, None))
    return end_random_level_forest(level)
  
  def create_level_3_0(self, X, Z):
    level = random_level_forest(X, Z, 2)
    add_bridge(level, 0, 1, 1, 4)
    
    place_on_bridge(level.bridge_1_0, [(Grid(1, 80), 4.0), (GridOpener(1, 1, 0), 4.0)])
    
    place(level, level.static_part.children[0], GridOpener(1, 1, None))
    
    #place_on_bridge(level.bridge_2_1, [(GridOpener(2, 1, 0), 4.0), (Grid(2, 80), 4.0)])
    
    place(level, level.static_part.children[0], LifeAltar())
    
    return end_random_level_forest(level)
  
  def create_level_3_1(self, X, Z):
    level = random_level_forest(X, Z, 1)
    add_bridge(level, 1, 1, 0, 4)
    
    place_on_bridge(level.bridge_1_0, [(MiamGrid(), 4.0), (MiamGrid(), 4.0), (MiamGrid(), 4.0)])
    
    #place(level, level.static_part.children[0], Chest())

    #vigie = building.Vigie()
    #place_in_line(level, [building.MovingVigie(level), 0.0, 0.0], [FallingVigie(), 10.0, 0.0], [vigie, 10.0, 0.0])
    #put_uppon_vigie(level, vigie, Chest())
    
    
    return end_random_level_forest(level, 10, 4)
  
  
  def create_level_4__1(self, X, Z):
    level = random_level_forest(X, Z, 2)
    add_bridge(level, 1, 0, 0, 0)
    
    level.random_monster_proba = 0.0 # No monster, because they would take the sword !
    
    c = Chest(1, 0)
    c.monsters = [balazar.morkul.Morkul]    
    place_on_altar(level, c)
    
    c = Chest(2, 0)
    c.monsters = [balazar.morkul.MorkulChiefWithScepter]
    place_on_altar(level, c)
    light_cone = soya.Volume(level, soya.Shape.get("cone_lumiere"))
    light_cone.set_xyz(c.x, c.y - 5.0, c.z)
    light_cone.solid = 0
    return end_random_level_forest(level)
    
  def create_level_4_0(self, X, Z):
    level = random_level_forest(X, Z, 1)
    add_bridge(level, 1, 0, 1, 0)
    
    level.bridge_0_1.y += 15.0
    
    put_uppon_vigie(level, place_in_line(level, [building.MovingVigie(level), 0.0, 0.0], [building.RotatingVigie(level), 12.0, 0.0], [building.RotatingVigie(level), 16.0, 0.0], [building.Vigie(level), 16.0, 0.0]), Chest())
    
    return end_random_level_forest(level, 10, 6)
  
  def create_level_4_1(self, X, Z):
    level = random_level_forest(X, Z, 1)
    add_bridge(level, 1, 1, 0, 0)
    
    level.bridge_0_1.x -= 8.0
    
    m1 = MiamGrid()
    m2 = MiamGrid()
    place_on_bridge(level.bridge_1_0, [(m1, 4.0), (m2, 0.0)])
    m1.turn_vertical( -30.0)
    m2.turn_vertical(30.0)
    
    place(level, level.static_part.children[0], LifeAltar())
    
    c = Chest(1)
    place(level, level.static_part.children[0], c)
    
    for i in range(4): place(level, level.static_part.children[0], vegetation.Mushroom(), uppon = c)
    return end_random_level_forest(level, 10, 6)
  
    
    
UNIVERSE = Universe()

    
