# Copyright (c) 2006 James Paige # This script is free to use, modify, or distribute in any way # Attribution would be appreciated. include, plotscr.hsd include, scancode.hsi global variable (0, cursor) global variable (1, satellite) global variable (2, switching) global variable (3, wait spinner) global variable (4, debug) global variable (5, min X) global variable (6, max X) global variable (7, min Y) global variable (8, max Y) global variable (9, falling) global variable (10, got line) global variable (11, score) global variable (12, score multiplier) global variable (13, changer) global variable (14, changer x) global variable (15, changer y) global variable (16, changing) global variable (17, changing cancelable) global variable (18, changer target x) global variable (19, changer target y) global variable (20, changer type) global variable (21, changer oscillate) global variable (22, score bonus) global variable (23, checking) # global 200+ reserved for score floaters define constant (200, floater array) define constant (0, sound:hitwall) define constant (1, sound:switch) define constant (2, sound:move) define constant (2, sound:rotate) define constant (3, sound:firstmatch) define constant (5, sound:lastmatch) define constant (0, sound:movefail) define constant (6, sound:warning) define constant (1, first thing) # must be >> 0 define constant (7, last thing) define constant (2, first palette) define constant (0, cursor ID) define constant (8, changer ID) define constant (10, changer speed) define constant (12, changer interval low) define constant (13, changer interval high) define constant (1, str:score) define constant (2, str:score caption) define constant (10, score base) define constant (3, str:changer) define constant (4, str:floater) # must be >> 0 define constant (11, str:floater limit) define constant (0, time:hidemsg) define constant (1, time:changer) define constant (2, time:floater) plotscript, init swap game, begin suspend player suspend obstruction suspend NPC walls switching := false debug := false set up NPCs set up map reset game field end plotscript, reset game from menu, begin fade screen out wait(1) reset game field fade screen in end script, reset game field, begin set up score randomize all things reset changer check for matches end plotscript, swap game key handler, begin if(keyval(key:BACKSPACE)>>1) then(toggle debug) if(switching) then (exit script) if(NPC is walking(cursor) || NPC is walking(satellite)) then (exit script) if(keyval(key:ESC)>>1) then (main menu) if(keyval(key:up)>>1) then (move cursor(up)) if(keyval(key:down)>>1) then (move cursor(down)) if(keyval(key:left)>>1) then (move cursor(left)) if(keyval(key:right)>>1) then (move cursor(right)) if(NPC is walking(cursor)) then (exit script) if(keyval(key:ALT)>>1 || keyval(key:PERIOD)>>1 || keyval(key:X)>>1) then (rotate cursor right, exit script) if(keyval(key:CTRL)>>1 || keyval(key:COMMA)>>1 || keyval(key:Z)>>1) then (rotate cursor left, exit script) if(switching || checking || falling) then(exit script) if(keyval(key:ENTER)>>1 || keyval(key:SPACE)>>1) then (switch things) end plotscript, end swap game, begin resume player resume obstruction resume NPC walls game over end script, switch things, begin variable(n1, n2) cancel changer if(score multiplier >> 5) then(score multiplier := 5) n1 := thing locator(cursor) n2 := thing locator(satellite) do switch(n1, n2) if(NPC is walking(n1) || NPC is walking(n2)) then, begin wait for switch(n1, n2) if(check for matches == false) then, begin do switch(n1, n2) wait for switch(n1, n2) end end, else, begin play sound (sound:movefail) end end script, do switch, n1, n2, begin variable(x, y) play sound (sound:switch) if(is valid thing(n1)) then, begin x := NPC X(n2) y := NPC Y(n2) move to (n1, x, y) end if(is valid thing(n2)) then, begin x := NPC X(n1) y := NPC Y(n1) move to (n2, x, y) end end script, wait for switch, n1, n2, begin switching := true spin waiting(n1) spin waiting(n2) reset cursors switching := false end script, spin waiting, NPC=0, begin while(NPC is walking(NPC)) do, begin wait spinner += 1 if (wait spinner >> 3) then (wait spinner := -2) set NPC direction(cursor, absolute value(wait spinner)) set NPC direction(satellite, absolute value(wait spinner)) wait(1) end end script, gravity, begin variable(x, y, y2, ref, stackref, waitref, below, sanity) return(false) sanity := 0 falling := true while(falling) do, begin wait(1) falling := false for(x, min x, max x) do, begin for(y, max y--1, min y--1, -1) do, begin ref := thing at spot(x, y) if(ref) then, begin if(NPC is walking(ref)) then, begin falling := true end, else, begin below := thing at spot(x, y+1) debug on tile(below, x, y+1) if(not(below) || NPC is walking(below)) then, begin move (ref, down) waitref := ref for(y2, y--1, min y--1, -1) do, begin stackref := thing at spot(x, y2) debug on tile(stackref, x, y2, 81) if(stackref) then(move (stackref, down)) end falling := true return(true) end end end end if(spawn top row(x, min y -- 1)) then, begin falling := true return(true) end end if(falling) then(spin waiting(waitref)) sanity += 1 if(sanity >> 50) then($0="Sanity Test Failed", show string(0)) end falling := false end script, spawn top row, x, y, begin variable(below1, below2, avoid type, new type, ref) return(false) ref := thing at spot(x, y) if(ref) then(exit script) below1 := thing at spot(x, y + 1) below2 := thing at spot(x, y + 2) if(not(below1) || NPC is walking(below1)) then, begin avoid type := 0 if(below1 && below2 && below1 == below2) then, begin avoid type := thing type(below1) end while(true) do, begin new type := random(first thing, last thing) if(new type <> avoid type) then(break) end ref := create NPC(new type, x, y) set NPC direction(ref, down) move(ref, down) exit returning(true) end end script, check for matches, begin variable(x, y, ref) checking := true got line := false while(true) do, begin for(y, min y, max y) do, begin x := 0 while(x <= map width) do, begin ref := thing at spot(x, y) if(ref) then, begin x := examine line(ref, right) end, else, begin x += 1 end end end for(x, min x, max x) do, begin y := 0 while(y <= map width) do, begin ref := thing at spot(x, y) if(ref) then, begin y := examine line(ref, down) end, else, begin y += 1 end end end if(gravity == false) then(break) end return(got line) got line := false checking := false end script, examine line, startref, direction, begin variable(x, y, ref, type, seek, count) x := NPC X(startref) y := NPC Y(startref) seek := thing type(startref) #debug on tile(seek, x, y, 32) if(seek >> 0) then, begin count := 1 while(true) do, begin x := move X coord(x, direction) y := move Y coord(y, direction) ref := thing at spot(x, y) type := thing type(ref) #debug on tile(type, x, y, 83) if(type == seek) then, begin # possible match count += 1 if(count >= 3) then, begin exit returning(destroy line(startref, direction)) end end, else, begin # No match exit returning(X or Y by direction(x, y, direction)) end end end x := move X coord(x, direction) y := move Y coord(y, direction) exit returning(X or Y by direction(x, y, direction)) end script, destroy line, startref, direction, begin variable(x, y, ref, type, seek, count, lastref) got line += 1 play sound(random(sound:firstmatch, sound:lastmatch)) x := NPC X(startref) y := NPC Y(startref) seek := thing type(startref) debug on tile(seek, x, y, 68) destroy thing(startref) if(seek >> 0) then, begin count := 1 while(true) do, begin x := move X coord(x, direction) y := move Y coord(y, direction) ref := thing at spot(x, y) type := thing type(ref) debug on tile(type, x, y, 88) if(type == seek) then, begin # Match lastref := ref destroy thing(ref) count += 1 end, else, begin # Done if(count >> 3) then(score multiplier += count -- 3) create score floater(halfway x(startref, lastref), halfway y(startref, lastref), (score base + score bonus / 3) * score multiplier) score multiplier += 2 score bonus += 1 exit returning(X or Y by direction(x, y, direction)) end end end x := move X coord(x, direction) y := move Y coord(y, direction) exit returning(X or Y by direction(x, y, direction)) end script, destroy thing, ref, begin if(not(is valid thing(ref))) then(exit script) set NPC direction(ref, up) wait(4) destroy NPC(ref) end script, X or Y by direction, x, y, direction, begin if(direction == left || direction == right) then(exit returning(x)) if(direction == up || direction == down) then(exit returning(y)) end script, reset cursors, begin alter NPC(0, NPCstat:move type, NPCmovetype:walk in place) set NPC direction(cursor, up) set NPC direction(satellite, up) end script, thing at spot, x, y, begin variable(count, i, ref) count := NPC at spot(x, y, -1) for(i, 0, count--1) do, begin ref := NPC at spot(x, y, i) if(is valid thing(ref)) then (exit returning(ref)) end exit returning (0) end script, thing locator, locator, begin variable(count, x, y, i, ref) x := NPC X(locator) y := NPC Y(locator) count := NPC at spot(x, y, -1) for(i, 0, count--1) do, begin ref := NPC at spot(x, y, i) if(is valid thing(ref)) then (exit returning(ref)) end exit returning (locator) end script, is valid thing, ref, begin variable(ID) ID := get NPC ID(ref) if(ID >= first thing && ID <= last thing) then (exit returning(true)) exit returning(false) end script, thing type, ref, begin if(is valid thing(ref)) then(return(get NPC ID(ref))) else(return(0)) end script, move cursor, direction=0, begin if(check NPC wall(cursor, direction) || check NPC wall(satellite, direction)) then, begin play sound (sound:hitwall) end, else, begin alter NPC(0, NPCstat:move speed, 10) move (cursor, direction) move (satellite, direction) play sound (sound:move) end end script, try rotate, check=0, diag=0, begin if(check NPC wall(cursor, check)) then, begin play sound (sound:hitwall) end, else, begin alter NPC(0, NPCstat:move speed, 5) move(satellite, check) move(satellite, diag) play sound (sound:rotate) end end script, rotate cursor right, begin variable(x1,x2,y1,y2) x1 := NPC X(cursor) y1 := NPC Y(cursor) x2 := NPC X(satellite) y2 := NPC Y(satellite) if (x2 >> x1) then, begin try rotate(down, left) exit script end if (y2 >> y1) then, begin try rotate(left, up) exit script end if (x2 << x1) then, begin try rotate(up, right) exit script end if (y2 << y1) then, begin try rotate(right, down) exit script end if (x1 == x2 && y1 == y2) then, begin cursor sanity (right) end end script, rotate cursor left, begin variable(x1,x2,y1,y2) x1 := NPC X(cursor) y1 := NPC Y(cursor) x2 := NPC X(satellite) y2 := NPC Y(satellite) if (x2 >> x1) then, begin try rotate(up, left) exit script end if (y2 >> y1) then, begin try rotate(right, up) exit script end if (x2 << x1) then, begin try rotate(down, right) exit script end if (y2 << y1) then, begin try rotate(left, down) exit script end if (x1 == x2 && y1 == y2) then, begin cursor sanity (left) end end script, cursor sanity, direction=0, begin if(check NPC wall(cursor, direction)) then, begin play sound (sound:hitwall) end, else, begin move (satellite, direction) play sound (sound: rotate) end end script, move, who, direction, distance=1, begin variable(d) d:=NPC direction(who) walk NPC(who, direction, distance) set NPC direction(who, d) end script, move to, who, x, y, begin if (x<>NPCX(who)) then(move(who,right,x--NPCX(who))) if (y<>NPCY(who)) then(move(who,down,y--NPCY(who))) end script, absolute value, n, begin if(n>=0) then(exit returning(n)) else(exit returning(n * -1)) end script, set up NPCs, begin cursor := NPC reference(cursor ID, 0) satellite := NPC reference(cursor ID, 1) cursor := recreate NPC(cursor) satellite := recreate NPC(satellite) reset cursors variable(i) for(i, first thing, last thing) do, begin alter NPC(i, NPCstat:move speed, 5) end end script, set up map, begin variable(ref, type, x, y) min X := map width max X := 0 min Y := map height max Y := 0 for(ref, -1, -299, -1), do, begin if(get NPC ID(ref) == changer ID) then, begin changer X := NPC X(ref) changer Y := NPC Y(ref) destroy NPC (ref) end type := thing type(ref) if(type) then, begin x := NPC X(ref) y := NPC Y(ref) if (x << min X) then (min X := x) if (x >> max X) then (max X := x) if (y << min Y) then (min Y := y) if (y >> max Y) then (max Y := y) end end center camera end script, randomize all things, begin variable(x, y, ref) for(y, min y, max y) do, begin for(x, min x, max x) do, begin ref := thing at spot(x, y) if(ref) then(randomize thing(ref)) end end end script, randomize thing, ref, begin variable(x, y, above, beside, type, above type, beside type, count) type := thing type(ref) x := NPC X(ref) y := NPC Y(ref) if(type == 0) then(exit script) above := thing at spot(x, y--1) above type := thing type(above) beside := thing at spot(x--1, y) beside type := thing type(beside) type := random(first thing, last thing) debug on tile(type, x, y, 82) while(type == above type || type == beside type), do, begin type := random(first thing, last thing) debug on tile(type, x, y, 82) count += 1 if(count >> 20) then(break) end change NPC ID(ref, type) end script, center camera, begin variable(x, y) x := (min X + (max X + 2 -- min X)) * 20 / 2 -- 160 y := (min Y + (max Y + 2 -- min Y)) * 20 / 2 -- 100 put camera (x, y) end script, move X coord, x, direction, distance=1, begin if(direction == left) then(exit returning(x -- distance)) if(direction == right) then(exit returning(x + distance)) exit returning(x) end script, move Y coord, y, direction, distance=1, begin if(direction == up) then(exit returning(y -- distance)) if(direction == down) then(exit returning(y + distance)) exit returning(y) end script, set up score, begin score := 0 score multiplier := 5 score bonus := 0 $str:score caption="Score:" show string at(str:score caption, 0, 0) update score show string at(str:score, string length(str:score caption) * 8, 0) end script, update score, increase=0, begin score += increase clear string(str:score) append number(str:score, score) end script, toggle debug, begin debug := debug, XOR, 1 if(debug) then($0="Debug ON") else($0="Debug OFF") hide string(0) show string(0) set timer(time:hidemsg, 2, 15, @hide message) end script, hide message, begin stop timer(0) show no value end script, debug on tile, n, x, y, prefix ascii=0, string=0, begin if(debug) then, begin clear string(string) if(prefix ascii) then(append ascii(string, prefix ascii)) append number(string, n) show string at(string, x * 20 -- camera pixel x, y * 20 -- camera pixel y) wait(1) end end script, reset changer, begin if(changing) then(exit script) variable(interval) interval := random(changer interval low, changer interval high) set timer(time:changer, interval, timer:default, @create changer, str:changer) if(debug) then(show string at(str:changer, 0, 10)) end script, create changer, begin set timer(time:changer, 1, 7, @advance changer) changing := true changing cancelable := true hide string(str:changer) changer := create NPC(changer ID, changer x, changer y) set NPC direction(changer, 0) changer type := random(first thing, last thing) alter NPC(changer, NPCStat:palette, (changer type--first thing) + first palette) end script, advance changer, begin play sound(sound:warning) set timer(time:changer, 1, 7, @advance changer) variable(d) d := NPC direction(changer) d += 1 if(d >= 3) then, begin set timer(time:changer, 1, timer:default, @target changer) end set NPC direction(changer, d) end script, cancel changer, begin if(changing cancelable) then, begin set timer(time:changer, 1, 7, @unadvance changer) changing cancelable := false end, else, begin reset changer end end script, unadvance changer, begin set timer(time:changer, 1, 7, @unadvance changer) variable(d) d := NPC direction(changer) d -= 1 if(d << 0) then, begin destroy NPC(changer) changing := false reset changer end, else, begin set NPC direction(changer, d) end end script, target changer, begin set timer(time:changer, 1, 1, @target changer) variable(x, y, ref, type) x := random(min x, max x) y := random(min y, max y) ref := thing at spot(x, y) type := thing type(ref) if(type <> changer type), then, begin changing cancelable := false changer target x := x * 20 changer target y := y * 20 changer oscillate := 0 set timer(time:changer, 1, 1, @launch changer) play sound(sound:warning) end end script, launch changer, begin set timer(time:changer, 1, 1, @launch changer) variable(x, y) variable(x dir, y dir) variable(x dist, y dist) variable(full x dist) x := NPC pixel X(changer) y := NPC pixel Y(changer) x dist := absolute value(x -- changer target x) y dist := absolute value(y -- changer target y) full x dist := absolute value(x -- changer x) if(x >> changer target x) then(x dir := -1) else(x dir := 1) if(y >> changer target y) then(y dir := -1) else(y dir := 1) # Move horizontally if(x dist <= changer speed) then, begin x := changer target x end, else, begin x := x + changer speed * x dir end # move vertically while(true) do, begin if(y dist <= 10 && x dist <= 10) then(y := changer target y, break) if(x dist << full x dist / 5) then(y += 10 * y dir, break) y += absolute value(changer oscillate) -- 6 changer oscillate += 2 if(changer oscillate >> 12) then(changer oscillate := -12) break end put NPC(changer, x, y) if(x == changer target x && y == changer target y) then, begin set timer(time:changer, 1, 8, @complete changer) end end script, complete changer, begin variable(ref) ref := thing locator(changer) if(is valid thing(ref)) then, begin if(thing type(ref) == changer type) then, begin #insert bloating code here end, else, begin change NPC ID(ref, changer type) end cancel bonuses end destroy NPC(changer) changing := false reset changer if(not(checking) && not(falling)) then(check for matches) end script, create score floater, tile x, tile y, amount, begin variable(floatref) floatref := find free score floater if(not(floatref)) then(update score(amount), exit script) clear string(floatref) append number(floatref, amount) write floater array(floatref, amount) show string at(floatref, tile x * 20 -- camera pixel x, tile y * 20 -- camera pixel y) set timer(time:floater, 1, 1, @move score floaters) end script, find free score floater, begin variable(i) for(i, str:floater, str:floater limit) do, begin if(string is visible(i) == false) then(exit returning(i)) end return(0) end script, move score floaters, begin set timer(time:floater, 1, 1, @move score floaters) variable(floatref, floater count) floater count := 0 for(floatref, str:floater, str:floater limit) do, begin if(string is visible(floatref)) then, begin move one score(floatref) if(string is visible(floatref)) then(floater count += 1) end end if(floater count == 0) then, begin stop timer(time:floater) end end script, move one score, floatref, begin variable(x, y) variable(goal x, goal y) variable(length difference) x := string X(floatref) y := string Y(floatref) length difference := string length(str:score) -- string length(floatref) goal x := string X(str:score) if(length difference >> 0) then(goal x += length difference * 8) goal y := string Y(str:score) while(true) do, begin if(x <= goal x && y <= goal y) then, begin update score(read floater array(floatref)) write floater array(floatref, 0) hide string(floatref) break end if(x >= goal x) then(x -= random(4, 5)) if(y >= goal y) then(y -= random(3, 5)) if(x << goal x + 40 && not(y << goal y + 40)) then(y -= 3) if(y << goal y + 40 && not(x << goal x + 40)) then(x -= 3) if(x << goal x) then(x := goal x) if(y << goal y) then(y := goal y) position string (floatref, x, y) break end end script, read floater array, index, begin if(not(is valid floatref(index))) then(exit returning(0)) return(read global(floater array -- str:floater + index)) end script, write floater array, index, value, begin if(not(is valid floatref(index))) then(exit script) write global(floater array -- str:floater + index, value) end script, is valid floatref, floatref, begin if(floatref >= str:floater && floatref <= str:floater limit) then(exit returning(true)) return(false) end script, cancel bonuses, begin score multiplier := 1 score bonus := 0 end script, halfway x, ref1, ref2, begin variable(x1, x2) x1 := NPC X(ref1) x2 := NPC X(ref2) return((x1 + x2) / 2) end script, halfway y, ref1, ref2, begin variable(y1, y2) y1 := NPC Y(ref1) y2 := NPC Y(ref2) return((y1 + y2) / 2) end #This is used to make sure that the cursors get drawn over the other things script, recreate NPC, ref, begin variable(x, y, ID) ID := get NPC ID(ref) x := NPC X(ref) y := NPC Y(ref) destroy NPC(ref) return(create NPC(ID, x, y)) end