
listeners <- []
destroyed_listeners <- []


monster_spawn_enabled<-true


function reset_listeners()
{
  listeners.clear()
  destroyed_listeners.clear()
}


function destroy_listeners()
{
  foreach (l in destroyed_listeners)
    foreach (i,v in listeners)
      if (v==l) { listeners.remove(i); break; }

  destroyed_listeners.clear()
}


class Listener
{
  </persistent=1/> isDestroyed=false

  constructor(params)
  {
    if (params) foreach (n,v in params)
      if (n in this) this[n]=v

    listeners.append(this)
  }

  function destroy()
  {
    if (isDestroyed) return;
    isDestroyed=true
    destroyed_listeners.append(this)
  }

  function _processEvent(event)
  {
    if (isDestroyed) return;
    processEvent(event)
  }

  function processEvent(event) {}
}


//ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ//


class SingleEventListener extends Listener
{
  </persistent=1/> event=null
  </persistent=1/> once=true

  constructor(e, params=null)
  {
    event=e
    base.constructor(params)
  }

  function processEvent(e)
  {
    if (e==event)
    {
      onEvent()
      if (once) destroy()
    }
  }

  function onEvent() {}
}


//ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ//


class TriggerSoundListener extends SingleEventListener
{
  sound="trigger_activated"

  function onEvent()
  {
    play_sound_event(sound)
  }
}


//ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ//


class SingleSpawnListener extends SingleEventListener
{
  </persistent=1/> posX=0
  </persistent=1/> posZ=0

  topo=null
  ox=0
  oz=0

  constructor(e, params=null)
  {
    base.constructor(e, params)

    if (topo!=null)
    {
      posX=topo.ox+ox
      posZ=topo.oz+oz
      topo=null
    }
  }

  function onEvent()
  {
    spawnObject(posX, posZ)
  }

  function spawnObject(x, z) {}
}


//ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ//


class SpawnListener extends SingleEventListener
{
  </persistent=1/> number=10
  </persistent=1/> mask=null

  topo=null
  maskName="mask"

  constructor(e, params=null)
  {
    base.constructor(e, params)

    if (topo!=null)
    {
      mask=topo[maskName].shiftedCopy(topo.ox, topo.oz)
      topo=null
    }
  }

  function onEvent()
  {
    if (mask!=null) mask.placePoints(number, spawnObject.bindenv(this))
  }

  function spawnObject(x, z) {}
}


//ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ//


class SpawnBirdsListener extends SpawnListener
{
  </persistent=1/> number=30

  function spawnObject(x, z)
  {
    if (!monster_spawn_enabled) return;
    BirdMonster(vec4f(x, get_land_height(x, z)-5, z, 1), make_dir_xz(frnd()))
  }
}


class SpawnCaterpillarsListener extends SpawnListener
{
  </persistent=1/> number=5

  function spawnObject(x, z)
  {
    if (!monster_spawn_enabled) return;
    LandMonster(vec4f(x, get_land_height(x, z)-5, z, 1), make_dir_xz(frnd()))
  }
}


class SpawnWyvernsListener extends SpawnListener
{
  </persistent=1/> number=3

  function spawnObject(x, z)
  {
    if (!monster_spawn_enabled) return;
    WyvernMonster(vec4f(x, get_land_height(x, z)-10, z, 1))
  }
}


function get_monster_spawn_listener()
{
  local r=frnd()
  if (r<0.33) return SpawnWyvernsListener;
  else if (r<0.66) return SpawnCaterpillarsListener;
  else return SpawnBirdsListener;
}


//ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ//


class SpawnManaListener extends SpawnListener
{
  </persistent=1/> amount=2
  </persistent=1/> randomness=0.5

  function spawnObject(x, z)
  {
    local a=1+srnd()*randomness
    if (a<0.01) return;

    ManaBall(
      vec4f(x, get_land_height(x, z)-10, z, 1),
      a*amount/number,
      vec4f(0, rndf(15, 30), 0, 0))
  }
}


//ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ//


class SpawnSpellListener extends SingleSpawnListener
{
  </persistent=1/> spellId=ID_FireballSpell

  constructor(e, s, params=null)
  {
    spellId=s
    base.constructor(e, params)
  }

  function spawnObject(x, z)
  {
    SpellObject(x, z, spellId)
  }
}


//ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ//


class SpawnPortalListener extends SingleSpawnListener
{
  function spawnObject(x, z)
  {
    WorldPortal(vec4f(x, get_land_height(x, z), z, 1))
  }
}


//ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ//


class StoneCupCircleTrigger extends Listener
{
  </persistent=1/> objects=null
  </persistent=1/> event=null
  </persistent=1/> once=true

  constructor(o, e, params=null)
  {
    objects=o
    event=e
    base.constructor(params)
  }

  function processEvent(e)
  {
    if (e=="update")
    {
      local ok=true
      foreach (o in objects)
        if (!o.isBurning()) { ok=false; break; }

      if (ok)
      {
        foreach (o in objects) o.quenchFire()
        process_event(event)
        if (once)
        {
          foreach (o in objects) o.setActive(false)
          destroy()
        }
      }
    }
  }
}


//ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ//


class TreeCircleTrigger extends Listener
{
  </persistent=1/> objects=null
  </persistent=1/> event=null
  </persistent=1/> once=true

  constructor(o, e, params=null)
  {
    objects=o
    event=e
    base.constructor(params)
  }

  function processEvent(e)
  {
    if (e=="update")
    {
      local ok=true
      foreach (o in objects)
        if (!o.hasFoliage()) { ok=false; break; }

      if (ok)
      {
        process_event(event)
        if (!once)
          foreach (o in objects) o.setFoliage(false)
        else
          destroy()
      }
    }
  }
}


//ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ//


class TriggerHub extends Listener
{
  </persistent=1/> completionEvent=null
  </persistent=1/> events=null
  </persistent=1/> noMonsters=true

  function processEvent(e)
  {
    if (e=="update")
    {
      if (!events || events.len()==0)
        if (!noMonsters || !are_any_monsters_alive())
        {
          process_event(completionEvent)
          destroy()
        }
    }
    else if (events)
    {
      for (local i=events.len()-1; i>=0; --i)
        if (e==events[i]) events.remove(i)
    }
  }
}
