Skip to content

Instantly share code, notes, and snippets.

@Slashin8r

Slashin8r/1aTabOrder

Last active Dec 19, 2015
Embed
What would you like to do?
RPGenerator Release v0.7.23 -Make your own RPG in Codea by Slashin8r
RPGenerator Tab Order Version: 0.7.23
------------------------------
This file should not be included in the Codea project.
#ChangeLog
#Characters
#Events
#GenerateMap
#GenerateMap2
#Icons
#Main
#Menus
#Weather
#Shaders
--[[
Version 0.7.23
-Added player.allParty table to reference partyMembers since the player will be able to collect unlimited partyMembers, but only use 5 at a time
-Modified player.party to reference player.allParty
-Modified generateMap to create a new mesh only when a new tilesheet is used
-Merged similar tilesheets together to limit the total number of meshes created per map to 4 (not including character meshes)
-Added d variable to charasets to determine the direction order (row order for up, left, right, down)
-Modified the getCharasetImage function to use d and also to properly scale a character
-Modified dialogue event to utilize charaset d variable
-Added a realtime average FPS display above the current FPS display
Version 0.7.22
-Removed player.inventory and added items variable count to store the item quantities
-Added sorting buttons to inventory menu
-Added inventory menu sorting functionality for sort by type, sort by name, and sort manually
Version 0.7.21
-Refined the inventory menu
-Added Back button to inventory menu
-Added item variables info1 and info2 (info1 displayed when item is selected, info2 displayed when item sub menu info button is touched)
-Added item info to inventory menu as well as the functionality for item sub menu info button
-Fixed a problem with invZeroCount when inventory menu is scrolled to the bottom
Version 0.7.20
-Changed some text formatting in the game menu
-Fixed a problem where the first item in the inventory menu was returning the wrong itemPopup value
-Fixed a problem in the inventory menu where the 45th item's popup text was the wrong color
-Fixed a problem with the inventory menu where items were not touchable if exactly 45 items were not being displayed
Version 0.7.19
-Added map event for changing weather
-Fixed a bug in the inventory menu related to items with quantity 0
-Added partyMembers table
-Finished game menu, except for status ailments
Version 0.7.18
-Added a lot of new variables to the player table in RPG
-Added the functionality to change between party member sprites
-New parameter added to quickly test the changing of party members
Version 0.7.17
-Added main menu and game menu
-Added a menu button in the game to open the game menu
-Fixed a bug that allowed you to scroll the inventory while an item sub menu was open
-Added doors (well just 1 for testing)
-Can walk over bottom door tile (or tiles specified as a door) regardless of other objects (such as walls)
Version 0.7.16
-Items in inventory menu can now be selected by touching them once
-Added item sub menu which will display when a selected item is touched
-Item sub menu will disappear when selected item is touched again
-When item sub menu is displayed you will not be able to touch any other item except the current selected item. This prevents a touch on the item sub menu from triggering a touch on the item the sub menu is being displayed over
-Item sub menu touch functions are working, but no functionality is bound to them
Version 0.7.15
-Fixed a bug where puddles already generated instantly change to snow piles, or vice versa, when WeatherType is changed
Version 0.7.14
-Fixed a bug where snow piles were rendering the same color as the joypad or joystick when joypad is touched
Version 0.7.13
-Fixed a bug where inventory did not list every 2nd item in the groups of 3
Version 0.7.12
-Optimized code as per http://lua-users.org/wiki/OptimisationCodingTips
-Removed instances of table.insert and changed most divisions to multiplications
Version 0.7.11
-Changed inventory menu to display 45 items, up from 30 (slight frame rate hit, but acceptable for a menu)
-Made icons and text bigger in inventory menu
-Added functionality for puddles and snow piles (basic ellipses used to display them)
-Puddles/snow piles generate based off weather spawnRate (heavier rain/snow means quicker puddles/snow piles)
-Puddles/snow piles will remove themselves over time when weather is turned off
-Puddles/snow piles can be toggled on/off
Version 0.7.10
-Removed smooth from menus as it caused frame rate drops
-Modified scroll bar to adjust for noSmooth
Version 0.7.9
-Added a scroll bar to the inventory menu
-Added swipe functionality to scroll through the inventory menu
-Fixed frame rate issue (changes in CiderControls)
Version 0.7.8
-Updated the inventory menu so each item is touchable and returns their corresponding index for future use
-Allowed for 30 items to be listed in inventory up from 18
Version 0.7.7
-Added iconsets table to store menu and item icons
-Added a large iconset image
-Added items table to store item variables
-Added getIconImage function
-Added gIcons table to store generated iconsets
-Added inventoryScroll and inventoryScrollMax variables which are used to scroll the inventory menu
-Configured drawMenu to work for my basic inventory menu
Version 0.7.6
-Added a ghost effect to the shader so characters can appear as ghosts
-Added weather effects for rain and snow thanks to code supplied by Aalok
-Added light variable to change between day, dawn/dusk, and night
-All of these effects are currently toggleable using parameters
-Added drawMenu function
Version 0.7.5
-Forgot to add shader code which can now be found in the Shaders tab
Version 0.7.4
-Added a shader so characters look like they are walking in grass, forests, water, etc.
-Removed unnecessary code in the testBelow function
Version 0.7.3
-Fixed a character render issue dealing with decor objects
Version 0.7.2
-Removed unnecessary code used to debug the addition of the 3x4 sprite charasets
Version 0.7.1
-Added support for 3x4 sprite charasets
Version 0.7.0
-Had problems with odd shaped decor objects so now every object is 32x32 and will have to be pieced together to form the larger objects
-Removed xSpan, ySpan and h variables
-Rewrote testBelow and type 4 algorithms
Version 0.6.11
-Split project into 2 projects so my updates will not overwrite your own custom tiles, characters, and maps
Version 0.6.10
-Moved type 4 decorations to decor table
-Added decor map
-Added h variable to decor table to determine the height (in tiles) of the decoration
-With the new h variable, you can now have characters appear behind a decoration up to 4 tiles above its base
Version 0.6.9
-Characters being able to walk over unpassable animated tiles has been fixed
-Rewrote type 4 algorithm
-Added xSpan and ySpan variables to indicate the additinal tiles a decoration object's base covers
-Temporarily added sprites from Small World to test type 4 algorithm
Version 0.6.8
-Updated documentation for the charasets, tiles, aTiles, and objects tables so you can now tell what image will be used without having to count tiles on a tilesheet
Version 0.6.7
-Added drawMap function and moved code related to drawing the map into it
-Added showFPS function and moved code into it
-The functions above cleaned up the draw function to get it ready for menu options
-Added currentMenu variable for future use
Version 0.6.6
-Added new character function animateMove to clean up the draw function
-Refined the animateMove function to work with speeds that are not factors of 32 (e.g. 3, 5, 6, fractions, etc.)
Version 0.6.5
-Fixed a bug that caused NPCs to share the same mesh which would cause them to not render behind an object while an identical NPC was rendering on top of an object
Version 0.6.4
-Fixed NPCs not rendering on top of objects they should
-Rewrote testBelow function
Version 0.6.3
-Added animated tiles to testBelow function so characters properly render above or below objects when an animated tile is below them
Version 0.6.2
-New character animations caused the characters to render in front of objects they should be behind while moving, this has been fixed
Version 0.6.1
-Fixed game crashing
Version 0.6.0
-Added some facesets for dialogue
-Added gFaces table to store generated faces
-Faces now generate from mesh instead of sprite
-Added an animated tileset
-Added animated map to maps, only animated tiles will go here
-Added animated tile functionality
-Updated some documentation
Version 0.5.5
-Added gCharacters table once again to store character meshes
Version 0.5.4
-Fixed a few rendering errors
-NPCs can no longer walk off the map and trigger an error
Version 0.5.3
-NPCs were rendering too far to the right, that has now been fixed
-Talking to NPCs that have moved away from you has now been fixed
Version 0.5.2
-Changed the animation of the player character
-Added animation to the NPCs
-Added NPC variable speed to determine whether or not an NPC can walk around (0 is stationary)
-Added 3 more charasets so all the characters are not clones
-Added 2 additional NPCs for debugging
Version 0.5.1
-Added map variable to NPCs table so that NPCs will only be drawn in their map
Version 0.5.0
-Walking and Talking NPCs
Version 0.4.7
-Removed dialogue map event
-Added an NPC on map to talk to
-NPCs can move
Version 0.4.6
-Refined the generateMap function
-Due to the changes in generateMap, the tiles and objects tables are now much easier to read
-Added documentation to the events tab
-Added events functions: popup, teleport, dialogue
-Added a move function for player/npc movement
-Events functions and move function cleaned up the draw function substantially
Version 0.4.5
-Update to AutoGist
Version 0.4.4
-Update to AutoGist
Version 0.4.3
-I messed up the AutoGist and had to fix it
Version 0.4.2
-Changed title of project to RPGenerator
Version 0.4.1
-Some minor changes to code layout
Version 0.4.0
-Added wall tilesheet
-Added roof tilesheet
-Completed wall auto-tiling algorithm
-Completed roof auto-tiling algorithm
-Rearranged ChangeLog so you you don't have to scroll down to see latest updates
Version: 0.3.4
-Updated some documentation
-Refined dialogue functionality - it now pulls variables from npcs table and chooses a random dialogue to display
-Moved FPS display to upper right corner so it does not cover popup text
Version: 0.3.3
-Added ChangeLog
Version: 0.3.2
-Added AutoGist Updater
Version: 0.3.1
-Added AutoGist Installer
Version: 0.3
-Added AutoGist
Version: 0.2
-Characters are now rendered from a single charaset mesh
-Generated character meshes are stored in gCharacters so characters are only generated once
-Added generateCharasetImage() function to get the current image of the player (or an NPC in the future)
-NPCs table added for future use
-Images necessary for the project to run are now downloaded automatically
-Refined the auto-tile algorithm for floor/ceiling tiles
-Generated maps are now stored in gFloor and gObjects tables so the maps are only generated once
Version 0.1
-Auto-tile algorithm for floor/ceiling tiles
-2D maps tables added to store map of tiles, objects, and events
-Map generated from single mesh instead of individual sprites
-Tiles table used to store the locations of each floor tilesheet on the mesh
-Objects table used to store the locations of each object tilesheet on the mesh
-Events table used to store events that will be called from the map
-Characters table added to store character sprites
--]]
-- Characters
function getCharacterFace(p,x,y,w,h)
local c = characters[p.character]
local f = c.face
local face
if gFaces[f] == nil then
face = mesh()
face.texture = facesets[f].sheet
gFaces[f] = face
else
face = gFaces[f]
end
local fObj = face:addRect(x,y,w,h)
face:setRectTex(fObj,(1/facesets[f].x)*c.faceX,(1/facesets[f].y)*c.faceY,1/facesets[f].x,1/facesets[f].y)
return face
end
function getCharasetImage(p,tw,th,moveX,moveY)
local m
local cs = characters[p.character].sheet
if gCharacters[p.character][p.id] == nil then
m = mesh()
m.texture = charasets[cs].sheet
m.shader = shader(shaders.vertexAlpha, shaders.fragmentAlpha)
gCharacters[p.character][p.id] = m
else
m = gCharacters[p.character][p.id]
end
m.shader.toggle = false
m.shader.ghost = p.ghost
local w,h = charasets[cs].sheet.width,charasets[cs].sheet.height
w = w / charasets[cs].x
h = h / charasets[cs].y
if h ~= th then -- resize the object to fit the current map
w,h = (th/h)*w,(th/h)*h
end
if w > (tw * 0.75) then
w,h = ((tw * 0.75)/w)*w,((tw * 0.75)/w)*h
end
local pObj
if p.position.x == player.position.x and p.position.y == player.position.y then
pObj = m:addRect(WIDTH*0.5,HEIGHT*0.5-math.ceil((th-h)/2)+math.floor(h*0.2),w,h)
else
pObj = m:addRect(((p.position.x-1)*tw)+(tw*0.25)+moveX,-((p.position.y-1)*th)+(th*0.375)+moveY,w,h)
end
m:setRectTex(pObj,(1/charasets[cs].x)*charasets[cs].m[p.imgX+1],(1/charasets[cs].y)*p.imgY,1/charasets[cs].x,1/charasets[cs].y)
return m
end
function move(character,direction)
if character.moveX ~= 0 and character.moveY ~= 0 then
return nil
end
local cs = characters[character.character].sheet
local cd = charasets[cs].d
local dx,dy = 0,0
if direction == "up" then
character.imgY = cd[1]
dx = 0
dy = -1
elseif direction == "right" then
character.imgY = cd[2]
dx = 1
dy = 0
elseif direction == "left" then
character.imgY = cd[3]
dx = -1
dy = 0
elseif direction == "down" then
character.imgY = cd[4]
dx = 0
dy = 1
end
local t,a,o,d = 0,0,0,0
if (dx == -1 and character.position.x ~= 1) or (dy == -1 and character.position.y ~= 1) or
(dx == 1 and character.position.x ~= #maps[currentMap].floor[1]) or (dy == 1 and character.position.y ~= #maps[currentMap].floor) then
t = maps[currentMap].floor[character.position.y+dy][character.position.x+dx]
a = maps[currentMap].animated[character.position.y+dy][character.position.x+dx]
o = maps[currentMap].objects[character.position.y+dy][character.position.x+dx]
d = maps[currentMap].decor[character.position.y+dy][character.position.x+dx]
end
local opass,dpass,npcpass,ppass,door = true,true,true,true,false
local di = nil
for i,v in pairs(npcs) do
if v.map == currentMap then
if v.position.x == character.position.x+dx and v.position.y == character.position.y+dy then
di = i
npcpass = false
end
end
end
if player.position.x == character.position.x+dx and player.position.y == character.position.y+dy then
ppass = false
end
if o == 0 then
opass = true
else
opass = objects[o].passable
end
if d == 0 then
dpass = true
else
dpass = decor[d].passable
if decor[d].door ~= nil then
door = decor[d].door
end
end
if a ~= 0 then
if (player.position == character.position and door) or (aTiles[a].passable and opass and dpass and npcpass and ppass) then
character.position.x = character.position.x+dx
character.position.y = character.position.y+dy
if player.position.x == character.position.x and player.position.y == character.position.y then
character.moveX = dx*maps[currentMap].tileWidth
character.moveY = -dy*maps[currentMap].tileHeight
else
character.moveX = -dx*maps[currentMap].tileWidth
character.moveY = dy*maps[currentMap].tileHeight
end
end
elseif t ~= 0 then
if (player.position == character.position and door) or (tiles[t].passable and opass and dpass and npcpass and ppass) then
character.position.x = character.position.x+dx
character.position.y = character.position.y+dy
if player.position == character.position then
character.moveX = dx*maps[currentMap].tileWidth
character.moveY = -dy*maps[currentMap].tileHeight
else
character.moveX = -dx*maps[currentMap].tileWidth
character.moveY = dy*maps[currentMap].tileHeight
end
end
end
return di
end
function animateMove(character)
local cs = characters[character.character].sheet
local cx = charasets[cs].x
local speed = character.speed*maps[currentMap].tileWidth*0.03125
local mw = maps[currentMap].tileWidth*0.25
local a,b,c,d = 0,mw,mw*2,mw*3
if character.moveX ~= 0 then
if (math.abs(character.moveX)-speed <= d and character.imgX == 0) or
(math.abs(character.moveX)-speed <= c and character.imgX == 1) or
(math.abs(character.moveX)-speed <= b and character.imgX == 2) or
(math.abs(character.moveX)-speed <= a and character.imgX == 3) then
if character.imgX < 3 then
character.imgX = character.imgX + 1
else
character.imgX = 0
end
end
elseif character.moveY ~= 0 then
if (math.abs(character.moveY)-speed <= d and character.imgX == 0) or
(math.abs(character.moveY)-speed <= c and character.imgX == 1) or
(math.abs(character.moveY)-speed <= b and character.imgX == 2) or
(math.abs(character.moveY)-speed <= a and character.imgX == 3) then
if character.imgX < 3 then
character.imgX = character.imgX + 1
else
character.imgX = 0
end
end
end
if character.moveX == 0 and character.moveY == 0 then
character.imgX = 0
end
if character.moveX < 0 then
character.moveX = character.moveX + speed
if character.moveX > 0 then
character.moveX = 0
end
elseif character.moveX > 0 then
character.moveX = character.moveX - speed
if character.moveX < 0 then
character.moveX = 0
end
end
if character.moveY < 0 then
character.moveY = character.moveY + speed
if character.moveY > 0 then
character.moveY = 0
end
elseif character.moveY > 0 then
character.moveY = character.moveY - speed
if character.moveY < 0 then
character.moveY = 0
end
end
end
function testBelow(character)
local w,o,d = 0,0,maps[currentMap].decor[character.position.y][character.position.x]
local t = maps[currentMap].objects[character.position.y][character.position.x]
local a = maps[currentMap].animated[character.position.y][character.position.x]
local tw,th = maps[currentMap].tileWidth,maps[currentMap].tileHeight
if character.position.y < #maps[currentMap].objects then -- 2 tiles below character current position
o = maps[currentMap].objects[character.position.y+1][character.position.x]
end
if character.position.y+1 < #maps[currentMap].objects then -- 2 tiles below character current position
w = maps[currentMap].objects[character.position.y+2][character.position.x]
end
if t ~= 0 then
if objects[t].passable then
t = true
else
t = false
end
else
t = false
end
if a ~= 0 then
if aTiles[a].passable then
a = true
else
a = false
end
else
a = false
end
if o ~= 0 then
if objects[o].type == 2 then
o = true
else
o = false
end
else
o = false
end
if w ~= 0 then
if objects[w].type == 2 then
w = true
else
w = false
end
else
w = false
end
if d ~= 0 then
if decor[d].passable then
d = true
else
d = false
end
else
d = false
end
if math.abs(character.moveX) <= tw*0.5 and math.abs(character.moveY) <= th*0.5 then
character.behind = false
end
if not w and not o and not d then
if t or a then
return true
end
return false
end
if math.abs(character.moveX) <= tw*0.5 and math.abs(character.moveY) <= th*0.5 then
character.behind = true
end
if t or a then
return true
end
return false
end
-- Events
function popup(t)
pushStyle()
rectMode(CENTER)
textMode(CENTER)
strokeWidth(4)
stroke(0, 0, 255, 255)
fill(255, 255, 255, 255)
font("ArialRoundedMTBold")
fontSize(20)
local w,h = textSize(t)
w = w + 20
h = h + 20
rect(512, 768-(h*0.5), w, h)
fill(0, 0, 0, 255)
text(t, 512, 768-(h*0.5))
popStyle()
end
function teleport(map,x,y)
if map ~= currentMap then
currentMap = map
if gFloor[currentMap] == nil then
gFloor[currentMap] = generateMap(maps[currentMap].floor, tiles, maps[currentMap].tileWidth, maps[currentMap].tileHeight, nil)
end
if gAnimated[1][currentMap] == nil then
gAnimated[1][currentMap] = generateMap(maps[currentMap].animated, aTiles, maps[currentMap].tileWidth, maps[currentMap].tileHeight, 0)
end
if gAnimated[2][currentMap] == nil then
gAnimated[2][currentMap] = generateMap(maps[currentMap].animated, aTiles, maps[currentMap].tileWidth, maps[currentMap].tileHeight, 1)
end
if gAnimated[3][currentMap] == nil then
gAnimated[3][currentMap] = generateMap(maps[currentMap].animated, aTiles, maps[currentMap].tileWidth, maps[currentMap].tileHeight, 2)
end
if gObjects[currentMap] == nil then
gObjects[currentMap] = generateMap(maps[currentMap].objects, objects, maps[currentMap].tileWidth, maps[currentMap].tileHeight, nil)
end
if gDecor[currentMap] == nil then
gDecor[currentMap] = generateMap(maps[currentMap].decor, decor, maps[currentMap].tileWidth, maps[currentMap].tileHeight, nil)
end
gWeather = {}
for i,_ in ipairs(maps[currentMap].floor) do
local g = {}
for k,_ in ipairs(maps[currentMap].floor[i]) do
g[k] = 0
end
gWeather[i] = g
end
end
player.position.x = x
player.position.y = y
end
function teleport2(map,x,y)
if map ~= currentMap then
currentMap = map
generateSprites()
gWeather = {}
for i,_ in ipairs(maps[currentMap].floor) do
local g = {}
for k,_ in ipairs(maps[currentMap].floor[i]) do
g[k] = 0
end
gWeather[i] = g
end
end
player.position.x = x
player.position.y = y
end
function changeWeather(type,rate,puddles)
if type == "rain" then
WeatherType = 0
weather.type = 0
elseif type == "snow" then
WeatherType = 1
weather.type = 1
end
WeatherRate = rate
weather.spawnRate = rate
SpawnPuddles = puddles
weather.puddles = puddles
end
function dialogue(npc)
local ncs = characters[npcs[npc].character].sheet
local cs = characters[player.character].sheet
local npcd = charasets[ncs].d
local cd = charasets[cs].d
if player.imgY == cd[1] then
npcs[npc].imgY = npcd[4]
elseif player.imgY == cd[2] then
npcs[npc].imgY = npcd[3]
elseif player.imgY == cd[3] then
npcs[npc].imgY = npcd[2]
elseif player.imgY == cd[4] then
npcs[npc].imgY = npcd[1]
end
local dialogue = npcs[npc].dialogue
if dialogueChoice == nil and currentPage == 0 then
dialogueChoice = math.random(#dialogue)
currentPage = 1
end
if dialogueChoice ~= nil then
if currentPage > #dialogue[dialogueChoice] then
showDialogue = false
dialogueChoice = nil
dialogueNPC = nil
else
showDialogue = true
local face = getCharacterFace(npcs[npc],100,100,192,192)
pushStyle()
rectMode(CORNER)
spriteMode(CORNER)
textMode(CENTER)
textWrapWidth(800)
strokeWidth(4)
stroke(0, 0, 255, 255)
fill(255, 255, 255, 255)
font("ArialRoundedMTBold")
fontSize(20)
rect(0, 0, 1024, 200)
rect(0, 0, 200, 200)
rect(0, 196, 200, 50)
face:draw()
face:clear()
fill(0, 0, 0, 255)
text(dialogue[dialogueChoice][currentPage], 612, 100)
text(npcs[npc].name, 100, 221)
if currentPage < #dialogue[dialogueChoice] then
sprite("Tyrian Remastered:Arrow Right", 990, 10, 24, 24)
end
popStyle()
end
end
end
-- GenerateMap
-- generates the current map if it has not already been generated
function generateMap(map, tilesheet, tileWidth, tileHeight, animation)
local mm = {} -- table to store our meshes
local maxy,maxx = #map,#map[1] -- size of 2D map
for y,_ in ipairs(map) do
for x,t in ipairs(map[y]) do
local sheet = tilesheet[t]
if t ~= 0 then
if mm[sheet.set] == nil then -- add new mesh if we don't have this tile image yet
mm[sheet.set] = mesh()
mm[sheet.set].texture = tilesets[sheet.set].sheet
end
local ul,u,ur,r,dr,d,dl,l -- designates the 8 surrounding tiles: u = upper, d = down, r = right, l = left
if y == 1 then ul,u,ur=0,0,0 end -- the following make sure the map table is not read out of bounds
if y == maxy then dl,d,dr=0,0,0 end
if x == 1 then ul,l,dl=0,0,0 end
if x == maxx then ur,r,dr=0,0,0 end
if ul ~= 0 then ul=map[y-1][x-1] end -- if map will not be read out of bounds, use map to figure out surrounding tiles
if u ~= 0 then u=map[y-1][x] end
if ur ~= 0 then ur=map[y-1][x+1] end
if r ~= 0 then r=map[y][x+1] end
if dr ~= 0 then dr=map[y+1][x+1] end
if d ~= 0 then d=map[y+1][x] end
if dl ~= 0 then dl=map[y+1][x-1] end
if l ~= 0 then l=map[y][x-1] end
local w,h = (1/tilesets[sheet.set].x),1/tilesets[sheet.set].y
local tx,ty
if animation ~= nil then
tx,ty = (sheet.x+animation)*w,sheet.y*h
else
tx,ty = sheet.x*w,sheet.y*h
end
local type
if sheet.type == 1 then
w = w * 0.25
h = h * 0.16667
type = "floor"
elseif sheet.type == 2 then
if tilesets[sheet.set].sheet.height%10 == 0 then
w = w * 0.25
h = h * 0.1
type = "wall"
else
w = w * 0.25
h = h * 0.125
type = "roof"
end
elseif sheet.type == 3 then
end
local tw2,th2,th4 = tileWidth*0.5,tileHeight*0.5,tileHeight*0.25
if sheet.type == 1 then -- algorithm used for auto tiling the floor tiles
local tul,tur,tdl,tdr -- tile pieces: tul=upper left, tur=upper right, tdl=bottom left, tdr=bottom right
if u ~= t and l ~= t then
tul = mm[sheet.set]:addRect(((x-1)*tileWidth),-((y-1)*tileHeight)+th2,tw2,th2)
mm[sheet.set]:setRectTex(tul,tx+(w*0),ty+(h*3),w,h)
elseif u ~= t and l == t then
tul = mm[sheet.set]:addRect(((x-1)*tileWidth),-((y-1)*tileHeight)+th2,tw2,th2)
mm[sheet.set]:setRectTex(tul,tx+(w*2),ty+(h*3),w,h)
elseif u == t and l ~= t then
tul = mm[sheet.set]:addRect(((x-1)*tileWidth),-((y-1)*tileHeight)+th2,tw2,th2)
mm[sheet.set]:setRectTex(tul,tx+(w*0),ty+(h*1),w,h)
elseif u == t and l == t and ul ~= t then
tul = mm[sheet.set]:addRect(((x-1)*tileWidth),-((y-1)*tileHeight)+th2,tw2,th2)
mm[sheet.set]:setRectTex(tul,tx+(w*2),ty+(h*5),w,h)
else
tul = mm[sheet.set]:addRect(((x-1)*tileWidth),-((y-1)*tileHeight)+th2,tw2,th2)
mm[sheet.set]:setRectTex(tul,tx+(w*2),ty+(h*1),w,h)
end
if u ~= t and r ~= t then
tur = mm[sheet.set]:addRect(((x-1)*tileWidth)+tw2,-((y-1)*tileHeight)+th2,tw2,th2)
mm[sheet.set]:setRectTex(tur,tx+(w*3),ty+(h*3),w,h)
elseif u ~= t and r == t then
tur = mm[sheet.set]:addRect(((x-1)*tileWidth)+tw2,-((y-1)*tileHeight)+th2,tw2,th2)
mm[sheet.set]:setRectTex(tur,tx+(w*1),ty+(h*3),w,h)
elseif u == t and r ~= t then
tur = mm[sheet.set]:addRect(((x-1)*tileWidth)+tw2,-((y-1)*tileHeight)+th2,tw2,th2)
mm[sheet.set]:setRectTex(tur,tx+(w*3),ty+(h*1),w,h)
elseif u == t and r == t and ur ~= t then
tur = mm[sheet.set]:addRect(((x-1)*tileWidth)+tw2,-((y-1)*tileHeight)+th2,tw2,th2)
mm[sheet.set]:setRectTex(tur,tx+(w*3),ty+(h*5),w,h)
else
tur = mm[sheet.set]:addRect(((x-1)*tileWidth)+tw2,-((y-1)*tileHeight)+th2,tw2,th2)
mm[sheet.set]:setRectTex(tur,tx+(w*1),ty+(h*1),w,h)
end
if d ~= t and l ~= t then
tdl = mm[sheet.set]:addRect(((x-1)*tileWidth),-((y-1)*tileHeight),tw2,th2)
mm[sheet.set]:setRectTex(tdl,tx+(w*0),ty+(h*0),w,h)
elseif d ~= t and l == t then
tdl = mm[sheet.set]:addRect(((x-1)*tileWidth),-((y-1)*tileHeight),tw2,th2)
mm[sheet.set]:setRectTex(tdl,tx+(w*2),ty+(h*0),w,h)
elseif d == t and l ~= t then
tdl = mm[sheet.set]:addRect(((x-1)*tileWidth),-((y-1)*tileHeight),tw2,th2)
mm[sheet.set]:setRectTex(tdl,tx+(w*0),ty+(h*2),w,h)
elseif d == t and l == t and dl ~= t then
tdl = mm[sheet.set]:addRect(((x-1)*tileWidth),-((y-1)*tileHeight),tw2,th2)
mm[sheet.set]:setRectTex(tdl,tx+(w*2),ty+(h*4),w,h)
else
tdl = mm[sheet.set]:addRect(((x-1)*tileWidth),-((y-1)*tileHeight),tw2,th2)
mm[sheet.set]:setRectTex(tdl,tx+(w*2),ty+(h*2),w,h)
end
if d ~= t and r ~= t then
tdr = mm[sheet.set]:addRect(((x-1)*tileWidth)+tw2,-((y-1)*tileHeight),tw2,th2)
mm[sheet.set]:setRectTex(tdr,tx+(w*3),ty+(h*0),w,h)
elseif d ~= t and r == t then
tdr = mm[sheet.set]:addRect(((x-1)*tileWidth)+tw2,-((y-1)*tileHeight),tw2,th2)
mm[sheet.set]:setRectTex(tdr,tx+(w*1),ty+(h*0),w,h)
elseif d == t and r ~= t then
tdr = mm[sheet.set]:addRect(((x-1)*tileWidth)+tw2,-((y-1)*tileHeight),tw2,th2)
mm[sheet.set]:setRectTex(tdr,tx+(w*3),ty+(h*2),w,h)
elseif d == t and r == t and dr ~= t then
tdr = mm[sheet.set]:addRect(((x-1)*tileWidth)+tw2,-((y-1)*tileHeight),tw2,th2)
mm[sheet.set]:setRectTex(tdr,tx+(w*3),ty+(h*4),w,h)
else
tdr = mm[sheet.set]:addRect(((x-1)*tileWidth)+tw2,-((y-1)*tileHeight),tw2,th2)
mm[sheet.set]:setRectTex(tdr,tx+(w*1),ty+(h*2),w,h)
end
elseif sheet.type == 2 then -- algorithm used for auto tiling roof/wall tiles
local tul,tur,tdl,tdr -- tile pieces: tul=upper left, tur=upper right, tdl=bottom left, tdr=bottom right
if u ~= t and l ~= t then
tul = mm[sheet.set]:addRect(((x-1)*tileWidth),-((y-3)*tileHeight)+th2,tw2,th2)
mm[sheet.set]:setRectTex(tul,tx+(w*0),ty+(h*7),w,h)
elseif u ~= t and l == t then
tul = mm[sheet.set]:addRect(((x-1)*tileWidth),-((y-3)*tileHeight)+th2,tw2,th2)
mm[sheet.set]:setRectTex(tul,tx+(w*2),ty+(h*7),w,h)
elseif u == t and l ~= t then
tul = mm[sheet.set]:addRect(((x-1)*tileWidth),-((y-3)*tileHeight)+th2,tw2,th2)
mm[sheet.set]:setRectTex(tul,tx+(w*0),ty+(h*5),w,h)
elseif u == t and l == t and ul ~= t then
tul = mm[sheet.set]:addRect(((x-1)*tileWidth),-((y-3)*tileHeight)+th2,tw2,th2)
mm[sheet.set]:setRectTex(tul,tx+(w*2),ty+(h*9),w,h)
else
tul = mm[sheet.set]:addRect(((x-1)*tileWidth),-((y-3)*tileHeight)+th2,tw2,th2)
mm[sheet.set]:setRectTex(tul,tx+(w*2),ty+(h*5),w,h)
end
if u ~= t and r ~= t then
tur = mm[sheet.set]:addRect(((x-1)*tileWidth)+tw2,-((y-3)*tileHeight)+th2,tw2,th2)
mm[sheet.set]:setRectTex(tur,tx+(w*3),ty+(h*7),w,h)
elseif u ~= t and r == t then
tur = mm[sheet.set]:addRect(((x-1)*tileWidth)+tw2,-((y-3)*tileHeight)+th2,tw2,th2)
mm[sheet.set]:setRectTex(tur,tx+(w*1),ty+(h*7),w,h)
elseif u == t and r ~= t then
tur = mm[sheet.set]:addRect(((x-1)*tileWidth)+tw2,-((y-3)*tileHeight)+th2,tw2,th2)
mm[sheet.set]:setRectTex(tur,tx+(w*3),ty+(h*5),w,h)
elseif u == t and r == t and ur ~= t then
tur = mm[sheet.set]:addRect(((x-1)*tileWidth)+tw2,-((y-3)*tileHeight)+th2,tw2,th2)
mm[sheet.set]:setRectTex(tur,tx+(w*3),ty+(h*9),w,h)
else
tur = mm[sheet.set]:addRect(((x-1)*tileWidth)+tw2,-((y-3)*tileHeight)+th2,tw2,th2)
mm[sheet.set]:setRectTex(tur,tx+(w*1),ty+(h*5),w,h)
end
if d ~= t and l ~= t then
tdl = mm[sheet.set]:addRect(((x-1)*tileWidth),-((y-3)*tileHeight),tw2,th2)
mm[sheet.set]:setRectTex(tdl,tx+(w*0),ty+(h*4),w,h)
tdl = mm[sheet.set]:addRect(((x-1)*tileWidth),-((y-2)*tileHeight)-th4,tw2,tileHeight*2)
mm[sheet.set]:setRectTex(tdl,tx+(w*0),ty+(h*0),w,h*4)
elseif d ~= t and l == t then
tdl = mm[sheet.set]:addRect(((x-1)*tileWidth),-((y-3)*tileHeight),tw2,th2)
mm[sheet.set]:setRectTex(tdl,tx+(w*2),ty+(h*4),w,h)
tdl = mm[sheet.set]:addRect(((x-1)*tileWidth),-((y-2)*tileHeight)-th4,tw2,tileHeight*2)
mm[sheet.set]:setRectTex(tdl,tx+(w*2),ty+(h*0),w,h*4)
elseif d == t and l ~= t then
tdl = mm[sheet.set]:addRect(((x-1)*tileWidth),-((y-3)*tileHeight),tw2,th2)
mm[sheet.set]:setRectTex(tdl,tx+(w*0),ty+(h*6),w,h)
elseif d == t and l == t and dl ~= t then
tdl = mm[sheet.set]:addRect(((x-1)*tileWidth),-((y-3)*tileHeight),tw2,th2)
if type == "roof" then
mm[sheet.set]:setRectTex(tdl,tx+(w*0),ty+(h*6),w,h)
else
mm[sheet.set]:setRectTex(tdl,tx+(w*2),ty+(h*8),w,h)
end
else
tdl = mm[sheet.set]:addRect(((x-1)*tileWidth),-((y-3)*tileHeight),tw2,th2)
mm[sheet.set]:setRectTex(tdl,tx+(w*2),ty+(h*6),w,h)
end
if d ~= t and r ~= t then
tdr = mm[sheet.set]:addRect(((x-1)*tileWidth)+tw2,-((y-3)*tileHeight),tw2,th2)
mm[sheet.set]:setRectTex(tdr,tx+(w*3),ty+(h*4),w,h)
tdr = mm[sheet.set]:addRect(((x-1)*tileWidth)+tw2,-((y-2)*tileHeight)-th4,tw2,tileHeight*2)
mm[sheet.set]:setRectTex(tdr,tx+(w*3),ty+(h*0),w,h*4)
elseif d ~= t and r == t then
tdr = mm[sheet.set]:addRect(((x-1)*tileWidth)+tw2,-((y-3)*tileHeight),tw2,th2)
mm[sheet.set]:setRectTex(tdr,tx+(w*1),ty+(h*4),w,h)
tdr = mm[sheet.set]:addRect(((x-1)*tileWidth)+tw2,-((y-2)*tileHeight)-th4,tw2,tileHeight*2)
mm[sheet.set]:setRectTex(tdr,tx+(w*1),ty+(h*0),w,h*4)
elseif d == t and r ~= t then
tdr = mm[sheet.set]:addRect(((x-1)*tileWidth)+tw2,-((y-3)*tileHeight),tw2,th2)
mm[sheet.set]:setRectTex(tdr,tx+(w*3),ty+(h*6),w,h)
elseif d == t and r == t and dr ~= t then
tdr = mm[sheet.set]:addRect(((x-1)*tileWidth)+tw2,-((y-3)*tileHeight),tw2,th2)
if type == "roof" then
mm[sheet.set]:setRectTex(tdr,tx+(w*3),ty+(h*6),w,h)
else
mm[sheet.set]:setRectTex(tdr,tx+(w*3),ty+(h*8),w,h)
end
else
tdr = mm[sheet.set]:addRect(((x-1)*tileWidth)+tw2,-((y-3)*tileHeight),tw2,th2)
mm[sheet.set]:setRectTex(tdr,tx+(w*1),ty+(h*6),w,h)
end
elseif sheet.type == 3 then
elseif sheet.type == 4 then -- no algorithm used, simply place the object at designated location
local obj
local width = tilesets[sheet.set].sheet.width/tilesets[sheet.set].x
local height = tilesets[sheet.set].sheet.height/tilesets[sheet.set].y
if width%tileWidth ~= 0 then -- resize the object to fit the current map
local s = math.ceil(width/tileWidth)
width,height = (tileWidth/width)*width*s,(tileWidth/width)*height*s
elseif height%tileHeight ~= 0 and width < tileWidth then
local s = math.ceil(width/tileWidth)
width,height = (tileHeight/height)*width*s,(tileHeight/height)*height*s
end
obj = mm[sheet.set]:addRect(((x-1)*tileWidth)+(width*0.25),-((y-1)*tileHeight)+(height*0.375),width,height)
mm[sheet.set]:setRectTex(obj,tx,ty,w,h)
end
end
end
end
return mm
end
function drawMap()
player.character = partyMembers[player.allParty[player.party[PartyMember]]].character
if aStep < ElapsedTime then
aStep = ElapsedTime + 1
if ca == 3 and aDirection > 0 then
aDirection = -aDirection
elseif ca == 1 and aDirection < 0 then
aDirection = -aDirection
end
ca = ca + aDirection
end
animateMove(player)
local tw,th = maps[currentMap].tileWidth,maps[currentMap].tileHeight -- tile width and height of current map
local pimg = getCharasetImage(player,tw,th,0,0) -- player image pulled from mesh
local npcimgs = {}
local offx,offy = (WIDTH/tw)*0.5+1,(HEIGHT/th)*0.5+1
pushMatrix()
-- move floor and animated maps to match player location
translate(-(tw*0.25)-(player.position.x-offx)*tw+player.moveX, (768-(th*0.25))+(player.position.y-offy)*th+player.moveY)
for i,v in pairs(gFloor[currentMap]) do
v:draw()
end
-- puddles
for y,_ in pairs(gWeather) do
for x,v in ipairs(gWeather[y]) do
if v ~= 0 then
pushStyle()
noStroke()
if v == 1 then
fill(60, 25, 255, 180)
elseif v == 2 then
fill(255, 255, 255, 255)
end
ellipseMode(CORNER)
ellipse(((x-1)*tw)-8,-((y-1)*th)-8,tw,th)
popStyle()
end
end
end
for i,v in pairs(gAnimated[ca][currentMap]) do
v:draw()
end
for i,v in pairs(npcs) do
if v.map == currentMap then
if v.speed ~= 0 then
animateMove(v)
if v.moveX == 0 and v.moveY == 0 then
if math.random(v.rate*100) == 1 and not showDialogue then
if dialogueNPC == i then
dialogueNPC = nil
end
local dir = math.random(4)
if dir == 1 then
move(v,"right")
elseif dir == 2 then
move(v,"left")
elseif dir == 3 then
move(v,"up")
elseif dir == 4 then
move(v,"down")
end
end
end
end
local npcimg = getCharasetImage(v,tw,th,v.moveX,v.moveY)
npcimg:draw()
npcimgs[i] = npcimg
end
end
popMatrix()
pimg:draw() -- draw player
pushMatrix()
-- move object and decor maps to match player location
translate(-(tw*0.25)-(player.position.x-offx)*tw+player.moveX, (768-(th*0.25))+(player.position.y-offy)*th+player.moveY)
for i,v in pairs(gObjects[currentMap]) do
v:draw()
end
for i,v in pairs(gDecor[currentMap]) do
v:draw()
end
-- test whether or not the npc should be drawn on top of an object
for i,v in pairs(npcs) do
if v.map == currentMap then
npcimgs[i].shader.toggle = testBelow(v)
if not v.behind then
npcimgs[i]:draw()
end
npcimgs[i]:clear()
end
end
popMatrix()
pimg.shader.toggle = testBelow(player)
pimg.shader.ghost = player.ghost
if not player.behind then
pimg:draw()
end
pimg:clear()
weather.type = WeatherType
weather.spawnRate = WeatherRate
weather.puddles = SpawnPuddles
weather:update()
weather:draw()
if Light == 1 then
pushStyle()
fill(0, 0, 0, 90)
rect(0,0,WIDTH,HEIGHT)
popStyle()
elseif Light == 2 then
pushStyle()
fill(0, 0, 0, 180)
rect(0,0,WIDTH,HEIGHT)
popStyle()
end
if dialogueNPC ~= nil and showDialogue then
dialogue(dialogueNPC)
return
end
local e = maps[currentMap].events[player.position.y][player.position.x] -- check if an event at the current player location exists
if e > 0 and ((player.moveX == 0 and player.moveY == 0) or currentPopup == e) then
local event = events[e]
if event.type == "teleport" then -- execute teleport event
teleport(event.map,event.x,event.y)
elseif event.type == "popup" or event.type == "entrance" then -- display text for popup and entrance events
currentPopup = e
popup(event.text)
elseif event.type == "weather" then -- execute teleport event
changeWeather(event.weather,event.rate,event.puddles)
end
else
currentPopup = 0
end
GameMenuDraw()
JoypadDraw() -- draw joypad
if joypadOP.status and not showDialogue then
currentPage = 0
if player.moveX == 0 and player.moveY == 0 then
if math.abs(joypadOP.x) > math.abs(joypadOP.y) then
if joypadOP.x > 0 then
dialogueNPC = move(player,"right")
else
dialogueNPC = move(player,"left")
end
else
if joypadOP.y > 0 then
dialogueNPC = move(player,"up")
else
dialogueNPC = move(player,"down")
end
end
end
end
end
-- GenerateMap
-- generates the current map if it has not already been generated
function generateSprites()
local map,tilesheet
for m=1,4 do
if m == 1 then
map = maps[currentMap].floor
tilesheet = tiles
elseif m == 2 then
map = maps[currentMap].animated
tilesheet = aTiles
elseif m == 3 then
map = maps[currentMap].objects
tilesheet = objects
else
map = maps[currentMap].decor
tilesheet = decor
end
for y,_ in ipairs(map) do
for _,t in ipairs(map[y]) do
if t ~= 0 then
local sheet = tilesheet[t]
local w,h = tilesets[sheet.set].sheet.width/tilesets[sheet.set].x,tilesets[sheet.set].sheet.height/tilesets[sheet.set].y
local tsx,tsy
if m == 2 then
tsx,tsy = (sheet.x+aStep)*w,sheet.y*h
else
tsx,tsy = sheet.x*w,sheet.y*h
end
local type
if sheet.type == 1 then
w = w/4
h = h/6
type = "floor"
elseif sheet.type == 2 then
if tilesets[sheet.set].sheet.height%10 == 0 then
w = w/4
h = h/10
type = "wall"
else
w = w/4
h = h/8
type = "roof"
end
tsy = tsy + h*4
elseif sheet.type == 3 then
end
if ss[m] == nil then
ss[m] = {}
end
if ss[m][t] == nil then
if sheet.type == 4 then
ss[m][t] = tilesets[sheet.set].sheet:copy(1+tsx,1+tsy,w,h)
else
ss[m][t] = {}
local txc,tyc = 3,5
if type == "roof" then
tyc = 3
end
for ty=0,tyc do
for tx=0,txc do
ss[m][t][#ss[m][t]+1] = tilesets[sheet.set].sheet:copy(1+tsx+tx*w,1+tsy+ty*h,w,h)
end
end
if sheet.type == 2 then
ss[m][t][25] = tilesets[sheet.set].sheet:copy(1+tsx,1+tsy-4*h,w,h*4)
ss[m][t][26] = tilesets[sheet.set].sheet:copy(1+tsx+w,1+tsy-4*h,w,h*4)
ss[m][t][27] = tilesets[sheet.set].sheet:copy(1+tsx+2*w,1+tsy-4*h,w,h*4)
ss[m][t][28] = tilesets[sheet.set].sheet:copy(1+tsx+3*w,1+tsy-4*h,w,h*4)
end
end
end
end
end
end
end
end
function generateMap2(m)
local map,tilesheet
if m == 1 then
map = maps[currentMap].floor
tilesheet = tiles
elseif m == 2 then
map = maps[currentMap].animated
tilesheet = aTiles
elseif m == 3 then
map = maps[currentMap].objects
tilesheet = objects
else
map = maps[currentMap].decor
tilesheet = decor
end
local tw,th = maps[currentMap].tileWidth,maps[currentMap].tileHeight
local maxy,maxx = #map,#map[1] -- size of 2D map
local tilesX,tilesY = math.floor(WIDTH/tw)+4,math.floor(HEIGHT/th)+4
local posY = (HEIGHT/2)+(math.ceil(tilesY/2)*th)
if player.position.y-math.ceil(tilesY/2) < 1 then
local difY = 1-(player.position.y-math.ceil(tilesY/2))
posY = posY - (difY*th)
end
for y=math.max(player.position.y-math.ceil(tilesY/2),1),math.min(player.position.y+math.ceil(tilesY/2),maxy) do
local posX = (WIDTH/2)-(math.ceil(tilesX/2)*tw)
if player.position.x-math.ceil(tilesX/2) < 1 then
local difX = 1-(player.position.x-math.ceil(tilesX/2))
posX = posX + (difX*tw)
end
for x=math.max(player.position.x-math.ceil(tilesX/2),1),math.min(player.position.x+math.ceil(tilesX/2),maxx) do
local t = map[y][x]
if t ~= 0 then
local ul,u,ur,r,dr,d,dl,l -- designates the 8 surrounding tiles: u = upper, d = down, r = right, l = left
if y == 1 then ul,u,ur=0,0,0 end -- the following make sure the map table is not read out of bounds
if y == maxy then dl,d,dr=0,0,0 end
if x == 1 then ul,l,dl=0,0,0 end
if x == maxx then ur,r,dr=0,0,0 end
if ul ~= 0 then ul=map[y-1][x-1] end -- if map will not be read out of bounds, use map to figure out surrounding tiles
if u ~= 0 then u=map[y-1][x] end
if ur ~= 0 then ur=map[y-1][x+1] end
if r ~= 0 then r=map[y][x+1] end
if dr ~= 0 then dr=map[y+1][x+1] end
if d ~= 0 then d=map[y+1][x] end
if dl ~= 0 then dl=map[y+1][x-1] end
if l ~= 0 then l=map[y][x-1] end
local sheet = tilesheet[t]
local w,h = tilesets[sheet.set].sheet.width/tilesets[sheet.set].x,tilesets[sheet.set].sheet.height/tilesets[sheet.set].y
local tsx,tsy
if m == 2 then
tsx,tsy = (sheet.x+aStep)*w,sheet.y*h
else
tsx,tsy = sheet.x*w,sheet.y*h
end
local type
if sheet.type == 1 then
w = w/4
h = h/6
type = "floor"
elseif sheet.type == 2 then
if tilesets[sheet.set].sheet.height%10 == 0 then
w = w/4
h = h/10
type = "wall"
else
w = w/4
h = h/8
type = "roof"
end
tsy = tsy + h*4
elseif sheet.type == 3 then
end
if sheet.type == 1 then -- algorithm used for auto tiling the floor tiles
local tul,tur,tdl,tdr -- tile pieces: tul=upper left, tur=upper right, tdl=bottom left, tdr=bottom right
if u ~= t and l ~= t then
sprite(ss[m][t][13],posX-w/2,posY+h/2,tw/2,th/2)
elseif u ~= t and l == t then
sprite(ss[m][t][15],posX-w/2,posY+h/2,tw/2,th/2)
elseif u == t and l ~= t then
sprite(ss[m][t][5],posX-w/2,posY+h/2,tw/2,th/2)
elseif u == t and l == t and ul ~= t then
sprite(ss[m][t][23],posX-w/2,posY+h/2,tw/2,th/2)
else
sprite(ss[m][t][7],posX-w/2,posY+h/2,tw/2,th/2)
end
if u ~= t and r ~= t then
sprite(ss[m][t][16],posX+w/2,posY+h/2,tw/2,th/2)
elseif u ~= t and r == t then
sprite(ss[m][t][14],posX+w/2,posY+h/2,tw/2,th/2)
elseif u == t and r ~= t then
sprite(ss[m][t][8],posX+w/2,posY+h/2,tw/2,th/2)
elseif u == t and r == t and ur ~= t then
sprite(ss[m][t][24],posX+w/2,posY+h/2,tw/2,th/2)
else
sprite(ss[m][t][6],posX+w/2,posY+h/2,tw/2,th/2)
end
if d ~= t and l ~= t then
sprite(ss[m][t][1],posX-w/2,posY-h/2,tw/2,th/2)
elseif d ~= t and l == t then
sprite(ss[m][t][3],posX-w/2,posY-h/2,tw/2,th/2)
elseif d == t and l ~= t then
sprite(ss[m][t][9],posX-w/2,posY-h/2,tw/2,th/2)
elseif d == t and l == t and dl ~= t then
sprite(ss[m][t][19],posX-w/2,posY-h/2,tw/2,th/2)
else
sprite(ss[m][t][11],posX-w/2,posY-h/2,tw/2,th/2)
end
if d ~= t and r ~= t then
sprite(ss[m][t][4],posX+w/2,posY-h/2,tw/2,th/2)
elseif d ~= t and r == t then
sprite(ss[m][t][2],posX+w/2,posY-h/2,tw/2,th/2)
elseif d == t and r ~= t then
sprite(ss[m][t][12],posX+w/2,posY-h/2,tw/2,th/2)
elseif d == t and r == t and dr ~= t then
sprite(ss[m][t][20],posX+w/2,posY-h/2,tw/2,th/2)
else
sprite(ss[m][t][10],posX+w/2,posY-h/2,tw/2,th/2)
end
elseif sheet.type == 2 then -- algorithm used for auto tiling roof/wall tiles
local tul,tur,tdl,tdr -- tile pieces: tul=upper left, tur=upper right, tdl=bottom left, tdr=bottom right
if u ~= t and l ~= t then
sprite(ss[m][t][13],posX-w/2,posY+h/2+th*2,tw/2,th/2)
elseif u ~= t and l == t then
sprite(ss[m][t][15],posX-w/2,posY+h/2+th*2,tw/2,th/2)
elseif u == t and l ~= t then
sprite(ss[m][t][5],posX-w/2,posY+h/2+th*2,tw/2,th/2)
elseif u == t and l == t and ul ~= t then
if type == "roof" then
sprite(ss[m][t][5],posX-w/2,posY+h/2+th*2,tw/2,th/2)
else
sprite(ss[m][t][23],posX-w/2,posY+h/2+th*2,tw/2,th/2)
end
else
sprite(ss[m][t][7],posX-w/2,posY+h/2+th*2,tw/2,th/2)
end
if u ~= t and r ~= t then
sprite(ss[m][t][16],posX+w/2,posY+h/2+th*2,tw/2,th/2)
elseif u ~= t and r == t then
sprite(ss[m][t][14],posX+w/2,posY+h/2+th*2,tw/2,th/2)
elseif u == t and r ~= t then
sprite(ss[m][t][8],posX+w/2,posY+h/2+th*2,tw/2,th/2)
elseif u == t and r == t and ur ~= t then
if type == "roof" then
sprite(ss[m][t][8],posX+w/2,posY+h/2+th*2,tw/2,th/2)
else
sprite(ss[m][t][24],posX+w/2,posY+h/2+th*2,tw/2,th/2)
end
else
sprite(ss[m][t][6],posX+w/2,posY+h/2+th*2,tw/2,th/2)
end
if d ~= t and l ~= t then
sprite(ss[m][t][1],posX-w/2,posY-h/2+th*2,tw/2,th/2)
sprite(ss[m][t][25],posX-w/2,posY-h/2+th,tw/2,th*2)
elseif d ~= t and l == t then
sprite(ss[m][t][3],posX-w/2,posY-h/2+th*2,tw/2,th/2)
sprite(ss[m][t][27],posX-w/2,posY-h/2+th,tw/2,th*2)
elseif d == t and l ~= t then
sprite(ss[m][t][9],posX-w/2,posY-h/2+th*2,tw/2,th/2)
elseif d == t and l == t and dl ~= t then
if type == "roof" then
sprite(ss[m][t][9],posX-w/2,posY-h/2+th*2,tw/2,th/2)
else
sprite(ss[m][t][19],posX-w/2,posY-h/2+th*2,tw/2,th/2)
end
else
sprite(ss[m][t][11],posX-w/2,posY-h/2+th*2,tw/2,th/2)
end
if d ~= t and r ~= t then
sprite(ss[m][t][4],posX+w/2,posY-h/2+th*2,tw/2,th/2)
sprite(ss[m][t][28],posX+w/2,posY-h/2+th,tw/2,th*2)
elseif d ~= t and r == t then
sprite(ss[m][t][2],posX+w/2,posY-h/2+th*2,tw/2,th/2)
sprite(ss[m][t][26],posX+w/2,posY-h/2+th,tw/2,th*2)
elseif d == t and r ~= t then
sprite(ss[m][t][12],posX+w/2,posY-h/2+th*2,tw/2,th/2)
elseif d == t and r == t and dr ~= t then
if type == "roof" then
sprite(ss[m][t][12],posX+w/2,posY-h/2+th*2,tw/2,th/2)
else
sprite(ss[m][t][20],posX+w/2,posY-h/2+th*2,tw/2,th/2)
end
else
sprite(ss[m][t][10],posX+w/2,posY-h/2+th*2,tw/2,th/2)
end
elseif sheet.type == 3 then
elseif sheet.type == 4 then -- no algorithm used, simply place the object at designated location
if w%tw ~= 0 then -- resize the object to fit the current map
local s = math.ceil(w/tw)
w,h = (tw/w)*w*s,(tw/w)*h*s
elseif h%th ~= 0 and w < tw then
local s = math.ceil(w/tw)
w,h = (th/h)*w*s,(th/h)*h*s
end
sprite(ss[m][t],posX,posY,w,h)
end
end
posX = posX + tw
end
posY = posY - th
end
end
function drawMap2()
player.character = partyMembers[player.allParty[player.party[PartyMember]]].character
if aStep < ElapsedTime then
aStep = ElapsedTime + 1
if ca == 3 and aDirection > 0 then
aDirection = -aDirection
elseif ca == 1 and aDirection < 0 then
aDirection = -aDirection
end
ca = ca + aDirection
end
animateMove(player)
local tw,th = maps[currentMap].tileWidth,maps[currentMap].tileHeight -- tile width and height of current map
local pimg = getCharasetImage(player,tw,th,0,0) -- player image pulled from mesh
local npcimgs = {}
local offx,offy = (WIDTH/tw)*0.5+1,(HEIGHT/th)*0.5+1
pushMatrix()
-- move floor and animated maps to match player location
translate(player.moveX, player.moveY)
generateMap2(1)
-- puddles
for y,_ in pairs(gWeather) do
for x,v in ipairs(gWeather[y]) do
if v ~= 0 then
pushStyle()
noStroke()
if v == 1 then
fill(60, 25, 255, 180)
elseif v == 2 then
fill(255, 255, 255, 255)
end
ellipseMode(CORNER)
ellipse(((x-1)*tw)-8,-((y-1)*th)-8,tw,th)
popStyle()
end
end
end
generateMap2(2)
for i,v in pairs(npcs) do
if v.map == currentMap then
if v.speed ~= 0 then
animateMove(v)
if v.moveX == 0 and v.moveY == 0 then
if math.random(v.rate*100) == 1 and not showDialogue then
if dialogueNPC == i then
dialogueNPC = nil
end
local dir = math.random(4)
if dir == 1 then
move(v,"right")
elseif dir == 2 then
move(v,"left")
elseif dir == 3 then
move(v,"up")
elseif dir == 4 then
move(v,"down")
end
end
end
end
local npcimg = getCharasetImage(v,tw,th,v.moveX,v.moveY)
npcimg:draw()
npcimgs[i] = npcimg
end
end
popMatrix()
pimg:draw() -- draw player
pushMatrix()
-- move object and decor maps to match player location
translate(player.moveX, player.moveY)
generateMap2(3)
generateMap2(4)
-- test whether or not the npc should be drawn on top of an object
for i,v in pairs(npcs) do
if v.map == currentMap then
npcimgs[i].shader.toggle = testBelow(v)
if not v.behind then
npcimgs[i]:draw()
end
npcimgs[i]:clear()
end
end
popMatrix()
pimg.shader.toggle = testBelow(player)
pimg.shader.ghost = player.ghost
if not player.behind then
pimg:draw()
end
pimg:clear()
weather.type = WeatherType
weather.spawnRate = WeatherRate
weather.puddles = SpawnPuddles
weather:update()
weather:draw()
if Light == 1 then
pushStyle()
fill(0, 0, 0, 90)
rect(0,0,WIDTH,HEIGHT)
popStyle()
elseif Light == 2 then
pushStyle()
fill(0, 0, 0, 180)
rect(0,0,WIDTH,HEIGHT)
popStyle()
end
if dialogueNPC ~= nil and showDialogue then
dialogue(dialogueNPC)
return
end
local e = maps[currentMap].events[player.position.y][player.position.x] -- check if an event at the current player location exists
if e > 0 and ((player.moveX == 0 and player.moveY == 0) or currentPopup == e) then
local event = events[e]
if event.type == "teleport" then -- execute teleport event
teleport(event.map,event.x,event.y)
elseif event.type == "popup" or event.type == "entrance" then -- display text for popup and entrance events
currentPopup = e
popup(event.text)
elseif event.type == "weather" then -- execute teleport event
changeWeather(event.weather,event.rate,event.puddles)
end
else
currentPopup = 0
end
GameMenuDraw()
JoypadDraw() -- draw joypad
if joypadOP.status and not showDialogue then
currentPage = 0
if player.moveX == 0 and player.moveY == 0 then
if math.abs(joypadOP.x) > math.abs(joypadOP.y) then
if joypadOP.x > 0 then
dialogueNPC = move(player,"right")
else
dialogueNPC = move(player,"left")
end
else
if joypadOP.y > 0 then
dialogueNPC = move(player,"up")
else
dialogueNPC = move(player,"down")
end
end
end
end
end
-- Icons
function getIconImage(i,x,y,w,h)
local m
local iconset = iconsets[i.set]
if gIcons[i.set] == nil then
m = mesh()
m.texture = iconset.sheet
gIcons[i.set] = m
else
m = gIcons[i.set]
end
local pObj
pObj = m:addRect(x,y,w,h)
m:setRectTex(pObj,(1/iconset.x)*i.x,(1/iconset.y)*i.y,1/iconset.x,1/iconset.y)
return m
end
-- Main
-- RPGenerator
VERSION = "0.7.23"
PROJECTNAME = "RPGenerator"
BUILD = false
-- Slashin8r
-- Thank You for all your help and contributions: Ignatz, Briarfox, and Aalok
function setup()
VersionUpdateChecker.check()
end
-----------------------------
--Update Checker Code added by AutoGist
-----------------------------
VersionUpdateChecker = {}
VersionUpdateChecker.gistURL = "https://api.github.com/gists/5962430"
VersionUpdateChecker.check = function()
local jsonURL = "https://dl.dropboxusercontent.com/s/9e4nvqeu4hsux2q/Json.lua?token_hash=AAFyMB98j4bnt_1gawf9wSke52hsoC7hsIvARcTuZNeOEw&dl=1"
local jsonSuccess = function(data)
local jsonCode = data
if jsonCode then local l = loadstring(jsonCode) l() end
local handleSuccess = function(data)
local gist = json.decode(data)
local version = string.match(gist.description,"%d+%.%d+%.%d+")
if VERSION ~= version then
displayMode(STANDARD)
print("Click Update_Project.")
alert("New Update Availiable! Click Update.","Update")
parameter.action("Update_Project",function() VersionUpdateChecker.GetProject() end)
end
end
http.request(VersionUpdateChecker.gistURL,handleSuccess,function() print("Update Failed") end)
end
http.request(jsonURL,jsonSuccess,function() print("Check Internet Connection") end)
end
function VersionUpdateChecker.GetProject()
local handleSuccess = function(data,i,j)
if listProjectTabs(PROJECTNAME) == nil then
error("Check Project Name")
end
local gist = json.decode(data)
local projName = PROJECTNAME
if gist.files["1aTabOrder"] then
print("***Tab Order Found***")
local taborder = gist.files["1aTabOrder"].content
local strStart =1
local strEnd =0
strStart = string.find(taborder,"#",strEnd)
strEnd = string.find(taborder,"\n",strStart)
while strStart do
local tmp = string.sub(taborder,strStart+1,strEnd-1)
local name = PROJECTNAME..":"..tmp
tmp = tmp..".lua"
saveProjectTab(name,gist.files[tmp].content)
strStart = string.find(taborder,"#",strEnd)
strEnd = string.find(taborder,"\n",strStart)
end
else
for k,v in pairs(gist.files) do
local name = PROJECTNAME .. ":" .. string.gsub(k,".lua","")
saveProjectTab(name, v.content)
end
end
if gist.files["ChangeLog.lua"] then
local str = gist.files["ChangeLog.lua"].content
alert(str,"Version Notes")
end
sound(SOUND_PICKUP, 24058)
print("Reload Project to finish update!")
end
http.request(VersionUpdateChecker.gistURL,handleSuccess,function(data) print("Update Failed") end)
end
--End of Update Checker
--------------------------------------------------
-- Menus
function drawMenu(m)
for _,v in ipairs(m.shapes) do
pushStyle()
spriteMode(CORNERS)
ellipseMode(CORNERS)
rectMode(CORNERS)
if v[9] ~= nil then
lineCapMode(v[9])
end
strokeWidth(v[7])
stroke(v[8])
fill(v[6])
v[1](v[2], v[3], v[4], v[5])
popStyle()
end
if m.name == "Inventory" then
local y = 580
local invCount = 0
pushStyle()
textMode(CORNER)
fontSize(24)
pushStyle()
--smooth()
stroke(0, 0, 0, 255)
strokeWidth(2)
fill(219, 219, 219, 255)
rectMode(CORNERS)
rect(-0.5,599.5,1024.5,708)
popStyle()
if inventorySort == 9 and itemSelected ~= nil and manualSort ~= nil then
local tempItem = items[itemSelected]
items[itemSelected] = items[manualSort]
items[manualSort] = tempItem
itemSelected = nil
manualSort = nil
end
if currentSort ~= inventorySort then
if inventorySort < 0 then
if inventorySort == -1 then
table.sort(items, function(x,y) return x.name < y.name end)
elseif inventorySort == -2 then
table.sort(items, function(x,y) return x.name > y.name end)
end
elseif inventorySort >= 0 and inventorySort ~= 9 and inventorySort ~= 10 then
local itemSort = {}
local iSort = inventorySort
for t=0,8 do
for i,v in ipairs(items) do
if v.type == iSort then
itemSort[#itemSort+1] = v
end
end
iSort = iSort + 1
if iSort > 8 then iSort = 0 end
end
items = itemSort
end
currentSort = inventorySort
end
for i,v in ipairs(items) do
local x
if #invZeroCount < #items then
if i == 1 then
invZeroCount[i] = 0
else
invZeroCount[i] = invZeroCount[i-1]
end
end
if v.count == 0 then
if #invZeroCount <= #items then
if i == 1 then
invZeroCount[i] = 1
else
invZeroCount[i] = invZeroCount[i-1] + 1
end
end
elseif i-invZeroCount[i] > inventoryScroll*3 then
if invCount%3 == 0 then
x = 0
if invCount ~= 0 then
y = y - 40
end
elseif invCount%3 == 1 then
x = 335
else
x = 670
end
if invCount < 45 then
invCount = invCount + 1
if #inventoryControls < invCount then
local control = TextButton("", x, y-20, x+335, y+20,
function ()
if itemSelected ~= i then
if itemSelected ~= nil and inventorySort == 9 then
manualSort = itemSelected
else
manualSort = nil
end
itemSelected = i
itemControls = {}
if inventorySort ~= 9 then
itemInfo = 0
end
elseif inventorySort ~= 9 then
if itemPopup == 0 then
itemControls[1] = itemuse
itemControls[2] = iteminfo
itemControls[3] = itemtrash
itemPopup = itemSelected - (inventoryScroll * 3) - invZeroCount[itemSelected]
else
itemPopup = 0
itemControls = {}
itemInfo = 0
end
end
end
)
inventoryControls[invCount] = control
end
if itemInfo ~= nil then
pushStyle()
fill(0, 0, 0, 255)
fontSize(26)
textAlign(LEFT)
textWrapWidth(1000)
textMode(CENTER)
if itemInfo == 0 then
text(items[itemSelected].info1,WIDTH/2,655)
else
text(itemInfo,WIDTH/2,655)
end
popStyle()
end
if itemSelected == i then
inventoryControls[invCount].selected = true
if #itemControls == 0 then
local invSlot = itemSelected - (inventoryScroll * 3) - invZeroCount[itemSelected]
local yA,yB
if invSlot > 42 then
yA,yB = 20,60
else
yA,yB = -60,-20
end
local usetext
if items[itemSelected].type == 0 or items[itemSelected].type == 7 or items[itemSelected].type == 8 then
usetext = "USE"
else
usetext = "EQUIP"
end
itemuse = TextButton(usetext, x+32, y+yA, x+122, y+yB,
function ()
if items[itemSelected].type == 0 or items[itemSelected].type == 7 or items[itemSelected].type == 8 then
alert("Use Item", itemPopup)
else
alert("Goes To Equipment Menu")
end
end
)
iteminfo = TextButton("INFO", x+122, y+yA, x+212, y+yB,
function () itemInfo = items[itemSelected].info2 end)
itemtrash = TextButton("TRASH", x+212, y+yA, x+303, y+yB,
function () alert("Throw Item Away", itemSelected) end)
end
else
inventoryControls[invCount].selected = false
end
inventoryControls[invCount]:draw()
local icon = getIconImage(v,x+20,y,32,32)
icon:draw()
icon:clear()
if inventoryControls[invCount].over or inventoryControls[invCount].selected then
fill(255, 255, 255, 255)
else
fill(inventoryControls[invCount].foreground)
end
text(v.name, x+40, y-15)
pushStyle()
textMode(CENTER)
text(v.count, x+305, y)
popStyle()
end
end
end
if #itemControls > 0 then
pushStyle()
stroke(0, 0, 0, 255)
strokeWidth(1)
fill(itemControls[1].background)
rectMode(CORNERS)
rect(itemControls[1].left-32,itemControls[1].bottom,itemControls[1].left,itemControls[1].top)
rect(itemControls[3].right,itemControls[3].bottom,itemControls[3].right+32,itemControls[3].top)
popStyle()
textMode(CENTER)
itemControls[1]:draw()
itemControls[2]:draw()
itemControls[3]:draw()
end
inventoryScrollMax = math.max(math.ceil((#items-45-invZeroCount[#invZeroCount])*0.3333),0)
local scrollHeight = 594/(inventoryScrollMax+1)
strokeWidth(18)
stroke(255, 255, 255, 255)
line(1014,0,1014,600)
strokeWidth(12)
stroke(127, 127, 127, 255)
line(1014,597-(inventoryScroll*scrollHeight),1014,597-(inventoryScroll*scrollHeight)-scrollHeight)
popStyle()
elseif m.name == "Game Menu" then
pushStyle()
stroke(0, 0, 0, 255)
strokeWidth(3)
fill(219, 219, 219, 255)
rectMode(CORNERS)
rect(-1,612,625,768)
rect(-1,459,625,615)
rect(-1,306,625,462)
rect(-1,153,625,309)
rect(-1,0,625,156)
strokeWidth(2)
rect(623.5,-0.5,1024.5,168.5)
fill(0, 0, 0, 255)
textMode(CORNER)
for i,v in ipairs(player.party) do
local pm = partyMembers[player.allParty[v]]
local face = getCharacterFace(pm,77,843-(i*153),150,150)
face:draw()
face:clear()
fontSize(30)
fontMetrics()
text(pm.name,160,880-(i*153))
fontSize(24)
text("Lvl:",210-textSize("Lvl:"),850-(i*153))
text("Exp:",210-textSize("Exp:"),820-(i*153))
text(pm.stats.lvl,220,850-(i*153))
text(string.format("%.0f",pm.stats.exp),400-textSize(string.format("%.0f",pm.stats.exp)),820-(i*153))
text("/",410,820-(i*153))
text(string.format("%.0f",pm.stats.nxtlvl),425,820-(i*153))
text("HP:",440-textSize("HP:"),880-(i*153))
text("MP:",440-textSize("MP:"),850-(i*153))
text(pm.stats.hp,525-textSize(pm.stats.hp),880-(i*153))
text(pm.stats.mp,525-textSize(pm.stats.mp),850-(i*153))
text("/",535,880-(i*153))
text("/",535,850-(i*153))
text(pm.stats.maxHp,550,880-(i*153))
text(pm.stats.maxMp,550,850-(i*153))
end
font("ArialRoundedMTBold")
fontSize(30)
text("Time",660,70)
text("Money",660,20)
local totaltime = player.time + math.floor(ElapsedTime)
local hours = string.format("%02d",math.floor(totaltime/3600))
local minutes = string.format("%02d",math.floor((totaltime-(hours*3600))/60))
local time = hours..":"..minutes
text(time,989-textSize(time),70)
text(string.format("%.0f",player.money),989-textSize(string.format("%.0f",player.money)),20)
fontSize(40)
textMode(CENTER)
text(maps[currentMap].name,825,135)
popStyle()
elseif m.name == "Status" then
for i,v in ipairs(player.allParty) do
local x
end
else
itemControls = {}
itemSelected = 0
itemPopup = 0
end
for _,v in ipairs(m.controls) do
pushMatrix()
pushStyle()
v:draw()
popStyle()
popMatrix()
end
end
shaders = {
vertexAlpha = [[
uniform mat4 modelViewProjection;
attribute vec4 position;
attribute vec4 color;
attribute vec2 texCoord;
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
void main()
{
vColor = color;
vTexCoord = texCoord;
gl_Position = modelViewProjection * position;
}
]],
fragmentAlpha = [[
precision highp float;
uniform lowp sampler2D texture;
uniform bool toggle;
uniform bool ghost;
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
void main()
{
lowp vec4 col = texture2D( texture, vTexCoord ) * vColor;
if (ghost) {
col.rgba = col.rgba*0.25;
}
else if (toggle) {
if (vTexCoord.y <= 1./16. ||
(vTexCoord.y <= 5./16. && vTexCoord.y > 4./16.) ||
(vTexCoord.y <= 9./16. && vTexCoord.y > 8./16.) ||
(vTexCoord.y <= 13./16. && vTexCoord.y > 12./16.)) {
col.rgba = col.rgba*0.25;
}
}
gl_FragColor = col;
}
]]
}
-- Weather
-- Aalok
WeatherAnimation = class()
function WeatherAnimation:init(type, pos, vel, mass)
self.type = type
self.position = pos
self.velocity = vel
self.mass = mass
end
function WeatherAnimation:update()
self.position.y = self.position.y - self.velocity
end
function WeatherAnimation:draw()
local p = self.position
if self.type == 0 then
strokeWidth(self.mass)
line(p.x,p.y,p.x,p.y + self.velocity)
elseif self.type == 1 then
noStroke()
ellipse(p.x,p.y,self.mass)
end
end
function WeatherAnimation:shouldCull()
if (self.position.y + self.velocity) < 0 then
return true
end
return false
end
WeatherAnimations = class()
function WeatherAnimations:init()
self.type = 0
self.minSpeed = 5
self.speed = 30
self.spawnRate = 2
self.spawnMore = 0
self.puddles = false
self.weather = {}
end
function WeatherAnimations:updateAndCull()
for i,v in ipairs(self.weather) do
if v:shouldCull() then
table.remove(self.weather, i)
else
v:update()
end
end
end
function WeatherAnimations:update()
if self.spawnMore < ElapsedTime then
if self.type == 0 then
self.spawnMore = ElapsedTime
elseif self.type == 1 then
self.spawnMore = ElapsedTime + 0.05
end
for i=1,self.spawnRate do
local vel,mass
if self.type == 0 then
vel = math.random(self.minSpeed, self.speed)
mass = math.random(3)
elseif self.type == 1 then
vel = math.random(self.minSpeed, self.speed/4)
mass = math.random(4)
end
local spawn = vec2(math.random(WIDTH), HEIGHT)
self.weather[#self.weather+1] = WeatherAnimation(self.type, spawn, vel, mass)
end
end
local puddle
if self.spawnRate == 0 then
puddle = math.random(200)
else
puddle = math.random((5-self.spawnRate)*200)
end
if puddle == 1 and self.puddles then
local puddleX,puddleY = math.random(#gWeather[1]),math.random(#gWeather)
local startX,startY = puddleX,puddleY
if self.spawnRate > 0 then
while gWeather[puddleY][puddleX] == self.type + 1 do
if puddleX < #gWeather[1] then
puddleX = puddleX + 1
else
puddleX = 1
if puddleY < #gWeather then
puddleY = puddleY + 1
else
puddleY = 1
end
end
if startX == puddleX and startY == puddleY then
break
end
end
gWeather[puddleY][puddleX] = self.type + 1
end
if self.spawnRate == 0 then
while gWeather[puddleY][puddleX] == 0 do
if puddleX < #gWeather[1] then
puddleX = puddleX + 1
else
puddleX = 1
if puddleY < #gWeather then
puddleY = puddleY + 1
else
puddleY = 1
end
end
if startX == puddleX and startY == puddleY then
break
end
end
gWeather[puddleY][puddleX] = 0
end
end
self:updateAndCull()
end
function WeatherAnimations:draw()
pushStyle()
ellipseMode(RADIUS)
stroke(60, 25, 225, 180)
fill(255, 255, 255, 180)
for _,v in ipairs(self.weather) do
v:draw()
end
popStyle()
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.