RCRPG/Julia
To use the color text feature, run within the Julia REPL. <lang julia>using Crayons
struct Point
x::Int y::Int z::Int
end
const obstacles = (wall = '\u2592', permwall = '\u2593', water = '~')
const defaults = (baseluck = 0.5, levelrooms = 8, itemrarity = 0.2, maxroomdim = 20,
wallheight = 3, leveldim = 100, prizeroomlevel = 5, fastdig = true, map = 1, showlevel = true)
abstract type Item end
struct Room
origin::Point xsize::Int ysize::Int
end
struct Level
map::Int grid::Matrix{Char} zbase::Int floorlevel::Int height::Int rooms::Vector{Room} content::Dict{Point, Vector{Item}}
end
struct Location
p::Point room::Union{Room, Nothing} level::Level
end
mutable struct Agent <: Item
location::Location inventory::Vector{Item} wearing::Vector{Item} wielding::Vector{Item} Agent(loc) = new(loc, Item[Gold(0)], Item[], Item[])
end
mutable struct Gold <: Item
value::Int disp::Char Gold(v::Int) = new(v, 'g')
end
struct Sledge <: Item
weight::Int disp::Char Sledge() = new(10, 's')
end
struct Ladder <: Item
weight::Int disp::Char Ladder() = new(7, 'l')
end
struct Pit <: Item
disp::Char Pit() = new('_')
end
struct Skylight <: Item
disp::Char Skylight() = new('^')
end
const charcrayons = Dict{Char,Crayon}('p' => crayon"white", ' ' => crayon"black", 'g' => crayon"yellow",
's' => crayon"blue", 'l' => crayon"green", '_' => crayon"red", '^' => crayon"light_cyan", '~' => crayon"blue", '\u2592' => crayon"light_gray", '\u2593' => crayon"dark_gray")
onpoint(p, level) = haskey(level.content, p) ? level.content[p] : Item[] hastype(t::Type, arr) = begin for i in arr if typeof(i) == t return true end end; false end hastype(t, p, level) = hastype(t, onpoint(p, level)) x1y1x2y2(room) = [room.origin.x, room.origin.y, room.origin.x + room.xsize, room.origin.y + room.ysize] inroom(x, y, rm) = begin x1, y1, x2, y2 = x1y1x2y2(rm); x1 <= x <= x2 && y1 <= y <= y2 end inroom(p::Point, rm) = inroom(p.x, p.y, rm) isinventory(t::Type, player) = hastype(t, player.inventory) iswielding(t::Type, player) = hastype(t, player.wielding) iswearing(t::Type, player) = hastype(t, player.wearing) isequip(i::Ladder) = true isequip(i::Sledge) = true isequip(i::Item) = false
function overlaps(testroom::Room, baseroom::Room, level::Level)
x1, y1, x2, y2 = x1y1x2y2(testroom) xx1, yy1, xx2, yy2 = x1y1x2y2(baseroom) x2 < xx1 || xx2 < x1 || y2 < yy1 || yy2 < y1 ? false : true
end
overlaps(notyet::Room, level::Level) = (for r in level.rooms if overlaps(notyet, r, level) return true end end; false) randpoint(level) = begin xmax, ymax = size(level.grid); Point(rand(1:xmax), rand(1:ymax), level.zbase) end addatpoint(i, p, lev) = (if !haskey(lev.content, p) lev.content[p] = Vector{Item}() end; push!(lev.content[p], i))
function randorigin(level, xd=defaults.maxroomdim, yd=defaults.maxroomdim)
xmax, ymax = size(level.grid) Point(rand(1:xmax-xd), rand(1:ymax-yd), level.zbase)
end
function notyetaroom(level::Level, origin::Point, minx=6, maxx=50, miny=6, maxy=25)
levsize = size(level.grid) if origin.x < 3 || origin.y < 3 origin = Point(3, 3, origin.z) end xmax, ymax = min(maxx, levsize[1] - origin.x), min(maxy, levsize[2] - origin.y) xsize = rand(minx:xmax) ysize = rand(miny:ymax) Room(origin, xsize, ysize)
end
function emptypoints(room, level)
vpt = Vector{Point}() x1, y1, x2, y2 = x1y1x2y2(room) for x in x1+1:x2-1, y in y1+1:y2-1 if level.grid[x, y] == ' ' push!(vpt, Point(x, y, level.zbase)) end end vpt
end randemptypoint(room, level) = (pts = emptypoints(room, level); isempty(pts) ? room.origin .+ 2 : rand(pts))
function addtoroom(item, room, level)
p = randemptypoint(room, level) addatpoint(item, p, level) level.grid[p.x, p.y] = item.disp
end
function fillroom(room, level, uppossible=true, downpossible=true)
grid = level.grid x1, y1, x2, y2 = x1y1x2y2(room) grid[x1:x2, y1:y2] .= ' ' grid[x1:x2, y1] .= obstacles.wall grid[x1:x2, y2] .= obstacles.wall grid[x1, y1:y2] .= obstacles.wall grid[x2, y1:y2] .= obstacles.wall for _ in 1:Int(floor(defaults.baseluck * 8)) if rand() < defaults.baseluck val = Int(floor(100.0 * rand() * defaults.baseluck)) addtoroom(Gold(val), room, level) end end if rand() * defaults.baseluck * 10 > 1.0 && uppossible addtoroom(Ladder(), room, level) end if rand() * defaults.baseluck * 15 > 1.0 && downpossible addtoroom(Pit(), room, level) end if rand() * defaults.baseluck * 5 > 1.0 addtoroom(Sledge(), room, level) end room
end
function makeprizeroom(level)
prizeroom = Room(Point(2, 2, level.zbase), defaults.maxroomdim, defaults.maxroomdim) x1, y1, x2, y2 = x1y1x2y2(prizeroom) level.grid[x1:x2, y1:y2] .= ' ' level.grid[x1:x2, y1] .= obstacles.permwall level.grid[x1:x2, y2] .= obstacles.wall level.grid[x1, y1:y2] .= obstacles.permwall level.grid[x2, y1:y2] .= obstacles.wall for _ in 1:Int(floor(defaults.baseluck * 40)) val = Int(floor(5000.0 * rand() * defaults.baseluck)) addtoroom(Gold(val), prizeroom, level) end prizeroom
end
function makelevel(floorlevel, gridsize=defaults.leveldim, height=defaults.wallheight, map=defaults.map)
grid = fill(obstacles.wall, gridsize, gridsize) grid[:, 1] .= obstacles.permwall grid[:, end] .= obstacles.permwall grid[1, :] .= obstacles.permwall grid[end, :] .= obstacles.permwall rooms = Vector{Room}() level = Level(map, grid, floorlevel * height, floorlevel, height, rooms, Dict()) if floorlevel == defaults.prizeroomlevel push!(rooms, makeprizeroom(level)) end trycount = 0 while trycount < defaults.levelrooms * 5 && length(rooms) < defaults.levelrooms rm = notyetaroom(level, randorigin(level)) if !overlaps(rm, level) fillroom(rm, level) push!(rooms, rm) end trycount += 1 end level
end
function displaytunnel(player)
p = player.location.p visgrid = view(player.location.level.grid, p.x-1:p.x+1, p.y-1:p.y+1) for y in 1:3 for x in 1:3 ch = (x == 2 == y) ? 'p' : visgrid[x, y] print(charcrayons[ch], ch) end println() end println()
end
function updateroom(player)
r = nothing for room in player.location.level.rooms if inroom(player.location.p, room) r = room break end end if r != player.location.room player.location = Location(player.location.p, r, player.location.level) end
end
function displayroom(player, lit=true)
println() if !lit return end x1, y1, x2, y2 = x1y1x2y2(player.location.room) playerx, playery = player.location.p.x, player.location.p.y for y in y1:y2 for x in x1:x2 ch = (x == playerx && y == playery) ? 'p' : player.location.level.grid[x, y] print(charcrayons[ch], ch) end println() end println()
end
function displaylevel(player, showunvisited=true)
mat = player.location.level.grid p = player.location.p for y in 1:size(mat)[2] println() for x in 1:size(mat)[1] print((x == p.x && y == p.y) ? 'p' : mat[x, y]) end end println
end
function queryprompt(query, choices, choicetxt="")
carr = map(x -> uppercase(strip(string(x))), collect(choices)) while true print(query, " ", choicetxt == "" ? carr : choicetxt, ": ") choice = uppercase(strip(readline(stdin))) if choice in carr return choice end println() end
end
help(player=nothing) =
println("n north, s south, w west, e east, d down, u up, i inventory, o roominfo, t take, r drop, q equip, a attack/dig")
function playerdisplayinventory(player)
println("Inventory: $(player.inventory)\n") println("Wielding: $(player.wielding)\n")
end
function playerroominfo(player)
if player.location.room == nothing println("You are between rooms on level $(player.location.level).") return end if player.location.level.floorlevel == defaults.prizeroomlevel println("The prize room is on this level!") end println("You are on level $(player.location.level.floorlevel). You look around the current room.") hasgold, hasladder, haspit, hassledge, hasdiggable = false, false, false, false, false saytype(t::Gold) = if !hasgold println("There is gold here."); hasgold = true end saytype(t::Ladder) = println("There is a ladder here.") saytype(t::Pit) = println("There is a pit here.") saytype(t::Sledge) = println("There is a sledgehammer here.") for (p, v) in player.location.level.content if inroom(p, player.location.room) for i in v saytype(i) end end end
end
function playerequip(player)
for (i, item) in enumerate(player.inventory) if isequip(item) yn = queryprompt("Equip $item? (y)es, (n)o, (q)uit choice", ["Y", "N", "Q"]) if yn == "Y" if iswielding(typeof(item), player) println("You are already wielding a $(typeof(item)).") else push!(player.wielding, item) deleteat!(player.inventory, i) end elseif yn == "Q" break end end end
end
function playerremove(player)
for (i, item) in enumerate(player.wielding) if iswielding(typeof(item), player) yn = queryprompt("Unequip $item? (y)es, (n)o, (q)uit choice", ["Y", "N", "Q"]) if yn == "Y" push!(player.inventory, item) deleteat!(player.wielding, i) elseif yn == "Q" break end end end for (i, item) in enumerate(player.inventory) if isinventory(item) yn = queryprompt("Drop $item? (y)es, (n)o, (q)uit choice", ["Y", "N", "Q"]) if yn == "Y" if !haskey(player.location.level.content, player.location.p) player.location.level.content[player.location.p] = Vector{Item}() end push!(player.location.level.content[player.location.p], item) deleteat!(player.inventory, i) elseif yn == "Q" break end end end
end
function playertake(player)
function addgold(gitem) if findfirst(x -> typeof(x) == Gold, player.inventory) == nothing push!(player.inventory, Gold(0)) end player.inventory[findall(x -> typeof(x) == Gold, player.inventory)[1]].value += gitem.value end canpickup(i::Gold) = true canpickup(i::Sledge) = true canpickup(i::Ladder) = true canpickup(i::Item) = (println("Cannot pick that up."); false) takeit(g::Gold) = begin addgold(g); println("You add $(g.value) in gold.") end takeit(s::Sledge) = begin push!(player.inventory, s); println("You have a sledge.") end takeit(s::Ladder) = begin push!(player.inventory, s); println("You have a ladder.") end
d = player.location.level.content if player.location.room != nothing && haskey(d, player.location.p) for _ in 1:length(d[player.location.p]) item = pop!(d[player.location.p]) if canpickup(item) takeit(item) else pushfirst!(d[player.location.p], item) end end if isempty(d[player.location.p]) delete!(d, player.location.p) player.location.level.grid[player.location.p.x, player.location.p.y] = ' ' else player.location.level.grid[player.location.p.x, player.location.p.y] = d[player.location.p][1].disp end end
end
function exitmap(player)
println("You exit the game with inventory $(player.inventory), wielding $(player.wielding).") exit()
end
function newlevel(player, levelsdown, startup=false)
oldlevel = player.location.level.floorlevel newlevel = oldlevel + levelsdown if newlevel < 1 exitmap(player) else level = makelevel(newlevel) room = rand(level.rooms) point = randemptypoint(room, level) player.location = Location(point, room, level) playerroominfo(player) end if defaults.showlevel displaylevel(player) end player
end
function playerup(player)
if haskey(player.location.level.content, player.location.p) && hastype(Skylight, player.location.p, player.location.level) && iswielding(Ladder, player) deleteat!(player.wielding, findfirst(i -> typeof(i) == Ladder, player.wielding)) println("You have to leave your ladder behind.") newlevel(player, -1) else println("You need to wield a ladder while at a skylight to go upwards.") end
end
function playerdown(player)
if haskey(player.location.level.content, player.location.p) && hastype(Pit, player.location.p, player.location.level) newlevel(player, 1) else println("You can only move downwards at a Pit in the floor.") end
end
function playerdig(player)
movedirs = Dict("N" => [0, -1], "W" => [-1, 0], "S" => [0, 1], "E" => [1, 0]) if !iswielding(Sledge, player) println("You need to be wielding a Sledge.") return end direc = queryprompt("Direction?", ["N", "S", "E", "W", "U", "D"]) if direc == "U" if rand() < defaults.baseluck addatpoint(Skylight(), player.location.p, player.location.level) player.location.level.grid[player.location.p.x, player.location.p.y] = '^' println("There is now a hole in the ceiling above you.") end elseif direc == "D" if rand() < defaults.baseluck addatpoint(Pit(), player.location.p, player.location.level) player.location.level.grid[player.location.p.x, player.location.p.y] = '_' println("There is now a pit in the floor at your feet.") end else goalp = Point(player.location.p.x + movedirs[direc][1], player.location.p.y + movedirs[direc][2], player.location.p.z) if player.location.level.grid[goalp.x, goalp.y] == obstacles.wall if rand() < defaults.baseluck if defaults.fastdig depth = 0 for i in 1:div(defaults.leveldim, 10) p = Point(player.location.p.x + movedirs[direc][1] * i, player.location.p.y + movedirs[direc][2] * i, player.location.p.z) if player.location.level.grid[p.x, p.y] == obstacles.wall player.location.level.grid[p.x, p.y] = ' ' depth += 1 end end if depth > 3 println("Boom! Your sledgehammer's attack on this wall is amazing.") end else player.location.level.grid[goalp.x, goalp.y] = ' ' end end elseif player.location.level.grid[goalp.x, goalp.y] == obstacles.permwall println("This kind of wall cannot be removed.") end end
end
function playermove(player, dx, dy)
goalp = Point(player.location.p.x + dx, player.location.p.y + dy, player.location.p.z) if player.location.level.grid[goalp.x, goalp.y] in obstacles println("Something is in the way.") else player.location = Location(goalp, player.location.room, player.location.level) end
end playerN(player) = playermove(player, 0, -1) playerW(player) = playermove(player, -1, 0) playerS(player) = playermove(player, 0, 1) playerE(player) = playermove(player, 1, 0)
const usercommands = Dict("N" => playerN, "S" => playerS, "E" => playerE, "W" => playerW,
"U" => playerup, "D" => playerdown, "I" => playerdisplayinventory, "O" => playerroominfo, "T" => playertake, "R" => playerremove, "Q" => playerequip, "A" => playerdig, "?" => help)
function runuser(player)
updateroom(player) if player.location.room == nothing displaytunnel(player) else displayroom(player) end choice = queryprompt("Choose move (nsewudiotrqa?)", ["N", "S", "E", "W", "U", "D", "I", "O", "T", "R", "Q", "A", "?"]) usercommands[choice](player)
end
function newplayer(newlevel=1)
level = makelevel(newlevel) room = rand(level.rooms) point = randemptypoint(room, level) player = Agent(Location(point, room, level)) playerroominfo(player) if defaults.showlevel displaylevel(player) end player
end
function rungame()
player = newplayer() while true runuser(player) end
end
rungame() </lang>