# Cursed Potion Of Resist Ice include, cpori.hsi include, cpori_constants.hsi include, cpori_util.hsi include, cpori_mapedit.hsi include, cpori_fakearray.hsi include, cpori_units.hsi include, cpori_pathfinding.hsi include, cpori_animate.hsi include, cpori_screens.hsi include, scriptnamestring.hsi #----------------------------------------------------------------------- global variable(100, player) global variable(101, fake array holder) global variable(102, highlight) global variable(103, route) global variable(104, astar open) global variable(105, astar closed) global variable(106, move range) global variable(107, moving unit) global variable(108, input handler) global variable(109, input handler needs init) global variable(110, move dest tile) global variable(111, effects) global variable(112, ticks) global variable(113, which party) global variable(114, foes) global variable(115, long click) global variable(116, long click unit) global variable(117, pie) global variable(118, click tile) global variable(119, info unit) global variable(120, camera drag tick) global variable(121, camera drag x) global variable(122, camera drag y) global variable(123, world x) global variable(124, world y) global variable(125, world map) global variable(126, hexmap holder) global variable(127, map layer) global variable(128, load mode) global variable(10000, tile slot cache) # 10000 to 10288 reserved for tile slot cache fake array #----------------------------------------------------------------------- plotscript, new game script, begin init game show title screen create world create blank map regenerate player init arrays main loop end plotscript, load game script, begin init game set parent(hexmap holder, map layer) switch(load mode) do( case(load:newgame) regenerate player set input handler(@turn picker) reload current map case(load:editor) set input handler(@level editor mode) remove player ) load mode := load:default main loop end #----------------------------------------------------------------------- script, init game, begin play song(0) suspend player init mouse unhide mouse cursor long click := 0 long click unit := 0 click tile := 0 camera drag tick := 0 camera drag x := 0 camera drag y := 0 map layer := lookup slice(sl:walkabout layer) end script, init arrays, begin highlight := create array(sli:highlight array) route := create array(sli:route array) astar open := create array(sli:astar open) astar closed := create array(sli:astar closed) move range := create array(sli:move range array) effects := create array(sli:effects array) foes := create array(sli:foes array) end script, regenerate player, begin if(player) then( free slice(player) ) player := create player(8, 8) end script, remove player, begin if(player) then( free slice(player) ) player := 0 end #----------------------------------------------------------------------- script, main loop, begin while(true) do( update effects for each unit on map(@do animate) ui animate action mode animate highlight wait(1) ticks += 1 ) end script, action mode, begin #show string(script name string(0, input handler)) variable(new handler) new handler := run script by id(input handler, input handler needs init) input handler needs init := false if(new handler) then( set input handler(new handler) ) end script, set input handler, new handler, begin input handler := new handler input handler needs init := true end script, level editor mode, begin allow camera drag editor mode end script, turn picker, init, begin which party := loopvar(which party, 0, 1) switch(which party) do( case(party:hero) handle player armor heating set input handler(@plan action) autosave game exit returning(@plan action) case(party:foes) exit returning(@foes turn setup) ) end script, handle player armor heating, begin if(get unit armored(player)) then( if(get unit overheated(player)) then( armor explosion(player) set unit armored(player, false) set unit overheated(player, false) update player armor sprite )else( set unit overheated(player, true) ) ) end script, allow camera drag, begin if(camera drag tick + 1 < ticks) then( #show string(string sprintf(0, $1="camera drag expired by %d ticks", ticks -- camera drag tick)) camera drag tick := 0 ) if(mouse button(right button)) then( if(camera drag tick > 0) then( variable(dx, dy, px, py) dx := camera drag x -- mouse pixel x dy := camera drag y -- mouse pixel y px := camera pixel x + dx py := camera pixel y + dy put camera(px, py) ) ) camera drag x := mouse pixel x camera drag y := mouse pixel y camera drag tick := ticks end script, plan action, init, begin if(init) then( long click := 0 long click unit := 0 click tile := 0 show move range(player) ) allow camera drag if(keyval(key:A) > 1) then(toggle player armor) variable(tile) tile := get tile at mouse variable(unit) if(tile) then(unit := tile unit(tile)) if(not(mouse button(left button))) then( # mouse released long click := 0 long click unit := 0 if(tile) then( if(tile == click tile) then( # tile click action if(tile in move range(tile)) then( find route(unit tile(player), tile) highlight array with arrows(route) exit returning(@route confirmation) )else if(unit) then( if(not(get unit inert(unit))) then( info unit := unit exit returning(@showing unit info) ) ) )else( click tile := 0 ) ) ) if(update long click(unit)) then( # long click action info unit := unit exit returning(@showing unit info) ) if(mouse button(left button) && not(click tile) && long click < second / 2) then( if(tile) then( click tile := tile if(unit) then( long click := 1 long click unit := unit ) ) ) end script, update long click, unit, begin if(long click <= 0) then(exit returning(false)) if(not(unit) || unit <> long click unit) then( long click := 0 long click unit := 0 exit returning(false) ) if(long click >= 15) then( long click := 0 long click unit := 0 exit returning(true) ) long click := small(long click + 1, 15) exit returning(false) end script, showing unit info, init, begin if(init) then( show move range(info unit, @create stripes highlight) ) allow camera drag if(mouse click(left button)) then( variable(tile) tile := get tile at mouse variable(unit) if(tile) then(unit := tile unit(tile)) if(unit && unit <> player && not(get unit inert(unit))) then( info unit := unit show move range(info unit, @create stripes highlight) )else( exit returning(@plan action) ) ) end script, route confirmation, begin allow camera drag if(mouse click(left button)) then( variable(tile) tile := get tile at mouse if(tile) then( if(tile is route end(tile)) then( on player movement confirmation moving unit := player exit returning(@focus moving unit) )else( exit returning(@plan action) ) ) ) end script, on player movement confirmation, begin clear effects of animation type(@danger shimmer animate) end script, focus moving unit, begin variable(px, py, dx, dy, step) px := camera pixel x + slice screen x(moving unit) py := camera pixel y + slice screen y(moving unit) -- slice height(moving unit) / 2 dx := abs(camera pixel x + slice width(sprite layer) / 2 -- px) dy := abs(camera pixel y + slice height(sprite layer) / 2 -- py) if(dx <> 0 || dy <> 0) then( step := large(2, large(dx, dy) / 10) pixel focus camera(px, py, step) wait for camera ) camera follows slice(moving unit) exit returning(@animate move action) end script, animate move action, begin if(slice is moving(moving unit)) then( exit script ) while(true) do( attack other units(moving unit) if(array length(route) == 0) then( if(get unit die after move(moving unit)) then( create dice apart(moving unit) free unit(moving unit) moving unit := 0 ) ) if(check for player death) then(exit returning(@player death mode)) if(array length(route) == 0) then( # Movement completed variable(next mode) next mode := @foe turn if(moving unit == player) then( next mode := @turn picker ) moving unit := 0 exit returning(next mode) ) variable(tile) tile := fetch out(route, 0) if(unit tile(moving unit) == tile) then( # unit is already on the target tile )else( break ) ) move dest tile := tile start move step exit returning(@await move step) end script, start move step, begin variable(tile) tile := unit tile(moving unit) variable(i, flip) i := hex around index(tile, move dest tile) switch(i) do( case(0,4,5) flip := false case(1,2,3) flip := true ) if(get unit backwards(moving unit)) then( flip := not(flip) set unit backwards(moving unit, false) ) horiz flip sprite(moving unit, flip) set last step(moving unit, i) variable(ux, uy, dx, dy) ux := x for tile(tile) uy := y for tile(tile) dx := x for tile(move dest tile) dy := y for tile(move dest tile) if(dy > uy || (dy == uy && dx > ux)) then( set parent preserve pos(moving unit, move dest tile) ) move unit to(moving unit, move dest tile, get unit step speed(moving unit)) end script, await move step, begin if(not(slice is moving(moving unit))) then( set parent preserve pos(moving unit, move dest tile) move dest tile := 0 exit returning(@animate move action) ) end script, check for player death, begin exit returning(player == 0) end script, player death mode, init, begin if(init) then( delete autosave show string($0="Player killed") clear highlight exit script ) if(mouse click(left button)) then( reset game ) end #----------------------------------------------------------------------- script, foes turn setup, begin clear array(foes) for each tile(@add foe to foes array) for each in array set sort order(foes, @sort foes) sort children(foes, true) exit returning(@foe turn) end script, foe turn, begin if(array length(foes) == 0) then( exit returning(@turn picker) ) variable(unit) unit := fetch out(foes, 0) # Do foe ai clear array(route) moving unit := none run ai callback(unit) highlight array with arrows(route) if(moving unit) then( exit returning(@animate move action) ) end script, sort foes, unit, begin variable(n, tile) # Main sort key is hexmanhattan distance n := hexmanhattan unit distance(unit, player) * 10000 # Second sort key is y pos tile := unit tile(unit) n += y for tile(tile) * 100 # Last sort key is x pos n += x for tile(tile) exit returning(n) end script, add foe to foes array, tile, begin variable(unit) unit := tile unit(tile) if(unit) then( if(is foe(unit)) then( append(foes, unit) ) ) end #----------------------------------------------------------------------- script, attack other units, attacker, begin variable(tile) tile := unit tile(attacker) if(for each unit(tile, @do attack other unit, attacker)) then( # At least one target unit was armored step back(attacker) ) end script, do attack other unit, target, attacker, sprite=1, begin if(target == attacker) then(exit returning(false)) if(same team(target, attacker)) then(exit returning(false)) if(get unit armored(target)) then( exit returning(true) ) animate kill(target, attacker, sprite) free unit(target) exit returning(false) end script, armor explosion, unit, begin for each unit around unit(unit, @do attack other unit, unit, 3) create attack animation(2, unit, unit) end #----------------------------------------------------------------------- script, create world, begin world x := 8 world y := 8 world map := create container(0, 0) set slice lookup(world map, sli:world map) set slice visible(world map, false) set slice width(world map, worldsize) set slice height(world map, worldsize) end script, create blank map, begin hexmap holder := create container(0, 0) set slice lookup(hexmap holder, sli:hexmap holder) set parent(hexmap holder, map layer) fill parent(hexmap holder, true) variable(x, y) for(y, 0, gridsize -- 1) do( for(x, 0, gridsize -- 1) do( if(valid map pos(x, y)) then( create tile slot(x, y) set tile at(x, y, tile:snow) ) ) ) end script, reload current map, begin load map chunk(world x, world y) end #----------------------------------------------------------------------- script, tile pic for kind, tile kind, begin switch(tile kind) do( case(tile:snow) exit returning(1) case(tile:wall) exit returning(2) ) exit returning(0) end script, create tile slot, x, y, begin variable(sl, tile pic) sl := create container(32, 32) variable(map) map := hexmap holder set parent(sl, map) set slice x(sl, (x * tilesize) + (y * tilesize) / 2) set slice y(sl, (y * tilesize) * 3 / 4) set slice extra(sl, 0, x) set slice extra(sl, 1, y) write global(@tile slot cache + y * gridsize + x, sl) end script, valid map pos, x, y, begin exit returning(x + y >= gridsize / 2 && x + y < (gridsize * 3 / 2)) end script, for each tile, callback, arg2=0, begin variable(x, y, tile) for(y, 0, gridsize -- 1) do( for(x, 0, gridsize -- 1) do( if(valid map pos(x, y)) then( tile := get tile at(x, y) if(tile) then( run script by id(callback, tile, arg2) ) ) ) ) end script, for each unit on map, callback, arg2=0, begin variable(x, y, tile, unit) for(y, 0, gridsize -- 1) do( for(x, 0, gridsize -- 1) do( if(valid map pos(x, y)) then( tile := get tile at(x, y) if(tile) then( for each unit(tile, callback, arg2) ) ) ) ) end script, get tile slot at, x, y, begin variable(tslot) tslot := read global(@tile slot cache + y * gridsize + x) exit returning(tslot) end script, set tile at, x, y, tilekind, begin variable(tile) tile := get tile at(x, y) if(tile) then( change tile(tile, tilekind) )else( script error(string sprintf(0, $1="Can't set tile at invalid coords %d %d", x, y)) ) end script, change tile, tile, tilekind, begin replace hero sprite(tile, tilekind) end script, get tilekind, tile, begin exit returning(get sprite set number(tile)) end script, get tile at, x, y, begin variable(tslot) tslot := get tile slot at(x, y) if(not(tslot)) then(exit returning(0)) exit returning(get tile by slot(tslot)) end script, get tile at pixel, px, py, begin variable(x, y) x := tile x by pixel pos(px, py) y := tile y by pixel pos(px, py) exit returning(get tile at(x, y)) end script, get tile at mouse, begin variable(px, py) px := mouse pixel x + camera pixel x py := mouse pixel y + camera pixel y exit returning(get tile at pixel(px, py)) end script, get tile by slot, tslot, begin variable(tile) tile := lookup slice(sli:tile, tslot) if(not(tile)) then( # No tile exists, lazy-load one tile := create tile(tslot) ) exit returning(tile) end script, create tile, tslot, begin variable(tile) tile := load hero sprite(0) set parent(tile, tslot) realign slice(tile, edge:center, edge:bottom, edge:center, edge:bottom) set slice lookup(tile, sli:tile) exit returning(tile) end script, x for tile, tile, begin variable(slot) slot := parent slice(tile) if(not(slot)) then( script error(string sprintf(0, $1="Tile %d is not parented to a tile slot", tile)) ) exit returning(get slice extra(slot, 0)) end script, y for tile, tile, begin variable(slot) slot := parent slice(tile) if(not(slot)) then( script error(string sprintf(0, $1="Tile %d is not parented to a tile slot", tile)) ) exit returning(get slice extra(slot, 1)) end script, hex around x, i, begin switch(i) do( case(0) exit returning(0) case(1) exit returning(1) case(2) exit returning(1) case(3) exit returning(0) case(4) exit returning(-1) case(5) exit returning(-1) ) script error(string sprintf(0, $1="hex around: %d is not a valid index", i)) end script, hex around y, i, begin switch(i) do( case(0) exit returning(-1) case(1) exit returning(-1) case(2) exit returning(0) case(3) exit returning(1) case(4) exit returning(1) case(5) exit returning(0) ) script error(string sprintf(0, $1="hex around: %d is not a valid index", i)) end script, hex around index, tile, other, begin variable(i, x, y, ox, oy, ax, ay) x := x for tile(tile) y := y for tile(tile) ox := x for tile(other) oy := y for tile(other) for(i, 0, 5) do( ax := x + hex around x(i) ay := y + hex around y(i) if(ox == ax && oy == ay) then( exit returning(i) ) ) # Tiles are not adjacent exit returning(-1) end script, direction to tile, t1, t2, begin # ties favor low-indexed directions if(t1 == t2) then( script error(string sprintf(0, $1="direction to tile: same tile %d", t1)) exit returning(-1) ) variable(i, x, y, ax, ay, at, dist, best, best i) x := x for tile(t1) y := y for tile(t1) best i := -1 best := 999999 for(i, 0, 5) do( ax := x + hex around x(i) ay := y + hex around y(i) at := get tile at(ax, ay) dist := hexmanhattan distance(at, t2) if(dist < best) then( best := dist best i := i ) ) exit returning(best i) end script, tiles are aligned, t1, t2, begin variable(x1, y1, z1, x2, y2, z2) x1 := x for tile(t1) y1 := y for tile(t1) z1 := -1 * x1 -- y1 x2 := x for tile(t2) y2 := y for tile(t2) z2 := -1 * x2 -- y2 exit returning(x1 == x2 || y1 == y2 || z1 == z2) end script, tile ahead, tile, dir, distance, begin variable(step, x, y, ax, ay, dest, result) x := x for tile(tile) y := y for tile(tile) ax := hex around x(dir) ay := hex around y(dir) for(step, 1, distance) do( x += ax y += ay dest := get tile at(x, y) if(not(dest)) then(break) result := dest ) exit returning(result) end #----------------------------------------------------------------------- script, debug tile under mouse, begin variable(x, y) x := mouse tile x y := mouse tile y debug tile at(x, y) end script, debug tile at, x, y, begin show string(string sprintf(0, $1="X=%d Y=%d", x, y)) variable(tile) tile := get tile at(x, y) highlight tile(tile, @create rainbow highlight) end script, tile x by pixel pos, px, py, begin variable(x, y, odd) y := tile y by pixel pos(px, py) odd := (y ,mod, 2) == 1 if(odd) then( px -= tilesize / 2) x := px / tilesize -- (py -- tilesize / 8) / 2 / (tilesize * 3 / 4) exit returning(x) end script, tile y by pixel pos, px, py, begin exit returning((py -- tilesize / 8) / (tilesize * 3 / 4)) end script, mouse tile x, begin variable(px, py, x, y) px := mouse pixel x + camera pixel x py := mouse pixel y + camera pixel y x := tile x by pixel pos(px, py) exit returning(x) end script, mouse tile y, begin variable(px, py, x, y) px := mouse pixel x + camera pixel x py := mouse pixel y + camera pixel y y := tile y by pixel pos(px, py) exit returning(y) end #----------------------------------------------------------------------- script, animate highlight, begin for each in array(highlight, @do animate highlight) end script, do animate highlight, tile, begin variable(hl) hl := lookup slice(sli:highlight img, tile) if(not(hl)) then( script error(string sprintf(0, $1="No highlight image when trying to animate highlighted tile %d", tile)) exit script ) variable(callback) callback := get slice extra(hl, 2) if(callback) then( run script by id(callback, hl) ) end script, highlight array, a, highlight creator, begin clear highlight for each in array(a, @add highlight, highlight creator) end script, highlight tile, tile, highlight creator, begin if(array length(highlight) == 1 && in array(highlight, tile)) then( # Already highlighted, nothing more to do exit script ) clear highlight add highlight(tile, highlight creator) end script, add highlight, tile, highlight creator, begin if(in array(highlight, tile)) then( script error(string sprintf(0, $1="Tile %d is already in the highlight array", tile)) exit returning(0) ) append(highlight, tile) variable(hl) hl := lookup slice(sli:highlight img, tile) if(hl) then( script error(string sprintf(0, $1="Tile %d already has a highlight image overlay", tile)) )else( hl := run script by id(highlight creator) set parent(hl, tile) set slice lookup(hl, sli:highlight img) variable(unit) unit := lookup slice(sli:unit, tile) if(unit) then( move slice below(hl, unit) ) ) exit returning(hl) end script, create rainbow highlight, begin variable(hl) hl := load hero sprite(sprite:rainbow highlight) set slice extra(hl, 2, @rainbow highlight animate) exit returning(hl) end script, rainbow highlight animate, hl, begin variable(p) p := get sprite palette(hl) if(p == -1) then(p := pal:highlight first) p += 1 if(p > pal:highlight last) then(p := pal:highlight first) set sprite palette(hl, p) end script, create stripes highlight, begin variable(hl) hl := load hero sprite(sprite:stripes highlight) set slice extra(hl, 2, @stripes highlight animate) exit returning(hl) end script, stripes highlight animate, hl, begin variable(fr) fr := get sprite frame(hl) fr += 1 if(fr > 7) then(fr := 0) set sprite frame(hl, fr) end script, create dizzy highlight, begin variable(hl) hl := load hero sprite(sprite:dizzy highlight) set slice extra(hl, 2, @dizzy highlight animate) exit returning(hl) end script, dizzy highlight animate, hl, begin variable(pic, fr, n) pic := get sprite set number(hl) -- sprite:dizzy highlight fr := get sprite frame(hl) n := pic * 8 + fr n += 1 if(n > 15) then(n := 0) set sprite set number(hl, sprite:dizzy highlight + n / 8) set sprite frame(hl, (n ,mod, 8)) end script, create subtle highlight, begin variable(hl) hl := load hero sprite(sprite:subtle highlight) set slice extra(hl, 2, @subtle highlight animate) exit returning(hl) end script, subtle highlight animate, hl, begin if((ticks + hl) ,mod, 8 == 0) then( variable(fr) fr := get sprite frame(hl) fr += 1 if(fr > 7) then(fr := 0) set sprite frame(hl, fr) ) end script, clear highlight, begin for each in array(highlight, @do clear highlight) clear array(highlight) end script, do clear highlight, tile, begin variable(hl) hl := lookup slice(sli:highlight img, tile) if(hl) then( free slice(hl) ) end script, highlight array with arrows, a, begin clear highlight variable(e, tile, hl, pe, prev, ne, next) e := first child(a) while(e) do( tile := get slice extra(e, 0) pe := previous sibling(e) if(pe) then( prev := get slice extra(pe, 0) )else( prev := 0 ) ne := next sibling(e) if(ne) then( next := get slice extra(ne, 0) )else( next := 0 ) hl := add highlight(tile, @create arrow highlight) update arrow sprite(hl, tile, prev, next) e := next sibling(e) ) end script, create arrow highlight, begin variable(hl) hl := load hero sprite(sprite:arrowhead highlight) set sprite frame(hl, 7) # default to unconnected node exit returning(hl) end script, update arrow sprite, hl, tile, prev, next, begin variable(pic, fr) if(not(prev) && not(next)) then( # Lone node pic := sprite:arrowhead highlight fr := 7 )else if(not(prev)) then( # Arrow base pic := sprite:arrowbase highlight fr := hex around index(tile, next) if(fr == -1) then(pic := sprite:arrowhead highlight, fr := 6) )else if(not(next)) then( # Arrow head pic := sprite:arrowhead highlight fr := hex around index(tile, prev) if(fr == -1) then(pic := sprite:arrowhead highlight, fr := 6) )else( # Arrow shaft variable(pi, ni) pi := hex around index(tile, prev) ni := hex around index(tile, next) if(pi == ni || pi == -1 || ni == -1) then( # Error pic := sprite:arrowhead highlight, fr := 6 )else( variable(n1, n2, st, o2, num) n1 := small(pi, ni) n2 := large(pi, ni) switch(n1) do( case(0) st := 0, o2 := 1 case(1) st := 5, o2 := 2 case(2) st := 9, o2 := 3 case(3) st := 12, o2 := 4 case(4) st := 14, o2 := 5 else(script error(string sprintf(0, $1="Low arrowshaft index invalid %d", n1))) ) if(n2 < 1 || n2 > 5) then(script error(string sprintf(0, $1="High arrowshaft index invalid %d", n1))) num := st + (n2 -- o2) pic := sprite:arrowshaft highlight + (num / 8) fr := num ,mod, 8 ) ) set sprite set number(hl, pic) set sprite frame(hl, fr) end #----------------------------------------------------------------------- script, show move range, unit, highlight callback=0, begin if(not(highlight callback)) then(highlight callback := @create subtle highlight) run move preview callback(unit) highlight array(move range, highlight callback) end script, tile in move range, tile, begin exit returning(in array(move range, tile)) end script, tile is route end, tile, begin exit returning(tile == fetch last(route)) end #----------------------------------------------------------------------- script, autosave game, begin set parent(hexmap holder, sprite layer) save in slot(30) set parent(hexmap holder, map layer) end script, load autosave, begin load from slot(30) end script, autosave exists, begin exit returning(save slot used(30)) end script, delete autosave, begin delete save(30) end script, load leveledit, begin # Holding shift+X erases the saved levels and starts fresh if(keyval(key:shift) > 0 && keyval(key:x) > 0) then(exit script) load mode := load:editor export globals(29, @load mode, @load mode) load mode := load:default load from slot(29) end script, save leveledit, begin save map chunk(world x, world y) set parent(hexmap holder, sprite layer) save in slot(29) set parent(hexmap holder, map layer) end script, load newgame, begin load mode := load:newgame export globals(29, @load mode, @load mode) load mode := load:default load from slot(29) end #-----------------------------------------------------------------------