# # piraduel.icn - a simple rogue-like pirate duel game # link graphics global player, world # open a window, create the world, create the player, draw procedure main() &window := open("Pirate Duel!","g", "rows=24","columns=80") world := ship("ship.dat") player := character() world.add_npc("P") world.draw() repeat { player.turn() if (!world.npcs).turn() then break world.draw() } world.draw() while Event() ~== "q" stop() end # a virtual world is a grid with npcs/monsters, treasure etc. class World(grid, npcs) method add_npc(s) put(npcs, npc(s)) end method draw() every r := 1 to 24 do { GotoRC(r,1) WWrites(left(grid[r], 80, " ")) } grid[1] := "" end initially npcs := [] end # a ship is a type of virtual world whose shape is read from a file. # there is nothing actually ship-like about this semantics yet. class ship : World(shipfile) initially self.World.initially() grid := list(24) every !grid := repl(" ", 80) f := open(shipfile) | stop("can't open ", image(shipfile)) i := 1 while line := read(f) do { grid[i][1:*line+1] := line i +:= 1 } close(f) end # the player character is modeled with a location, name, etc. class character(x, y, name, hitpoints, armorclass, damage) method hittest(enemy) die := ?20 if die > 10 + (9 - enemy.armorclass) then { enemy.hitpoints -:= ?damage return } end method attack(enemy) if hittest(enemy) then { world.grid[1] ||:= "You have hit the " || enemy.name || ". " enemy.awake := 1 if enemy.hitpoints < 1 then { world.grid[1] ||:= "You have defeated the " || enemy.name enemy.die() } } else { world.grid[1] ||:= "You missed the " || enemy.name || ". " if / (enemy.awake) := (?1=1) then world.grid[1] ||:= "The enemy awakes. " } end method move(newx,newy, letter : "@") world.grid[y,x] := "." x := newx; y := newy world.grid[y,x] := letter end method trymove(newx, newy) if world.grid[newy, newx] == "." then move(newx, newy) else if (m := !world.npcs) & m.y = newy & m.x = newx then attack(m) end method turn() e := Event() case e of { "q": { stop("goodbye") } "h": { trymove(x-1, y) } "l": { trymove(x+1, y) } "k": { trymove( x , y-1) } "j": { trymove( x , y+1) } "y": { trymove(x-1, y-1) } "u": { trymove(x+1, y-1) } "b": { trymove(x-1, y+1) } "n": { trymove(x+1, y+1) } } end # keep trying random placement until you get a legal spot method place(c) repeat { x := ?80 y := ?24 if world.grid[y,x] == "." then { world.grid[y,x] := c break } } end initially place("@") hitpoints := 12 damage := 6 armorclass := 6 end class npc : character(awake, letter) method die() world.grid[y,x] := "." every 1 to *world.npcs do { put(world.npcs, self ~=== pop(world.npcs)) } end method distance(enemy, x, y) return abs(y-enemy.y)^2 + abs(x-enemy.x)^2 end method attack(enemy) if hittest(enemy) then { world.grid[1] ||:= "The " || name || " has hit you. " if enemy.hitpoints < 1 then { world.grid[1] ||:= "You were defeated by the " || name return } } else world.grid[1] ||:= "The " || name || " missed you. " end method turn() local dist, minx, miny if /awake then fail every row := y-1 to y+1 do every col := x-1 to x+1 do { if world.grid[row,col] == "@" then { return attack(player) } } dist := distance(player, x, y) every row := y-1 to y+1 do every col := x-1 to x+1 do { if world.grid[row,col] == "." & dist >:= distance(player,col,row) then { minx := col miny := row } } if \minx then move(minx, miny, letter) end initially(c) static nametable /nametable := table("O","Orc","P","Pirate") letter := \c | ?"PO" name := nametable[letter] if awake := (?3 = 1) then world.grid[1] ||:= "The " || name || " is awake. " hitpoints := ?8 armorclass := 6 damage := 8 place(letter) end