define constant(20, last map)
define constant(19, default map)

define constant(4, music count)
define constant(1, timer:turn and go)
define constant(2, timer:back and forth)
define constant(2, str:turn and go)

global variable(1, turn and go ref)
global variable(2, music i)
global variable(3, find x)
global variable(4, find y)

#-----------------------------------------------------------------------

plotscript, mulberry bush autorun, begin
  follow all in a loop(1)
end

plotscript, hungry caterpillar autorun, begin
  follow in a chain(6, 1)
end

plotscript, circle the wagons autorun, begin
  variable(ref)
  ref := npc reference(3)
  if(ref) then(
    clicky switch(none, ref)
  )
  follow all in a loop(1)
end

plotscript, back and forth autorun, begin
  trace(string sprintf(0, $1="back and forth autorun"))
  back and forth loop
end

#-----------------------------------------------------------------------

plotscript, on new game, begin
  # Screen defaults to 400x400 on desktop
  if(running on console || running on mobile) then(
    set screen resolution(400, 225)
  )
  set hero speed(me, 4)
  jump to map(none, default map)
end

plotscript, quit this game, begin
  game over
end

plotscript, on menu button, begin
  suspend player
  suspend npcs
  suspend timers
  variable(i, m, mi)
  m := create menu()
  set menu on close script(m, @on menu close)
  for(i, 0, last map) do(
    mi := add menu item(m)
    get map name(0, i)
    set menu item caption(mi, 0)
    set menu item type(mi, menutype:script)
    set menu item subtype(mi, @jump to map)
    set menu item extra(mi, 0, i)
    set menu item bit(mi, menu item bit:Close menu when selected, true)
    if(i == current map) then(select menu item(mi))
  )
  mi := add menu item(m)
  set menu item caption(mi, $0="Quit Game")
  set menu item type(mi, menutype:script)
  set menu item subtype(mi, @quit this game)
end

plotscript, on menu close, begin
  resume player
  maybe resume npcs
  resume timers
end

plotscript, jump to map, mi, mapnum, begin
  suspend player
  suspend npcs
  teleport to map(mapnum)
  set hero position(0, NPC x(0), NPC y(0))
  delete npc(0)
  stop timer(timer:turn and go)
  hide string(str:turn and go)
  resume npcs
  resume player
  next music
end

script, next music, begin
  play song(music i)
  music i := (music i + 1) ,mod, music count
end

plotscript, on key press handler, begin
  if(keyval(key:space) > 1 || keyval(key:enter) > 1 || keyval(key:ctrl) > 1) then(
    if(try to splat) then(exit script)
    call blue slime
  )
end

script, try to splat, begin
  variable(x, y, d, ref, count, i, result)
  result := false
  x := hero x(me)
  y := hero y(me)
  d := hero direction(me)
  x := ahead x(x, d)
  y := ahead y(y, d)
  count := npc at spot(x, y, get count)
  for (i, 0, count -- 1) do(
    ref := npc at spot(x, y, i)
    if(ref) then(splat slime(ref), result := true)
  )
  exit returning(result)
end

script, ahead x, x, d, dist=1, begin
  switch(d) do(
    case(left) exit returning(x -- dist)
    case(right) exit returning(x + dist)
  )
  exit returning(x)
end

script, ahead y, y, d, dist=1, begin
  switch(d) do(
    case(up) exit returning(y -- dist)
    case(down) exit returning(y + dist)
  )
  exit returning(y)
end

script, splat slime, ref, begin
  # Only unsplatted slimes can splat
  if(get npc id(ref) == 1) then(
    wait for NPC
    change npc id(ref, 2)
    set npc obstructs(ref, false)
    play sound(1)
  )
end

plotscript, clicky switch, arg, ref, begin
  variable(tile, pass)
  play sound(2)
  if(npc direction(ref) == down) then(
    # rock doors are open, we want to close them.
    tile := 42
    pass := north wall + east wall + south wall + west wall
    set npc direction(ref, up)
  )else(
    # rock doors are closed, we want to open them
    tile := 16
    pass := none
    set npc direction(ref, down)
  )
  variable(x, y)
  for (y, 0, map height -- 1) do(
    for (x, 0, map width -- 1) do(
      if(read zone(2, x, y)) then(
        write map block(x, y, tile)
        write pass block(x, y, pass)
      )
    )
  )
end

script, call blue slime, begin
  variable(x, y, d, ref, result)
  result := false
  x := hero x(me)
  y := hero y(me)
  d := hero direction(me)
  x := ahead x(x, d)
  y := ahead y(y, d)
  ref := npc reference(4)
  if(ref) then(
    pathfind npc to(ref, x, y)
    result := true
    play sound(3)
  )
  exit returning(result)
end

script, maybe resume npcs, begin
  variable(ref)
  ref := npc reference(5)
  if(ref) then(
    # Suspender switch found, toggle it twice
    suspender switch(none, ref)
    suspender switch(none, ref)
  )else(
    # No suspender switch, just resume npcs
    resume npcs
  )
end

