

GOLD_RATIO <- 0.618033988749895


function smoothstep(a, b, x)
{
  local ba=b-a
  local t=(x-a)/ba
  if (t<=0) return 0;
  if (t>=1) return 1;
  return t=(3-2*t)*t*t;
}


function random_hue_harmony(r, range0, range1, range2, ofs1, ofs2)
{
  r*=(range0+range1+range2)
  if (r<range0)
    return r*2-range0
  else if (r<range0+range1)
    return (r-range0)*2-range1+ofs1
  else
    return (r-range0-range1)*2-range2+ofs2
}


ColorRamp.multi <- function(values)
{
  local num=values.len()

  return ColorRamp(function(t)
  {
    local f=t*num
    local i=floor(f).tointeger()
    if (i<0) return values[i];
    if (i>=num-1) return values[num-1];
    f-=i
    return lerpcolor(values[i], values[i+1], f)
  });
}


function add_water_by_height(percent, ofs)
{
  local hmap=HmapMask.fromHeight()
  local minmax=hmap.calcMinMax()
  local level=(minmax[1]-minmax[0])*percent/100+minmax[0]+ofs
  apply_water_mask(hmap, @(w, h) h<level ? level-h : 0)
}


function add_grass_by_slope(color=0xff557722, opacity=0.8, threshold=0.95, fuzzyness=0.1, mask=null)
{
  show_splash("adding grass", -1)
  local minSlope=threshold-fuzzyness*0.5
  local smap=HmapMask.fromSlope()
  local wmap=HmapMask.fromWater()
  if (!mask) mask=BitMap(1)
  repaint_land(function(x, z, bc, sc)
  {
    local s=smap.getraw(x, z)-minSlope
    if (s<0 || wmap.getraw(x, z)>0 || !mask.get(x, z))
      return [bc, GT_SOIL, sc, ST_NONE]
    else
    {
      if (s>=fuzzyness) s=1 else s/=fuzzyness
      return [bc, GT_SOIL, lerpcolor(sc, color, s*opacity), (s>=0.5?ST_GRASS:ST_NONE)]
    }
  })
}


function add_common_grass()
{
  // add_grass_by_slope(hsv2rgba(rndf(0, 110), 0.61+srnd()*0.1, rndf(0.4, 0.5), 1),
  //   rndf(0.7, 0.85), rndf(0.85, 0.99), 0.2)
  add_grass_by_slope(get_pal_color(cur_world_pal, "grass"),
    rndf(0.7, 0.85), rndf(0.85, 0.99), 0.2)
}


function recolor_land3(cr, cg, cb)
{
  repaint_land(function(x, z, bc, sc)
  {
    local sum=bc[0]+bc[1]+bc[2]
    return [mix3colors(cr, bc[0]/sum, cg, bc[1]/sum, cb, bc[2]/sum), GT_SOIL]
  })
}


function random_reaction(speed)
{
  local left =[0 0 0 0]
  local right=[0 0 0 0]
  local a=irnd()&3, b, c
  do b=irnd()&3 while (b==a);
  c=irnd()&3
  left [a]=irnd()%6+1
  left [b]=irnd()%6+1
  right[c]=irnd()%6+1
  return [left, right, speed]
}


function random_chem_layer_color()
{
  return 0xff<<((irnd()&3)*8)
}


function chem_layers_by_height(numExtra=null, map=null)
{
  if (numExtra==null) numExtra=irnd()%5

  local colors=[0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff]
  for (local n=numExtra; n; --n) colors.insert(irnd()%(colors.len()+1), random_chem_layer_color())
  local ramp=ColorRamp.multi(colors)
  recolor_by_height(ramp, map)
}


function common_erosion(n, p={})
{
  if (!("reactions" in p))
  {
    // local reactions=[]
    // for (local n=irnd()%5; n; --n) reactions.push(random_reaction(rndf(0.1, 5)))
    // p.reactions<-reactions
  }

  if (!("depositBlend" in p)) p.depositBlend<-5

  // cur_mod.beforeErosion()
  gen_droplet_erosion(n, p)
  // cur_mod.afterErosion()
}


