RCRPG/Julia

From Rosetta Code
Revision as of 10:31, 12 February 2019 by Wherrera (talk | contribs) (Created page with "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', per...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

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>