plotscript, suspender switch, arg, ref, begin
  play sound(2)
  if(npc direction(ref) == down) then(
    # suspend npc is off, turn it on
    suspend npcs
    set npc direction(ref, up)
  )else(
    # suspend npc is on, turn it off
    resume npcs
    set npc direction(ref, down)
  )
end

script, follow all in a loop, id, begin
  variable(count, ref, dest, i)
  count := npc copy count(id)
  for(i, 0, count -- 1) do(
    ref := npc reference(id, i)
    if(i == count -- 1) then(
      dest := npc reference(id, 0)
    )else(
      dest := npc reference(id, i + 1)
    )
    npc chases npc(ref, dest)
  )
end

script, follow in a chain, leader ref, id, begin
  variable(count, ref, dest, i)
  count := npc copy count(id)
  for(i, 0, count -- 1) do(
    ref := npc reference(id, i)
    if(i == 0) then(
      dest := leader ref
    )else(
      dest := npc reference(id, i -- 1)
    )
    npc chases npc(ref, dest)
  )
end

plotscript, turn and go, arg, ref, begin
  variable(d)
  d := (hero direction(me) + 2) ,mod, 4
  set npc direction(ref, d)
  turn and go ref := ref
  set timer(timer:turn and go, 3, 30, @already turned so now go, str:turn and go)
  show string at(str:turn and go, npc pixel x(ref) -- camera pixel x, npc pixel y(ref) -- camera pixel y)
  play sound(4)
end

script, already turned so now go, begin
  variable(ref)
  ref := turn and go ref
  hide string(str:turn and go)
  walk npc(ref, npc direction(ref), 100000)
  play sound(5)
end

plotscript, cancel all npc walk, begin
  variable(ref)
  ref := next npc reference()
  while(ref) do(
    cancel npc walk(ref)
    ref := next npc reference(ref)
  )
  play sound(6)
end

script, back and forth loop, begin
  if(current map <> map:back and forth) then(exit script)
  variable(ref)
  ref := next npc reference()
  while(ref) do(
    if(get npc id(ref) == 1) then(
      if(one tile from hero(ref)) then(
        npc chases npc(ref, 4, true)
      )
    )
    ref := next npc reference(ref)
  )
  set timer(timer:back and forth, 0, 1, @back and forth loop)
end

script, one tile from hero, ref, begin
  exit returning(manhattan dist to hero(ref) <= 1)
end

script, manhattan dist to hero, ref, begin
  exit returning(abs(npc x(ref) -- hero x(me)) + abs(npc y(ref) -- hero y(me)))
end

script, one tile from npc, ref, other, begin
  exit returning(manhattan dist between npcs(ref, other) <= 1)
end

script, manhattan dist between npcs, ref, other, begin
  exit returning(abs(npc x(ref) -- npc x(other)) + abs(npc y(ref) -- npc y(other)))
end

script, one tile from spot, ref, x, y, begin
  exit returning(manhattan dist to spot(ref, x, y) <= 1)
end

script, manhattan dist to spot, ref, x, y, begin
  exit returning(abs(npc x(ref) -- x) + abs(npc y(ref) -- y))
end

plotscript, trigger summoner, arg, ref, begin
  suspend player
  camera follows npc(ref)
  if(find slime well) then(
    pathfind npc to(ref, find x, find y -- 1, 1)
    wait for npc(ref)
    if(one tile from spot(ref, find x, find y)) then(
      create npc(1, find x, find y, down)
    )
    pathfind npc to(ref, hero x(me), hero y(me), 1)
    wait for npc(ref)
  )
  camera follows hero
  resume player
end

script, find slime well, begin
  exit returning(find random zone tile(3))
end

script, find random zone tile, seek zone, begin
  variable(count, which, x, y, found)
  count := zone number of tiles(seek zone)
  if(count == 0) then(exit returning(false))
  which := random(1, count)
  found := 0
  for(y, 0, map height -- 1) do(
    for(x, 0, map width -- 1) do(
      if(read zone(seek zone, x, y)) then(
        found += 1
        if(found == which) then(
          find x := x
          find y := y
          exit returning(true)
        )
      )
    )
  )
  exit returning(false)
end

# Map 20: Change the default pathfinding rule
plotscript, toggle pathfinding rule, _, npc , begin
  set npc direction(npc, npc direction(npc), xor, 2)
  if (read gmap(378) == 2) then (  # Ignore NPCs
    write gmap(378, 1)  # NPCs obstruct
  ) else (
    write gmap(378, 2)  # Ignore NPCs
  )
end

# Map 20: Make all instances of an NPC type swap places with a partner
plotscript, npcs swap places, _, used npc, begin
  variable(id, npc1, npc2, copy)
  id := get npc id(used npc)

  for (copy, 0, npc copy count(id) -- 1, 2) do (
    npc1 := npc reference(id, copy)
    npc2 := npc reference(id, copy + 1)

    pathfind npc to(npc1, npc x(npc2), npc y(npc2))
    pathfind npc to(npc2, npc x(npc1), npc y(npc1))
  )
end