function recolor_chem_layers_old(range0=5, range1=5, range2=45, ofs1=90, ofs2=180, baseHue=null)
{
  if (baseHue==null) baseHue=frnd()*360

  // local range0=20, range1=20, range2=20, ofs1=120, ofs2=240
  // local range0=10, range1=10, range2=10, ofs1=120, ofs2=240
  // local range0=45, range1=45, range2=0, ofs1=180, ofs2=0

  local hofs=frnd(), sofs=frnd(), vofs=frnd()
  local colors=array(4)
  for (local i=0; i<4; ++i)
  {
    local v=(vofs+GOLD_RATIO*i)%1*0.8+0.1
    local s=(sofs+GOLD_RATIO*i)%1*0.5+0.5

    colors[i]=hsv2rgba(
      baseHue+random_hue_harmony((hofs+GOLD_RATIO*i)%1, range0, range1, range2, ofs1, ofs2),
      s*(1-v), v, 0)
  }

  recolor_land4(colors[0], colors[1], colors[2], colors[3])
}


function recolor_chem_layers()
{
  recolor_land4(
    get_pal_color(cur_world_pal, "rock1"),
    get_pal_color(cur_world_pal, "rock2"),
    get_pal_color(cur_world_pal, "rock3"),
    get_pal_color(cur_world_pal, "rock4"))
}


function choose_scheme(rv)
{
  local num=schemes.len()
  local s=array(num)
  local w=array(num)

  local sum=0.0
  local i=0
  foreach (n,f in schemes)
  {
    s[i]=n
    if (n in scheme_weights) w[i]=scheme_weights[n]
    else w[i]=1
    sum+=w[i]
    i++
  }

  local a=0.0
  rv*=sum
  for (i=0; i<num; ++i)
  {
    if (w[i]==0) continue;
    a+=w[i]
    if (rv<=a) return s[i]
  }

  return s[s.len()-1];
}


//ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ//


function place_object(o, mark=MARK_OBJ, test=MARK_PLACEMENT|MARK_WATER)
{
  local p=o.getPos()
  local rad=o.getRadius()
  if (test_circle_mark(getx(p), getz(p), rad, test, 0))
  {
    mark_circle(getx(p), getz(p), rad, mark, 0)
    return true;
  }
  else
  {
    o.destroy()
    return false;
  }
}


//ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ//


cur_world_pal<-null


function get_world_pal(name)
{
  if (name in world_palettes) return world_palettes[name];
  return null;
}


function get_pal_color(pal, name)
{
  if (pal==null || !(name in pal)) return 0xffff00ff;
  return pal[name];
}


function set_pal_color(name, color)
{
  if (cur_world_pal==null || ("name" in cur_world_pal)) cur_world_pal={}
  cur_world_pal[name]<-color
}


//ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ//


function get_global_name(o)
{
  foreach (n,v in getroottable()) if (v==o) return n;
  return "<unknown>";
}


// modifiers<-{}
// cur_mod<-null


// class WorldModifier
// {
//   name="WorldModifier"
//   chance=0

//   function _newmember(index, value, attrs, isstatic)
//   {
//     this[index]<-value
//     if (index=="name") modifiers[value]<-this
//   }

//   function reset() {}

//   function beforeErosion() {}
//   function  afterErosion() {}
//   function beforeObjects() {}
// }


// class MultiModifier extends WorldModifier
// {
//   subs=null

//   constructor() { subs=[] }

//   function reset        () { foreach (s in subs) s.reset        () }
//   function beforeErosion() { foreach (s in subs) s.beforeErosion() }
//   function  afterErosion() { foreach (s in subs) s. afterErosion() }
//   function beforeObjects() { foreach (s in subs) s.beforeObjects() }
// }
