Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- MKDIR darklands
- MKFIL darklands/darklands
- WRITE 2647
- --[[
- RPG Engine
- Written by: Nitrogen Fingers
- Version: -0.4a (pre-alpha)
- Created: 29/06/2012
- Last Updated: 10/03/2013
- : This program allows the running of a fairly simple, Ultima-style RPG. It includes the ability to run simple :
- : scripts to define enemies, towns, quests, weapons and so on and will include them within the game engine :
- : dynamically. Note this is solely the engine- the game itself is defined by it's scripts, which are kept in :
- : the script, towns and world files in this directory
- Immediate Goals for next version:
- - Skill sheet
- - Greater control over random generation of enemies (type, level etc)
- - Dynamic dungeons- levers, secret doors and more
- - More graphical elements in dungeons
- Goals for later versions:
- - Magic
- - Serialization of some sort
- - Music
- ]]--
- local version = 0.4
- local reponame = "Darklands"
- local requiresColour = false
- if requiresColour then
- print("This game can only be played on an Advanced Computer.")
- end
- local function displayNitrosoftTitle()
- shell.run("clear")
- local _w,_h = term.getSize()
- local _t1,_t2 = "nitrosoft", "games"
- term.setCursorPos(math.floor(_w/2-#_t1), math.floor(_h/2))
- if term.isColour() then term.setTextColour(colours.blue) end
- term.write(_t1)
- term.setCursorPos(math.floor(_w/2-#_t2/2),math.floor(_h/2)+1)
- term.setTextColour(colours.white)
- term.write(_t2)
- if term.isColour() then term.setTextColour(colours.red) end
- term.setCursorPos(math.floor(_w/2-#_t1), math.floor(_h/2)+2)
- term.write(string.rep("-",#_t1*2))
- if term.isColour() then
- term.setBackgroundColour(colours.green)
- term.setCursorPos(_w/2+#_t1-4, math.floor(_h/2)-2)
- term.write(" ")
- term.setCursorPos(_w/2+#_t1-4, math.floor(_h/2)-1)
- term.write(" ")
- term.setBackgroundColour(colours.lime)
- term.setCursorPos(_w/2+#_t1-3, math.floor(_h/2)-1)
- term.write(" ")
- term.setCursorPos(_w/2+#_t1-3, math.floor(_h/2))
- term.write(" ")
- end
- term.setBackgroundColour(colours.black)
- term.setTextColour(colours.white)
- term.setCursorPos(1,_h)
- term.write("v"..version)
- os.pullEvent("key")
- end
- displayNitrosoftTitle()
- w,h = term.getSize()
- gameOver = false
- debugging = false
- --[[SECTION: GLOBAL parameters & variables]]--
- --name: How the quest appears in your logbook
- --activestage: At what stage the quest is up to. 0 is inactive, -1 is complete.
- --variables: You may insert anything you like here- space for the filling!
- --stages: the ID for each stage and what it means:
- --desc: a short description of what's needed to complete this stage of the quest
- --condition: a function that checks the state of the world and each action the player makes,
- --if this returns true it runs a function that modifies the game state (usually a display or something)
- --and updates the quest stage.
- --reward: a table with the type and value of the reward (at present types are GOLD and ITEM).
- quests = {}
- --name: The town's title
- --xpos,ypos: coordinates it should appear on the world map
- --startx,starty: coordinates the player should appear when entering/exiting the town
- --level: the environment of the town itself
- --dialogue: dialog options that are true for everyone in the town. Used when personal dialog choices are exhasted
- --visible: Whether or not the town is presently visible
- --npc: a list of all npcs in the town
- --name: The name of the npc
- --xpos,ypos: Position of the x and y
- --moving: boolean, whether or not the NPC walks around. They never move more than 2 or 3 steps from where they start.
- --dialog: A list of every dialog option with the word or phrase needed to start it. # tags are added, and similies should # the actual option
- --Defaults: onfirstgreet, ongreet, onfarewell
- --job: How the user interacts with the npc
- --level: Affects the equipment the NPC sells, and how much XP each quest he assigns is worth
- --inventory: Smiths/Mystics only: The items they have available for sale
- --priceModifier: Smiths/Merchants/Mystics only: Their buying or selling modifier price, normalized percentage (ex. 0.75 for 75%)
- --goldLimit: Merchants only: Their maximum buying price on any item (will never pay more than this)
- town = { }
- --Global dialogue- dialogue shared by all members of the game
- globalDialogue = { }
- --name: The title of the dungeon, as given in the over display
- --xpos,ypos: coordinates it should appear on the world map
- --startx,starty,startlevel: the coodinates the player starts at within the dungeon. Place these NEXT to E, not in an E.
- --startfx, startfy: the facing the player starts at
- --level: the level itself, divided into sublevels by index
- --creatures: A list of all enemies currently in the dungeon. Can start at zero, if there are none yet.
- --creaturesSpawn: A boolean- if true enemies will occasionally spawn at random within the dungeon.
- dungeons = { }
- creatures = { }
- items = { }
- enchantments = { }
- --The function to be run when the game starts. Here you should initialize things like the player's class, stats,
- --and any other initialization your script will require.
- onInit = nil
- --A 2D array of strings, for displaying images. You can use this however you like, the displayImage function will do it for you.
- images = { }
- --Quest State information, to do with conversations.
- --activeDialog: the name of the person the PC engaged in conversation with
- activeDialog = nil
- --[[SECTION: Character, RP Stats]]--
- charName = "Unnamed"
- charClass = "Adventurer"
- charLevel = 1
- charHealth = 80
- charMaxHealth = 80
- charXP = 0
- charNextXP = 50
- charGold = 0
- --The player's RPG stats, modifiers for his combat ability
- local charStrength = 10
- local charEndurance = 10
- local charAgility = 10
- local charSpeed = 10
- activeQuests = {}
- inventory = {}
- --What the player has equipped
- charEquipment = {
- swordhand = { value = nil, suffix = "(S)" };
- offhand = { value = nil, suffix = "(O)" };
- head = { value = nil, suffix = "(H)" };
- chest = { value = nil, suffix = "(C)" };
- legs = { value = nil, suffix = "(L)" };
- feet = { value = nil, suffix = "(F)" };
- }
- --The last page the journal was opened to
- local journalPage = 1
- --A little timesavaer
- local directions = { {x = -1, y = 0}, {x = 0, y = -1}, {x = 1, y = 0}, {x = 0, y = 1} }
- --[[SECTION: Overword Information]]--
- overworld = { }
- --Overcreatures also track:
- --chaseturns: How many turns the overcreature has been chasing the player. If this exceeds 10, they should give up
- --stillturns: How many turns the overcreature has been unable to move. If this exceeds 3, they should give up
- local overcreatures = { }
- worldname = "The World"
- local mapw = 29
- local maph = 16
- pox = 1
- poy = 1
- local overEventList = {}
- --[[SECTION: Town information]]--
- activeTown = 0
- inTown = false
- townpx, townpy = 0,0
- local townEventList = {}
- --[[SECTION: Dungeon, State Attributes]]--
- --The player's position and facing
- dunpx = 0
- dunpy = 0
- dunlevel = 0
- dunfx = 0
- dunfy = 0
- inDungeon = false
- activeDungeon = 0
- local dungeonEventList = {}
- --[[SECTION: General, Graphics Interface]]--
- local inventoryInterface = {
- "//=============================\\ /===============\\\\",
- "|| | __ ||",
- "|| | / \\H ||",
- "|| | \\__/ ||",
- "|| | /--^-- --^--\\ ||",
- "|| | S----\\ /----O ||",
- "|| | C| | ||",
- "|| | | | ||",
- "|| | | |L ||",
- "|| | / \\ ||",
- "|| | |_.._|F ||",
- "|| | ||",
- "**------------------------------*----------------**",
- }
- local journalInterface = {
- " _______________________ _ _______________________ ",
- "/ [ ] \\",
- "| | | |",
- "\\_______________________[_]_______________________/"
- }
- local shopInterface = {
- "//===========================\\\\",
- "|| ||",
- "|| |/-~-~-~-~-~-~-~-~-~-~-\\",
- "|| || |",
- "\\\\===========================//-~-~-~-~-~-~-~-~-~-~-/"
- }
- --[[SECTION: Dungeon, Display Components]]--
- --The algorithms behind this are ingenius, but complicated. They involve overlaying various sprites on top of one another
- --at given areas to give the illusion of 3D.
- local cor = {
- "\\ /",
- " \\ / ",
- " \\ / ",
- " \\ / ",
- " \\ / ",
- " \\ / ",
- " \\ / ",
- " \\ / ",
- " / \\ ",
- " \\ / ",
- " / \\ ",
- " / \\ ",
- " / \\ ",
- " / \\ ",
- " / \\ ",
- " / \\ ",
- " / \\ ",
- "/ \\",
- }
- local corx = 1
- local cory = 1
- local twoend = {
- " ___ ",
- "| |",
- "| |",
- "| |",
- " --- "
- }
- local twoexitend = {
- " ___ ",
- "| _ |",
- "|/ \\|",
- "|| ||",
- ",- -."
- }
- local twoendx = 10
- local twoendy = 7
- local oneend = {
- "\\_________/",
- "| |",
- "| |",
- "| |",
- "| |",
- "| |",
- "| |",
- "| |",
- "|_________|"
- }
- local oneexitend = {
- "\\_________/",
- "| |",
- "| _____ |",
- "| / \\ |",
- "| | | |",
- "| | | |",
- "| | | |",
- "| | | |",
- "|_| |_|"
- }
- local oneendx = 7
- local oneendy = 5
- local trightturn = {
- " |",
- "||",
- "||",
- "||",
- "||",
- " |"
- }
- local trightturnx = 14
- local trightturny = 7
- local tleftturn = {
- "| ",
- "||",
- "||",
- "||",
- "||",
- "| "
- }
- local tleftturnx = 9
- local tleftturny = 7
- local rightturn = {
- " |",
- " |",
- " __|",
- "| |",
- "| |",
- "| |",
- "| |",
- "| |",
- "| |",
- "| |",
- "|__|",
- " |",
- " |",
- " |"
- }
- local rightturnx = 17
- local rightturny = 3
- local leftturn = {
- "| ",
- "| ",
- "|__ ",
- "| |",
- "| |",
- "| |",
- "| |",
- "| |",
- "| |",
- "| |",
- "|__|",
- "| ",
- "| ",
- "| "
- }
- local leftturnx = 4
- local leftturny = 3
- local bwturn = {
- " ",
- " ",
- " ",
- " ",
- "____",
- " ",
- " ",
- " ",
- " ",
- " ",
- " ",
- " ",
- "____",
- " ",
- " ",
- " ",
- " ",
- " "
- }
- local bwleftturnx = 1
- local bwrightturnx = 20
- local bwturny = 1
- local ladderd = {
- " |---| ",
- " | | ",
- "/|---|\\",
- "\\|___|/"
- }
- local ladderdx = 9
- local ladderdy = 13
- local ladderu = {
- " _____ ",
- "/|---|\\",
- "\\| |/",
- " |---| ",
- " | | ",
- " |---| ",
- " | | ",
- " |---| ",
- " | | ",
- " |---| ",
- " | | ",
- " |---| ",
- " | | ",
- " |---| ",
- " | | "
- }
- local ladderux = 9
- local ladderuy = 2
- local ladderud = {
- " _____ ",
- "/|---|\\",
- "\\| |/",
- " |---| ",
- " | | ",
- " |---| ",
- " | | ",
- " |---| ",
- " | | ",
- " |---| ",
- " | | ",
- " |---| ",
- " | | ",
- "/|---|\\",
- "\\|___|/"
- }
- local ladderudx = 9
- local ladderudy = 2
- local chestc = {
- "/-------\\",
- "| 0 |",
- "|_______|"
- }
- local chestcx = 8
- local chestcy = 14
- local chesto = {
- " _______ ",
- "/ \\",
- "\\_______/",
- "| 0 |",
- "|_______|"
- }
- local chestox = 8
- local chestoy = 12
- local compass = {
- " ___ ",
- " / \\ ",
- "| |",
- "| |",
- " \\___/ "
- }
- --Determines the left facing depending on the current facing
- function getLeft(curx, cury)
- if curx == 1 then return 0,-1 end
- if curx == -1 then return 0,1 end
- if cury == 1 then return 1,0 end
- if cury == -1 then return -1,0 end
- end
- --Determines the right facing depending on the current facing
- function getRight(curx, cury)
- if curx == 1 then return 0,1 end
- if curx == -1 then return 0,-1 end
- if cury == 1 then return -1,0 end
- if cury == -1 then return 1,0 end
- end
- --[[SECTION: Overworld, Helper]]--
- --Determines if the chosen letter is a vowel. Used mostly for determining articles.
- function isVowel(letter)
- return letter == "a" or letter == "e" or letter == "i" or letter == "o" or letter == "u"
- end
- --Prints a string with left justification, given a height and an optional offset
- function printLeft(msg, height, offset)
- if not offset then offset = 0 end
- term.setCursorPos(1 + offset, height)
- term.write(msg)
- end
- --A more advanced version of the above method including wrapping over specified width.
- --It will move to a new line each time and return the number of lines it takes.
- function wprintLeft(msg, height, width, offset)
- local inc = 0
- local ops = 1
- while #msg - ops > width do
- local nextspace = 0
- while string.find(msg, " ", ops + nextspace) and
- string.find(msg, " ", ops + nextspace) - ops < width do
- nextspace = string.find(msg, " ", nextspace + ops) + 1 - ops
- end
- local ox,oy = term.getCursorPos()
- term.setCursorPos(offset, height + inc)
- inc = inc + 1
- term.write(string.sub(msg, ops, nextspace + ops - 1))
- ops = ops + nextspace
- end
- term.setCursorPos(offset, height + inc)
- term.write(string.sub(msg, ops))
- return inc + 1
- end
- --Prints a string with right justfication, given a height and optional offset
- function printRight(msg, height, offset)
- if not offset then offset = 0 end
- term.setCursorPos(w - #msg - offset, height)
- term.write(msg)
- end
- --Prints a string in the center of the space specified- with the given height and offset
- function printOffCenter(msg, height, width, offset)
- if not offset then offset = 0 end
- term.setCursorPos(width/2 - #msg/2 + offset, height)
- term.write(msg)
- end
- --A slightly more advanced version of the above method, to include text wrapping over the
- --specified width. It will move to a new line each time, and return the number of lines it takes.
- function wprintOffCenter(msg, height, width, offset)
- local inc = 0
- local ops = 1
- while #msg - ops > width do
- local nextspace = 0
- while string.find(msg, " ", ops + nextspace) and
- string.find(msg, " ", ops + nextspace) - ops < width do
- nextspace = string.find(msg, " ", nextspace + ops) + 1 - ops
- end
- local ox,oy = term.getCursorPos()
- term.setCursorPos(width/2 - (nextspace)/2 + offset, height + inc)
- inc = inc + 1
- term.write(string.sub(msg, ops, nextspace + ops - 1))
- ops = ops + nextspace
- end
- term.setCursorPos(width/2 - #string.sub(msg, ops)/2 + offset, height + inc)
- term.write(string.sub(msg, ops))
- return inc + 1
- end
- --Displays an image statically, in a frame and waits on a key event to dispel it. Offset on width and height can be chosen, leaving them null will center the image.
- --Note- any images with an uneven length will have spaces appended to them to make them fit.
- function displayStaticImage(index, woffset, hoffset)
- local image = images[index]
- local longest = 0
- for i=1,#image do longest = math.max(longest, #image[i]) end
- if not woffset then
- woffset = w/2 - longest/2
- end
- if not hoffset then
- hoffset = h/2 - #image/2
- end
- printLeft("/"..string.rep("-",longest).."\\", hoffset-1, woffset-1)
- printLeft("\\"..string.rep("-",longest).."/", hoffset+#image+1, woffset-1)
- for i=1,#image do
- printLeft("|"..image[i]..string.rep(" ",longest - #image[i]).."|", hoffset-1+i, woffset-1)
- end
- os.pullEvent("key")
- end
- --Displays an animated series of images in a frame with the start and end index of the image buffer. A key event will close the image. The time delay indicates the
- --space of time between each frame.
- function displayAnimatedImage(startindex, endindex, timedelay, woffset, hoffset)
- end
- --[[SETION: Overworld, Initialization]]--
- --Opens the overworld directory and adds it to the overworld parameters
- function readOverWorld()
- local file = io.open(shell.resolve(".").."/scripts/world", "r")
- if not file then error("Error: \'world\' file not present!") end
- local fline = file:read()
- local count = 1
- while fline do
- overworld[count] = { }
- for i=1,#fline do
- overworld[count][i] = string.sub(fline,i,i)
- end
- fline = file:read()
- count = count + 1
- end
- file:close()
- print("Overworld successfully parsed.")
- end
- --[[SECTION: Town, Helper]]--
- --This will set any given quest to be active and in the user's set. Though this occurs most commonly in town, it can be done anywhere.
- function activateQuest(quest)
- table.insert(activeQuests, quest)
- if quest.onActivation then quest:onActivation() end
- addEvent("New Quest: "..quest.name)
- if debugging then
- writeToLog("New Quest Added", "\""..activeQuests[#activeQuests].name.."\"", activeQuests[#activeQuests].activeStage)
- end
- end
- --Completes a quest, and removes it from the active quest list. You can specify the message you want to display when the quest is removed, otherwise a default one is used.
- function deactivateQuest(quest, message)
- table.remove(activeQuests, quest)
- if message then
- addEvent(message)
- else
- addEvent("Quest Complete!")
- end
- end
- --This checks anytime you want to see if a quest objective (or more than one) have been completed. The boolean determines
- --if only one is checked (i.e. if true once then it stops), or if it keeps going one after another.
- function checkQuestUpdate(event)
- local questupdated = false
- for i=1,#activeQuests do
- --It only allows one per quest giver (the newest)
- local quest, qstage = activeQuests[i], activeQuests[i].activestage
- local statechange = quest.stages[qstage].condition(quest, event)
- if statechange then
- if activeQuests[i].activestage == -1 then
- deactivateQuest(i, quest.name.." Complete!")
- end
- return statechange
- end
- end
- return nil
- end
- --Finds an NPC by name, if he exists within the town (to access his details but not change them)
- function findNPC(name, townindex)
- for i=1,#town[townindex].npc do
- local locnpc = town[townindex].npc[i]
- if locnpc.name == name then return locnpc end
- end
- return nil
- end
- --Allows you to change an NPC's dialog, by name and town
- function setNPCDialogue(name, townindex, subject, dialogue)
- for i=1,#town[townindex].npc do
- local locnpc = town[townindex].npc[i]
- if locnpc.name == name then
- town[townindex].npc[i].dialogue[subject] = dialogue
- return
- end
- end
- end
- --Moves an NPC to a new location
- function setNPCPosition(name, townindex, newx, newy)
- for i=1,#town[townindex].npc do
- local locnpc = town[townindex].npc[i]
- if locnpc.name == name then
- town[townindex].npc[i].xpos = newx
- town[townindex].npc[i].ypos = newy
- return
- end
- end
- end
- --Determines how much a given item will be valued by any NPC.
- function getNPCValueOn(locnpc, item)
- if not item then return 0 end
- local value = math.floor(locnpc.priceModifier * item.value)
- if value < 1 then value = 1
- elseif locnpc.job == "merchant" and value > locnpc.goldLimit then value = locnpc.goldLimit end
- return value
- end
- --This takes everyNPC in the world and generates a fresh inventory for them
- function refreshNPCInventory()
- for t=1,#town do
- for n=1,#town[t].npc do
- local chosenNPC = town[t].npc[n]
- if chosenNPC.job == "smith" then
- chosenNPC.inventory = { }
- for i=1, math.random(5,15) do
- table.insert(chosenNPC.inventory, generateLoot(chosenNPC.level + 3))
- end
- end
- end
- end
- end
- --Simply returns the character's name- for dynamic use in dialogues etc.
- function getCharName()
- return charName
- end
- --Sets the character's name... weird bug means I have to do this. Sorry.
- function setCharName(newName)
- charName = newName
- end
- --[[SECTION: Dungeon, Initialization]]--
- --This adds a 2 point space to each part of the level. It's not necessary but it makes level design easier. Part of the level parse.
- function bufferLevel(index)
- for i=1,#dungeons[index].level do
- for j=1, #dungeons[index].level[i] do
- dungeons[index].level[i][j] = " "..dungeons[index].level[i][j].." "
- end
- table.insert(dungeons[index].level[i], 1, string.rep(" ", #dungeons[index].level[i][1]))
- table.insert(dungeons[index].level[i], 1, string.rep(" ", #dungeons[index].level[i][1]))
- table.insert(dungeons[index].level[i], string.rep(" ", #dungeons[index].level[i][1]))
- table.insert(dungeons[index].level[i], string.rep(" ", #dungeons[index].level[i][1]))
- end
- dungeons[index].startx = dungeons[index].startx + 2
- dungeons[index].starty = dungeons[index].starty + 2
- end
- --[[SECTION: Combat, Helper]]--
- --This takes an index from the creature library, creates a clone and returns it
- --You can use a numerical index or the creatures name, if it's easier
- function cloneCreature(index, xpos, ypos, level)
- if type(index) == "string" then
- for i,v in ipairs(creatures) do
- if v.name == index then
- index = i
- break
- end
- end
- end
- if type(index) == "string" then error(index.." creature not found!") end
- local newcreature = {
- name = creatures[index].name,
- xpos = xpos,
- ypos = ypos,
- levelpos = level,
- level = creatures[index].level,
- hit = creatures[index].hit,
- xp = creatures[index].xp,
- weapon = creatures[index].weapon,
- ac = creatures[index].ac,
- imageone = creatures[index].imageone,
- imagetwo = creatures[index].imagetwo
- }
- return newcreature
- end
- --This function adds a creature to a random position somewhere on the overworld map.
- function addCreatureToWorldMap()
- --It only tries 5 times right now
- for i=1,5 do
- local xval = math.random(pox - 3, pox + 3)
- local yval = math.random(poy - 3, poy + 3)
- if xval > 0 and yval > 0 and xval <= #overworld[1] and yval <= #overworld and
- overworld[yval][xval] == " " and (xval ~= pox or yval ~= poy) then
- local safe = true
- for k,v in pairs(overcreatures) do
- if v.xpos == xval and v.ypos == yval then
- safe = false
- break
- end
- end
- if safe then
- local newCreature = cloneCreature(math.random(1,2), xval, yval, nil)
- newCreature.chaseturns = 0
- newCreature.stillturns = 0
- table.insert(overcreatures, newCreature)
- addEvent(newCreature.name.." has appeared!")
- return
- end
- end
- end
- end
- --This function adds a creature to a random position somewhere nearby the player in a dungeon.
- function addCreatureToDungeon()
- local viableSpots = {}
- for xval = dunpx - 3, dunpx + 3 do
- for yval = dunpy - 3, dunpy + 3 do
- if xval >= 2 and yval >= 2 and xval <= #dungeons[activeDungeon].level[dunlevel] - 2 and yval <= #dungeons[activeDungeon].level[dunlevel] - 2 and
- dungeonTileAt(xval,yval,dunlevel) ~= " " and (xval ~= dunpx or yval ~= dunpy) then
- table.insert(viableSpots, { x = xval, y = yval })
- end
- end
- end
- --I have no idea, but this apparently is happening. Solve for later
- if #viableSpots == 0 then return end
- local spot = viableSpots[math.random(1, #viableSpots)]
- local possibleMonsters = {}
- for k,v in pairs(creatures) do
- if v.level <= charLevel then table.insert(possibleMonsters, k) end
- end
- local newCreature = cloneCreature(possibleMonsters[math.random(1,#possibleMonsters)], spot.x, spot.y, dunlevel)
- table.insert(dungeons[activeDungeon].creatures, newCreature)
- addEvent("You hear a noise nearby.")
- return
- end
- --This determines how much is done, by a given weapon
- function calculateDamage(weapon, isPlayer)
- return math.random(weapon.base - weapon.base/5, weapon.base + weapon.base/5)
- end
- --Adds XP and increases the player's level
- function addXP(amount)
- charXP = charXP + amount
- if charXP >= charNextXP then
- charXP = charXP - charNextXP
- charNextXP = charNextXP * 2
- charMaxHealth = charMaxHealth + 10
- charHealth = charMaxHealth
- charLevel = charLevel + 1
- addEvent("You are now level "..charLevel.."!")
- end
- end
- --Adds Gold to the player's inventory
- function addGold(amount)
- charGold = charGold + amount
- addEvent("Got "..amount.." gold!")
- end
- --This section determines how much damage is deflected by a given creatures, or player (using nil)
- --[[SECTION: Inventory, Helper]]--
- --This takes an index from the item library, creates a clone and returns it.
- function cloneItem(index)
- return {
- name = items[index].name,
- class = items[index].class,
- desc = items[index].desc,
- bodypart = items[index].bodypart,
- level = items[index].level,
- requirements = items[index].requirements,
- base = items[index].base,
- enchantment = items[index].enchantment,
- enchantTypes = items[index].enchantTypes,
- value = items[index].value,
- weight = items[index].weight
- }
- end
- --This takes a string index from the enchantment library, creates a clone and returns it.
- function cloneEnchantment(index)
- if string.find(index, "#") == 1 then return { } end
- if not enchantments[index] then return { } end
- return {
- name = enchantments[index].name,
- desc = enchantments[index].desc,
- prefix = enchantments[index].prefix,
- skill = enchantments[index].skill,
- effect = enchantments[index].effect,
- level = enchantments[index].level,
- value = enchantments[index].value
- }
- end
- --This generates a random bit of loot, based on the provided level, and returns it.
- function generateLoot(maxlevel)
- --We have a 1 in 5 of the loot being enchanted.
- local hasEnchant = math.random(1,5) == 1
- local viableItems = {}
- for i=1,#items do
- --We copy every index of value to us before creating the item
- if items[i].level <= maxlevel then
- table.insert(viableItems, i)
- end
- end
- local finalItem = cloneItem(viableItems[math.random(1,#viableItems)])
- if not hasEnchant then return finalItem end
- --If we need to enchant...
- local viableEnchant = {}
- --First we filter down so we have only "base" enchants (not categories, or #'s)
- for i=1,#finalItem.enchantTypes do
- table.insert(viableEnchant, finalItem.enchantTypes[i])
- end
- local allBaseEnchants = true
- repeat
- allBaseEnchants = true
- for i=1,#viableEnchant do
- local currEnchant = viableEnchant[i]
- if string.find(currEnchant, "#") == 1 then
- table.remove(viableEnchant, i)
- for j=1,#enchantments[currEnchant] do
- table.insert(viableEnchant, enchantments[currEnchant][j])
- end
- allBaseEnchants = false
- break
- end
- end
- until allBaseEnchants
- --Then we whittle off those that the weapon can't handle
- local it = 1
- while it <= #viableEnchant do
- if enchantments[viableEnchant[it]].level > finalItem.level then
- table.remove(viableEnchant, it)
- else
- it = it+1
- end
- end
- --Finally, we randomly pick one of those enchantments and bam! Cut, print, that's the show.
- finalItem.enchantment = cloneEnchantment(viableEnchant[math.random(1,#viableEnchant)])
- finalItem.value = math.floor(finalItem.value * finalItem.enchantment.value)
- if finalItem.value < 1 then finalItem.value = 1 end
- if finalItem.enchantment.skill == "base" then
- if math.abs(finalItem.enchantment.effect) > (0.5 * finalItem.enchantment.effect) then
- finalItem.base = math.floor(finalItem.base + finalItem.enchantment.effect)
- else
- finalItem.base = math.floor((finalItem.base/2) * (finalItem.enchantment.effect/math.abs(finalItem.enchantment.effect)))
- end
- end
- return finalItem
- end
- --[[SECTION: General, Interface]]--
- --Runs a display for the interface, overlaying the interface screen and runs input on that interface
- --until a user termiantes the interaction.
- --We use an NPC here- depending on the profession we buy from his inventory or sell from our own.
- --This is toggled by the buy tag as the second parameter.
- function runTradeInterface(chosenNPC, buying)
- local selected = 1
- local scroll = 1
- local selectInv = nil
- if buying then selectInv = inventory
- else selectInv = chosenNPC.inventory end
- while true do
- --This is actually a tidy little time saver- shows the player's gold and his stats!
- printTownInterface()
- --First we draw our display, as always
- printLeft(shopInterface[1], 1)
- for i=2,12 do printLeft(shopInterface[2], i) end
- printLeft(shopInterface[3], 13)
- for i=14,18 do printLeft(shopInterface[4], i) end
- printLeft(shopInterface[5], 19)
- for i=scroll,math.min(scroll+16,scroll+#selectInv-1) do
- local prefix = " "
- if i==selected then prefix = ">>" end
- local suffix = ""
- for __,v in pairs(charEquipment) do
- if v.value == selectInv[i] then
- if selectInv[i].bodypart == "both" then suffix = "(S/O)"
- else suffix = v.suffix end
- break
- end
- end
- if selectInv[i].enchantment then
- if selectInv[i].enchantment.prefix then prefix = prefix..selectInv[i].enchantment.name.." "
- else suffix = selectInv[i].enchantment.name..suffix end
- end
- printLeft(prefix..selectInv[i].name.." "..suffix, i-scroll+2, 2)
- end
- term.setCursorPos(31, 14)
- if chosenNPC.job == "merchant" then
- printLeft("Trader buys at "..(chosenNPC.priceModifier * 100).."%", 14, 31)
- printLeft("Gold limit: "..chosenNPC.goldLimit, 15, 31)
- printLeft("Final price: "..getNPCValueOn(chosenNPC, selectInv[selected]), 17, 31)
- else
- printLeft("Trader sells at "..(chosenNPC.priceModifier * 100).."%", 14, 31)
- printLeft("Final price: "..getNPCValueOn(chosenNPC, selectInv[selected]), 17, 31)
- end
- local id,key = os.pullEvent("key")
- if key == keys.enter then break
- elseif key == keys.up and selected > 1 then
- selected = selected-1
- if selected < scroll + 2 and scroll > 1 then
- scroll = scroll - 1
- end
- elseif key == keys.down and selected < #selectInv then
- selected = selected+1
- if selected > scroll + 14 and selected < #selectInv - 1 then
- scroll = scroll+1
- end
- elseif key == keys.space then
- if buying then
- charGold = charGold + getNPCValueOn(chosenNPC, selectInv[selected])
- if isEquipped(selected) then equipItem(selected) end
- table.remove(selectInv, selected)
- if selected > #selectInv then selected = selected-1 end
- if #selectInv == 0 then
- local ctitle = chosenNPC.name.." the "..chosenNPC.job.." says:"
- displayConfirmDialogue(ctitle, "That's all your gear! Pleasure doing business with you.")
- break
- end
- elseif not buying and charGold >= getNPCValueOn(chosenNPC, selectInv[selected]) then
- charGold = charGold - getNPCValueOn(chosenNPC, selectInv[selected])
- table.insert(inventory, selectInv[selected])
- table.remove(selectInv, selected)
- if selected > #selectInv then selected = selected-1 end
- if #selectInv == 0 then
- local ctitle = chosenNPC.name.." the "..chosenNPC.job.." says:"
- displayConfirmDialogue(ctitle, "I'm sold out of stock! Come back when I have some more stuff.")
- break
- end
- end
- end
- end
- end
- --Runs a display interface that displays each quest the user has, and allows them to cycle between them.
- --A quest is displayed on each page of a book, with the user able to flip pages left and right to see other
- --quests.
- function runJournalInterface()
- if #activeQuests == 0 then
- addEvent("You have no currently active quests.")
- return
- end
- if journalPage * 2 - 1 > #activeQuests then journalPage = 1 end
- while true do
- --Draw the backdrop
- for i=1,2 do
- term.setCursorPos(1,i)
- term.write(journalInterface[i])
- end
- for i=3,h-1 do
- term.setCursorPos(1,i)
- term.write(journalInterface[3])
- end
- term.setCursorPos(1,h)
- term.write(journalInterface[4])
- --Draw the quest on each page
- local dquest = activeQuests[journalPage * 2 - 1]
- if dquest then
- wprintLeft(dquest.name, 3, 22, 3)
- local _,ny = term.getCursorPos()
- ny = ny + 3
- wprintLeft(dquest.generalDescription, ny, 22, 3)
- local _,ny = term.getCursorPos()
- ny = ny + 3
- wprintLeft(dquest.stages[dquest.activestage].desc, ny, 22, 3)
- wprintOffCenter("Page "..(journalPage * 2 - 1), h-1, 22, 3)
- end
- dquest = activeQuests[journalPage * 2]
- if dquest then
- wprintLeft(dquest.name, 3, 22, 29)
- local _,ny = term.getCursorPos()
- ny = ny + 3
- wprintLeft(dquest.generalDescription, ny, 22, 29)
- local _,ny = term.getCursorPos()
- ny = ny + 3
- wprintLeft(dquest.stages[dquest.activestage].desc, ny, 22, 29)
- wprintOffCenter("Page "..(journalPage * 2), h-1, 22, 29)
- end
- --Left and right arrow events shift from one page to the next, enter quits.
- local _,key = os.pullEvent("key")
- if key == keys.left and journalPage > 1 then
- journalPage = journalPage - 1
- elseif key == keys.right and activeQuests[(journalPage + 1) * 2 - 1] then
- journalPage = journalPage + 1
- elseif key == keys.enter then break end
- end
- end
- --[[INCOMPLETE]]--
- --Displays a screen with each of the player's attributes
- function printPlayerStats()
- end
- --[[SECTION: Inventory, Interface]]--
- --This method creates a display for the interface, overlaying the current screen, and runs input on
- --that interface until the user terminates the interaction.
- function runInventoryInterface()
- local selected = 1
- local scroll = 1
- if #inventory == 0 then
- displayConfirmDialogue("*", "Your inventory is empty.")
- return
- end
- while true do
- --This actually produces the physical display for the interface
- for i=1,#inventoryInterface do
- term.setCursorPos(1, i)
- term.write(inventoryInterface[i])
- end
- for i=#inventoryInterface+1,h-1 do
- term.setCursorPos(1,i)
- term.clearLine()
- end
- term.setCursorPos(1,h)
- term.write(inventoryInterface[#inventoryInterface])
- for i=scroll,math.min(scroll+10,scroll+#inventory-1) do
- local prefix = " "
- if i==selected then prefix = ">>" end
- local suffix = ""
- for __,v in pairs(charEquipment) do
- if v.value == inventory[i] then
- if inventory[i].bodypart == "both" then suffix = "(S/O)"
- else suffix = v.suffix end
- break
- end
- end
- if inventory[i].enchantment then
- if inventory[i].enchantment.prefix then prefix = prefix..inventory[i].enchantment.name.." "
- else suffix = inventory[i].enchantment.name..suffix end
- end
- printLeft(prefix..inventory[i].name.." "..suffix, i-scroll+2, 2)
- end
- local itemdesc = inventory[selected].desc
- if inventory[selected].enchantment then
- itemdesc = itemdesc.." "..inventory[selected].enchantment.desc
- printOffCenter(inventory[selected].enchantment.skill.." "..inventory[selected].enchantment.effect, h-1, w, 0)
- end
- wprintOffCenter(itemdesc, #inventoryInterface + 1, w-4, 2)
- if inventory[selected].class == "weapon" then
- printLeft("Damage:"..inventory[selected].base, h-1, 1)
- elseif inventory[selected].class == "shield" then
- printLeft("Block: "..(inventory[selected].base*100).."%", h-1, 1)
- elseif inventory[selected].class == "armour" then
- printLeft("AC:"..inventory[selected].base, h-1, 1)
- end
- printRight("Value: "..inventory[selected].value, h-1, 1)
- --This handles input, which is repeated until the method is complete
- local id, key = os.pullEvent("key")
- if key == keys.enter then break
- elseif key == keys.down and selected < #inventory then
- selected = selected+1
- if selected > scroll + 8 and selected < #inventory - 1 then
- scroll = scroll+1
- end
- elseif key == keys.up and selected > 1 then
- selected = selected-1
- if selected < scroll + 2 and scroll > 1 then
- scroll = scroll - 1
- end
- elseif key == keys.space then
- equipItem(selected)
- end
- end
- end
- --This takes a selectedIndex and equips it (or unequips it)
- function equipItem(itemIndex)
- if inventory[itemIndex].bodypart == "both" then
- if charEquipment["swordhand"].value == inventory[itemIndex] then
- charEquipment["swordhand"].value = nil
- charEquipment["offhand"].value = nil
- else
- charEquipment["swordhand"].value = inventory[itemIndex]
- charEquipment["offhand"].value = inventory[itemIndex]
- end
- elseif charEquipment[inventory[itemIndex].bodypart].value == inventory[itemIndex] then
- charEquipment[inventory[itemIndex].bodypart].value = nil
- else
- local oldValue = charEquipment[inventory[itemIndex].bodypart].value
- if oldValue and oldValue.bodypart == "both" then
- charEquipment["swordhand"].value = nil
- charEquipment["offhand"].value = nil
- end
- charEquipment[inventory[itemIndex].bodypart].value = inventory[itemIndex]
- end
- end
- --Returns true or false depending on whether or not the item has been equipped
- function isEquipped(itemIndex)
- local selItem = inventory[itemIndex]
- if selItem.bodypart == "both" then
- return charEquipment["swordhand"].value == selItem
- else
- return charEquipment[selItem.bodypart].value == selItem
- end
- end
- --[[SECTION: Overworld, Display]]--
- --Prints the world map for the overworld
- function printWorldMap()
- local moffx = 1
- local moffy = 1
- if(pox > math.ceil(mapw / 2)) then moffx = pox - math.ceil(mapw/2) end
- if(pox > #overworld[1] - math.ceil(mapw / 2) + 1) then moffx = #overworld[1] - mapw end
- if(poy > math.ceil(maph / 2)) then moffy = poy - math.ceil(maph/2) end
- if(poy > #overworld - math.ceil(maph / 2) + 1) then moffy = #overworld - maph + 1 end
- --local mpx = mapw/2
- --local mpy = maph/2
- for y = moffy, moffy + maph -1 do
- term.setCursorPos(2, y - moffy + 3)
- local line = ""
- for x = moffx, moffx + mapw - 1 do
- line = line..overworld[y][x]
- end
- --term.write(string.sub(overworld[i + moffy - 1], moffx, mapw + moffx - 1))
- term.write(line)
- end
- --For every town within the world, it prints a "T", where it can be found
- for i = 1, #town do
- if town[i].visible and town[i].xpos >= moffx and town[i].xpos <= moffx + mapw - 1 and
- town[i].ypos >= moffy and town[i].ypos <= moffy + maph - 1 then
- term.setCursorPos(2 + town[i].xpos - moffx, 3 + town[i].ypos - moffy)
- term.write("T")
- end
- end
- --For every dungeon within the world, the numeral "0" is used (to indicate cave opening)
- for i = 1, #dungeons do
- if dungeons[i].visible and dungeons[i].xpos >= moffx and dungeons[i].xpos <= moffx + mapw - 1 and
- dungeons[i].ypos >= moffy and dungeons[i].ypos <= moffy + maph - 1 then
- term.setCursorPos(2 + dungeons[i].xpos - moffx, 3 + dungeons[i].ypos - moffy)
- term.write("0")
- end
- end
- --Monsters in the overworld are displayed with the first letter of their name.
- for i = 1, #overcreatures do
- term.setCursorPos(2 + overcreatures[i].xpos - moffx, 3 + overcreatures[i].ypos - moffy)
- term.write(string.sub(overcreatures[i].name, 1, 1))
- end
- term.setCursorPos(2 + pox - moffx - 1, 3 + poy - moffy - 1)
- term.write("@")
- end
- --This draws the "external" interface, the map frame, player stats and the history log
- function printOverworldInterface()
- term.clear()
- --Draws the frame
- term.setCursorPos(2,2)
- term.write(string.rep("_", mapw))
- term.setCursorPos(2,maph + 3)
- term.write(string.rep("-", mapw))
- for i=3,maph+2 do
- term.setCursorPos(1, i)
- term.write("("..string.rep(" ", mapw)..")")
- end
- --Draws the frame title
- printOffCenter(worldname, 1, mapw, 2)
- --Draws player information
- printOffCenter(charName, 2, (w - mapw - 5), mapw + 5)
- printOffCenter(string.rep("=", #charName), 3, (w - mapw - 5), mapw + 5)
- term.setCursorPos(mapw + 4, 5)
- term.write("Level "..charLevel.." "..charClass)
- term.setCursorPos(mapw + 4, 7)
- term.write("HP: "..charHealth.."/"..charMaxHealth)
- term.setCursorPos(mapw + 4, 8)
- term.write("XP: "..charXP.."/"..charNextXP)
- term.setCursorPos(mapw + 4, 9)
- term.write("Gold: "..charGold)
- term.setCursorPos(mapw + 4, 10)
- term.write(string.rep("-", w - (mapw + 4)))
- --Draws the event log
- local hoffset = 0
- for i=1,#overEventList do
- hoffset = hoffset + wprintLeft(overEventList[i], h - 9 + i + hoffset, w - (mapw + 4), mapw + 4) - 1
- end
- end
- --[[SECTION: Town, Display]]--
- --This draws the "external" interface, the map frame, player stats and the history log
- function printTownInterface()
- term.clear()
- --Draws the event log (this may/perhaps should? be eclipsed)
- local hoffset = 0
- for i=1,#townEventList do
- hoffset = hoffset + wprintLeft(townEventList[i], h - 9 + i + hoffset, w - (mapw + 4), mapw + 4) - 1
- end
- --Draws the frame
- term.setCursorPos(1,2)
- term.write("/"..string.rep("=", mapw).."\\")
- term.setCursorPos(1,maph + 3)
- term.write("\\"..string.rep("=", mapw).."/")
- --This draws the town itself
- for i=3,maph+2 do
- term.setCursorPos(1, i)
- term.write("|"..town[activeTown].level[i-2].."|")
- end
- --Draws each NPC within the town
- for i=1,#town[activeTown].npc do
- term.setCursorPos(town[activeTown].npc[i].xpos + 1, town[activeTown].npc[i].ypos + 2)
- term.write("&")
- end
- --Draws the frame title
- printOffCenter(town[activeTown].name, 1, mapw, 2)
- term.setCursorPos(1, 10)
- --Draws player information
- printOffCenter(charName, 2, (w - mapw - 5), mapw + 5)
- printOffCenter(string.rep("=", #charName), 3, (w - mapw - 5), mapw + 5)
- term.setCursorPos(mapw + 4, 5)
- term.write("Level "..charLevel.." "..charClass)
- term.setCursorPos(mapw + 4, 7)
- term.write("HP: "..charHealth.."/"..charMaxHealth)
- term.setCursorPos(mapw + 4, 8)
- term.write("XP: "..charXP.."/"..charNextXP)
- term.setCursorPos(mapw + 4, 9)
- term.write("Gold: "..charGold)
- term.setCursorPos(mapw + 4, 10)
- term.write(string.rep("-", w - (mapw + 4)))
- --Draws the player himself
- term.setCursorPos(1 + townpx, 2 + townpy)
- term.write("@")
- end
- --[[SECTION: Dungeon, Display]]--
- --This, of course, draws the corridor.
- function drawDungeonCorridor()
- term.clear()
- --Draws the corridor (the basis for every scene)
- for i=1,#cor do
- term.setCursorPos(corx + 2, cory + i - 1)
- term.write(cor[i])
- end
- --These point to coordinates left and right of the player
- local rx,ry = getRight(dunfx,dunfy)
- local lx,ly = getLeft(dunfx,dunfy)
- --[[Section: Two tiles away]]--
- --If there is a turn on the left or right of the forward tile, this draws the branch
- local turningtright = false
- if string.sub(dungeons[activeDungeon].level[dunlevel][dunpy + dunfy + ry], dunpx + dunfx + rx, dunpx + dunfx + rx) ~= " " then
- turningtright = true
- for i=1,#trightturn do
- term.setCursorPos(trightturnx, trightturny + i - 1)
- term.write(trightturn[i]);
- end
- end
- local turningtleft = false
- if string.sub(dungeons[activeDungeon].level[dunlevel][dunpy + dunfy + ly], dunpx + dunfx + lx, dunpx + dunfx + lx) ~= " " then
- turningtleft = true
- for i=1,#tleftturn do
- term.setCursorPos(tleftturnx, tleftturny + i - 1)
- term.write(tleftturn[i]);
- end
- end
- --Determines if there is a wall 2 tiles from the facing position- draws a wall ahead
- local twoendtile = string.sub(dungeons[activeDungeon].level[dunlevel][dunpy + dunfy + dunfy], dunpx + dunfx + dunfx, dunpx + dunfx + dunfx)
- if twoendtile == " " or twoendtile == "E" then
- for i=1,#twoend do
- term.setCursorPos(twoendx, twoendy + i - 1)
- if twoendtile == " " then term.write(twoend[i])
- else term.write(twoexitend[i]) end
- --If turning left or right, the wall needs to be slightly modified to remove the "extra" edge
- if turningtright then
- term.setCursorPos(trightturnx, trightturny + i - 1)
- if i == 1 then term.write("_")
- elseif i == #twoend then term.write("-")
- else term.write(" ") end
- end
- if turningtleft then
- term.setCursorPos(tleftturnx + #tleftturn[i] - 1, tleftturny + i - 1)
- if i == 1 then term.write("_")
- elseif i == #twoend then term.write("-")
- else term.write(" ") end
- end
- end
- end
- --Draws creatures two tiles away, if they're visible
- for i=1, #dungeons[activeDungeon].creatures do
- local c = dungeons[activeDungeon].creatures[i]
- if c.xpos == dunpx + dunfx + dunfx and c.ypos == dunpy + dunfy + dunfy and c.levelpos == dunlevel and
- string.sub(dungeons[activeDungeon].level[dunlevel][dunpy+dunfy], dunpx+dunfx, dunpx+dunfx) ~= " " then
- for j=1,#c.imagetwo do
- term.setCursorPos(23/2 - #c.imagetwo[j]/2 + 1, 12 - (#c.imagetwo - j))
- term.write(c.imagetwo[j])
- end
- end
- end
- --[[Section: On the immediate tile]]--
- --If there is a left or right turn on the current tile, this draws the branch
- local turningright = false
- if string.sub(dungeons[activeDungeon].level[dunlevel][dunpy + ry], dunpx + rx, dunpx + rx) ~= " " then
- turningright = true
- for i=1,#rightturn do
- term.setCursorPos(rightturnx, rightturny + i - 1)
- term.write(rightturn[i])
- end
- end
- local turningleft = false
- if string.sub(dungeons[activeDungeon].level[dunlevel][dunpy + ly], dunpx + lx, dunpx + lx) ~= " " then
- turningleft = true
- for i=1,#leftturn do
- term.setCursorPos(leftturnx, leftturny + i - 1)
- term.write(leftturn[i])
- end
- end
- --Determines if there is a wall immediately facing the player- draws the wall ahead
- local oneendtile = string.sub(dungeons[activeDungeon].level[dunlevel][dunpy + dunfy], dunpx + dunfx, dunpx + dunfx)
- if oneendtile == " " or oneendtile == "E" then
- for i=1,#oneend do
- term.setCursorPos(oneendx, oneendy + i - 1)
- if oneendtile == " " then term.write(oneend[i])
- else term.write(oneexitend[i]) end
- --If turning left or right, the wall needs to be slightly modified to remove the "extra" edge
- if turningright then
- term.setCursorPos(rightturnx, rightturny + i + 1)
- if i == 1 or i == #oneend then term.write("_")
- else term.write(" ") end
- end
- if turningleft then
- term.setCursorPos(leftturnx + #leftturn[i] - 1, leftturny + i + 1)
- if i == 1 or i == #oneend then term.write("_")
- else term.write(" ") end
- end
- end
- end
- --If the player has their back to a wall, the "front corners" of the corridor need to be erased
- if turningleft and string.sub(dungeons[activeDungeon].level[dunlevel][dunpy - dunfy], dunpx - dunfx, dunpx - dunfx) == " " then
- for i=1,#bwturn do
- term.setCursorPos(bwleftturnx, bwturny + i - 1)
- term.write(bwturn[i])
- end
- end if turningright and string.sub(dungeons[activeDungeon].level[dunlevel][dunpy - dunfy], dunpx - dunfx, dunpx - dunfx) == " " then
- for i=1,#bwturn do
- term.setCursorPos(bwrightturnx, bwturny + i - 1)
- term.write(bwturn[i])
- end
- end
- --[[Section: Physical objects]]--
- --This draws creatures on the immediate tile
- for i = 1, #dungeons[activeDungeon].creatures do
- local c = dungeons[activeDungeon].creatures[i]
- if c.xpos == dunpx + dunfx and c.ypos == dunpy + dunfy and c.levelpos == dunlevel then
- for j=1,#c.imageone do
- term.setCursorPos(23/2 - #c.imageone[j]/2 + 1, 15 - (#c.imageone - j))
- term.write(c.imageone[j])
- end
- end
- end
- --Ladders- Up, Down and Both, and chests Open and Closed
- if string.sub(dungeons[activeDungeon].level[dunlevel][dunpy], dunpx, dunpx) == "D" then
- for i=1,#ladderd do
- term.setCursorPos(ladderdx, ladderdy + i - 1)
- term.write(ladderd[i])
- end
- elseif string.sub(dungeons[activeDungeon].level[dunlevel][dunpy], dunpx, dunpx) == "U" then
- for i=1,#ladderu do
- term.setCursorPos(ladderux, ladderuy + i - 1)
- term.write(ladderu[i])
- end
- elseif string.sub(dungeons[activeDungeon].level[dunlevel][dunpy], dunpx, dunpx) == "B" then
- for i=1,#ladderud do
- term.setCursorPos(ladderudx, ladderudy + i - 1)
- term.write(ladderud[i])
- end
- elseif string.sub(dungeons[activeDungeon].level[dunlevel][dunpy], dunpx, dunpx) == "C" then
- for i=1,#chestc do
- term.setCursorPos(chestcx, chestcy + i - 1)
- term.write(chestc[i])
- end
- elseif string.sub(dungeons[activeDungeon].level[dunlevel][dunpy], dunpx, dunpx) == "O" then
- for i=1,#chesto do
- term.setCursorPos(chestox, chestoy + i - 1)
- term.write(chesto[i])
- end
- end
- end
- --This draws the side interface
- function drawDungeonInterface()
- --Draws a simple border on the side of the screen
- for i=1,h do
- term.setCursorPos(24, i)
- term.write("#")
- end
- printOffCenter(dungeons[activeDungeon].name, 1, w-25, 25)
- printOffCenter(string.rep("*", #dungeons[activeDungeon].name), 2, w-25, 25)
- if dungeons[activeDungeon].playerHasCompass then drawCompass(26,2) end
- term.setCursorPos(38, 4)
- term.write("Level "..charLevel)
- term.setCursorPos(38, 5)
- term.write("HP: "..charHealth.."/"..charMaxHealth)
- term.setCursorPos(38, 6)
- term.write("XP: "..charXP.."/"..charNextXP)
- term.setCursorPos(38, 7)
- term.write("Gold: "..charGold)
- term.setCursorPos(25, 9)
- term.write(string.rep("#",w-25))
- --Draws an altimeter (for lack of a better word)
- for i=1,#dungeons[activeDungeon].level do
- term.setCursorPos(33, i + 3)
- if dunlevel == i then term.write("["..i.."]")
- else term.write(" "..i.." ") end
- end
- --Draws the event log
- local hoffset = 0
- for i=1,#dungeonEventList do
- hoffset = hoffset + wprintLeft(dungeonEventList[i], h - 10 + i + hoffset, w - 25, 25) - 1
- end
- end
- --Uses facing and nearby enemies to draw a simple compass
- function drawCompass(x,y)
- for i=1,#compass do
- term.setCursorPos(x, y + i)
- term.write(compass[i])
- end
- if dunfx == -1 then
- term.setCursorPos(x + 1, y + 3)
- term.write("__")
- elseif dunfx == 1 then
- term.setCursorPos(x + 4, y + 3)
- term.write("__")
- elseif dunfy == -1 then
- term.setCursorPos(x + 3, y + 2)
- term.write("|")
- term.setCursorPos(x + 3, y + 3)
- term.write("|")
- elseif dunfy == 1 then
- term.setCursorPos(x + 3, y + 4)
- term.write("|")
- term.setCursorPos(x + 3, y + 5)
- term.write("|")
- end
- --If an enemy is nearby, within 1 or 2 squares (and visible to the player, not behind walls)
- --then the compass will draw a x or a X to indicate danger in that direction.
- local nn = 0
- local ns = 0
- local ne = 0
- local nw = 0
- for i=1,#dungeons[activeDungeon].creatures do
- local c = dungeons[activeDungeon].creatures[i]
- if c.levelpos == dunlevel then
- if dunpx - c.xpos == 1 and dunpy - c.ypos == 0 then
- ne = 1
- term.setCursorPos(x, y + 3)
- term.write("X")
- elseif ne ~= 1 and dunpx - c.xpos == 2 and dunpy - c.ypos == 0 then
- term.setCursorPos(x, y + 3)
- term.write("x")
- end
- if dunpx - c.xpos == -1 and dunpy - c.ypos == 0 then
- nw = 1
- term.setCursorPos(x + 6, y + 3)
- term.write("X")
- elseif nw ~= 1 and dunpx - c.xpos == -2 and dunpy - c.ypos == 0 then
- term.setCursorPos(x + 6, y + 3)
- term.write("x")
- end
- if dunpy - c.ypos == 1 and dunpx - c.xpos == 0 then
- nn = 1
- term.setCursorPos(x + 3, y + 1)
- term.write("X")
- elseif nn ~= 1 and dunpy - c.ypos == 2 and dunpx - c.xpos == 0 then
- term.setCursorPos(x + 3, y + 1)
- term.write("x")
- end
- if dunpy - c.ypos == -1 and dunpx - c.xpos == 0 then
- ns = 1
- term.setCursorPos(x + 3, y + 5)
- term.write("X")
- elseif ns ~= 1 and dunpy - c.ypos == -2 and dunpx - c.xpos == 0 then
- term.setCursorPos(x + 3, y + 5)
- term.write("x")
- end
- end
- end
- end
- --This draws a dungeon map, with the player, his orientation and the location of all enemies
- function drawDungeonMap()
- term.setCursorPos(3, 3)
- term.write("*"..string.rep("-", #dungeons[activeDungeon].level[dunlevel][1]).."*")
- term.setCursorPos(3, 3 + #dungeons[activeDungeon].level[dunlevel] + 1)
- term.write("*"..string.rep("-", #dungeons[activeDungeon].level[dunlevel][1]).."*")
- for i=1,#dungeons[activeDungeon].level[dunlevel] do
- local str = dungeons[activeDungeon].level[dunlevel][i]
- str = string.gsub(str, "C", "#")
- str = string.gsub(str, "O", "C")
- term.setCursorPos(3, 3 + i)
- term.write("|"..str.."|")
- end
- --[[
- for i=1,#dungeons[activeDungeon].creatures do
- local crea = dungeons[activeDungeon].creatures[i]
- if crea.levelpos == dunlevel then
- term.setCursorPos(3 + crea.xpos, 3 + crea.ypos)
- term.write(string.sub(crea.name, 1, 1))
- end
- end]]--
- term.setCursorPos(3 + dunpx, 3 + dunpy)
- term.write("@")
- end
- --[[SECTION: Overword, Input]]--
- --A simple text input reader, this is more powerful than "io.read" as it doesn't blank the line it's working
- --on, it has a numerical limit and it trims whitespace. Non-sensible or null lims are set to the end of the screen.
- function readInput(lim)
- sleep(0.1)
- term.setCursorBlink(true)
- local inputString = ""
- local ox,oy = term.getCursorPos()
- term.write(" ")
- term.setCursorPos(ox, oy)
- if not lim or type(lim) ~= "number" or lim < 1 then lim = w - ox end
- while true do
- local id,key = os.pullEvent()
- if id == "key" and key == 14 and #inputString > 0 then
- inputString = string.sub(inputString, 1, #inputString-1)
- term.setCursorPos(ox + #inputString,oy)
- term.write(" ")
- elseif id == "key" and key == 28 and inputString ~= string.rep(" ", #inputString) then
- break
- elseif id == "char" and #inputString < lim then
- inputString = inputString..key
- end
- term.setCursorPos(ox,oy)
- term.write(inputString)
- term.setCursorPos(ox + #inputString, oy)
- end
- while string.sub(inputString, 1, 1) == " " do
- inputString = string.sub(inputString, 2, #inputString)
- end
- while string.sub(inputString, #inputString, #inputString) == " " do
- inputString = string.sub(inputString, 1, #inputString-1)
- end
- term.setCursorBlink(false)
- return inputString
- end
- --Handles all input from the overworld
- function handleOverworldInput()
- while true do
- local id,key = os.pullEvent("key")
- local desX = pox
- local desY = poy
- if key == keys.left then desX = pox - 1
- elseif key == keys.right then desX = pox + 1
- elseif key == keys.up then desY = poy - 1
- elseif key == keys.down then desY = poy + 1
- elseif key == keys.i then runInventoryInterface() return
- elseif key == keys.j then runJournalInterface() return
- elseif key == keys.space then break
- elseif key == keys.enter then
- gameOver = true
- break
- end
- --In the event the player has come upon a town
- for i=1,#town do
- if town[i].visible and town[i].xpos == desX - 1 and town[i].ypos == desY - 1 then
- activeTown = i
- inTown = true
- townpx = town[i].startx
- townpy = town[i].starty
- while #overEventList > 0 do table.remove(overEventList, 1) end
- addEvent("Healing...")
- charHealth = charMaxHealth
- qUpdate = {
- type = "move";
- town = activeTown;
- xpos,ypos = townpx,townpy;
- }
- qUpdate = checkQuestUpdate(qUpdate)
- if qUpdate then displayConfirmDialogue("Quest Update", qUpdate) end
- return
- end
- end
- for i=1,#dungeons do
- if dungeons[i].visible and dungeons[i].xpos == desX - 1 and dungeons[i].ypos == desY - 1 then
- activeDungeon = i
- inDungeon = true
- dunpx = dungeons[i].startx
- dunpy = dungeons[i].starty
- dunlevel = dungeons[i].startlevel
- dunfx = dungeons[i].startfx
- dunfy = dungeons[i].startfy
- clearEvents()
- qUpdate = {
- type = "move";
- dungeon = activeDungeon;
- xpos,ypox,zpos = dunpx,dunpy,dunlevel;
- }
- qUpdate = checkQuestUpdate(qUpdate)
- if qUpdate then displayConfirmDialogue("Quest Update", qUpdate) end
- return
- end
- end
- if desX ~= pox or desY ~= poy then
- --We attack creatures with our chosen weapon by "walking into them", for simplicity's sake
- for i=1,#overcreatures do
- if overcreatures[i].xpos + 1 == desX and overcreatures[i].ypos + 1 == desY then
- if charEquipment["swordhand"].value ~= nil then
- local damagedone = calculateDamage(charEquipment["swordhand"].value, true)
- addEvent("You strike for "..damagedone)
- overcreatures[i].hit = overcreatures[i].hit - damagedone
- if overcreatures[i].hit <= 0 then
- addEvent(overcreatures[i].name.." has been slain")
- term.setCursorPos(1,1)
- addXP(overcreatures[i].xp)
- addEvent("You earned "..overcreatures[i].xp.."XP")
- local qUpdate = {
- type = "kill";
- monster = overcreatures[i];
- }
- table.remove(overcreatures, i)
- qUpdate = checkQuestUpdate(qUpdate)
- if qUpdate then displayConfirmDialogue("Quest Update", qUpdate) end
- end
- else
- addEvent("You have no weapon!")
- end
- return
- end
- end
- if overworld[desY - 1][desX - 1] == " " then
- pox, poy = desX, desY
- local qUpdate = {
- type = "move";
- xpos,ypos = pox,poy
- }
- qUpdate = checkQuestUpdate(qUpdate)
- if qUpdate then displayConfirmDialogue("Quest Update", qUpdate) end
- break
- end
- end
- end
- end
- --[[SECTION: Overworld, Helper]]--
- --Handles state and monster updates from the overworld
- function updateOverworld()
- for i = 1, #overcreatures do
- if not overcreatures[i] then
- table.remove(overcreatures, i)
- i = i+1
- else
- local desx = pox - overcreatures[i].xpos - 1
- local desy = poy - overcreatures[i].ypos - 1
- local ndesx = desx/math.abs(desx)
- local ndesy = desy/math.abs(desy)
- if desx == 0 then ndesx = 0 end
- if desy == 0 then ndesy = 0 end
- if math.abs(desx) + math.abs(desy) == 1 then
- local damagedone = calculateDamage(overcreatures[i].weapon, false)
- addEvent(overcreatures[i].name.." strikes for "..damagedone)
- charHealth = charHealth - damagedone
- overcreatures[i].chaseturns = 0
- overcreatures[i].stillturns = 0
- if charHealth <= 0 then
- displayConfirmDialogue("RIP!", "You were slain by a "..overcreatures[i].name.."! Better luck next time")
- gameOver = true
- end
- else
- if debugging then
- writeToLog("index "..i, " X:", overcreatures[i].xpos, " Y:", overcreatures[i].ypos, " Desx:", ndesx, " Desy", ndesy)
- end
- overcreatures[i].chaseturns = overcreatures[i].chaseturns + 1
- if ndesx ~= 0 and math.abs(desx) - math.abs(desy) >= 0 and overworld[overcreatures[i].ypos][overcreatures[i].xpos + ndesx] == " " then
- --We check for other monsters too
- local obstructed = false
- for j=1,#overcreatures do
- local oc = overcreatures[j]
- if i ~= j and overcreatures[i].xpos + ndesx == oc.xpos and overcreatures[i].ypos == oc.ypos then
- obstructed = true
- end
- end
- if not obstructed then
- overcreatures[i].xpos = overcreatures[i].xpos + ndesx
- end
- elseif ndesy ~= 0 and overworld[overcreatures[i].ypos][overcreatures[i].xpos + ndesx] == " " then
- --We check for other monsters too
- local obstructed = false
- for j=1,#overcreatures do
- local oc = overcreatures[j]
- if i ~= j and overcreatures[i].xpos == oc.xpos and overcreatures[i].ypos + ndesy == oc.ypos then
- obstructed = true
- end
- end
- if not obstructed then
- overcreatures[i].ypos = overcreatures[i].ypos + ndesy
- end
- else
- overcreatures[i].stillturns = overcreatures[i].stillturns + 1
- end
- end
- if overcreatures[i].chaseturns > 10 or overcreatures[i].stillturns > 3 then
- addEvent(overcreatures[i].name.." gave up")
- table.remove(overcreatures, i)
- i = i - 1
- end
- end
- end
- --We've used a fixed value here of 1 in 30 chance
- if math.random(1,30) == 1 then
- addCreatureToWorldMap()
- end
- end
- --[[SECTION: Dungeon, Helper]]--
- --Gets the tile at the currently active dungeon at the specified x, y and level
- function dungeonTileAt(x,y,lev)
- return string.sub(dungeons[activeDungeon].level[lev][y], x, x)
- end
- --[[SECTION: Logging, Helper]]--
- --Adds an event to the current event list- over event, town event or dungeon event
- function addEvent(msg)
- if inTown then
- if debugging then
- writeToLog("TownEvent:", msg)
- end
- table.insert(townEventList, 1, msg)
- if #townEventList > 7 then
- table.remove(townEventList, #townEventList)
- end
- elseif inDungeon then
- if debugging then
- writeToLog("DungeonEvent:", msg)
- end
- table.insert(dungeonEventList, 1, msg)
- if #dungeonEventList > 9 then
- table.remove(dungeonEventList, #dungeonEventList)
- end
- else
- if debugging then
- writeToLog("OverEvent:", msg)
- end
- table.insert(overEventList, 1, msg)
- if #overEventList > 7 then
- table.remove(overEventList, #overEventList)
- end
- end
- end
- --Clears all events the active event log (should be called when changing between modes)
- function clearEvents()
- if inTown then
- while #townEventList > 0 do table.remove(townEventList, 1) end
- elseif inDungeon then
- while #dungeonEventList > 0 do table.remove(dungeonEventList, 1) end
- else
- while #overEventList > 0 do table.remove(overEventList, 1) end
- end
- end
- --[[SECTION: Town, Input]]--
- --Handles key events and all actions that occur in town
- function handleTownInput(key)
- --Contextually a space bar movement is important- it handles input across town
- if key == 57 then
- --Most importantly, it's used for dialog
- for i=1,#town[activeTown].npc do
- local currnpc = town[activeTown].npc[i]
- for d=1,#directions do
- if currnpc.xpos + directions[d].x == townpx and currnpc.ypos + directions[d].y == townpy then
- converseWith(currnpc)
- break
- end
- end
- end
- return
- end
- if key == keys.i then
- runInventoryInterface()
- return
- end
- if key==keys.j then
- runJournalInterface()
- return
- end
- --In all other cases, input handled manages movement.
- local desX = townpx
- local desY = townpy
- local posmove = true
- if key == 203 then desX = townpx - 1
- elseif key == 205 then desX = townpx + 1
- elseif key == 200 then desY = townpy - 1
- elseif key == 208 then desY = townpy + 1
- else posmove = false end
- for i=1,#town[activeTown].npc do
- if town[activeTown].npc[i].xpos == desX and town[activeTown].npc[i].ypos == desY
- --Tiny optimizer
- or not posmove then
- posmove = false
- break
- end
- end
- if posmove and not(desX < 1 or desX > mapw or desY < 1 or desY > maph) and string.sub(town[activeTown].level[desY], desX, desX) == " " then
- townpx, townpy = desX, desY
- --We check the quests in the event of a successful move
- if townpx == town[activeTown].startx and townpy == town[activeTown].starty then
- inTown = false
- activeTown = 0
- while #townEventList > 0 do table.remove(townEventList, 1) end
- local qUpdate = {
- type = "move";
- xpos,ypos = pox,poy
- }
- qUpdate = checkQuestUpdate(qUpdate)
- if qUpdate then displayConfirmDialogue("Quest Update", qUpdate) end
- else
- local qUpdate = {
- type = "move";
- town = activeTown;
- xpos,ypos = townpx,townpy
- }
- qUpdate = checkQuestUpdate(qUpdate)
- if qUpdate then displayConfirmDialogue("Quest Update", qUpdate) end
- end
- end
- end
- --Handles the display and all input for when an NPC performs a dialog. This function may encompass
- --more than one, depending on how complex they get
- --NOTE: This method is now DEPRECATED. "Converse With" is now the updated method.
- function displayDialog(locnpc)
- local msg = locnpc.dialogue
- activeDialog = locnpc.name
- --DEPRECATED- no longer functional
- --if checkQuestUpdate(false) then return end
- --If they're a quest giver, they say their piece
- if locnpc.quest ~= nil then
- local lquest = quests[locnpc.quest]
- lquest.activestage = 1
- msg = lquest.acceptMessage
- table.insert(activeQuests, lquest)
- locnpc.quest = nil
- end
- --If they're a merchant they you can purchase some items
- local toTrade = false
- if not msg then
- if stocktrade[locnpc.job] then
- if locnpc.job == "merchant" and #inventory == 0 or ((locnpc.job == "smith" or locnpc.job == "mystic") and
- #locnpc.inventory == 0) then
- msg = stocksoldout[locnpc.job][#stocksoldout[locnpc.job]]
- else
- toTrade = true
- msg = stocktrade[locnpc.job][#stocktrade[locnpc.job]]
- end
- else
- msg = stockmessages[math.random(1, #stockmessages)]
- end
- end
- msg = "\""..msg.."\""
- local ctitle = locnpc.name.." the "..locnpc.job.." says:"
- displayConfirmDialogue(ctitle, msg)
- if toTrade then runTradeInterface(locnpc) end
- --This needs to be reset (so as not to confuse other quest objectives)
- activeDialog = nil
- end
- --This is a typical function used to display any message, with a title and dialog. It can be used for NPC
- --dialogues, notifications of quest updates and other things. The title is displayed at the top, the message
- --in the center and the aftermessage (optional, like "type a key to continue") near the bottom.
- function displayConfirmDialogue(ctitle, msg)
- local dialogoffset = 5
- --We actually print twice- once to get the lines, second time to print proper. Easier this way.
- local lines = wprintOffCenter(msg, 6, w - (dialogoffset+2) * 2, dialogoffset + 2)
- --This cluster of statements prints a nice box with the message the NPC has for the player in it.
- term.setCursorPos(dialogoffset, 3)
- term.write(string.rep("*", w - dialogoffset * 2))
- term.setCursorPos(dialogoffset, 4)
- term.write("* "..ctitle..string.rep(" ", w - (dialogoffset) * 2 - #ctitle - 3).."*")
- for i=5,6+lines do
- term.setCursorPos(dialogoffset, i)
- term.write("*"..string.rep(" ", w - (dialogoffset) * 2 - 2).."*")
- end
- term.setCursorPos(dialogoffset, 6 + lines + 1)
- term.write(string.rep("*", w - (dialogoffset) * 2))
- wprintOffCenter(msg, 6, w - (dialogoffset+2) * 2, dialogoffset + 2)
- --In the event of a message, the player hits anything to continue
- os.pullEvent("key")
- end
- --This dialogue is much the same as a confirm but offers explicit options rather than requiring manual input.
- --The options are simply fit on the one line and designed to be short (yes/no sort of things). Returns the option
- --chosen. A yes/no option set is default for empty or null lists.
- function displayOptionDialogue(ctitle, msg, options)
- if not options or type(options) ~= "table" or #options == 0 then
- options = {"yes", "no"}
- end
- local dialogoffset = 5
- --We actually print twice- once to get the lines, second time to print proper. Easier this way.
- local lines = wprintOffCenter(msg, 6, w - (dialogoffset+2) * 2, dialogoffset + 2)
- --This cluster of statements prints a nice box with the message the NPC has for the player in it.
- term.setCursorPos(dialogoffset, 3)
- term.write(string.rep("*", w - dialogoffset * 2))
- term.setCursorPos(dialogoffset, 4)
- term.write("* "..ctitle..string.rep(" ", w - (dialogoffset) * 2 - #ctitle - 3).."*")
- for i=5,8+lines do
- term.setCursorPos(dialogoffset, i)
- term.write("*"..string.rep(" ", w - (dialogoffset) * 2 - 2).."*")
- end
- term.setCursorPos(dialogoffset, 8 + lines + 1)
- term.write(string.rep("*", w - (dialogoffset) * 2))
- wprintOffCenter(msg, 6, w - (dialogoffset+2) * 2, dialogoffset + 2)
- --Next we display our options- this is done regularly
- local selection = 1
- while true do
- local optoffset = 0
- for i=1,#options do
- term.setCursorPos(dialogoffset + optoffset + 1, 8 + lines)
- if selection==i then
- term.write("["..options[i].."]")
- else
- term.write(" "..options[i].." ")
- end
- optoffset = optoffset + 3 + #options[i]
- end
- local _,key = os.pullEvent("key")
- if key == keys.left and selection > 1 then selection = selection-1
- elseif key == keys.right and selection < #options then selection = selection+1
- elseif key == keys.enter then return options[selection] end
- end
- end
- --This displays an input dialogue- it's essentially identical to the "displayConfirmDialogue" but rather than simply
- --displaying a message and an keypress ending it, instead it requests a text input to terminate. This returns the
- --input the user has given.
- function displayInputDialogue(ctitle, msg, prompt)
- if not prompt then prompt = ">" end
- local dialogoffset = 5
- --We actually print twice- once to get the lines, second time to print proper. Easier this way.
- local lines = wprintOffCenter(msg, 6, w - (dialogoffset+2) * 2, dialogoffset + 2)
- --This cluster of statements prints a nice box with the message the NPC has for the player in it.
- term.setCursorPos(dialogoffset, 3)
- term.write(string.rep("*", w - dialogoffset * 2))
- term.setCursorPos(dialogoffset, 4)
- term.write("* "..ctitle..string.rep(" ", w - (dialogoffset) * 2 - #ctitle - 3).."*")
- for i=5,8+lines do
- term.setCursorPos(dialogoffset, i)
- term.write("*"..string.rep(" ", w - (dialogoffset) * 2 - 2).."*")
- end
- term.setCursorPos(dialogoffset, 8 + lines + 1)
- term.write(string.rep("*", w - (dialogoffset) * 2))
- wprintOffCenter(msg, 6, w - (dialogoffset+2) * 2, dialogoffset + 2)
- term.setCursorPos(dialogoffset + 1, 8 + lines)
- term.write(" "..prompt)
- --We get our input here
- local input = readInput(#prompt + (w - (dialogoffset+2) * 2 + 1))
- return input
- end
- --This allows the user to have a conversation with an NPC
- function converseWith(locnpc)
- local input = ""
- if locnpc.greeted then
- input = "ongreet"
- else
- locnpc.greeted = true
- input = "onfirstgreet"
- end
- local dialogue = nil
- while true do
- local qUpdate = {
- type = "dialogue";
- town = activeTown;
- npc = locnpc;
- topic = input;
- }
- local qUpdate = checkQuestUpdate(qUpdate)
- dialogue = qUpdate
- if not dialogue then
- dialogue = locnpc.dialogue["#"..input]
- end
- if not dialogue and locnpc.useGeneral and inTown then
- dialogue = town[activeTown].dialogue["#"..input]
- end
- if not dialogue then
- dialogue = globalDialogue["#"..input]
- end
- if not dialogue then
- dialogue = locnpc.nodialogue
- end
- --This really, should never happen. Be careful with your scripting guys.
- if not dialogue then dialogue = "I don't have an answer to your question." end
- if input=="onfarewell" then break end
- if type(dialogue) == "function" then
- dialogue = dialogue(locnpc)
- if not dialogue then break end
- end
- if type(dialogue) == "table" then
- local locd = nil
- for i=1,#dialogue-1 do
- locd = replaceDialogueTags(dialogue[i])
- if inTown then printTownInterface() end
- displayConfirmDialogue(locnpc.name.." the "..locnpc.job.." says:", dialogue)
- end
- locd = replaceDialogueTags(dialogue[#dialogue])
- if inTown then printTownInterface() end
- input = string.lower(displayInputDialogue(locnpc.name.." the "..locnpc.job.." says:", dialogue, "Ask>"))
- elseif string.sub(dialogue, 1, 1) == "#" then input = string.sub(dialogue, 2, #dialogue)
- else
- printTownInterface()
- if inTown then dialogue = replaceDialogueTags(dialogue) end
- input = string.lower(displayInputDialogue(locnpc.name.." the "..locnpc.job.." says:", dialogue, "Ask>"))
- end
- end
- if inTown then printTownInterface() end
- if dialogue then
- dialogue = replaceDialogueTags(dialogue)
- displayConfirmDialogue(locnpc.name.." the "..locnpc.job.." says:", dialogue)
- end
- end
- --Takes the tags from a conversation and replaces them with user attributes
- function replaceDialogueTags(dialogue)
- local findex = string.find(dialogue, "#name")
- while findex do
- dialogue = string.sub(dialogue, 1, findex-1)..getCharName()..string.sub(dialogue, findex+5, #dialogue)
- findex = string.find(dialogue, "#name")
- end
- findex = string.find(dialogue, "#class")
- while findex do
- dialogue = string.sub(dialogue, 1, findex-1)..charClass..string.sub(dialogue, findex+6, #dialogue)
- findex = string.find(dialogue, "#class")
- end
- return dialogue
- end
- --[[SECTION: Dungeon, Input]]--
- --Replaces a tile in a dungeon with a new tile- used for removing chests, secret doors, etc.
- function subDungeonTile(index, x, y, level, char)
- dungeons[index].level[level][y] = string.sub(dungeons[index].level[level][y], 1, x - 1)..char..
- string.sub(dungeons[index].level[level][y], x + 1, #dungeons[index].level[level][y])
- end
- --This moves the player according to the key specified
- function handleDungeonInput(key)
- local currtile = string.sub(dungeons[activeDungeon].level[dunlevel][dunpy], dunpx, dunpx)
- if key==keys.m then
- if dungeons[activeDungeon].playerHasMap then
- drawDungeonMap()
- os.pullEvent("key")
- else
- addEvent("You don't have a dungeon map!")
- end
- end
- if key==keys.i then
- runInventoryInterface()
- end
- if key==keys.j then
- runJournalInterface()
- end
- if key==keys.up and string.sub(dungeons[activeDungeon].level[dunlevel][dunpy + dunfy], dunpx + dunfx, dunpx + dunfx) ~= " " then
- --If a creature occupies the desired space, combat occurs
- for i = 1, #dungeons[activeDungeon].creatures do
- local c = dungeons[activeDungeon].creatures[i]
- if c.xpos == dunpx + dunfx and c.ypos == dunpy + dunfy and c.levelpos == dunlevel then
- if charEquipment["swordhand"].value ~= nil then
- local damagedone = calculateDamage(charEquipment["swordhand"].value, true)
- addEvent("You strike for "..damagedone)
- c.hit = c.hit - damagedone
- if c.hit <= 0 then
- addEvent(c.name.." has been slain!")
- addXP(c.xp)
- addEvent("You earned "..c.xp.."XP")
- local qUpdate = {
- type = "kill";
- dungeon = activeDungeon;
- monster = dungeons[activeDungeon].creatures[i]
- }
- qUpdate = checkQuestUpdate(qUpdate)
- if qUpdate then displayConfirmDialogue("Quest Update:", qUpdate) end
- table.remove(dungeons[activeDungeon].creatures, i)
- end
- else
- addEvent("You have no weapon!")
- end
- return
- end
- end
- --Otherwise, the player simply moves forward (how much easier is that!)
- dunpx = dunpx + dunfx
- dunpy = dunpy + dunfy
- --Quest updates are checked on movement (direction doesn't matter)
- local qUpdate = {
- type = "move";
- dungeon = activeDungeon;
- xpos = dunpx;
- ypos = dunpy;
- zpos = dunlevel;
- }
- qUpdate = checkQuestUpdate(qUpdate)
- if qUpdate then displayConfirmDialogue("Quest Update: ", qUpdate) end
- elseif key==keys.left then
- dunfx, dunfy = getLeft(dunfx, dunfy)
- elseif key==keys.right then
- dunfx, dunfy = getRight(dunfx, dunfy)
- elseif key==keys.down then
- dunfx = dunfx * -1
- dunfy = dunfy * -1
- elseif key==keys.pageUp and (currtile == "U" or currtile == "B") then
- dunlevel = dunlevel - 1
- local qUpdate = {
- type = "move";
- dungeon = activeDungeon;
- xpos = dunpx;
- ypos = dunpy;
- zpos = dunlevel;
- }
- qUpdate = checkQuestUpdate(qUpdate)
- if qUpdate then displayConfirmDialogue("Quest Update: ", qUpdate) end
- elseif key==keys.pageDown and (currtile == "D" or currtile == "B") then
- dunlevel = dunlevel + 1
- local qUpdate = {
- type = "move";
- dungeon = activeDungeon;
- xpos = dunpx;
- ypos = dunpy;
- zpos = dunlevel;
- }
- qUpdate = checkQuestUpdate(qUpdate)
- if qUpdate then displayConfirmDialogue("Quest Update: ", qUpdate) end
- --The player opens a chest
- elseif key==keys.space and currtile == "C" then
- subDungeonTile(activeDungeon, dunpx, dunpy, dunlevel, "O")
- --It is possible to have scripted loot, given by a quest...
- local qUpdate = {
- type = "chest";
- dungeon = activeDungeon;
- xpos = dunpx;
- ypos = dunpy;
- zpos = dunlevel;
- }
- qUpdate = checkQuestUpdate(qUpdate)
- if qUpdate then
- displayConfirmDialogue("Quest Update: ", qUpdate)
- else
- --Or scripted loot given by the dungeon
- local cscriptFound = false
- for i,v in ipairs(dungeons[activeDungeon].chests) do
- if v.xpos == dunpx and v.ypos == dunpy and v.zpos == dunlevel then
- cscriptFound = true
- if type(v.content == "string") then
- if v.content == "item" then
- local newItem = generateLoot(charLevel)
- table.insert(inventory, newItem)
- addEvent("Chest contained a "..newItem.name)
- elseif v.content == "map" then
- dungeons[activeDungeon].playerHasMap = true
- addEvent("Chest contained a dungeon map!")
- elseif v.content == "compass" then
- dungeons[activeDungeon].playerHasCompass = true
- addEvent("Chest contained a compass!")
- else
- cscriptFound = false
- end
- elseif type(v.content == "table") then
- table.insert(inventory, v.content)
- addEvent("Chest contained a "..v.content.name)
- else
- charGold = charGold + v.content
- addEvent("Chest contained "..v.content.." gold")
- end
- break
- end
- end
- --Or just some random amount of gold.
- if not cscriptFound then
- local basegold = math.pow(2, charLevel + 1)
- basegold = math.random(basegold - basegold/2, basegold + basegold/2)
- addEvent("Chest contained "..basegold.." gold!")
- charGold = charGold + basegold
- end
- end
- end
- if string.sub(dungeons[activeDungeon].level[dunlevel][dunpy], dunpx, dunpx) == "E" then
- inDungeon = false
- activeDungeon = 0
- while #dungeonEventList > 0 do table.remove(dungeonEventList, 1) end
- local qUpdate = {
- type = "move";
- xpos = pox;
- ypos = poy;
- }
- qUpdate = checkQuestUpdate(qUpdate)
- if qUpdate then displayConfirmDialogue(qUpdate) end
- end
- end
- --This updates the dungeon
- function updateDungeon()
- --This moves, and if necessary, causes creaturse to attack
- for i=1,#dungeons[activeDungeon].creatures do
- local c = dungeons[activeDungeon].creatures[i]
- --Ensuring the creature is close enough to need to act- just out of viewing distance, but this can be changed later
- --(maybe make it 3 or 4 for a more difficult game?
- if c.levelpos == dunlevel and math.abs(c.xpos - dunpx) + math.abs(c.ypos - dunpy) <= 3 then
- local desx = dunpx - c.xpos
- local desy = dunpy - c.ypos
- local ndesx = desx/math.abs(desx)
- local ndesy = desy/math.abs(desy)
- if desx == 0 then ndesx = 0 end
- if desy == 0 then ndesy = 0 end
- if math.abs(desx) + math.abs(desy) == 1 then
- local toBlock = math.random(1,100)
- if charEquipment["offhand"] and charEquipment["offhand"].class == "shield" and toBlock <=
- charEquipment["offhand"].base * 100 then
- addEvent("You block strike by "..c.name.."!")
- else
- local damagedone = calculateDamage(c.weapon, false)
- addEvent(c.name.." strikes for "..damagedone)
- charHealth = charHealth - damagedone
- if charHealth <= 0 then
- displayConfirmDialogue("RIP!", "You were slain by a "..c.name.."! Better luck next time")
- gameOver = true
- end
- end
- else
- --It should be noted monsters can't follow you up and down ladders (for obvious reasons!)
- if ndesx ~= 0 and math.abs(desx) - math.abs(desy) >= 0 and dungeonTileAt(c.xpos + ndesx, c.ypos, c.levelpos) ~= " " then
- --We check for other monsters too
- local obstructed = false
- for j=1,#dungeons[activeDungeon].creatures do
- local oc = dungeons[activeDungeon].creatures[j]
- if i ~= j and c.xpos + ndesx == oc.xpos and c.ypos == oc.ypos and c.levelpos == oc.levelpos then
- obstructed = true
- end
- end
- if not obstructed then
- c.xpos = c.xpos + ndesx
- end
- elseif ndesy ~= 0 and dungeonTileAt(c.xpos, c.ypos + ndesy, c.levelpos) ~= " " then
- --We check for other monsters too
- local obstructed = false
- for j=1,#dungeons[activeDungeon].creatures do
- local oc = dungeons[activeDungeon].creatures[j]
- if i ~= j and c.xpos == oc.xpos and c.ypos + ndesy == oc.ypos and c.levelpos == oc.levelpos then
- obstructed = true
- end
- end
- if not obstructed then
- c.ypos = c.ypos + ndesy
- end
- end
- end
- end
- end
- --Decides whether or not to add a creature to the dungeon
- if dungeons[activeDungeon].creaturesSpawn and math.random(1,15) == 1 then
- addCreatureToDungeon()
- end
- end
- --[[SECTION: Parser, Helper]]--
- --Reads in a file, the town, and uses it to produce each town map in the game (NPC's etc. are handled in the script)
- function parseTowns()
- local file = io.open(shell.resolve(".").."/scripts/towns", "r")
- if not file then error("\'"..towns.."\' file not found.") end
- local flines = { }
- flines [1] = file:read()
- while true do
- local towntitle = ""
- towntitle, flines[#flines] = readFromQuoteMarks(flines[#flines])
- if not towntitle then
- print("Malformed string, breaking")
- break
- end
- local coords = split(flines[#flines], ",")
- local townlevel = { }
- for i=1,16 do
- local townline = file:read()
- if #townline > mapw then
- townline = string.sub(townline, 1, mapw)
- elseif #townline < mapw then
- townline = townline..string.rep(" ", mapw - #townline)
- end
- table.insert(townlevel, townline)
- end
- table.insert(town, {
- name = towntitle,
- xpos = tonumber(coords[1]),
- ypos = tonumber(coords[2]),
- startx = tonumber(coords[3]),
- starty = tonumber(coords[4]),
- visible = true,
- level = townlevel,
- npc = { }
- })
- print("Created town "..towntitle.." ("..tonumber(coords[1])..","..tonumber(coords[2])..")")
- flines[#flines + 1] = file:read()
- if not (flines[#flines] and string.find(flines[#flines], "TOWN") == 1) then break end
- end
- file:close()
- end
- --This returns two things- the word in quotation marks, and the rest of the string
- function readFromQuoteMarks(quotestr)
- local firstQuote = quotestr:find("\"")
- if not firstQuote then return nil end
- local lastQuote = quotestr:find("\"", firstQuote + 1)
- if not lastQuote then return nil end
- if not firstQuote or not lastQuote then error("Malformed quotation marks!") end
- return string.sub(quotestr, firstQuote + 1, lastQuote - 1), string.sub(quotestr, lastQuote + 1)
- end
- --Splits a string according to a pattern into a table
- function split(str, pattern)
- local t = { }
- local fpat = "(.-)" .. pattern
- local last_end = 1
- local s, e, cap = str:find(fpat, 1)
- while s do
- if s ~= 1 or cap ~= "" then
- table.insert(t,cap)
- end
- last_end = e+1
- s, e, cap = str:find(fpat, last_end)
- end
- if last_end <= #str then
- cap = str:sub(last_end)
- table.insert(t, cap)
- end
- return t
- end
- --Finds if one of a series of words can be found in a given string
- function findIn(...)
- local str = arg[1]
- for i=2,#arg do
- if string.find(str, arg[i]) then return true end
- end
- return false
- end
- --[[SECTION: Logging]]--
- --This clears the log file.
- function clearLog()
- local file = io.open(shell.resolve(".").."/log", "w")
- file:write(os.time()..": Log opened.\n")
- file:close()
- end
- --This writes debug info to the log file
- function writeToLog(...)
- local file = io.open(shell.resolve(".").."/log", "a")
- file:write(os.time()..": ")
- for k,v in pairs(arg) do
- if not v then file:write("<nil> ")
- else file:write(v, " ") end
- end
- file:write("\n")
- file:close()
- end
- --Runs all the scripts
- function executeScripts()
- local scripts = { "script", "equipment", "creatures", "quest", "npc", "dungeons" }
- local scriptSuccessful = true
- for _,v in pairs(scripts) do
- print("Running "..v.." script...")
- if not fs.exists(shell.resolve(".").."/scripts/"..v) then error("Cannot find "..v.." file!") return false end
- if not shell.run("scripts/"..v) then
- print(v.." failed to execute")
- scriptSuccessful = false
- break
- end
- end
- return scriptSuccessful
- end
- --[[SECTION: Main]]--
- --The main function- prepares the game to play and manages game states
- function main()
- shell.run("clear")
- readOverWorld()
- parseTowns()
- if not executeScripts() then error("Error in script!") return end
- refreshNPCInventory()
- print("Initializing game...")
- sleep(1)
- onInit()
- clearLog()
- if debugging then writeToLog("Game Started") end
- for i = 1, #dungeons do bufferLevel(i) end
- while not gameOver do
- --In the event the player is in a town
- if inTown then
- printTownInterface()
- local id, key = os.pullEvent("key")
- if key == 28 then break
- else handleTownInput(key) end
- --In the event the player is in a dungeon
- elseif inDungeon then
- term.clear()
- drawDungeonCorridor()
- drawDungeonInterface()
- local id,key = os.pullEvent("key")
- if key == 28 then break
- else handleDungeonInput(key) end
- if inDungeon then updateDungeon() end
- else
- --In the event the player is in the world map
- printOverworldInterface()
- printWorldMap()
- handleOverworldInput()
- if not inDungeon and not inTown then updateOverworld() end
- end
- end
- shell.run("clear")
- print("Thanks for playing!")
- if debugging then writeToLog("Game terminated successfully.") end
- end
- main()
- MKFIL darklands/log
- WRITE 1
- 14.913: Log opened.
- MKDIR darklands/sacalin
- MKFIL darklands/sacalin/script
- WRITE 1076
- --[[
- Darklands: Tales from Transylvania
- --Fighter
- You are still but a young squire, consripted into the service under the command of Lord Klint, a
- Moldavian defector and an arrogant, cruel man. When he was not slaughtering men from whichever
- side appeared most likely to succeed, he was punishing his servants with the lash with almost
- sadistic pleasure.
- When first the call came for a scout amongst the ranks, embittered by your evil master, you rushed
- at all speed from your quarters to volunteer. There's no evil below that could stand toe to toe
- with what you have suffered through, and you are immediately conscripted.
- --Theif
- Profit always follows war, or so it's said. The life of an urchin in peace time is difficult and
- wearisome, but in war, where corpses lie where they fall, along with their purses and satchels,
- a young man can make good of himself, if only for a spell. So you have spent your youth, hiding
- in shadows and picking from the bones of conflits between those greater than yourself.
- So you had hoped would be the case with this latest invasion, but after weeks of hard travel shadowing
- the invasion force not so much as a drop of blood had been spilled. Starving and desparate, you
- made an attempt on a supply trailer, but the soldiers were too quick, and the rest of the journey
- you spent in chains. When the call came out for a scouting mission almost guaranteed to be suicide,
- it was no surprise to see you finding an audience with the general himself.
- --Mage
- Just six months from your training, barely a whelp, your magistrates saw fit to send you to the
- Carpathians in the hopes you would set up a herbarium. Though the first few quiet, contemplative
- weeks are relaxing, you soon find yourself venturing farther and father into the reaches of the
- mountains, to break the monotony.
- It is on one of your longer journeys that you come across the soldiers, camping above the ridge in
- an abandoned keep. As a member of the mystics you extend your authority to demand a meeting with the
- general and order him to hault his invasion- but he has other things in mind.
- ]]--
- --Note: throughout the script several "print" statements have been scattered to demonstrate progress. These are nonessential, but useful for error detection.
- --A simple single town, with a single quest giver and two storefronts, as well as one random NPC.
- --This demonstrates the primary features of writing a town script, and a simple dungeon with quest queues.
- --[[SECTION: Initialization]]--
- function debugDisplay(str)
- term.setCursorPos(1,1)
- print("Debug: "..str)
- os.pullEvent("key")
- end
- --Quick function that allows the user to choose his class, which is then assigned equipment, stats & gold
- function selectClass()
- term.clear()
- local str = "From what vocation do you hail, "..charName.."?"
- term.setCursorPos(w/2 - #str/2, 3)
- term.write(str)
- list = {"Squire", "Urchin", "Journeyman"}
- local sel = 1
- while true do
- for i=1,#list do
- term.setCursorPos(w/2 - #list[i]/2 - 2, 3 + i * 3)
- term.clearLine()
- if sel == i then term.write("[ "..list[i].." ]")
- else term.write(" "..list[i].." ") end
- end
- local id,key = os.pullEvent("key")
- if key == keys.up and sel > 1 then sel = sel - 1
- elseif key == keys.down and sel < #list then sel = sel + 1
- elseif key == keys.space or key == keys.enter then
- charClass = list[sel]
- break
- end
- end
- --All players start with rough robes and sandals
- local robes = cloneItem(6)
- robes.enchantment = cloneEnchantment("rough")
- local sandals = cloneItem(7)
- table.insert(inventory, robes)
- table.insert(inventory, sandals)
- if sel == 1 then
- --Squires are peniless but well equipped by their master
- table.insert(inventory, cloneItem(3))
- table.insert(inventory, cloneItem(5))
- charGold = 0
- elseif sel == 2 then
- --Urchins have a good weapon but little coin to their name
- local dagger = cloneItem(1)
- dagger.enchantment = cloneEnchantment("sharp")
- table.insert(inventory, dagger)
- charGold = 5
- elseif sel == 3 then
- --Journeymen have pathetic weapons but much more money
- local staff = cloneItem(2)
- staff.enchantment = cloneEnchantment("rotting")
- table.insert(inventory, staff)
- charGold = 30
- end
- for i=1,#inventory do
- equipItem(i)
- end
- end
- function onDebugInit()
- shell.run("clear")
- print("Type a key to begin debug session")
- table.insert(inventory, cloneItem(6))
- table.insert(inventory, cloneItem(7))
- table.insert(inventory, cloneItem(2))
- charName = "Debug"
- for i=1,#inventory do
- equipItem(i)
- end
- charGold = 100
- os.pullEvent("key")
- end
- function onInit()
- local darkSword = {
- " /\\",
- " ||",
- " ||",
- " ||",
- " ||",
- " ||",
- " /\\",
- "o======o",
- " ||",
- " ||",
- " __"
- }
- term.clear()
- sleep(1)
- for i=1,#darkSword do
- printLeft(darkSword[i], 2+i, w/2 - #darkSword[8]/2 - 1)
- end
- sleep(1.5)
- term.setCursorPos(w/2 - 9/2, h-4)
- textutils.slowPrint("DarkLands")
- sleep(1)
- printOffCenter("Tales from Transylvania", h-3, w, 0)
- printOffCenter("Press a key to begin", h-1, w, 0)
- os.pullEvent(key)
- term.clearLine()
- printLeft("Enter your name, hero: ", h-1, 2, 0)
- term.setCursorBlink(true)
- setCharName(io.read())
- term.setCursorBlink(false)
- --The generic story preface is told here.
- term.clear()
- sleep(1)
- term.setCursorPos(1, 1)
- print("It is 1430.\n")
- print(
- "Seeking to relieve the strain from a lengthy war with Moldava and widespread famines in the "..
- "south, the king of Wallachia ordered an invasion on Transylvania. The warlords of Transylvania, often "..
- "as busy fighting each other as foreign invaders are considered by His Grace a means to relieve the "..
- "strain on the armed forces, and restore faith in the monarchy.\n")
- print(
- "In November of that month, an army of 300 men assembled at the base "..
- "of the Carpathian mountain ranges, one of the most trecherous stretches of land in Europe. Two weeks "..
- "of bitter cold and snowstorms, with limited rations and low morale, they finally came upon a keep "..
- "within the mountains.\n")
- term.setCursorPos(1, h)
- term.write("Type any key to continue")
- os.pullEvent("key")
- term.clear()
- term.setCursorPos(1, 2)
- print(
- "The abandoned keep, Groazagol, afforded a view over the southern reaches of the country, but the land, in day "..
- "and night was shrouded in an impenetrable darkness. It was not long before rumours spread through "..
- "the garrison, of evil sweeping the country side- the black death, horsemen of the apocalypse, or "..
- "some other sinister force of evil.\n")
- print(
- "The commander general of the Wallachian forces Lord Jeirbe, fearing his men would mutiny or flee "..
- "at an order to trave into the abyss, called for a single scout to map the underlying country, "..
- "provide insight into what was happening, and inform the army on their next move.\n")
- term.setCursorPos(1, h)
- term.write("Type any key to continue")
- os.pullEvent("key")
- --The player selects their class here:
- selectClass()
- pox = 29
- poy = 24
- end
- --Comment out this line as needed.
- --onInit = onDebugInit
- --[[SECTION: Images]]--
- --[[SECTION: Items]]--
- print("Loading items...")
- --Note: Stores use these (not creatures) so be sure not to include creature weapons in this list- just
- --create those within the creature's insert page.
- table.insert(items, { name = "Copper Dagger",
- class = "weapon",
- desc = "A copper dagger, weathered and nicked from age.",
- bodypart = "swordhand",
- level = 1,
- requirements = { },
- base = 6,
- enchantment = nil,
- enchantTypes = {"#blade", "#metal", "#aged"},
- value = 5,
- weight = 5
- })
- table.insert(items, { name = "Wooden Staff",
- class = "weapon",
- desc = "A solid but soft yew staff, made from a fallen branch.",
- bodypart = "both",
- level = 1,
- requirements = { },
- base = 8,
- enchantment = nil,
- enchantTypes = {"#wood", "#aged"},
- value = 2,
- weight = 8
- })
- table.insert(items, { name = "Iron Gladius",
- class = "weapon",
- desc = "Shortsword of antiquity.",
- bodypart = "swordhand",
- level = 3,
- requirements = { },
- base = 8,
- enchantment = nil,
- enchantTypes = {"#iron", "#blade", "#aged"},
- value = 15,
- weight = 6
- })
- table.insert(items, { name = "Quarterstaff",
- class = "weapon",
- desc = "An oaken staff, without ornament.",
- bodypart = "both",
- level = 3,
- requirements = { },
- base = 11,
- enchantment = nil,
- enchantTypes = {"#wood"},
- value = 12,
- weight = 9
- })
- table.insert(items, { name = "Practice Buckler",
- class = "shield",
- desc = "A fist-sized shield made from wooden boards, repurposed from sword training.",
- bodypart = "offhand",
- level = 1,
- requirements = { },
- base = 0.5,
- enchantment = nil,
- enchantTypes = {"#wood", "#aged"},
- value = 8,
- weight = 4
- })
- table.insert(items, { name = "Hessian Robe",
- class = "armour",
- desc = "Old hessian robes, with flax fastenings and cotten piping.",
- bodypart = "chest",
- level = 1,
- requirements = { },
- base = 0,
- enchantment = nil,
- enchantTypes = {"#fabric", "#aged"},
- value = 3,
- weight = 8
- })
- table.insert(items, { name = "Sandals",
- class = "armour",
- desc = "Well worn flax sandals.",
- bodypart = "feet",
- level = 1,
- requirements = { },
- base = 0,
- enchantment = nil,
- enchantTypes = {"#fabric", "#aged"},
- value = 1,
- weight = 3
- })
- table.insert(items, { name = "Iron Mace",
- class = "weapon",
- desc = "An iron-shafed mace with a droplet-shaped head.",
- bodypart = "swordhand",
- level = 4,
- requirements = { },
- base = 10,
- enchantment = nil,
- enchantTypes = {"#mace", "#iron"},
- value = 12,
- weight = 9
- })
- --[[SECTION: Enchantments]]--
- enchantments["#blade"] = {"dull", "sharp", "ornamental"};
- enchantments["#metal"] = {"pure", "impure", "blistered", "smooth"};
- enchantments["#wood"] = {"rotting", "firm"};
- enchantments["#iron"] = {"#metal", "pig", "wrought", "cast"};
- enchantments["#fabric"] = {"rough", "quality"};
- enchantments["#aged"] = {"enduring", "weakened"};
- enchantments["#mace"] = {"flanged", "ornamental"};
- enchantments["dull"] = {
- name = "Dull",
- desc = "The edge of the blade has dulled.",
- prefix = true,
- skill = "base",
- effect = -2,
- level = 1,
- value = 0.5
- };
- enchantments["sharp"] = {
- name = "Sharp",
- desc = "The edge of the blade is very sharp.",
- prefix = true,
- skill = "base",
- effect = 2,
- level = 1,
- value = 1.5
- };
- enchantments["pure"] = {
- name = "Pure",
- desc = "It's pure metal base has left it well balanced.",
- prefix = true,
- skill = "speed",
- effect = 1,
- level = 1,
- value = 1.7
- };
- enchantments["impure"] = {
- name = "Impure",
- desc = "It's impure metal base has left it unbalanced.",
- prefix = true,
- skill = "speed",
- effect = -1,
- level = 1,
- value = 0.3
- };
- enchantments["blistered"] = {
- name = "Blistered",
- desc = "It has not been well forged, and the metal is inconsistent throughout.",
- prefix = true,
- skill = "strength",
- effect = -1,
- level = 1,
- value = 0.25
- };
- enchantments["smooth"] = {
- name = "Smooth",
- desc = "The metal was carefully forged, and is free of imperfections.",
- prefix = true,
- skill = "strength",
- effect = 1,
- level = 1,
- value = 1.75
- };
- enchantments["rotting"] = {
- name = "Rotten",
- desc = "Moisture has significantly weakened the strength of the wood.",
- prefix = true,
- skill = "strength",
- effect = -1,
- level = 1,
- value = 0.25
- };
- enchantments["firm"] = {
- name = "Firm",
- desc = "Particularly hard wood was used in its crafting.",
- prefix = true,
- skill = "strength",
- effect = 1,
- level = 1,
- value = 1.75
- };
- enchantments["pig"] = {
- name = "Pig",
- desc = "The unrefined iron is quite brittle.",
- prefix = true,
- skill = "durability",
- effect = 0.5,
- level = 1,
- value = 0.75
- };
- enchantments["wrought"] = {
- name = "Wrought",
- desc = "The bloomer-smelted iron is sturdy but quite heavy.",
- prefix = true,
- skill = "weight",
- effect = 1.2,
- level = 1,
- value = 0.8
- };
- enchantments["cast"] = {
- name = "Hard",
- desc = "Though a little heavy, you feel the strength the forge lends the iron.",
- prefix = true,
- skill = "strength",
- effect = 2,
- level = 1,
- value = 2.5
- };
- enchantments["rough"] = {
- name = "Rough",
- desc = "The weave of the material is inconsistent and holes have appeared.",
- prefix = true,
- skill = "endurance",
- effect = -1,
- level = 1,
- value = 0.4
- };
- enchantments["quality"] = {
- name = "of Quality",
- desc = "The careful weave has given the fabric extra strength.",
- prefix = false,
- skill = "endurance",
- effect = 1,
- level = 1,
- value = 1.6
- };
- enchantments["enduring"] = {
- name = "Enduring",
- desc = "Quality has been well preserved despite the advanced age.",
- prefix = true,
- skill = "durability",
- effect = 1.5,
- level = 2,
- value = 1.2
- };
- enchantments["weakened"] = {
- name = "of Age",
- desc = "After years of use, it is now a shadow of it's former self.",
- prefix = false,
- skill = "durability",
- effect = 0.5,
- level = 1,
- value = 0.1
- };
- enchantments["flanged"] = {
- name = "Flanged",
- desc = "The sharp flanges on the mace head make it lethal against armoured opponents.",
- prefix = false,
- skill = "base",
- effect = 2,
- level = 4,
- value = 2.2
- }
- enchantments["ornamental"] = {
- name = "Ornamental",
- desc = "Though beautifully decorated, it clearly was not designed for combat.",
- prefix = false,
- skill = "base",
- effect = -4,
- level = 1,
- value = 3.5
- }
- print("Loading creatures...")
- --[[SECTION: Creatures]]--
- --Rat: A common pest in Sacalin
- table.insert(creatures, { name = "Rat",
- xpos = -1,
- ypos = -1,
- levelpos = -1,
- level = 1,
- xp = 10,
- hit = 8,
- weapon = {
- name = "claws",
- base = 4,
- },
- ac = 0,
- imageone = {
- "_---_",
- "/ \\",
- "/ (\\-/) \\",
- "| /o o\\ |",
- "| \\ v / |",
- "\\-\\_/-/",
- "_M M_"
- },
- imagetwo = {
- "___",
- "/o_o\\",
- "|\\_/|",
- "M M"
- }
- })
- --Snake: Oversized lizards with sharp fangs, but quite weak.
- table.insert(creatures, { name = "Snake",
- xpos = -1,
- ypos = -1,
- levelpos = -1,
- level = 1,
- xp = 15,
- hit = 6,
- weapon = {
- name = "fangs",
- base = 8
- },
- ac = 0,
- imageone = {
- "/0-0\\",
- "\\___/",
- " | | ",
- "_\\ \\",
- "(__\\___)"
- },
- imagetwo = {
- " ;oo;",
- " _|| ",
- "(__) "
- }
- })
- --Tyrat: Small humanoid figures with sharp faces and carrying sickles. Weak and stupid.
- table.insert(creatures, { name = "Tyrat",
- xpos = -1,
- ypos = -1,
- levelpos = -1,
- level = 1,
- xp = 12,
- hit = 10,
- weapon = {
- name = "sickle",
- base = 6
- },
- ac = 0,
- imageone = {
- " _____ ",
- "< V V >",
- " \\___/ ",
- " / \\ ",
- " ( _ )",
- "/_) (_\\"
- },
- imagetwo = {
- " __ ",
- "<..>",
- " /\\ ",
- " || "
- }
- })
- --Wraith: Evil spirits that haunt caves. Tied to quest "Shadows over Aemelius"
- table.insert(creatures, { name = "Wraith",
- xpos = -1,
- ypos = -1,
- levelpos = -1,
- level = 3,
- xp = 50,
- hit = 30,
- ac = 0,
- weapon = {
- name = "WraithBlade",
- base = 6
- },
- imageone = {
- " /:\\ ",
- " |O#O| ",
- " |{x}| ",
- " / \\ ",
- "| /:",
- ": /||",
- " |/ |/ ",
- ":~:~:~:",
- " :~:~: ",
- },
- imagetwo = {
- " /:\\ ",
- " |:| ",
- " |/| ",
- " ~~~ "
- }
- })
- print("Loading towns & npcs...")
- --[[SECTION: Towns]]--
- --You can use in-text "#name", it will replace it with the player's name, and in-text "#class" for the player's class.
- --Dialogues are flexible. They can be:
- --Strings: This will simply display the message as though the NCP is delivering it
- --Tables: Work the same as strings but will cycle through each message, giving user the ability to respond at the end of the conversation.
- --Functions: These can do... anything you like really. Usually used for talking with NPC's and simultaneously updating info (such as quests). Functions can return
- --Strings and tables, which work the same way as they do for typical dialogues, or nil, which will break out of the conversation immediately.
- globalDialogue = {
- ["#name"] = function(me)
- local r = math.random(1,3)
- if r == 1 then return "You can call me "..me.name.."."
- elseif r == 2 then return "My name is "..me.name.."."
- elseif r == 3 then return "I am "..me.name.."." end
- end;
- ["#job"] = function(me)
- if me.job == "smith" then
- return "I'm a smith. I work a forge and produce weapons and armour. I can trade my inventory with you if you like."
- elseif me.job == "merchant" then
- return "I'm a merchant, buying goods from adventurers like yourselves and selling them to interested buyers. I'll trade good gold for any items you're carrying."
- elseif me.job == "mystic" then
- return "I'm a mystic, a trainer in the arts of magic. I can teach you spells, for a fee."
- else
- local article = "a"
- if isVowel(string.sub(me.job, 1, 1)) then article = "an" end
- return "I work as "..article.." "..me.job.."."
- end
- end;
- ["#hello"] = "#ongreet";
- ["#bye"] = "#onfarewell";
- ["#goodbye"] = "#onfarewell";
- ["#exit"] = "#onfarewell";
- ["#farewell"] = "#onfarewell";
- ["#leave"] = "#onfarewell";
- ["#work"] = "#quest";
- ["#quest"] = "I'm afraid I don't have any work for you, #name.";
- }
- --INDEX 1: NEBUN
- --The Mad Town. Home to a series of eccentrics that worship false gods and sacrifice food, supplies and life
- --to appease them.
- --I keep a list of "on activation" of quests here.
- local function onActivateKP(me)
- printTownInterface()
- displayConfirmDialogue("Quest Offer by Jeremiah: ", "It is a somewhat complicated matter. Kershel has lived here all his life and has always been lax in his obligations to the city.")
- local response = displayOptionDialogue("Quest Offer by Jeremiah: ", "Would you be able to extract his monthly due of 50 gold pieces? I would gladly share a portion of the sum with you.")
- if response == "yes" then
- activateQuest(quests["Kershel's Problem"])
- setNPCDialogue(me.name, 1, "#ongreet", "Hello again #name, have you recovered Kershel's debt yet?")
- setNPCDialogue(me.name, 1, "#quest", "Ask Kershel about his debt, and return to me when you have the money.")
- return "Great! You should be able to find Kershel in town- ask him about his debt, and let me know when you have the money."
- else
- return "Let me know if you change your mind."
- end
- end
- local function onActivateEITH(me)
- printTownInterface()
- displayConfirmDialogue("Quest Offer by Jeremiah: ", "There have been troubling reports from the hills to the north of Aemelius. Farms appear to be going... missing.")
- displayConfirmDialogue("Quest Offer by Jeremiah: ", "I would consider it a great favour should you venture to the hills, discover all that you can and report back to me.")
- local response = displayOptionDialogue("Quest Offer by Jeremiah: ", "I can probably put together a few pieces of gold too. Will you help us?")
- if response == "yes" then
- activateQuest(quests["Evil in the Hills"])
- setNPCDialogue(me.name, 1, "#ongreet", "Greetings #name, I trust you bring word of your adventures in the hills? Or do you wish something else of me?")
- setNPCDialogue(me.name, 1, "#quest", "Travel to the hills north of Aemelius, discover all that you can and report on what you find.")
- return "I thank you sir, and shall await your report with great anticipation."
- else
- return "The job will be waiting for you if you so desire."
- end
- end
- town[1].dialogue = {
- ["#transylvania"] = "";
- ["#nebun"] = "";
- ["#groazagol"] = "";
- ["#dark"] = "";
- ["#castle aemelius"] = "#aemelius";
- ["#castle"] = "#aemelius";
- ["#aemelius"] = "Aemelius is the only town in Sacalin. It's just a little port on the route from Turkey to Romania. It's not heavily trafficked, so it's quiet here.";
- ["#hills"] = "They're about a league north of town. A few farmers up that way, but it's pretty unremarkable.";
- ["#crypts"] = "The town crypts are about two leagues east of town- follow the river. No one's been there for months... not sure what's there now.";
- ["#spire"] = "Why would you want to go there? Well it's just a bit north of town, near the coast, but only a few kooky wizards are up that way.";
- ["#quest"] = "You should ask Jeremiah if you're looking for work- he would probably have something for you.";
- }
- table.insert(town[1].npc, { name = "Daedelus",
- job = "smith",
- xpos = 17, ypos = 6,
- level = 1,
- dialogue = {
- ["#onfirstgreet"] = "Greetings stranger. I'm Daedelus, town smith here. I can give you an offer on some of my goods if you want to trade." ;
- ["#ongreet"] = "Good morrow to you, #name. In need of fresh iron?";
- ["#name"] = "My name is Daedelus. It's an old Greek name on my mother's side, if you're curious.";
- ["#job"] = "I work the smithy here. We don't have much in the way of goods but I can offer you what I have, if you'd like to trade.";
- ["#kershel"] = "That bum? He's the town fool- spends most of his time dead to the world on drink. He has helped in the workshop when he's short, though.";
- ["#jeremiah"] = "He's a nervous fellow but runs the town quite well.";
- ["#erika"] = "She's just settled down here, used to travel a lot. I sometimes buy from her inventory to stock up my own supplies. Not bad prices.";
- ["#daedelus"] = "Yes, I'm Daedelus.";
- ["#buy"] = "#trade";
- ["#trade"] = function(me)
- if #me.inventory == 0 then
- return "Sorry, I don't have any inventory at the moment. I should have some new stuff ready soon."
- else
- displayConfirmDialogue(me.name.." the "..me.job.." says:", "Take a look, see if anything catches your fancy:")
- runTradeInterface(me, false)
- return "Thanks for your business, stranger."
- end
- end;
- ["#onfarewell"] = "Good travels to you, #name."
- },
- nodialogue = "I'm not sure what you're talking about, friend. Maybe someone else does?",
- inventory = { },
- priceModifier = 2.5,
- goldLimit = 0
- })
- table.insert(town[1].npc, { name = "Erika",
- job = "merchant",
- xpos = 22, ypos = 6,
- level = 1,
- dialogue = {
- ["#onfirstgreet"] = "It's #name is it? I'm Erika. Can give you a good price on any goods you want to sell.";
- ["#ongreet"] = "Looking to sell off some of your goods #name?";
- ["#kershel"] = "The town fool. He's pretty harmless.";
- ["#jeremiah"] = "Jeremiah? Don't know him too well, just moved here. Seems to do a good enough job, given the circumstances.";
- ["#erika"] = "I've just freshly arrived here in Aemelius- travelled the mediterranian before that.";
- ["#daedelus"] = "He's a good man. We occasionally do business. His goods are a little on the pricy side but they're good quality.";
- ["#trade"] = function(me)
- if #inventory == 0 then
- return "Looks like you're out of goods- let me know when you have something, I'll give you a good price on it."
- else
- displayConfirmDialogue(me.name.." the "..me.job.." says:", "All right, lets see what you've got...")
- runTradeInterface(me, true)
- return "Appreciate your business, #name."
- end
- end;
- ["#sell"] = "#trade";
- ["#onfarewell"] = "Until next time.";
- },
- nodialogue = "Couldn't tell you, #name.",
- inventory = { },
- priceModifier = 0.75,
- goldLimit = 25
- })
- table.insert(town[1].npc, { name = "Kershel",
- job = "fool",
- xpos = 18, ypos = 12,
- level = 1,
- dialogue = {
- ["#onfirstgreet"] = "Ah another fool to arrive in Sacalin. I am Kershel, the town fool.";
- ["#ongreet"] = "The fool returns... what is it you wish of me?";
- ["#name"] = "I am known by many names, but best by Kershel.";
- ["#job"] = "My trade is my own, one characterized by ignorance, and rewarded with bliss.";
- ["#kershel"] = "The town fool, at your service.";
- ["#jeremiah"] = "The taxman bleeds even my pockets dry.";
- ["#erika"] = "Is it that the road to hell is paved with the best of intentions, or the gold of the less fortunate?";
- ["#daedelus"] = "That rolling stone has gathered much moss.";
- ["#trade"] = "Were it that I carried anything of worth, I would not be a fool.";
- ["#onfarewell"] = "Shall we meet again? Only time will tell...";
- },
- nodialogue = "A wise man asks questions, but a fool asks questions and doesn't listen."
- })
- table.insert(town[1].npc, { name = "Jeremiah",
- job = "mayor",
- xpos = 2, ypos = 8,
- level = 1,
- dialogue = {
- ["#onfirstgreet"] = "A stranger! Greetings, and welcome to the town of Aemelius. I am Jeremiah, the town mayor. If you plan to stay for awhile I may have some work for you.";
- ["#ongreet"] = "Greetings, #name, I trust all is well. Are you looking for work?";
- ["#job"] = "Oh, I'm mayor here in Aemelius. I manage the city's finances and make most decisions regarding our future. I also employ adventurers to help the town- let me know if you're looking for work.";
- ["#kershel"] = "Kershel is a thorn in my side. He consistently dodges tax collection. In fact I would appreciate your help on that matter...";
- ["#jeremiah"] = "Yes, I am Jeremiah. How can I help you?";
- ["#erika"] = "Erika is a new resident to Aemelius, she appeared a few months ago and has set up shop here. So far she has made a valuable contribution to the city.";
- ["#daedelus"] = "Daedelus has lived here for many years, and is a fine smith.";
- ["#trade"] = "Both Daedelus and Erika offer trading services if you wish to purchase or sell of equipment.";
- ["#onfarewell"] = "Farewell, #name. Seek me out if you wish anything of our town.";
- ["#quest"] = onActivateKP;
- },
- nodialogue = "I'm afraid I can't help you with that.",
- })
- print("Loading quests...")
- --[[SECTION: Quests]]--
- --Quest: Kershel's Problem: Petition members of Aemelius for money to pay Kershel's debt to the mayor
- quests["Kershel's Problem"] = { name = "Kershel's Problem",
- activestage = 1,
- variables = { },
- generalDescription = "Kersehl of Aemelius is behind on his taxes, and owes 50 gold to the mayor, Jeremiah. Help Jeremiah to recover the owed money.",
- stages = {
- [1] = {
- desc = "Speak to Kershel about his overdue taxes",
- condition = function(quest, event)
- if event.type == "dialogue" and event.town == 1 and event.npc.name == "Kershel" and findIn(event.topic, "debt", "loan", "tax") then
- setNPCDialogue("Kershel", 1, "#ongreet", "Have you spoken to Erika about my debt?")
- quest.activestage = 2
- return "You have come to collect my taxes? Though all may laugh at a fool, it seems none shall lend him money... so I must seek what is owed of"..
- " me. The merchant has lent me a sum of moneys in the past. Ask her to increase this debt and I'm sure she will pay."
- end
- end
- }, [2] = {
- desc = "Ask Erika in Aemelius to increase Kershel's debt",
- condition = function(quest, event)
- if event.type == "dialogue" and event.town == 1 and event.npc.name == "Erika" and findIn(event.topic, "debt", "loan", "kershel", "tax") then
- quest.activestage = 3
- return "Kershel requires me to raise his debt? I can help but I do not have enough to cover him altogether. Here is what I can spare, and tell him that he must pay me back double for us to be square"
- end
- end
- }, [3] = {
- desc = "Return to Kershel to speak about his taxes",
- condition = function(quest, event)
- if event.type == "dialogue" and event.town == 1 and event.npc.name == "Kershel" and findIn(event.topic, "debt", "loan", "tax") then
- quest.activestage = 4
- setNPCDialogue("Kershel", 1, "#ongreet", "Seek out Daedelus to see if he will buoy our funds.")
- return "It seems we are in need of further funds. I have in the past helped Daedelus man his forge- perhaps he could part with his money just as all fools are."
- end
- end
- }, [4] = {
- desc = "Ask Daedelus to extend a debt to Kershel",
- condition = function(quest, event)
- if event.type == "dialogue" and event.town == 1 and event.npc.name == "Daedelus" and findIn(event.topic, "debt", "kershel", "tax") then
- quest.activestage = 5
- setNPCDialogue("Kershel", 1, "#ongreet", "It appears that the monies have been collected. Waste no time- return these to Jeremiah, that I may be left alone to my foolishness.")
- return "This is the LAST time I cover Kerhsel's debts. Take this to Jeremiah, and if you run into Kershel tell him he needs to get a job. And no, town fool doesn't count!"
- end
- end
- }, [5] = {
- desc = "Return Kershel's debt money to Jeremiah",
- condition = function(quest, event)
- if event.type == "dialogue" and event.town == 1 and event.npc.name == "Jeremiah" and findIn(event.topic, "debt", "kershel", "tax", "loan") then
- quest.activestage = -1
- setNPCDialogue("Kershel", 1, "#ongreet", "My leaseman has returned! This humble fool offers his welcome and thanks.")
- setNPCDialogue("Jeremiah", 1, "#ongreet", "Welcome back friend. There is a matter that troubles me- I would appreciate your assistance if you're looking for work.")
- setNPCDialogue("Jeremiah", 1, "#quest", onActivateEITH)
- addGold(25)
- return "Excellent, this meets the required amount! Now half the tax brings us to... 25 gold pieces I think? Job well done, however, another matter has arise, that may need attention..."
- end
- end
- }
- }
- }
- --Quest: Evil in the Hills: Explore the hills north of Aemelius and learn what you can about the creatures that dwell there
- quests["Evil in the Hills"] = { name = "Evil in the Hills",
- activestage = 1,
- variables = { [1] = true, [2] = true, [3] = true, [4] = true, [5] = true },
- generalDescription = "Jeremiah of Aemelius fears evil has taken root in the hills to the north. Head for the north and see what you can discover.",
- stages = {
- [1] = {
- desc = "Investigate the hills north of Aemelius",
- condition = function(quest, event)
- if inDungeon and activeDungeon == 1 then
- displayConfirmDialogue("You step inside...", "You step into the dark cave to see a slew of rough passages cut from the clay walls, and you hear the squeals of wild beasts from within.")
- return 2
- end
- return nil
- end
- }, [2] = {
- desc = "Explore the Hillside Caverns, north of Aemelius",
- --This function shows an example of multiple conditions occurring, which don't change the game state but provide the player with clues.
- --At certain dead-ends in the dungeon, messages regarding the dungeon will be given
- condition = function(quest, event)
- if inDungeon and activeDungeon == 1 and dunpx == 3 + 2 and dunpy == 3 + 2 and dunlevel == 2 then
- displayConfirmDialogue("You notice something:", "The walls here are freshly dug- the creatures that inhabit this place must need more space.")
- return nil
- elseif inDungeon and activeDungeon == 1 and dunpx == 8 + 2 and dunpy == 5 + 2 and dunlevel == 1 then
- displayConfirmDialogue("You notice something:", "There is a ladder leading down here- it appears these tunnels are not just the work of simple rats.")
- return nil
- elseif inDungeon and activeDungeon == 1 and dunpx == 1 + 2 and dunpy == 1 + 2 and dunlevel == 3 then
- displayConfirmDialogue("Oh no!", "You grasp the ladder but just as you do it gives way, plummeting you into the bottom of the tunnel. You'll have to find another way out.")
- return nil
- elseif inDungeon and activeDungeon == 1 and dunpx == 2 + 2 and dunpy == 1 + 2 and dunlevel == 3 then
- displayConfirmDialogue("You notice something", "Up ahead you spy a small creature- a Tyrat, mean little humanoids that like to steal metals from farmers. They're undoubtedly the creators of these tunnels.")
- return nil
- elseif inDungeon and activeDungeon == 1 and dunpx == 1 + 2 and dunpy == 2 + 2 and dunlevel == 1 then
- displayConfirmDialogue("At last!", "Finally you can see daylight- you have found a way out! You should return to Aemelius and report what you've found.")
- return 3
- end
- return nil
- end
- }, [3] = {
- desc = "Report to Jeremiah of Aemelius about the northern hills",
- condition = function(quest, event)
- if activeTown == 1 and activeDialog == "Jeremiah" then
- displayConfirmDialogue("Jeremiah the mayor says:", "You found infestations in the northern hills? I hope you were able to clear them out... thankyou for your help, but now I must ask of you an even greater task.")
- setNPCDialogue("Kershel", 1, "Is Sacalin really going to sink?")
- setNPCQuest("Jeremiah", 1, "Shadows over Aemelius")
- setNPCDialogue("Jeremiah", 1, "The fate of this village depends on you... good luck!")
- return -1
- end
- return nil
- end
- }
- }
- }
- --Quest: Shadows over Aemelius: A vengeful spirit has threatened to sink the island of Sacalin if he is not appeased.
- quests["Shadows over Aemelius"] = { name = "Shadows over Aemelius",
- acceptMessage = "A cave to the east of Aemelius has opened, and we have seen an evil spirit within its depths! Left as it is, who knows what could happen? Shut this open maw to the depths of Hades, and I shall see you well rewarded.",
- activestage = 1,
- variables = { },
- stages = {
- [1] = {
- desc = "Find the newly opened cave east of Aemelius",
- --This condition actively changes the state of the world (adds a dungeon)
- condition = function()
- if activeTown == 0 then
- displayConfirmDialogue("You notice something:", "Just as you step outside you hear a great explosion, and a plume of smoke rise in the foothills to the east. Something has changed.")
- addDungeonToWorld(mausoleum, 3)
- return 2
- end
- end
- }, [2] = {
- desc = "Find the newly opened cave east of Aemelius",
- condition = function()
- if inDungeon and activeDungeon == 3 then
- displayConfirmDialogue("You step inside...", "The halls here are lined with stone bricks that look centuries old, and a frozen breeze chills you to the bone. You can feel evil emanating from every inch of this place.")
- return 3
- end
- end
- }, [3] = {
- desc = "Close the Mausoelum east of Aemelius",
- condition = function()
- if inDungeon and activeDungeon == 3 and dunpx == 3 + 2 and dunpy == 1 + 2 and dunlevel == 1 then
- displayConfirmDialogue("You hear a quiet whisper:", "\"Those who enter shall have the honour of meeting Death- turn back now!\"")
- return nil
- elseif inDungeon and activeDungeon == 3 and dunpx == 9 + 2 and dunpy == 5 + 2 and dunlevel == 1 then
- displayConfirmDialogue("You hear a quiet whisper:", "\"Do you like my home, trespasser? You should know you face a foce you cannot possibly reckon\"")
- return nil
- elseif inDungeon and activeDungeon == 3 and dunpx == 8 + 2 and dunpy == 8 + 2 and dunlevel == 2 then
- displayConfirmDialogue("You hear a quiet whisper:", "\"Know that I was once a citizen of Aemelius, but by foul murder I was taken from my home, banished to the realm of spectres.\"")
- return nil
- elseif inDungeon and activeDungeon == 3 and dunpx == 2 + 2 and dunpy == 8 + 2 and dunlevel == 3 then
- displayConfirmDialogue("You hear a quiet whisper:", "\"With every breath of my body I shall wreak vengance on this place- by My almighty fury this island shall sink into the ocean!\"")
- return nil
- elseif inDungeon and activeDungeon == 3 and dunpx == 5 + 2 and dunpy == 8 + 2 and dunlevel == 3 then
- displayConfirmDialogue("You notice something:", "A frightning sight is ahead of you- the wraith that haunts this place, floating gently above the ground. You cannot see his face, but you are certain his expression is menacing.")
- return 4
- end
- end
- }, [4] = {
- desc = "Defeat the Wraith of the Mausoleum",
- condition = function ()
- if not inDungeon then
- displayConfirmDialogue("Quest Updated!", "You burst out from the mausolean, out of breath and barely alive. The wraith has been defeated, and with it, the mausoleum has been sealed. Aemelius is finally safe!")
- return 5
- end
- end
- }, [5] = {
- desc = "Return to Jeremiah of Aemelius to inform him of the battle",
- condition = function ()
- if activeTown == 1 and activeDialog == "Jeremiah" then
- displayConfirmDialogue("Jeremiah the mayor says:", "The wraith has been defeated? We of this town owe you the greatest of gratitudes! You shall forever be a friend of Aemelius and all of Sacalin!")
- setNPCDialogue("Kershel", 1, "Were I less foolish perhaps I could have helped...")
- setNPCDialogue("Jeremiah", 1, "We are forever in your debt!")
- return -1
- end
- return nil
- end
- }
- },
- reward = { "GOLD", 100 }
- }
- print("Loading dungeons...")
- --[[SECTION: Dungeons]]--
- --Dungeon 1: Hillside Caverns. Associated with the quest "Evil in the Hills"
- dungeons[1] = { name = "Hillside Caverns",
- xpos = 18,
- ypos = 5,
- startx = 8, starty = 1, startlevel = 1,
- startfx = -1, startfy = 0,
- level = {
- [1] = {
- --123456789
- "E C# # #E",--1
- "# # ### ",--2
- "##### # #",--3
- " # ###",--4
- "C####D D " --5
- --Monsters at: 6,2 7,4
- },
- [2] = {
- "D #####C ",
- "## # # ",
- " ###### ",
- " # ## ",
- "C# D#U U "
- },
- [3] = {
- "##### C #",
- " # ### #",
- " ### ###",
- "## C # #",
- "C U#### ",
- }
- },
- creatures = {
- cloneCreature(4, 6 + 2, 2 + 2, 1),
- cloneCreature(3, 5 + 2, 1 + 2, 3),
- cloneCreature(3, 6 + 2, 5 + 2, 3),
- cloneCreature(3, 5 + 2, 5 + 2, 2),
- cloneCreature(3, 1 + 2, 3 + 2, 1)
- },
- creaturesSpawn = true
- }
- --Dungeon 2: Gallery. This is a debug program written for Tommy Royall to view the monsters he's created
- dungeons[2] = { name = "Monster Gallery",
- xpos = 11,
- ypos = 10,
- startx = 2, starty = 1, startlevel = 1,
- startfx = 1, startfy = 0,
- creatures = {
- cloneCreature(1, 3 + 2, 3 + 2, 1)
- },
- creatureSpawn = false,
- level = {
- [1] = {
- "E######E",
- "########",
- "########",
- "########",
- "########",
- "########",
- "E######E"
- }
- }
- }
- --Dungeon 3: Mausoleum. Associated with the quest "Shadows over Aemelius". Note this dungeon is dynamically added (always to index 2)
- mausoleum = { name = "Mausoleum",
- xpos = 30,
- ypos = 9,
- startx = 2, starty = 1, startlevel = 1,
- startfx = 1, startfy = 0,
- creatures = { cloneCreature(4, 8 + 2, 8 + 2, 3) },
- creaturesSpawn = true,
- level = {
- [1] = {
- "E## #### ",
- " ### #C",
- " C# ### ",
- "C ### # ",
- "#### ###",
- " # # C# D",
- "## ## ",
- " C ###C "
- }, [2] = {
- "D# ### C ",
- " ### ####",
- " # # ",
- "###C # #",
- "# C####",
- "#### # B",
- " # ##### ",
- "D# # C "
- }, [3] = {
- "U# C#####",
- " ### # #",
- "# #C ",
- "##### #C",
- "#C# #### ",
- " # #U",
- " ### ",
- "U# #####E"
- },
- }
- }
- print("Script run successfully!")
- --This MUST be included- it indicates the script was successful. Failure to call/reach this will result in the game not running.
- return true
- MKFIL darklands/sacalin/towns
- WRITE 34
- TOWN "Castle Aemelius" 7,11,27,9
- \ /
- -__--__--__--__--__--__--| |
- _______________/_\__/_\__| |
- | //_\\//_\\ |
- | // \\// \\ |
- -------\=/ / \/ \ |
- | | |_ _||_ _| |
- |_| |--
- \=/ |--
- | | |
- -------|_| |
- | www
- | \ /
- -__--__--_ --__--__--__--| |
- | |
- TOWN "Ancient Spire" 7,5,27,7
- /// \\\ \ /
- ^-/_/___\_\-^-^-^/_\-^-^-| |
- __| : : |_____//_\\____| |
- | : ::| // \\ |
- | : | / \ |
- |: : : | |_ _| {--
- | : |
- |/-----\| {--
- | | |
- \_| |_/ {
- |
- , , ,
- / \ / \ / \
- \ / \ / \ /
- -| |-^-^-^-^-| |-^-^-^-^-| |
- |_| |_| |_|
- MKFIL darklands/sacalin/world
- WRITE 17
- ....................................
- ................___.................
- ...../\......../ \_...............
- .....||__-----/ \..............
- ..../| /___ ____\--*--____....
- ...| * \...
- ...| * \..
- ../ **** _ |..
- ..| ****** * / \ |..
- ..| ### ******** _ /...
- ..| # *** / \ /....
- ..\ ### |....
- ...\___ /.....
- .......\___ /......
- ...........\________________/.......
- ....................................
- ....................................
- MKDIR darklands/saves
- MKDIR darklands/scripts
- MKFIL darklands/scripts/creatures
- WRITE 168
- --[[
- Creatures Script
- This defines the mosters that the player will encounter, both in the overworld and in dungeons.
- ]]--
- --[[Creatures can have the following attributes:
- xpos:int = the x position (set to -1 in this file)
- ypos:int = the y position (set to -1 in this file)
- levelpos:int = the level of a dungeon the creature is on (again, -1 in this file)
- level:int = the player level at which this enemy starts appearing randomly
- xp:int = the amount of XP earnt for slaying it
- hit:int = the number of hit points the enemy has
- weapon:table = an item the enemy uses as a weapon. This can be anything specified in the equipment sheet, or a custom item
- ac:int = the creature's armour class- how easily it can deflect/avoid strikes
- imageone:table = an ASCII image of the enemy when 1 tile away from the player
- imagetwo:table = an ASCII image of the enemy when 2 tiles away from the player
- Warning- make sure the images are consistent in their spacing, or opponents may look... 'segmented'
- ]]--
- --Rat: A common pest in Transylvania
- table.insert(creatures, { name = "Rat",
- xpos = -1,
- ypos = -1,
- levelpos = -1,
- level = 1,
- xp = 10,
- hit = 12,
- weapon = {
- name = "claws",
- base = 4,
- },
- ac = 0,
- imageone = {
- "_---_",
- "/ \\",
- "/ (\\-/) \\",
- "| /o o\\ |",
- "| \\ v / |",
- "\\-\\_/-/",
- "_M M_"
- },
- imagetwo = {
- "___",
- "/o_o\\",
- "|\\_/|",
- "M M"
- }
- })
- --Snake: Oversized lizards with sharp fangs, but quite weak.
- table.insert(creatures, { name = "Snake",
- xpos = -1,
- ypos = -1,
- levelpos = -1,
- level = 1,
- xp = 15,
- hit = 8,
- weapon = {
- name = "fangs",
- base = 8
- },
- ac = 0,
- imageone = {
- "/0-0\\",
- "\\___/",
- " | | ",
- "_\\ \\",
- "(__\\___)"
- },
- imagetwo = {
- " ;oo;",
- " _|| ",
- "(__) "
- }
- })
- --Tyrat: Small humanoid figures with sharp faces and carrying sickles. Weak and stupid.
- table.insert(creatures, { name = "Tyrat",
- xpos = -1,
- ypos = -1,
- levelpos = -1,
- level = 1,
- xp = 20,
- hit = 16,
- weapon = {
- name = "sickle",
- base = 6
- },
- ac = 0,
- imageone = {
- " _____ ",
- "< V V >",
- " \\___/ ",
- " / \\ ",
- " ( _ )",
- "/_) (_\\"
- },
- imagetwo = {
- " __ ",
- "<..>",
- " /\\ ",
- " || "
- }
- })
- --Skeletons: Living dead, prowling tombs and now the darklands.
- table.insert(creatures, { name = "Skeleton",
- xpos = -1,
- ypos = -1,
- levelpos = -1,
- level = 5,
- xp = 25,
- hit = 25,
- weapon = {
- name = "fingers",
- base = 8
- },
- ac = 1,
- imageone = {
- " /o o\\";
- " | ^ |";
- " \\W/";
- "/--|--\\";
- "|/-|-\\\|";
- "||-|-||";
- "w|-|-|w";
- " /0-0\\";
- " | |";
- " V V";
- " | |";
- },
- imagetwo = {
- " {}";
- "/()\\";
- " ||";
- " ||";
- }
- })
- --Wraith: Evil spirits that haunt caves.
- table.insert(creatures, { name = "Wraith",
- xpos = -1,
- ypos = -1,
- levelpos = -1,
- level = 10,
- xp = 100,
- hit = 50,
- ac = 0,
- weapon = {
- name = "WraithBlade",
- base = 10
- },
- imageone = {
- " /:\\ ",
- " |O#O| ",
- " |{x}| ",
- " / \\ ",
- "| /:",
- ": /||",
- " |/ |/ ",
- ":~:~:~:",
- " :~:~: ",
- },
- imagetwo = {
- " /:\\ ",
- " |:| ",
- " |/| ",
- " ~~~ "
- }
- })
- MKFIL darklands/scripts/dungeons
- WRITE 193
- --Dungeon 1: Fargo's Place. Associated with the quest "An Impossible Errand"
- --Chests can contain
- -- Tables: These are items, constructed in much the same way standard items are constructed
- -- Number: A fixed amount of gold
- -- String: Can be one of the following: map, compass, item, gold
- -- Nothing: Either a small amount of gold or a piece of random equipment
- dungeons[1] = { name = "Fargo's Place",
- xpos = 3,
- ypos = 13,
- startx = 3, starty = 2, startlevel = 1,
- startfx = 0, startfy = 1,
- level = {
- [1] = {
- --1234567890123456789012
- " E ## ##### #D ### ",--1
- "C # ### ### ## ##E",--2
- "### C ### #C # ",--3
- " # #### ##### # #C##",--4
- "##C # # D D #",--5
- "# ### #### # C ##",--6
- "##### #C# D # D##### ",--7
- },
- [2] = {
- --1234567890123456789012
- "### ###### ###U ######",--1
- "# ### C # # # # ",--2
- "# # ### ##C ## C###",--3
- "## ## # ## # #",--4
- " # ##### #### #U U ##",--5
- "##### # # ### ",--6
- "C # C###U ##U ## #C",--7
- }
- },
- creatures = { },
- chests = { },
- creaturesSpawn = true,
- playerHasMap = false,
- playerHasCompass = false,
- visible = true
- }
- --Dungeon 2: Fell's Cave. Associataed with the quest "Fuel for the Pyre"
- dungeons[2] = { name = "Fell's Cave",
- xpos = 9,
- ypos = 24,
- startx = 2, starty = 1, startlevel = 1,
- startfx = 1, startfy = 0,
- level = {
- [1] = {
- --12345678901
- "E## ### C",--1
- "# # # # ###",--2
- "# ### ### ",--3
- "# C ###",--4
- "## C #",--5
- " ### # ###",--6
- " ###### C",--7
- }
- },
- creatures = { },
- chests = { {xpos = 11+2, ypos = 1+2, zpos = 1, content="map"},
- {xpos = 6+2, ypos = 5+2, zpos = 1, content="item"},
- {xpos = 11+2, ypos = 7+2, zpos = 1, content=25},
- {xpos = 3+2, ypos = 4+2, zpos = 1, content="compass"}
- };
- creatureSpawn = true,
- playerHasMap = false,
- playerHasCompass = false,
- visible = true
- }
- --Dungeon 3: Nebun Mausoleum. Associated with the quest "Laid to Rest"
- dungeons[3] = { name = "Nebun Mausoleum",
- xpos = 54,
- ypos = 24,
- startx = 11, starty = 2, startlevel = 1,
- startfx = 0, startfy = 1,
- level = {
- [1] = {
- --1234567890123456789012
- " E C # C E C C # E ",--1
- " # # # # # # # # # ",--2
- " D ############### D ",--3
- " # ",--4
- " C # C # # # C # C ",--5
- " ################# ",--6
- " C # # C C C # # C ",--7
- " # # ",--8
- " ##### ###### ",--9
- " ## #C C# #D ",--0
- },
- [2] = { --1 2
- --1234567890123456789012
- " ##D D###### ",--1
- " ## D # ",--2
- " U # C## #### U ",--3
- "# C ###C # # # ",--4
- "###### #####C## ##C ",--5
- "# C # # # # ",--6
- " C #### ### #####C",--7
- " #### #C ## #### # ",--8
- "## ## ### # ### ",--9
- " ### C ##C C## U #C",--0
- },
- [3] = {
- --1234567890123456789012
- " U ####U ",--1
- " # U # # ",--2
- " # # # # ",--3
- " # #### # ",--4
- " ## # ",--5
- " ######## ",--6
- " ",--7
- " ",--8
- " ",--9
- " ",--0
- }
- },
- creatures = {
- },
- chests = {
- {xpos = 11+2, ypos = 7+2, zpos = 1, content="compass"},
- {xpos = 9+2, ypos = 7+2, zpos = 1, content="item"},
- {xpos = 19+2, ypos = 7+2, zpos = 1, content="item"},
- {xpos = 15+2, ypos = 5+2, zpos = 1, content="item"},
- {xpos = 3+2, ypos = 5+2, zpos = 1, content="item"},
- {xpos = 5+2, ypos = 10+2, zpos = 2, content="item"},
- {xpos = 4+2, ypos = 6+2, zpos = 2, content="item"},
- {xpos = 11+2, ypos = 3+2, zpos = 2, content="item"},
- {xpos = 22+2, ypos = 7+2, zpos = 2, content="item"},
- {xpos = 15+2, ypos = 10+2, zpos = 2, content="item"},
- {xpos = 3+2, ypos = 4+2, zpos = 2, content="item"},
- },
- creaturesSpawn = true,
- playerHasMap = false,
- playerHasCompass = false,
- visible = true
- }
- --Dungeon 4: Lichen Cave. Associated with the quest "The Empty Larder"
- dungeons[4] = { name = "Lichen Cave",
- xpos = 59,
- ypos = 17,
- startx = 2, starty = 1, startlevel = 1,
- startfx = 1, startfy = 0,
- level = {
- [1] = {
- --123456789
- "E## #### ",--1
- " ### #C",--2
- " C# ### ",--3
- "C ### # ",--4
- "#### ###",--5
- " # # C# D",--6
- "## ## ",--7
- " C ###C "--8
- }, [2] = {
- --123456789
- "D# ### C ",--1
- " ### ####",--2
- " # # ",--3
- "###C # #",--4
- "# C####",--5
- "#### # B",--6
- " # ##### ",--7
- "D# # C "--8
- }, [3] = {
- --123456789
- "U# C#####",--1
- " ### # #",--2
- "# #C ",--3
- "##### #C",--4
- "#C# #### ",--5
- " # #U",--6
- " ### ",--7
- "U# #####E"--8
- },
- },
- creatures = { },
- chests = {
- {xpos = 6+2, ypos = 6+2, zpos = 1, content="compass"},
- {xpos = 4+2, ypos = 1+2, zpos = 3, content="map"},
- {xpos = 8+2, ypos = 1+2, zpos = 2, content="item"},
- {xpos = 2+2, ypos = 8+2, zpos = 1, content="item"},
- {xpos = 2+2, ypos = 5+2, zpos = 3, content="item"},
- },
- creaturesSpawn = true,
- playerHasMap = false,
- playerHasCompass = false,
- visible = true
- }
- MKFIL darklands/scripts/equipment
- WRITE 311
- --[[
- Equipment Script
- This defines all possible items the player can own, carry and equip, as well as enchantments that can be
- applied to them.
- ]]--
- --An item contains the following fields:
- --[[name:string = the name of the item, as it appears in one's inventory. Take care not to make too long
- desc:string = a short description of the item's appearance
- bodypart:string = can be any of the following:
- - swordhand: usually weapons
- - offhand: usually dual-wield weapons, shields or other tools like torches
- - both: usually two-handed weapons
- - head: helmets, cowls and the like
- - chest: chestplates, robes, tunics
- - legs: greaves, pants
- - feet: boots, shoes
- level:int = at what level this item will start appearing in loot- it apppears 3 levels earlier in stores
- requirements:table = a list of tables, including:
- name:string = can be endurance, strength, agility, speed, level
- level:int = the level the aforementioned trait needs to be to equip the item.
- base:int = WEAPONS, the base damage they do, SHIELDS, the normalized percentage of block effectiveness
- ARMOUR, the base AC they provide the player with
- enchantment:table = the enchantment the item has, if any. Note- you should NEVER apply an enchantment in
- this screen, but in the RPG script itself.
- enchantTypes:table = a list of tags indicating which enchantment classes the item can carry
- value:int = the item's base gold value to merchants
- weight:int = how much the item weights
- ]]--
- table.insert(items, { name = "Copper Dagger",
- class = "weapon",
- desc = "A copper dagger, weathered and nicked from age.",
- bodypart = "swordhand",
- level = 1,
- requirements = { },
- base = 6,
- enchantment = nil,
- enchantTypes = {"#blade", "#metal", "#aged"},
- value = 5,
- weight = 5
- })
- table.insert(items, { name = "Wooden Staff",
- class = "weapon",
- desc = "A solid but soft yew staff, made from a fallen branch.",
- bodypart = "both",
- level = 1,
- requirements = { },
- base = 8,
- enchantment = nil,
- enchantTypes = {"#wood", "#aged"},
- value = 2,
- weight = 8
- })
- table.insert(items, { name = "Iron Gladius",
- class = "weapon",
- desc = "Shortsword of antiquity.",
- bodypart = "swordhand",
- level = 3,
- requirements = { },
- base = 8,
- enchantment = nil,
- enchantTypes = {"#iron", "#blade", "#aged"},
- value = 15,
- weight = 6
- })
- table.insert(items, { name = "Quarterstaff",
- class = "weapon",
- desc = "An oaken staff, without ornament.",
- bodypart = "both",
- level = 3,
- requirements = { },
- base = 11,
- enchantment = nil,
- enchantTypes = {"#wood"},
- value = 12,
- weight = 9
- })
- table.insert(items, { name = "Practice Buckler",
- class = "shield",
- desc = "A fist-sized shield made from wooden boards, repurposed from sword training.",
- bodypart = "offhand",
- level = 1,
- requirements = { },
- base = 0.5,
- enchantment = nil,
- enchantTypes = {"#wood", "#aged"},
- value = 8,
- weight = 4
- })
- table.insert(items, { name = "Hessian Robe",
- class = "armour",
- desc = "Old hessian robes, with flax fastenings and cotten piping.",
- bodypart = "chest",
- level = 1,
- requirements = { },
- base = 0,
- enchantment = nil,
- enchantTypes = {"#fabric", "#aged"},
- value = 3,
- weight = 8
- })
- table.insert(items, { name = "Sandals",
- class = "armour",
- desc = "Well worn flax sandals.",
- bodypart = "feet",
- level = 1,
- requirements = { },
- base = 0,
- enchantment = nil,
- enchantTypes = {"#fabric", "#aged"},
- value = 1,
- weight = 3
- })
- table.insert(items, { name = "Iron Mace",
- class = "weapon",
- desc = "An iron-shafed mace with a droplet-shaped head.",
- bodypart = "swordhand",
- level = 4,
- requirements = { },
- base = 10,
- enchantment = nil,
- enchantTypes = {"#mace", "#iron"},
- value = 12,
- weight = 9
- })
- --[[SECTION: Enchantments]]--
- --[[An enchantment can either encompass a specific enchantment, one that can be applied to an item, or
- represent a class of enchantments that may apply to a number of different items. For example, being a
- rough item is an enchantment that hurts durability of cloth items, but #fabric is a class that encomasses
- any number enchantments that can apply to fabric items- roughness being one of them
- When items are generated there is a probablity (or it can be forced) that a random enchantment is applied
- to them, and the use of class tags allows the scripter to specify which enchantments are allowed.
- Nesting classes of enchantments is okay as well- an iron weapon is still made of metal, so exclusive iron
- enchantments like being cast or wrought iron apply as well as metal ones like purity and blistering.
- ]]--
- enchantments["#blade"] = {"dull", "sharp", "ornamental"};
- enchantments["#metal"] = {"pure", "impure", "blistered", "smooth"};
- enchantments["#wood"] = {"rotting", "firm"};
- enchantments["#iron"] = {"#metal", "pig", "wrought", "cast"};
- enchantments["#fabric"] = {"rough", "quality"};
- enchantments["#aged"] = {"enduring", "weakened"};
- enchantments["#mace"] = {"flanged", "ornamental"};
- --[[An enchantment specific can have the following properties:
- - name:string = the name the enchantment appears as when the item is looked at
- - desc:string = a short description of the enchantment, appended to the existing item description
- - prefix:bool = if the enchantment name is added to the front or back of the item name: "Dull Sword" vs. "Sword of Dullness"
- - skill:string = what the enchantment effects- can be a user atttribute or an item attribute
- - effect:int = the amount to be added (or removed) from the specified attribute
- - level:int = the level at which these enchantments begin to appear on weapons- merchants 3 levels earlier
- - value:number = the percentage by which this modifies the item's value- 1 leaves it the same, 0.5 to 50% etc.
- ]]--
- enchantments["dull"] = {
- name = "Dull",
- desc = "The edge of the blade has dulled.",
- prefix = true,
- skill = "base",
- effect = -2,
- level = 1,
- value = 0.5
- }
- enchantments["sharp"] = {
- name = "Sharp",
- desc = "The edge of the blade is very sharp.",
- prefix = true,
- skill = "base",
- effect = 2,
- level = 1,
- value = 1.5
- }
- enchantments["pure"] = {
- name = "Pure",
- desc = "It's pure metal base has left it well balanced.",
- prefix = true,
- skill = "speed",
- effect = 1,
- level = 1,
- value = 1.7
- }
- enchantments["impure"] = {
- name = "Impure",
- desc = "It's impure metal base has left it unbalanced.",
- prefix = true,
- skill = "speed",
- effect = -1,
- level = 1,
- value = 0.3
- }
- enchantments["blistered"] = {
- name = "Blistered",
- desc = "It has not been well forged, and the metal is inconsistent throughout.",
- prefix = true,
- skill = "strength",
- effect = -1,
- level = 1,
- value = 0.25
- }
- enchantments["smooth"] = {
- name = "Smooth",
- desc = "The metal was carefully forged, and is free of imperfections.",
- prefix = true,
- skill = "strength",
- effect = 1,
- level = 1,
- value = 1.75
- }
- enchantments["rotting"] = {
- name = "Rotten",
- desc = "Moisture has significantly weakened the strength of the wood.",
- prefix = true,
- skill = "strength",
- effect = -1,
- level = 1,
- value = 0.25
- }
- enchantments["firm"] = {
- name = "Firm",
- desc = "Particularly hard wood was used in its crafting.",
- prefix = true,
- skill = "strength",
- effect = 1,
- level = 1,
- value = 1.75
- }
- enchantments["pig"] = {
- name = "Pig",
- desc = "The unrefined iron is quite brittle.",
- prefix = true,
- skill = "durability",
- effect = 0.5,
- level = 1,
- value = 0.75
- }
- enchantments["wrought"] = {
- name = "Wrought",
- desc = "The bloomer-smelted iron is sturdy but quite heavy.",
- prefix = true,
- skill = "weight",
- effect = 1.2,
- level = 1,
- value = 0.8
- }
- enchantments["cast"] = {
- name = "Hard",
- desc = "Though a little heavy, you feel the strength the forge lends the iron.",
- prefix = true,
- skill = "strength",
- effect = 2,
- level = 1,
- value = 2.5
- }
- enchantments["rough"] = {
- name = "Rough",
- desc = "The weave of the material is inconsistent and holes have appeared.",
- prefix = true,
- skill = "endurance",
- effect = -1,
- level = 1,
- value = 0.4
- }
- enchantments["quality"] = {
- name = "of Quality",
- desc = "The careful weave has given the fabric extra strength.",
- prefix = false,
- skill = "endurance",
- effect = 1,
- level = 1,
- value = 1.6
- }
- enchantments["enduring"] = {
- name = "Enduring",
- desc = "Quality has been well preserved despite the advanced age.",
- prefix = true,
- skill = "durability",
- effect = 1.5,
- level = 2,
- value = 1.2
- }
- enchantments["weakened"] = {
- name = "of Age",
- desc = "After years of use, it is now a shadow of it's former self.",
- prefix = false,
- skill = "durability",
- effect = 0.5,
- level = 1,
- value = 0.1
- }
- enchantments["flanged"] = {
- name = "Flanged",
- desc = "The sharp flanges on the mace head make it lethal against armoured opponents.",
- prefix = true,
- skill = "base",
- effect = 2,
- level = 4,
- value = 2.2
- }
- enchantments["ornamental"] = {
- name = "Ornamental",
- desc = "Though beautifully decorated, it clearly was not designed for combat.",
- prefix = true,
- skill = "base",
- effect = -4,
- level = 1,
- value = 3.5
- }
- MKFIL darklands/scripts/npc
- WRITE 315
- --I keep a list of "on activation" functions here, to save me writing them out in long-hand later. Dialogue-activated quests are
- --stored here- a second list can be found in the quest script, for quest-actiavted quests (there's a mouthful :P)
- local function onActivateReport(me)
- printTownInterface()
- displayConfirmDialogue("Quest Offer by Petre: ", "Yes, as you may know there is a plan here to dispel the darkness of our valley, but it is rather... demanding of our people.")
- local response = displayOptionDialogue("Quest Offer by Petre: ", "I need a man to collect a report from the members of our community on the progress of this 'plan'. Can you do this?")
- if response == "yes" then
- activateQuest(quests["The Report"])
- setNPCDialogue(me.name, 1, "#ongreet", "I trust you are here to deliver to me the report, or do you have other questions about it?")
- setNPCDialogue(me.name, 1, "#quest", "You will need to speak with Fane, Matheus, Noff and Jocelyn. Ask them to report on their progress, then bring that information to me.")
- setNPCDialogue(me.name, 1, "#fane", "Fane can be found on the north side of town. You may visit him there.")
- setNPCDialogue(me.name, 1, "#matheus", "Matheus works as a smith in the merchant district to the north. I am particularly eager to here of his progress.")
- setNPCDialogue(me.name, 1, "#noff", "I believe Noff is currently in town. If so he is probably in the courtyard near the town gates.")
- setNPCDialogue(me.name, 1, "#jocelyn", "I don't know where Jocelyn is. You may need to enquire.")
- return "Good. You will need to speak with 4 people- Fane the merchant, Matheus the smith, Noff the farmer and Jocelyn the woodsman. Simply ask them about the report and return with their responses."
- else
- return "Then leave. I have no further use for you."
- end
- end
- --You can use in-text "#name", it will replace it with the player's name, and in-text "#class" for the player's class.
- --Dialogues are flexible. They can be:
- --Strings: This will simply display the message as though the NCP is delivering it
- --Tables: Work the same as strings but will cycle through each message, giving user the ability to respond at the end of the conversation.
- --Functions: These can do... anything you like really. Usually used for talking with NPC's and simultaneously updating info (such as quests). Functions can return
- --Strings and tables, which work the same way as they do for typical dialogues, or nil, which will break out of the conversation immediately.
- --Global dialogue is the core of most NPC's logic in conversation. This gives the most basic of functions, and therefore
- --I recommend not playing with it too much. Information the entire world knows or specific functioanlity you want to add to
- --convesations can be added here but I don't recommend messing with it too much.
- globalDialogue = {
- ["#name"] = function(me)
- local r = math.random(1,3)
- if r == 1 then return "You can call me "..me.name.."."
- elseif r == 2 then return "My name is "..me.name.."."
- elseif r == 3 then return "I am "..me.name.."." end
- end;
- ["#job"] = function(me)
- if me.job == "smith" then
- return "I'm a smith. I work a forge and produce weapons and armour. I can trade my inventory with you if you like."
- elseif me.job == "merchant" then
- return "I'm a merchant, buying goods from adventurers like yourselves and selling them to interested buyers. I'll trade good gold for any items you're carrying."
- elseif me.job == "mystic" then
- return "I'm a mystic, a trainer in the arts of magic. I can teach you spells, for a fee."
- else
- local article = "a"
- if isVowel(string.sub(me.job, 1, 1)) then article = "an" end
- return "I work as "..article.." "..me.job.."."
- end
- end;
- ["#hello"] = "#ongreet";
- ["#bye"] = "#onfarewell";
- ["#goodbye"] = "#onfarewell";
- ["#exit"] = "#onfarewell";
- ["#farewell"] = "#onfarewell";
- ["#leave"] = "#onfarewell";
- ["#work"] = "#quest";
- ["#quest"] = "I am in no need of your help, #name.";
- }
- --INDEX 1: NEBUN
- --The Mad Town. Home to a series of eccentrics that worship false gods and sacrifice food, supplies and life
- --to appease them.
- town[1].dialogue = {
- ["#transylvania"] = "What would we know of the world around us? A pit of evil and darkness. We keep to our town, and ward off the dark as best as we can.";
- ["#nebun"] = "Our town, ruled by Donmur Petre. Fane and Matheus are our merchants, and Hemlar our mystic. Do not linger here long. We care not for strangers.";
- ["#groazagol"] = "I know nothing of that place, nor any other outside these walls.";
- ["#darkness"] = "#dark";
- ["#dark"] = "Forces of unimaginable evil are responsible for the shroud. But we will banish it. Our Lord has a plan.";
- ["#plan"] = "It is not to be discussed with outsiders.";
- ["#lake"] = "Our water is drawn from Lake Cavril, but it too is plagued with evil. We drink only once it is boiled.";
- ["#cemetary"] = "#graveyard";
- ["#graveyard"] = "We bury our dead at the mouth of the river, where life flows from the mountains to us.";
- ["#mountain"] = "The ranges to the south contain as much evil as they keep at bay.";
- ["#carpathian"] = "#mountain";
- ["#forest"] = "They extend for miles, in all directions.";
- }
- table.insert(town[1].npc, { name = "Helmar",
- job = "mystic",
- xpos = 13, ypos = 10,
- level = 1,
- dialogue = {
- ["#onfirstgreet"] = "What is this face, that comes before my tired eyes? Be ye demon or angel, or neither?";
- ["#ongreet"] = "Must the lights endlessly torment me, like fireflies over the grave?";
- ["#name"] = "Did you ask my name, or was it just the whisper of the wind? I shall answer neither, demon.";
- ["#demon"] = "You see what you are, what you shall become? Perhaps you come in charity or goodwill but you are just a tool of fate, a knot in the weave.";
- ["#job"] = "Silver light surrounds all, consumes my work and my thoughts. It is all I know, all I can do. I cannot help you...";
- ["#jocelyn"] = function(me)
- me.dialogue["#jocelyn"] = "I pray he has found peace... remember to bring him the tonic."
- activateQuest(quests["The Unwilling Mage"])
- printTownInterface()
- displayConfirmDialogue(me.name.." the "..me.job.." says:", "Jocelyn... the woodsman, I remember. He came to me, to speak, for he too has the gift, like I. He is a lunamagus.")
- printTownInterface()
- displayConfirmDialogue(me.name.." the "..me.job.." says:", "So terrified, and for good reason- the talent threatened to rip him apart, as it does to all cursed with it.")
- return "He fled northwest, into the woods. If you can, find him and bring him this tonic. It should ease his mind."
- end;
- ["#onfarewell"] = "Yes... let me rest my eyes, my poor eyes...";
- },
- useGeneral = false,
- nodialogue = "Dreams and riddles, mysteries and enigmas. Must they plague me?",
- })
- table.insert(town[1].npc, { name = "Fane",
- job = "merchant",
- xpos = 9, ypos = 6,
- level = 1,
- dialogue = {
- ["#onfirstgreet"] = "You're a new face- quite a relief to meet you! I am Fane, merchant here in Nebun.";
- ["#ongreet"] = "Greetings once again #name, how do you fare today? If you've got some extra goods on you I'd gladly trade.";
- ["#name"] = "I am Fane. Not sure how I came by the name.";
- ["#nebun"] = "Hostile to strangers isn't it? I'm an outsider as well- and it is no coincidence my business is poor.";
- ["#sell"] = "#trade";
- ["#jocelyn"] = "I wish I knew where he was, he was my only friend in Nebun. He left mysteriously some days ago- I last saw him speaking with the mystic, Helmar.";
- ["#plan"] = "I don't mean to be evasive stranger, but I fear the consequences should I divulge any detail. We all trust in Petre and the Gods to do what is right.";
- ["#trade"] = function(me)
- if #inventory == 0 then
- return "You have nothing to sell? I'm afraid we can't do business then."
- else
- printTownInterface()
- displayConfirmDialogue(me.name.." the "..me.job.." says:", "Show me what you've come up with, I'll see what offers I can make.")
- runTradeInterface(me, true)
- return "Hope to see you again- come back if you find anything valuable."
- end
- end;
- ["#onfarewell"] = "Good travels to you, #name."
- },
- useGeneral = true,
- nodialogue = "I'm not sure what you're talking about, friend. Maybe someone else does?",
- inventory = { },
- priceModifier = 0.8,
- goldLimit = 50
- })
- table.insert(town[1].npc, { name = "Matheus",
- job = "smith",
- xpos = 2, ypos = 4,
- level = 1,
- dialogue = {
- ["#onfirstgreet"] = "A new arrival... you were allowed into the town? Well, I am Matheus, smith here." ;
- ["#ongreet"] = "You've returned... what is it you want?";
- ["#name"] = "My name is Matheus.";
- ["#job"] = "I have been smith here ten and five years now. I make iron and trade some of my inventory.";
- ["#father"] = "If you wish to remain in my good graces stranger, do not mention my father to me.";
- ["#jocelyn"] = "A fool, no doubt. He has fled some nights ago, though I do not know where to. Were it up to me, his head would adorn my shop window.";
- ["#buy"] = "#trade";
- ["#trade"] = function(me)
- if #me.inventory == 0 then
- return "I have no stock to trade, stranger."
- else
- printTownInterface()
- displayConfirmDialogue(me.name.." the "..me.job.." says:", "Very well, but be quick. It would not do to be seen trading with an outsider.")
- runTradeInterface(me, false)
- return "Right. Take your purchases and please leave."
- end
- end;
- ["#onfarewell"] = "Good travels to you, #name."
- },
- useGeneral = true,
- nodialogue = "On this subject I know nothing.",
- inventory = { },
- priceModifier = 2.5,
- goldLimit = 0
- })
- table.insert(town[1].npc, { name = "Noff",
- job = "farmer",
- xpos = 25, ypos = 7,
- level = 1,
- dialogue = {
- ["#onfirstgreet"] = "Ah... you are not from here? Well... I am Noff. Greetings.";
- ["#ongreet"] = "Yes, #name, of course. You don't plan to stay long do you?";
- ["#name"] = "It's Noff. Can you get to the point?";
- ["#jocelyn"] = "He was here and now he is not. I neither know nor care where he is now.";
- ["#job"] = "I grow grain outside the city walls. Now if you'll excuse me...";
- ["#trade"] = "I do not trade with outsiders, you may find your own grain.";
- ["#onfarewell"] = "Until next time.";
- },
- nodialogue = "I have no idea. Now please, leave me.";
- })
- table.insert(town[1].npc, { name = "Kershel",
- job = "fool",
- xpos = 11, ypos = 14,
- level = 1,
- dialogue = {
- ["#onfirstgreet"] = "Ah another fool to arrive in a town already full. I am Kershel, this one's fool named.";
- ["#ongreet"] = "You return... what is it you wish of me?";
- ["#name"] = "I am known by many names, but best by Kershel.";
- ["#job"] = "My trade is my own, one characterized by ignorance, and rewarded with bliss.";
- ["#kershel"] = "The town fool, at your service.";
- ["#petre"] = "Though you may not yet know, stranger, you have wandered into a town of children, and Donmur Petre is the piper that will lead them to their doom.";
- ["#matheus"] = "A rolling stone that has gathered much moss.";
- ["#helmar"] = "Proof that the wisest men choose to be fools, in fear of too much wisdom.";
- ["#jocelyn"] = "An enigma is it not? Very few come to Nebun but fewer leave... someone must know his secret.";
- ["#fane"] = "A foreign man from a foreign land. Like you.";
- ["#plan"] = "Oh, word has reached your ear of Petre's plan? It too has reached mine. We can only pray it never comes to fruit.";
- ["#onfarewell"] = "Shall we meet again? Only time will tell...";
- },
- useGeneral = true,
- nodialogue = "A wise man asks questions, but a fool asks questions and doesn't listen."
- })
- table.insert(town[1].npc, { name = "Erika",
- job = "widow",
- xpos = 24, ypos = 14,
- level = 1,
- dialogue = {
- ["#onfirstgreet"] = "Oh please, sir, can you please help me?";
- ["#ongreet"] = "Please sir... please help me...";
- ["#help"] = "#quest";
- ["#name"] = "I am Erika, sir.";
- ["#job"] = "I... I was a faithful wife for many years, but now I am alone.";
- ["#joachim"] = "I don't know... I'm sorry.";
- ["#quest"] = function(me)
- printTownInterface()
- displayConfirmDialogue("Erika the window says:", "Must fate be so cruel to those of us who mean no harm? So fearful I was of starving this winter I asked my husband to hunt one eve a month ago.")
- local response = displayOptionDialogue("Erika the widow says:", "But I came upon him two weeks ago, little more than a skeleton! Please, please would you put my dear husband's bones to rest?")
- if response == "yes" then
- me.dialogue["#ongreet"] = "Sir, you have returned from the mausoleum?"
- me.dialogue["#onfarewell"] = "Farewell, kind sir."
- activateQuest(quests["Laid to Rest"])
- printTownInterface()
- displayConfirmDialogue("Erika the widow says:", "Thank you sir! Please, take his remains to the mausoelum south east of here, where he may rest with his ancestors.")
- return "It's not much, but take this map, to help you find your way. His bones need rest on the third floor."
- else
- return "Please sir... I beg of you!"
- end
- end;
- ["#onfarewell"] = "Please don't leave... I beg of your help...";
- },
- useGeneral = true,
- nodialogue = "I don't know... I don't know what you speak of."
- })
- table.insert(town[1].npc, { name = "Donmur Petre",
- job = "ruler",
- xpos = 5, ypos = 14,
- level = 1,
- dialogue = {
- ["#onfirstgreet"] = "A new traveller to these lands. Know outsider, we do not customarily deal with your kind, and were it for the darkness, your head would be resting on a spike.";
- ["#ongreet"] = "So you have returned, not yet consumed by the darkness I see. I trust your reason to see me is important?";
- ["#dark"] = "This shroud has laid over our town for over a year, and the people have grown fearful, but there is yet hope. I have a plan. Perhaps you wish to help?";
- ["#job"] = "I am ruler of Nebun, priest of our church and ward from the forces of evil. You no doubt wish to ask if I have work for you?";
- ["#kershel"] = "Pay little heed to that fool. He entertains me from time to time, but speaks dangerous nonsense when free of my presence.";
- ["#Petre"] = "That is my name. What do you want?";
- ["#fane"] = "He is not from here. We have allowed him to stay, and ply his trade but he is not well liked.";
- ["#matheus"] = "Matheus has been smithy for this town for 5 and 10 years. I mourn the passing of his father, as we all do here.";
- ["#helmar"] = "He is a lunamagus, and his mind is well lost. But if you are patient you may gleam something of use from him.";
- ["#trade"] = "You shall find traders in our town- look for their storefronts in the north. Do not linger long however, lest you make the townsfolk nervous.";
- ["#onfarewell"] = "Return only if the need is urgent. We care not for strangers.";
- ["#plan"] = "This is something I, nor anyone here will discuss with you. Should you need to know, you will when the time is right.";
- ["#help"] = "#quest";
- ["#quest"] = onActivateReport;
- ["#report"] = "#quest";
- },
- useGeneral = true,
- nodialogue = "I know not what you speak of.",
- })
- --INDEX 2: GROAZAGOL
- --The staging post for Lord Jeirbe's assault. Currently unimplemented
- --INDEX 3: JOCELYN'S SHACK
- --The hiding place of a frightened mage
- table.insert(town[3].npc, { name = "Jocelyn",
- job = "woodsman",
- xpos = 17, ypos = 11,
- level = 1,
- dialogue = {
- ["#onfirstgreet"] = "#ongreet";
- ["#ongreet"] = "Why have you returned? I have nothing for you, please leave me to my fate. I fear being in any other's present, should I harm them...";
- ["#name"] = "What good is a name to me now, in a world where none can so much as be near me? I was once Joachim... but I am just an abomination now.";
- ["#job"] = "I was once a woodsman, and a fine one at that. But now I am little more than a rat in a warren, hiding from the world.";
- ["#lunamagus"] = "A gift, Helmar called it. To summon the light of the moon into one's body and project it as magic. The 'gift' has already take 2 lives.";
- ["#tonic"] = function(me)
- addXP(20)
- addGold(10)
- me.dialogue["#tonic"] = "It has helped to calm my nerves somewhat... thank you again for bringing it to me."
- me.dialogue["#ongreet"] = "Ah... #name, hello again. What can this poor lunamagus do for you?"
- return "One of Helmar's tonics? He is a kind man, and he truly understands my troubles. Thank you- please take this as thanks."
- end
- },
- useGeneral = false,
- nodialogue = "I can't answer your question.",
- })
- --INDEX 4: MOUNTAIN CAMP
- --The epilogue for the game
- table.insert(town[4].npc, { name = "Nestaayha",
- job = "stranger",
- xpos = 17, ypos = 11,
- level = 1,
- dialogue = {
- ["#onfirstgreet"] = function(me)
- printTownInterface()
- displayConfirmDialogue("A stranger appears", "You're not sure what brought you here, but there is a small man, dressed in black, standing outside a campfire.")
- printTownInterface()
- displayConfirmDialogue("Nestaayha says:", "So you have found me at last, no idea why I suppose... no matter, of course this will all come in time. You have no idea the role in which you are cast?")
- printTownInterface()
- displayConfirmDialogue("Nestaayha says:", "You have helped the fool Petre with his idiotic plan to dispel the clouds over the Darklands- see how he fruitlessly attempts to stop it!")
- printTownInterface()
- displayConfirmDialogue("You can see:", "Staring through the pitch you can just make out Nebun- it is alight! The town has been set on fire!")
- printTownInterface()
- displayConfirmDialogue("Nestaayha says:", "This is no simple darkness... this is a curse. An insidious, evil spell created by one of the blackest creatures in Transylvania. I am his advisor.")
- printTownInterface()
- displayConfirmDialogue("Nestaayha says:", "You shall come to know me in time... but for now you must be patient. Leave this place, and watch the skies for a sign. When the weave is ready for you, you will know.")
- printTownInterface()
- gameOver = true
- return "#farewell"
- end;
- ["#onfarewell"] = "This concludes the Darklands: Tales from Transylvania demo. Hope you enjoyed it! -NF"
- }
- })
- MKFIL darklands/scripts/quest
- WRITE 335
- --Quest 1: The Report - gather information regarding the progress of Petre's grand plan
- quests["The Report"] = { name = "The Report",
- activestage = 1,
- variables = { fane = false, matheus = false, noff = false, jocelyn = false, grainActivated = false},
- generalDescription = "Donmur Petre has asked you gather information regarding his secret plan",
- stages = {
- [1] = {
- desc = "Ask for a report from the following people: Fane Matheus Noff Jocelyn",
- condition = function(quest, event)
- local retstring = nil
- if event.type == "dialogue" and event.town == 1 and event.npc.name == "Matheus" and findIn(event.topic, "report")
- and not quest.variables.matheus then
- setNPCDialogue("Matheus", 1, "#report", "You have my response. Return it to Petre, if you have not already.")
- quest.variables.matheus = true
- retstring = "You are collecting the report? Well you may inform Petre that I have met expectations, and the equipment he requires is almost complete."
- end
- if event.type == "dialogue" and event.town == 1 and event.npc.name == "Fane" and findIn(event.topic, "report") and
- not quest.variables.fane then
- setNPCDialogue("Fane", 1, "#report", "I hope Petre is satisfied with my progress.")
- quest.variables.fane = true
- printTownInterface()
- displayConfirmDialogue("Fane the merchant says:", "Petre has trusted you with this task? I am surprised- it is something he would not usually so much as discuss with an outsider...")
- retstring = "You may inform him that my accounts are in order and my earnings are... sufficient, if a little below expectations. I will send him a full account soon."
- end
- if event.type == "dialogue" and event.town == 3 and event.npc.name == "Jocelyn" and findIn(event.topic, "report") and
- not quest.variables.jocelyn then
- quest.variables.jocelyn = true
- setNPCDialogue("Jocelyn", 3, "#report", "Still you pester me with this? I have nothing for Petre, nor shall I. He can find another unwitting soul to fulfill his lunacy.")
- return "Petre's report? You may tell him I am no longer woodsman of his town, in fact you may tell him I am dead, or as good as. He shall receive no aid from me."
- end
- if event.type == "dialogue" and event.town == 1 and event.npc.name == "Noff" and findIn(event.topic, "report") and
- not quest.variables.noff then
- if not quest.variables.grainActivated then
- printTownInterface()
- displayConfirmDialogue("Noff the farmer says:", "Oh, you're here about that... there is a small problem. I have reached expectations, but a great deal of my grain has been stolen, by Tyrats...")
- local response = displayOptionDialogue("Noff the farmer says:", "Of course, if you would be willing to find their lair and retrieve my grain... that would be a great help to me.")
- if response == "yes" then
- retstring = "Ah... what a relief. The tyrats took it somewhere to the west. Those cave-dwelling fools will pay for their larceny."
- activateQuest(quests["Fuel for the Pyre"])
- quest.variables.grainActivated = true
- else
- retstring = "Well without your help I cannot complete my report for Petre. So it is up to you."
- end
- elseif quests["Fuel for the Pyre"].activestage == -1 then
- quest.variables.noff = true
- setNPCDialogue("Noff", 1, "#report", "I have nothing more to say on the matter. Take the information to Petre, and quickly.")
- retstring = "Yes, the report. Well thanks to you I am at least somewhat prepared for the report. You may give Petre this- my current reserves."
- else
- retstring = "I can't discuss my progress without that grain. Please find it."
- end
- end
- local newdesc = "Ask for a report from the following people:"
- if not quest.variables.fane then newdesc = newdesc.." Fane" end
- if not quest.variables.matheus then newdesc = newdesc.." Matheus" end
- if not quest.variables.noff then newdesc = newdesc.." Noff" end
- if not quest.variables.jocelyn then newdesc = newdesc.." Jocelyn" end
- quest.stages[1].desc = newdesc
- if quest.variables.fane and quest.variables.matheus and quest.variables.noff and quest.variables.jocelyn then
- quest.activestage = 2
- printTownInterface()
- displayConfirmDialogue(event.npc.name.." the "..event.npc.job.." says:", retstring)
- retstring = "I think you've spoken to everyone now- you should return your report to Donmur Petre."
- end
- return retstring
- end
- }, [2] = {
- desc = "Return to Donmur Petre with the completed report.",
- condition = function(quest, event)
- if event.type == "dialogue" and event.town == 1 and event.npc.name == "Donmur Petre" and findIn(event.topic, "report", "quest") then
- quest.activestage = -1
- addXP(150)
- addGold(20)
- setNPCDialogue("Donmur Petre", 1, "#report", "Thank you for returning the report to me.")
- setNPCDialogue("Donmur Petre", 1, "#quest", function(me)
- printTownInterface()
- displayConfirmDialogue(me.name.." the "..me.job.." says:", "Part of my plan calls for a specific kind of plant that grows in the caves near here. It is hazardous to handle, but very useful.")
- printTownInterface()
- local response = displayOptionDialogue(me.name.." the "..me.job.." says:", "Would you consider visiting Lichen cave to collect it?")
- if response == "yes" then
- activateQuest(quests["The Empty Larder"])
- setNPCDialogue("Donmur Petre", 1, "#lichen", "#quest")
- setNPCDialogue("Donmur Petre", 1, "#quest", "Please make haste in collecting the lichen. It grows in a cave east of here.")
- return "Good. Go to Lichen Cave on the river a bit east of here, and gather up as much as you can. Just be careful with it..."
- else
- return "Very well. If you do not wish to help, I would ask you leave our town. There is no place here for those who cannot work."
- end
- end)
- return "It sounds as though all is going according to plan. I thank you, stranger. If you insist on staying I have another task for you."
- end
- end
- }
- }
- }
- --Quest 2: The Unwilling Mage - find Jocelyn, the woodsman
- quests["The Unwilling Mage"] = { name = "The Unwilling Mage",
- activestage = 1,
- variables = { },
- generalDescription = "Helmar the mystic has told you that Jocelyn fled upon learning he was a lunamagus.",
- onActivation = function(quest)
- town[3].visible = true
- end;
- stages = {
- [1] = {
- desc = "Find Jocelyn, in the woods to the northwest of Nebun",
- condition = function(quest, event)
- if event.type == "dialogue" and event.npc.name == "Jocelyn" then
- quest.activestage = -1
- addXP(50)
- printTownInterface()
- displayConfirmDialogue("Quest Update!", "You have found a man, sitting on a tree stump and staring at the ground morosely.")
- return "You have found me? Helmar has sent you? Well, here I am. I am Jocelyn, the lunamagus... the accursed lunamagus. And you have found me, it seems."
- end
- end
- }
- }
- }
- --Quest 3: Fuel for the Pyre - Gather stolen grain from a nearby cave
- quests["Fuel for the Pyre"] = { name = "Fuel for the Pyre",
- activestage = 1,
- variables = {
- { x = 3+2, y = 3+2, level = 1, message = "This cave has been freshly dug- the scratches are new and the sweaty Tyrat stench has yet to take a firm hold." },
- { x = 7+2, y = 1+2, level = 1, message = "In the darkness you can hear the sounds of twittering and the clink of metal- the tunnel is not empty." }
- },
- generalDescription = "Several bags of grain have been stolen from Noff by Tyrats.",
- onActivation = function(quest)
- subDungeonTile(2, 1+2, 3+2, 1, "C")
- table.insert(dungeons[2].creatures, cloneCreature(3, 11+2, 2+2, 1))
- table.insert(dungeons[2].creatures, cloneCreature(3, 11+2, 6+2, 1))
- table.insert(dungeons[2].creatures, cloneCreature(3, 6+2, 7+2, 1))
- table.insert(dungeons[2].creatures, cloneCreature(3, 1+2, 4+2, 1))
- end;
- stages = {
- [1] = {
- desc = "Find Fell's Cave, east of Nebun",
- condition = function(quest, event)
- if event.type == "move" and event.dungeon == 2 then
- quest.activestage = 2
- return "The sign of pickaxe scratchings and discarded metal strewn about this cave marks it as a lair for Tyrats. This must be the cave Noff spoke of. You take deep breath... and step inside."
- end
- end
- },
- [2] = {
- desc = "Find the missing grain in Fell's Cave",
- condition = function(quest, event)
- local chestLocation = { x = 1+2, y = 3+2, z = 1 }
- if event.type == "move" and event.dungeon == 2 then
- for i,v in ipairs(quest.variables) do
- if event.xpos == v.x and event.ypos == v.y and event.zpos == v.level then
- table.remove(quest.variables, i)
- return v.message
- end
- end
- end
- if event.type == "chest" and event.dungeon == 2 and event.xpos == chestLocation.x and event.ypos == chestLocation.y
- and event.zpos == chestLocation.z then
- quest.activestage = 3
- addGold(15)
- return "Within the chest you find several large sacks of grain, along with a few coins. You gather them up, and prepare to head back to Noff."
- end
- end
- },
- [3] = {
- desc = "Return the grain sacks to Noff in Nebun.",
- condition = function(quest, event)
- if event.type == "dialogue" and event.town == 1 and event.npc.name == "Noff" and findIn(event.topic, "grain", "sack") then
- quest.activestage = -1
- addGold(20)
- addXP(40)
- return "Ah you found my grain. Well, thank you, stranger. This has taken a weight off my mind."
- end
- end
- }
- }
- }
- --Quest 4: Laid to Rest - Leave the bones of a loved one in the local mausoleum
- quests["Laid to Rest"] = { name = "Laid to Rest",
- activestage = 1,
- variables = {
- { x = 4+2, y = 10+2, level = 1, message = "It appears as though there was once a way down here, but the way has since collapsed in. You wonder how safe these halls still are." },
- { x = 20+2, y = 9+2, level = 2, message = "The second part of the mausoleum is much less organized, with the stone passageways sprawling almost randomly." },
- { x = 8+2, y = 2+2, level = 3, message = "All here is deathly still. The air has no feel, and all you can smell is the musty scent of damp earth. It is pitch black." }
- },
- generalDescription = "Erika has asked you lay the bones of her deceased husband to rest in a nearby mausoleum.",
- onActivation = function(quest)
- dungeons[3].playerHasMap = true
- end;
- stages = {
- [1] = {
- desc = "Enter the Nebun Mausoleum, southeast of Nebun.",
- condition = function(quest, event)
- if event.type == "move" and event.dungeon == 3 then
- quest.activestage = 2
- return "Shrouded in dust and grime, you manage to find the ancient stone passageway leading into the crypt. You feel greatly uneasy as you step inside."
- end
- end
- },
- [2] = {
- desc = "Reach the third level of the mausoleum",
- condition = function(quest, event)
- local corpseLocation = { x = 11+2, y = 3+2, z = 3 }
- if event.type == "move" and event.dungeon == 3 then
- for i,v in ipairs(quest.variables) do
- if event.xpos == v.x and event.ypos == v.y and event.zpos == v.level then
- table.remove(quest.variables, i)
- return v.message
- end
- end
- end
- if event.type == "move" and event.dungeon == 3 and event.xpos == corpseLocation.x and event.ypos == corpseLocation.y
- and event.zpos == corpseLocation.z then
- quest.activestage = 3
- dungeons[3].creaturesSpawn = false
- displayConfirmDialogue("Quest Update", "You find an empty spot at last, and carefully place the remains Erika gave you into the space.")
- local wraith = cloneCreature(5, 11+2, 2+2, 3)
- table.insert(dungeons[3].creatures, wraith)
- quest.variables.wraith = wraith
- return "Just as you do, you feel a freezing wind blow past you- the bones rise from their resting place and reassemble themselves into a mosnterous apparition!"
- end
- end
- },
- [3] = {
- desc = "Slay the undead Wraith",
- condition = function(quest, event)
- if event.type == "kill" and event.monster == quest.variables.wraith then
- quest.activestage = 4
- addGold(50)
- return "Your final blow leaves blows the wraith to pieces, leaving nothing but a soft mist in the air. The darkness that consumes this places seems to have lifted."
- end
- end
- },
- [4] = {
- desc = "Speak with Erika in Nebun",
- condition = function(quest, event)
- if event.type == "dialogue" and event.town == 1 and event.npc.name == "Erika" and findIn(event.topic, "ongreet") then
- quest.activestage = -1
- setNPCDialogue("Erika", 1, "#onfarewell", "May you spend eternity in hell, you foolish do-gooder!")
- printTownInterface()
- displayConfirmDialogue("Erika the widow says:", "You fool! You've dispelled the curse I cast on that mausoleum- how can you still be alive! Everything is ruined!")
- printTownInterface()
- displayConfirmDialogue("Quest Update:", "You watch as Erika slowly turns into pure silver light, before disappearing in a flash, with only the whispers of her voice in the wind.")
- setNPCPosition("Erika", 1, -1, -1)
- return "#onfarewell"
- end
- end
- }
- }
- }
- --Quest 5: The Empty Larder - Collect Lichen from a nearby cave
- quests["The Empty Larder"] = { name = "The Empty Larder",
- activestage = 1,
- variables = {
- { x = 9+2, y = 2+2, level = 3, message = "Although the chest is empty, you can see the green lichen Petre mentioned growing around the edges. You gather it into your pack." },
- { x = 4+2, y = 4+2, level = 2, message = "Although the chest is empty, you can see the green lichen Petre mentioned growing around the edges. You gather it into your pack." },
- { x = 9+2, y = 2+2, level = 1, message = "Although the chest is empty, you can see the green lichen Petre mentioned growing around the edges. You gather it into your pack." }
- },
- generalDescription = "Donmur Petre has asked you to collect some special lichen from a nearby caves.",
- onActivation = function(quest)
- dungeons[3].playerHasMap = true
- end;
- stages = {
- [1] = {
- desc = "Find 3 good samples of green lichen from Lichen Cave.",
- condition = function(quest, event)
- if event.type == "chest" and event.dungeon == 4 then
- for i,v in ipairs(quest.variables) do
- if event.xpos == v.x and event.ypos == v.y and event.zpos == v.level then
- table.remove(quest.variables, i)
- if #quest.variables == 0 then
- quest.activestage = 2
- table.insert(quest.variables, "steppedOutside", false)
- displayConfirmDialogue("Quest Update:", v.message)
- return "You have a good collection of lichen now- you should return to Donmur Petre to collect your reward."
- else
- quest.stages[1].desc = "Find "..#quest.variables.." good samples of green lichen from Lichen Cave."
- return v.message
- end
- end
- end
- end
- end
- },
- [2] = {
- desc = "Return the supplies to Donmur Petre",
- condition = function(quest, event)
- if event.type == "move" and not event.town and not event.dungeon and not quest.variables["steppedOutside"] then
- quest.stages[2].desc = "Return empty-handed to Donmur Petre"
- quest.variables["steppedOutside"] = true
- return "Just as you take your first step outside you hear a bang- you pack has exploded! You rush to put it out, but are unable to save the lichen, which seemed to combust as soon as it entered daylight!"
- elseif event.type == "dialogue" and event.town == 1 and event.npc.name == "Donmur Petre" and findIn(event.topic, "lichen", "quest") then
- quest.activestage = -1
- setNPCDialogue("Donmur Petre", 1, "#lichen", "Yes it's too bad about the lichen... it can be somewhat volatile in sunlight.")
- setNPCDialogue("Donmur Petre", 1, "#quest", function(me)
- printTownInterface()
- displayConfirmDialogue(me.name.." the "..me.job.." says:", "As you seem so adept at surviving impossible odds, perhaps you would like to help us retrieve a very important ornamental artefact for our town?")
- printTownInterface()
- local response = displayOptionDialogue(me.name.." the "..me.job.." says:", "It's in a secret cave, filled with treasure to the east of here. Are you willing to help us?")
- if response == "yes" then
- activateQuest(quests["An Impossible Errand"])
- setNPCDialogue("Donmur Petre", 1, "#mace", "#quest")
- setNPCDialogue("Donmur Petre", 1, "#quest", "Fargo's Place is east, past the river. You'll find the mace in there.")
- return "Excellent! I'll mark Fargo's Place on your map- the cave lies east of here. The object is an ornamental mace. Good hunting..."
- else
- return "Very well. If you do not wish to help, I would ask you leave our town. There is no place here for those who cannot work."
- end
- end)
- printTownInterface()
- displayConfirmDialogue("Donmur Elenko the ruler says:", "The lichen exploded? And you survived? Well... that is unfortunate.")
- displayConfirmDialogue("Donmur Elenko the ruler says:", "What was before an insistence is now an order. Our plan is now ready to execute and we shall not have you interfere. You are hereby banished from Nebun, never to return.")
- printTownInterface()
- displayConfirmDialogue("Quest Update!", "With the snap of his fingers, Elenko orders two men to collect you and throw you from the town, closing the town gate behind you.")
- pox = town[1].xpos
- poy = town[1].ypos - 2
- overworld[town[1].ypos-1][town[1].xpos] = "#"
- inTown = false
- activeTown = 0
- printOverworldInterface()
- printWorldMap()
- town[4].visible = true
- setNPCDialogue("Donmur Petre", 1, "#onfarewell", "You are banished. Be gone from here. There is naught left for you.")
- return "#farewell"
- end
- end
- }
- }
- }
- MKFIL darklands/scripts/script
- WRITE 220
- --[[
- Darklands: Tales from Transylvania
- An undead invasion threatens the kingdom of Wallachia- as a Moldavan outlander, you must find and dispel the
- dark curse from the land.
- This file contains the header, onInit methods and any specific features you want your game to hvae that are not
- provided in the standard RPG- overwrites of the interface etc.
- ]]--
- --Note: throughout the script several "print" statements have been scattered to demonstrate progress.
- --These are nonessential, but useful for error detection.
- --A simple single town, with a single quest giver and two storefronts, as well as one random NPC.
- --This demonstrates the primary features of writing a town script, and a simple dungeon with quest queues.
- local function debugDisplay(str)
- term.setCursorPos(1,1)
- print("Debug: "..str)
- os.pullEvent("key")
- end
- --This makes a few little changes- hides certain towns for example
- local function modifyWorld()
- town[2].visible = false
- town[3].visible = false
- town[4].visible = false
- dungeons[1].visible = false
- end
- local function selectClass()
- term.clear()
- local str = "From what vocation do you hail, "..charName.."?"
- term.setCursorPos(w/2 - #str/2, 3)
- term.write(str)
- list = {"Squire", "Urchin", "Journeyman"}
- local sel = 1
- while true do
- for i=1,#list do
- term.setCursorPos(w/2 - #list[i]/2 - 2, 3 + i * 3)
- term.clearLine()
- if sel == i then term.write("[ "..list[i].." ]")
- else term.write(" "..list[i].." ") end
- end
- local id,key = os.pullEvent("key")
- if key == keys.up and sel > 1 then sel = sel - 1
- elseif key == keys.down and sel < #list then sel = sel + 1
- elseif key == keys.space or key == keys.enter then
- charClass = list[sel]
- break
- end
- end
- --All players start with rough robes and sandals
- local robes = cloneItem(6)
- robes.enchantment = cloneEnchantment("rough")
- local sandals = cloneItem(7)
- table.insert(inventory, robes)
- table.insert(inventory, sandals)
- term.clear()
- term.setCursorPos(1, 2)
- if sel == 1 then
- --Squires are peniless but well equipped by their master
- table.insert(inventory, cloneItem(3))
- table.insert(inventory, cloneItem(5))
- charGold = 0
- print(
- [[You have spent most of your natural life sworn to the service of the cruel Turkish defector, Lord Akalay. Bitter and tyrannical to his wards, you spend your days in hard training, and your nights at his beck and call. His drunken temper has left you with many scars.
- When word reached you that Lord Jeirbe was looking for volunteers for a solitary scouting mission, you ran as quickly as you could to beg his audience.]])
- elseif sel == 2 then
- --Urchins have a good weapon but little coin to their name
- local dagger = cloneItem(1)
- dagger.enchantment = cloneEnchantment("sharp")
- table.insert(inventory, dagger)
- charGold = 5
- print(
- [[You've spent your entire life living in the streets of Bucharest, scavenging what little food you can through theft and begging. By chance, you heard word a war party passing through on its way to Transylvania. Hoping to pick from the bones left by the soldiers, you followed in secret.
- For two weeks you lived well, sleeping in caves and trees and even picking up a few lost coins by the soldiers, but one evening you got too bold, and were caught pickpocketing a night guard.
- After a short time as prisoner, Lord Jeirbe approached you personally, with an offer for your freedom.]])
- elseif sel == 3 then
- --Journeymen have pathetic weapons but much more money
- local staff = cloneItem(2)
- staff.enchantment = cloneEnchantment("rotting")
- table.insert(inventory, staff)
- charGold = 30
- print(
- [[As an alchemist in training, you have been sent by your guild to set up a small herbarium at the foot of the Carpathian mountain range. There you have spent 3 months, earning a few pieces of gold and plying your peaceful trade.
- This came to an abrupt end when the war party stormed through your hut on their way north, robbing you of most of your possessions and forcing you to join their ranks. You remained unwilling prisoner in the garrison, treating the other soldiers until today- Lord Jeirbe has summoned you to speak with him.]])
- end
- for i=1,#inventory do
- equipItem(i)
- end
- term.setCursorPos(1, h)
- term.write("Type any key to continue")
- os.pullEvent("key")
- term.clear()
- wprintOffCenter("This is the demo of Darklands: Tales from Transylvania.", 3, w, 0)
- wprintOffCenter("You should start by heading to Nebun, a small village north of here.", 6, w, 0)
- wprintOffCenter("Good luck...", 8, w, 0)
- term.setCursorPos(1, h)
- term.write("Type any key to continue")
- os.pullEvent("key")
- sleep(1)
- end
- local function onDebugInit()
- shell.run("clear")
- print("Type a key to begin debug session")
- table.insert(inventory, cloneItem(6))
- table.insert(inventory, cloneItem(7))
- table.insert(inventory, cloneItem(2))
- charName = "Admin"
- charClass = "Debugger"
- worldname = "The Darklands"
- debugging = true
- for i=1,#inventory do
- equipItem(i)
- end
- charGold = 100
- pox = 32
- poy = 15
- modifyWorld()
- activateQuest(quests["The Empty Larder"])
- quests["The Empty Larder"].activestage = 2
- os.pullEvent("key")
- end
- function onInit()
- local darkSword = {
- " /\\",
- " ||",
- " ||",
- " ||",
- " ||",
- " ||",
- " /\\",
- "o======o",
- " ||",
- " ||",
- " __"
- }
- term.clear()
- sleep(1)
- for i=1,#darkSword do
- printLeft(darkSword[i], 2+i, w/2 - #darkSword[8]/2 - 1)
- end
- sleep(1.5)
- term.setCursorPos(w/2 - 9/2, h-4)
- textutils.slowPrint("DarkLands")
- sleep(1)
- printOffCenter("Tales from Transylvania", h-3, w, 0)
- printOffCenter("Press a key to begin", h-1, w, 0)
- os.pullEvent(key)
- term.clearLine()
- printLeft("Enter your name, hero: ", h-1, 2, 0)
- term.setCursorBlink(true)
- setCharName(io.read())
- worldname = "The Darklands"
- term.setCursorBlink(false)
- --The generic story preface is told here.
- term.clear()
- sleep(1)
- term.setCursorPos(1, 1)
- print("It is 1430.\n")
- print(
- [[Seeking to relieve the strain from a lengthy war with Moldava and widespread famines in the south, the king of Wallachia ordered an invasion on Transylvania. The warlords of Transylvania, often as busy fighting each other as foreign invaders are considered by His Grace a means to relieve the strain on the armed forces, and restore faith in the monarchy.
- In November of that month, an army of 300 men assembled at the base of the Carpathian mountain ranges, one of the most trecherous stretches of land in Europe. Two weeks of bitter cold and snowstorms, with limited rations and low morale, they finally came upon a keep within the mountains.]])
- term.setCursorPos(1, h)
- term.write("Type any key to continue")
- os.pullEvent("key")
- term.clear()
- term.setCursorPos(1, 2)
- print(
- [[The abandoned keep, Groazagol, afforded a view over the southern reaches of the country, but the land, in day and night was shrouded in an impenetrable darkness. It was not long before rumours spread through the garrison, of evil sweeping the country side- the black death, horsemen of the apocalypse, or some other sinister force of evil.
- The commander general of the Wallachian forces Lord Jeirbe, fearing his men would mutiny or flee at an order to trave into the abyss, called for a single scout to map the underlying country, provide insight into what was happening, and inform the army on their next move.]])
- term.setCursorPos(1, h)
- term.write("Type any key to continue")
- os.pullEvent("key")
- --The player selects their class here:
- selectClass()
- pox = 29
- poy = 25
- modifyWorld()
- end
- --Comment out this line as needed.
- --onInit = onDebugInit
- return true
- MKFIL darklands/scripts/towns
- WRITE 68
- TOWN "Nebun" 37,16,27,9
- \ /
- \__/\__/\__/\__/\__/\__/\| |
- |------|-----|------|____| |
- | | | | ^
- | ____|_/ \ |O_/ \_| |
- |/ \_O_| |_| ^
- |
- |----|------| |--
- |____|------|
- |_ _O_| |--
- ^
- /\_. ._/\ |
- || || |-- -O-|-- ---,^.
- || || | | \ /
- \__/\__/\_ /\__/\__/\__/\| |
- | |
- TOWN "Groazagol" 27,25,27,9
- www] / \ [www]
- | | |--/ /\ /\ \--| | |
- | |_-| |_-| |
- | |--|________________|--| |
- ||| | | |||
- | |____| |____| | /
- | | | |o
- | /|\ \
- | / | \
- | /|\ /o
- | / | \ /|\ | o
- www] / | \ [www]
- | | | |
- | |-_-_-_-_-_-_-_-_-_-_-_| |
- | |----------------------| |
- | | | |
- TOWN "Jocelyn's Shack" 6,7,16,15
- /__ __\
- ||
- /\
- /\ / \
- / \/_ _\ /\
- / \ || ____ / \
- /__ __\ / \ /_ _\
- || /______\ ||
- /\ | | /\
- / \ |_ _O_| / \
- /_ _\ / \
- || /__ __\
- /\ ||
- / \
- /_ _\ o o
- || | |
- TOWN "Mountain Camp", 57,9,16,15
- ______
- /######\
- _______/########\_______
- / o .. \
- / . \__
- _--_ .
- o/\. \__/ _ ..
- / \ .
- . o /\
- . .. o/\ .
- o . . . /\
- ./\ o o o...
- | |
- MKFIL darklands/scripts/world
- WRITE 26
- /\-----------------DEMO---..----------------------------*
- / \ ... |
- / \ ...... |
- /\ /\/\ ......... |
- /\ /\ ** * ..... * |
- /\ * ** ... * /\ |
- /\ * * * --- * * /\ /\ |
- /\ * * * * /\ |
- /\ * * --- * /\ /\ /\ |
- /\ * * * .. * / \ /\ /\ |
- /\ * * * .. * * * /\ / \ /\ |
- /\ .. * * /\/\ /\ D
- /\ ..... * * * * * E
- /\/\ ... * * * M
- / \ .... 0== ==0 * * * O
- \/ \ ... | | |
- \........ * * * | |..... ......
- \/\ * * 0=............ .......... |
- /\ * ............... |
- /\ * * ..... |
- /\ .. |
- /\ .. |
- \/\ /\/\ /\ /\ .. |
- / \/\ /\ /\ /\/\ /\/\/\ /\/\ / \ .. /\ /\ |
- / \ \/ /\ /\/ \/\ /\ /\ /\ / \ / \.../ \ /\ |
- / \ /\ /\ \ \ /\/\/\ /\ /\/ \ \.. /\
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement