Thank you very much for your help MadByte that helps a whole bunch.
Probably a good point to update:
Spent the day working on getting some extra functionality into the program. Seemingly minor but quite useful..
To avoid going over the same subject twice you can re-live the magic that was my question & enlightenment + my solution to the problem here...
viewtopic.php?f=4&p=200480#p200480
New demo is on the first post of this thread.
Here is the main.lua file.
Code: Select all
-- alphademo2_02.love.
---main.lua
-- This little demo shows the character stats extra info screens that would be part of my main game.
-- It implements some additional features that would be handled by separate modules in the program but for
-- convince sake they are included here in the main.lua file.
require".lib.TEsound.TEsound"
Camera = require ".lib.hump.camera"
Timer = require ".lib.hump.timer"
Signal = require '.lib.hump.signal'
introscreen = love.graphics.newImage("/assets/introduction_1.png")
--v help.
fade = {alpha = 255}
-- This is where I set the iconflags, they are used in the program to keep tabs on the state of various operations, at the moment
-- only nosound and issound are operational. loading, will be when a file is loading from the disk, saving will be when a file is
-- being written to the disk. problem is the error state from the program. Eventually that will be used to show a error code screen
iconflags = {
nosound = false,
issound = true,
loading = false,
saving = false,
problem = false,
keyboard = false,
mouse = false,
gamepad = false,
joystick = false,
touch = false
}
-- This helps us guess if the player has access to a keyboard and mouse, we do this my taking an educated guess based on the OS that
-- we have detected and go from there.
-- OS X, Windows, Linux We can be fairly confident there will be a keyboard and mouse present.
-- For Android & iOS we can be fairly sure there is not going to be a keyboard and mouse, if there is one the player could always set
-- the option in the configuration screen manually later. But for 99% of players who are using Android & iOS platforms they are going
-- to be using touch interfaces.
function osPeek()
osString = love.system.getOS()
if osString == "OS X" then
iconflags.keyboard = true
icon_mouse = true
elseif osString == "Windows" then
iconflags.keyboard = true
iconflags.mouse = true
elseif osString == "Linux" then
iconflags.keyboard = true
iconflags.mouse = true
elseif osString == "Android" then
iconflags.keyboard = false
iconflags.mouse = false
iconflags.touch = true
elseif osString == "iOS" then
iconflags.keyboard = false
iconflags.mouse = false
iconflags.touch = true
end
end
-- This is where we set the player variables, in the game this would be supplied by global game variables that the menu system would
-- access. But as this is a demo, and the program structure is incomplete were just supply them here for now.
-- One thing I need to find out is how to print leading zeros on the screen so that the positioning is correct when player starts the
-- game.
--
-- Another intresting issue is at the moment I am using the font functions to handle this display, ideally I should be using an
-- image font to make scaling issues easyer but I have yet to sucessfully implement this.
player = { credits = 100000000, xptonext = 10245000, xp = 9999999999 }
-- This is a table containing all of the text boxes that get swapped in & out while using the menu.
-- the table length is used to set the mod for the naviagion. I don't need to hardcode anything the program queries this structure to
-- check the length then uses that as the modular. Quite nice. If you need to add more screens just add an additional line to the screens
-- table and you are good to go.
screens = {
love.graphics.newImage("/assets/charactersheetb_3a.png"),
love.graphics.newImage("/assets/charactersheetb_3b.png"),
love.graphics.newImage("/assets/charactersheetb_3c.png"),
love.graphics.newImage("/assets/charactersheetb_3d.png"),
love.graphics.newImage("/assets/charactersheetb_3e.png"),
love.graphics.newImage("/assets/charactersheetb_3f.png"),
love.graphics.newImage("/assets/charactersheetb_3g.png"),
love.graphics.newImage("/assets/charactersheetb_3h.png"),
love.graphics.newImage("/assets/charactersheetb_3i.png"),
love.graphics.newImage("/assets/charactersheetb_3j.png"),
love.graphics.newImage("/assets/charactersheetb_3k.png"),
love.graphics.newImage("/assets/charactersheetb_3l.png"),
love.graphics.newImage("/assets/charactersheetb_3m.png")
}
-- This variable is used as a counter in the navigation for the menu to control what postion in the screens table is used for the
-- love.graphics.draw(screens[current_screen]). Navigation works by messing with this counter. You change the number of this you change
-- what asset is displayed.
current_screen = 1
-- This variable is a counter used in the music selection (when you press F8) this counter keeps track of what music is being played.
-- I wanted to make the function like that of screens but I couldnt figure out how to implement it properly. Its sad because as it stands
-- this is not a dynamic part of the code - I had to hardcode the options so that will make updating difficult.
current_music = 1
-- While this works, I would have liked the flexability of the screens structure, but I couldnt get it to work that way.
-- I still think there is a better way to do this.
function music_list()
if current_music == 1 then
music_list_choice = "/assets/sounds/Restricted-Zone_Looping.mp3"
end
if current_music == 2 then
music_list_choice = "/assets/sounds/Walled-City-of-Doom_Looping.mp3"
end
if current_music == 3 then
music_list_choice = "/assets/sounds/Deserted-Streets_Looping.mp3"
end
end
--music_selection = music_list[current_music]
-- I give up I just can't seem to get a simple tween efect to work right. I must be doing somthing screwed up.
-- The plan was to do back to white transition, then load the character stats menu.
function fadeinout2(tweentype)
color = {0, 0, 0}
Timer.tween(3, color, {255, 255, 255}, tweentype)
Timer.after(3, function() Timer.tween(3, color, {0,0,0}, tweentype) end)
Timer.after(6, function() color = {0, 0, 0} end)
love.graphics.draw(introscreen, 0, 0)
end
function fadeinout()
color = {0, 0, 0}
Timer.tween(3, color, {255, 255, 255}, 'in-out-quad')
Timer.after(3, function() Timer.tween(3, color, {0,0,0}, 'in-out-quad') end)
Timer.after(6, function() color = {0, 0, 0} end)
love.graphics.draw(introscreen, 0, 0)
end
function fadeout()
Timer.tween(3, fade, {alpha=255}, 'in-out-quad', function()
Timer.tween(3, fade, {alpha=0}, 'in-out-quad')end)
end
function fadein()
Timer.tween(10, fade, {alpha=255}, 'in-out-quad', function()
Timer.tween(10, fade, {alpha=0}, 'in-out-quad')end)
end
-- The tween transition efects kind of broke me last night, still cannot figure out how to implement them properly.
-- Figured I would leave them in here to annoy me till I get them to work.
-- eventually they will just live in there own little library that will be called by the various parts of the program.
-- where all the heavy lifting/loading should happen that is not dependant on love.update()
function love.load()
magamestate = {
splash = true,
splash2 = false,
splash3 = false,
mainMenu = false,
subMenuCharacterStats = false,
gamescreen = false,
quitscreen = false,
subMenuHelp = false,
tutorial = false,
combat = false,
dead = false,
trade = false,
judge = false,
cutscene = false
}
osPeek()
icon_keyboard_small = love.graphics.newImage("/assets/keyboard_small.png")
icon_mouse_small = love.graphics.newImage("/assets/mouse_small.png")
icon_gamepad_small = love.graphics.newImage("/assets/gamepad_small.png")
icon_joystick_small = love.graphics.newImage("/assets/joystick_small.png")
icon_touch_small = love.graphics.newImage("/assets/touch_small.png")
icon_nosound_small = love.graphics.newImage("/assets/nosound_small.png")
icon_sound_small = love.graphics.newImage("/assets/sound_small.png")
introfont = love.graphics.newFont(100)
font = love.graphics.newFont(36)
sansfont = love.graphics.newImageFont("/assets/fonts/36_sans.png", "ABCDEFGHIJKLMNOPQRSTUVWXYZ")
-- fadein()
-- fadeout()
--fadeinout2("out-in-linear")
-- Here is where the constant for the dimensions of the graphics assets live.
internalWidth = 1600
internalHeight = 1200
-- Here is where the hump.timer constants and variables live.
-- Here is where the hump.camera constants and variables live.
cam = Camera(500,384, 1, math.pi/2)
mframe = love.graphics.newImage("/assets/menuframe.png")
mbackground = love.graphics.newImage("/assets/framebkg.png")
-- Here we set up the variables and constants used in handling the music and sounds of the program.
sfxval = 0.1
musicval = 0.1
TEsound.volume("sfx",sfxval)
TEsound.volume("music",musicval)
music_list()
TEsound.playLooping(music_list_choice , "music")
end
-- This function draws the icons on the screen, eventually it will show loading, saving ect..
-- I still cant figure out why the icons don't fade the same as the rest of the screen.
-- drawicons() first checks the state of the sfxval variable and the musicval variable to check if both are 0 or less.
-- if both variables are 0 or less then the iconflags.nosound is set to true, and the iconflags.issound is set to false.
-- if either one of the variables sfxval or musicval is greater than 0 (meaning there is sound being output.. then the
-- iconflags.issound is set to true, and the iconflags.nosound is set to false.
-- The second part of this function draws either the icon_nosound_small image to the screen, or if iconflag.sound = true then
-- icon_sound_small is used instead.
-- I should make an error icon also for an error state for nill. Will get on with this later.
function drawicons()
if iconflags.keyboard == true then
love.graphics.draw(icon_keyboard_small, 0, 0)
end
if iconflags.mouse == true then
love.graphics.draw(icon_mouse_small, 0, 0)
end
if iconflags.joystick == true then
love.graphics.draw(icon_joystick_small, 0, 0)
end
if iconflags.gamepad == true then
love.graphics.draw(icon_gamepad_small, 0, 0)
end
if iconflags.touch == true then
love.graphics.draw(icon_touch_small, 0, 0)
end
if sfxval <= 0 and musicval <= 0 then
iconflags.issound = false
iconflags.nosound = true
elseif sfxval > 0 or musicval > 0 then
iconflags.issound = true
iconflags.nosound = false
end
if iconflags.nosound == true then
love.graphics.draw(icon_nosound_small, 0, 0)
elseif iconflags.issound == true then
love.graphics.draw(icon_sound_small, 0, 0)
end
end
-- love.draw() where the screen is blit. Try to avoid to much heavy work here as its called 30 times a second.
-- I am not so happy with the messy code here, I think it could be improved.
function love.draw()
if magamestate.splash == true then
cam:attach()
color = {0, 50, 0}
ink = {100, 0, 0}
love.graphics.setFont(introfont)
love.graphics.setColor(ink)
love.graphics.setBackgroundColor(color)
love.graphics.print("SPLASH SCREEN 1", 100, 500)
ink = {255, 255, 255}
love.graphics.setColor(ink)
Timer.after(3, function() magamestate.splash = false magamestate.splash2 = true end)
cam:detach()
end
if magamestate.splash2 == true then
cam:attach()
color = {50, 0, 0}
ink = {0, 100, 0}
love.graphics.setFont(introfont)
love.graphics.setColor(ink)
love.graphics.setBackgroundColor(color)
love.graphics.print("SPLASH SCREEN 2", 100, 500)
ink = {255, 255, 255}
Timer.after(3, function() magamestate.splash2 = false magamestate.splash3 = true end)
cam:detach()
end
if magamestate.splash3 == true then
cam:attach()
color = {0, 0, 50}
love.graphics.setFont(introfont)
love.graphics.setBackgroundColor(color)
love.graphics.setColor(ink)
love.graphics.print("SPLASH SCREEN 3", 100, 500)
Timer.after(3, function() magamestate.splash3 = false magamestate.subMenuCharacterStats = true end)
cam:detach()
end
if magamestate.subMenuCharacterStats == true then
color = {255, 255, 255}
cam:attach() -- Required to see what the camera is doing.
-- This is where we test the program window to obtain the dimensions.
local screenWidth = love.graphics.getWidth()
local screenHeight = love.graphics.getHeight()
-- This is where the maths is done to calculate the scaling values used.
local ratio = math.min(screenWidth/internalWidth, screenHeight/internalHeight)
-- This is where the scaling takes place
love.graphics.scale(ratio,ratio)
-- Set the background colour as we are using transparencies
love.graphics.setBackgroundColor(color)
-- Draw the base layer background.
love.graphics.draw(mbackground, 0, 0)
-- Draw the updated screens.
love.graphics.draw(screens[current_screen])
-- Draw the mouse coordinates.
--mousey()
drawicons()
love.graphics.setFont(font)
love.graphics.print(player.credits, 505, 115)
love.graphics.print(player.xptonext, 945, 115)
love.graphics.print(player.xp, 1325, 115)
--love.graphics.setFont(sansfont)
--love.graphics.print("THISISATEST", 500, 500)
love.graphics.setColor(255,255,255,fade.alpha)
cam:detach() -- Stop rendering the camera.
end
end
-- this is my very early attempt to start implementing mouse support its not used at the moment.
-- coordinate weirdness vs screen position somewhat put me off. I will use the suit library to handle mouse interactions I think as I
-- used this succesfully before. It beats doing things manually.
-- note to self. Change the silly name for this function. I like fingermouse() better.
function mousey()
fingermouse={}
fingermouse[1]=love.mouse.getX()
fingermouse[2]=love.mouse.getY()
love.graphics.print(fingermouse[1], 100, 100)
love.graphics.print(fingermouse[2], 200, 100)
end
-- love.update(dt) where all the fast stuff that needs to be constantly checked each dt tick needs to live. Avoid heavy processing of
-- stuff that can live elsewhere otherwise madness awaits. I managed to crash the program more times trying to put stuff that should
-- have gone elsewhere in love_update(dt).
-- functions currently hooking on love.update(dt), hump.camera, hump.timer, myJoystick() and TESound.cleanup(). Suit will need to hook into
-- this when I implment it.
-- I am happy with the state of love.update(dt). program seems to be running ok.
function love.update(dt)
cam:rotateTo(0)
Timer.update(dt)
myJoystick()
TEsound.cleanup()
end
-- This is the joystick/gamepad code. As my gamepad is not regonised by the system. I use the lower level library joystick to control the
-- input. As you can see its not fully implemented yet. Eventually this will live in its own library file and be called by each part of the
-- program that needs input from the device. But for now it lives here.
-- thank you to zork for the help in getting this working. There is a better, clearer way to implmenent this but I have yet to get around
-- to implmeenting the mans sugestions.
-- functionality of the myjoystick function -
-- first we define a local variable i and j - these are used to check if the player has really ment to press the button. If you don't have this
-- then you would need ninja like reactions to use the gamepad/joystick as the function would react to poling every frame. As it stands you need
-- to press the button for 7 dt's to get the function to change states and play the sound. i is one way j is the other.
-- second we create an object joysticks that contains the input data from the gamepad/joystick.
-- next we check joysticks to see if either button has been pressed? this is a bool value.
-- it checks if the back shoulder is pressed left or right, it then sets current_screen counter
-- to an updated value based on the modular derived from the length of the screens table.
-- Next I play a sound efect for feedback for the player when they hit the button. This should really not be hardcoded but should be found
-- in a table thats loaded at the start of the program to enable greater flexability. Will implement this later. As it makes updating the
-- code much easyer where I can update all of the sound effects for buttons in one place instead of all over the program.
-- the modular is hard coded, this is a mistake and it should update from a configuration table at the start of the program to enable
-- greater flexability. Will fix this later.
-- Finally we clear the j and i variables - otherwise you have a messy variable left. Its a little bit obsessive as normally this would not matter
-- but its nice to keep things tidy.
local i,j = 0,0
function myJoystick()
local joysticks = love.joystick.getJoysticks()
if #joysticks ~= 0 then
local button1down = joysticks[1]:isDown(5)
local button2down = joysticks[1]:isDown(6)
local isgamepad = joysticks[1]:isGamepad( )
if joysticks ~= nil then
iconflags.joystick = true
end
if isgamepad == true then
iconflags.gamepad = true
end
if button1down == true then
i = i + 1
if i % 7 == 1 then
current_screen = (current_screen - 2) % #screens +1
TEsound.play("/assets/sounds/Creepy-Roll-Over-3.mp3", "sfx")
end
else
i = 0
end
if button2down == true then
j = j + 1
if j % 7 == 1 then
current_screen = current_screen % #screens + 1
TEsound.play("/assets/sounds/Creepy-Roll-Over-4.mp3", "sfx")
end
else
j = 0
end
end
end
-- sadly I have not gotten very far with mouse support.
-- This is an error I should have commented this out. WIll remove once I get SUIT up and running.
function love.mousepressed(x,y,button,istouch)
if button == 1 then
if x >= 50 and x <= 100 then
if y >= 600 and y <= 700 then
love.graphics.print("BAZZINGA", 100, 120)
love.graphics.setColor(0,0,0)
end
if y >= 475 and y <= 495 then
love.graphics.print("BAZZINGA", 100, 120)
love.graphics.setColor(100,100,100)
end
end
end
end
-- love.keypressed(key) where user keyboard input is caught and processed. You see its a bunch of if ifelse conditions, I check the keys
-- I am intrested in then I change the relivant variables depending on what key was pressed. each key gets its own sound. Like I said earlier
-- this code is brittle and difficult to update because its all hardcoded with the soundfiles. The right way to do this is to refer to a
-- configuration table at the start of the program that can be updated by the user from a configuration file. That is the eventual plan. To
-- enable graeter flexability and choice.
-- keybindings should be accessed from a table that can be user defined. That is somthing to fix later. It would make it more flexable and
-- user friendly.
function love.keypressed(key)
-- arrow keys left and right cycles the menu backwards and forwards.
if key == "left" then
current_screen = (current_screen - 2) % #screens + 1
TEsound.play("/assets/sounds/Creepy-Roll-Over-3.mp3", "sfx")
elseif key == "right" then
current_screen = current_screen % #screens + 1
TEsound.play("/assets/sounds/Creepy-Roll-Over-4.mp3", "sfx")
end
-- f3 increases the sfx volume
if key == "f3" then
sfxval = sfxval + 0.1
if sfxval > 1 then
sfxval = 1
end
TEsound.volume("sfx", sfxval)
TEsound.play("/assets/sounds/Creepy-Roll-Over-2.mp3", "sfx")
end
-- f2 decreases the sfx volume
if key == "f2" then
sfxval = sfxval - 0.1
if sfxval < 0 then
sfxval = 0
end
TEsound.volume("sfx", sfxval)
TEsound.play("/assets/sounds/Creepy-Roll-Over-1.mp3", "sfx")
end
-- f6 increases the music volume
if key == "f6" then
musicval = musicval + 0.1
if musicval > 1 then
musicval = 1
end
TEsound.volume("music", musicval)
TEsound.play("/assets/sounds/Creepy-Roll-Over-2.mp3", "sfx")
end
-- f5 decreases the music volume
if key == "f5" then
musicval = musicval - 0.1
if musicval < 0 then
musicval = 0
end
TEsound.volume("music", musicval)
TEsound.play("/assets/sounds/Creepy-Roll-Over-1.mp3", "sfx")
end
-- f4 mutes or unmutes all sounds.
if key == "f4" then
if sfxval and musicval ~= 0 then
sfxval = 0
musicval = 0
TEsound.volume("sfx", sfxval)
TEsound.volume("music", musicval)
elseif sfxval and musicval == 0 then
sfxval = 0.5
musicval = 0.5
TEsound.volume("sfx", sfxval)
TEsound.volume("music", musicval)
end
end
-- the music select key, each time you hit the F8 key you cycle through the playlist.
if key == "f8" then
current_music = current_music % 3 +1
TEsound.stop("music")
music_list()
TEsound.playLooping(music_list_choice , "music")
end
-- finally if the user hits the ESC key it exits the program. Eventually I would have "are you sure you want to quit to desktop?" screen.
if key == "escape" then
TEsound.play("/assets/sounds/Creepy-Roll-Over-1.mp3", "sfx")
love.event.quit()
end
end
There was an annoying bug in the myJoystick() function, whereby because I just assumed a joystick/gamepad would always be plugged in the program crashed when I removed the device, well a conditional check later it was all good and working fine. Simple mistake fixed.
Looking forward to implementing all the good stuff from MadByte. Today was a good day in terms of progress (by my own humble self assessment anyway..) Will be nice to be able to split the project up into more manageable chucks and really for the first time be able to create a skeleton for the whole project. All of the screens and game-states, all the menus will get a place-holder and I will be able to gradually work my way through each one.
Don't think I will have the whole game in one file, would much prefer to split it up into more manageable chunks.
Looked at dds image encoding after installing the plug in for gimp but from what I can see the images are a fair bit larger than png. So its questionable what benefit I would get in performance for just the menu assets. I think I will save the dds stuff for the more active parts of the program where it might make a difference over png decoding. (like the main program.)
One nice thing about having a old laptop is you never get tempted to mess much with shaders and the like as the graphics card can't support it
Still am now thinking about how to implement the main game locations, I was thinking going oldschool and using a table like how a basic program would use an array of 1 and 0 and 2 to determine if a location is possible to travel to. (0 is not possible to move in that direction, 1 is possible and 2 is locked.)
You would then have another array that had a number or string that was used to as a conditional check to branch to that part of the program when position was updated and the screen was redrawn.
Then you would have another array to hold the items, another for npcs ect...
I can remember back in the day a 24x24 array was considered something of a big deal.
Still I think to update this idea instead of having a ton of tables for separate functions I could just use one table of tables with each entry holding all the information about a location in one table.
I would imagine this would have a bit of a performance hit but I can worry about that and split them out into separate tables later if that's the case.
The movement check is pretty straightforward in these old games, you would first check the keyboard to see what direction the player wanted to go, then you would look up the players location, use that to find the position in the path array, if the direction the player wanted to go was a 1 it was all good and you updated the player position to the new location. Otherwise you would error.
How I can do this in the game in lua well thats just updating the gamestate flag/room number depends how its implemented I guess. Not so bad.
Could be top down or side scrolling, or even static screen. The principle is still the same just implemented slightly differently I figure. Anyway that's something to play with tomorrow.