123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518 |
- --[========================================================================[--
- Main file for Thrust II Reloaded.
- Copyright © 2015-2018 Pedro Gimeno Fortea
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
- --]========================================================================]--
- -- This needs to be here because some versions don't use conf.lua
- love_version = love._version_major
- and love._version_major * 1000000
- + love._version_minor * 1000
- + love._version_revision
- or 0
- if love.default_moose then
- -- Version 0.2.1 doesn't close the window - force it to.
- -- Not the cleanest way but with no docs, that's the best
- -- we could do.
- function load()
- assert(love_version >= 0009001,
- "This game requires love2d version 0.9.1 or greater")
- end
- function draw() os.exit(1) end
- return
- end
- assert(love_version >= 0009000,
- "This game requires love2d version 0.9.1 or greater")
- -- For performance and licensing reasons, strict.lua is only run by the
- -- developer, not packaged. Include it in the strict/ directory if you want
- -- to develop using it.
- -- Must be activated after the assert, or 0.6.x crashes during error reporting
- pcall(require, 'strict.strict')
- local la,le,lfs,lf,lg,li,lj,lk,lm,lmo,lp,ls,lsys,lth,lt,lw = require'ns'()
- main = {}
- local active, canvas
- function main.activate(new)
- if active and active.deactivate then active.deactivate() end
- active = new
- if active.activate then active.activate() end
- end
- local DIV = love_version >= 11000000 and 255 or 1
- -- screens
- screens = {
- splash = require 'splash';
- menu = require 'menu';
- redef = require 'redef';
- getready = require 'getready';
- game = require 'game';
- gameover = require 'gameover';
- gamewon = require 'gamewon';
- orbexplode = require 'orbexplode';
- }
- -- modules that are not screens
- main.map,
- main.enemytypes,
- main.enemies,
- main.orbs,
- main.decoys,
- main.targets,
- main.respawn,
- main.agents = require 'layout' ()
- function main.setVolume(vol, save)
- vol = (vol < 0) and 0 or vol
- vol = (vol > 1) and 1 or vol
- la.setVolume(vol^2)
- main.vol = vol
- if save then
- local tmp = main.vol .. (main.music and "y" or "n")
- lfs.write("vol.txt", tmp, #tmp)
- end
- end
- local wparamtable = {fullscreen=true,vsync=love_version >= 11000000 and 0,
- resizable=true,fullscreentype="desktop"}
- function main.saveScreen()
- local tmp = string.format("%.17g\n%s\n%d\n%d\n",
- main.pixelsize,
- main.fullscreen and "y" or "n",
- main.wwNFS, main.whNFS)
- lfs.write("screen.txt", tmp, #tmp)
- end
- function main.setMode()
- wparamtable.fullscreen = main.fullscreen
- lw.setMode(main.wwNFS, main.whNFS, wparamtable)
- lw.setTitle("Thrust II reloaded")
- main.saveScreen()
- end
- function main.tomenu()
- main.activate(screens.menu)
- end
- -- deepcopy from http://lua-users.org/wiki/CopyTable
- -- minus the parts that we don't need
- function main.deepcopy(orig)
- local orig_type = type(orig)
- local copy
- if orig_type == 'table' then
- copy = {}
- for orig_key, orig_value in next, orig, nil do
- -- copy[deepcopy(orig_key)] = deepcopy(orig_value)
- copy[orig_key] = main.deepcopy(orig_value)
- end
- -- setmetatable(copy, deepcopy(getmetatable(orig)))
- else -- number, string, boolean, etc
- copy = orig
- end
- return copy
- end
- if love_version >= 11000000 then
- local info = {type = false, size = false, modtime = false}
- function main.isFile(f)
- return lfs.getInfo(f, info) ~= nil
- and (info.type == "file" or info.type == "symlink")
- end
- else
- function main.isFile(f)
- return lfs.isFile(f)
- end
- end
- function main.drawaligned(alignment, drawable, x, y, angle, xzoom, yzoom)
- -- alignment's value works as a numeric keypad ("normal" is 7):
- --
- -- 7 8 9
- -- 4 5 6
- -- 1 2 3
- if type(drawable) == "userdata" and drawable.typeOf and drawable:typeOf("Drawable") then
- -- it's a drawable
- local iw, ih = drawable:getDimensions()
- local ox, oy = 0, 0
- if alignment <= 3 then
- oy = ih
- elseif alignment <= 6 then
- oy = ih / 2
- end
- if alignment % 3 == 0 then
- ox = iw
- elseif alignment % 3 == 2 then
- ox = iw / 2
- end
- return lg.draw(drawable, math.floor(x), math.floor(y), angle, xzoom, yzoom, ox, oy)
- end
- end
- function love.load(args)
- lfs.setIdentity("ThrustIIreloaded")
- main.argv = args
- main.pixelsize = 1
- main.fullscreen = false
- main.wwNFS = 640 -- non-fullscreen width
- main.whNFS = 480 -- non-fullscreen height
- if main.isFile("screen.txt") then
- local lines = lfs.read("screen.txt")
- local pix, fs, ww, wh = lines:match("^(%d+%.?%d*[Ee]?%d*)\n([yn]?)\n(%d+)\n(%d+)\n")
- main.pixelsize = tonumber(pix)
- main.fullscreen = fs == "y"
- main.wwNFS = tonumber(ww)
- main.whNFS = tonumber(wh)
- else
- main.saveScreen()
- end
- main.setMode()
- lg.setBackgroundColor(0,0,0,0)
- lg.setDefaultFilter("nearest", "nearest")
- main.ww = math.ceil(lg.getWidth()/main.pixelsize)
- main.wh = math.ceil(lg.getHeight()/main.pixelsize)
- main.wcx, main.wcy = main.ww/2, main.wh/2
- canvas = lg.newCanvas(main.ww, main.wh)
- main.font = lg.newImageFont("img/Font16x16.png",
- " !\"#$%&'()*+,-./0123456789:;<=>?\127ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~@",
- 1) -- required for 0.10 compatibility, ignored by 0.9
- lg.setFont(main.font)
- local space = love._version_major == 0 and love._version_minor < 10 and " " or "space"
- main.keys =
- { left = "q", right = "w", thrust = "p", pickup = "l", fire = space }
- if main.isFile("keys.txt") then
- local line = 0
- for s in lfs.lines("keys.txt") do
- if s == " " or s == "space" then s = space end -- 0.10 compat
- line = line + 1
- if line == 1 then
- main.keys.left = s
- elseif line == 2 then
- main.keys.right = s
- elseif line == 3 then
- main.keys.thrust = s
- elseif line == 4 then
- main.keys.pickup = s
- elseif line == 5 then
- main.keys.fire = s
- else
- break
- end
- end
- else
- local s = main.keys.left .. "\n"
- .. main.keys.right .. "\n"
- .. main.keys.thrust .. "\n"
- .. main.keys.pickup .. "\n"
- .. main.keys.fire
- lfs.write("keys.txt", s, #s)
- end
- main.vol = 1
- main.music = true
- if main.isFile("vol.txt") then
- local line = lfs.read("vol.txt")
- local vol, music = line:match("^(%d+%.?%d*[Ee]?%d*)([yn]?)$")
- main.setVolume(tonumber(vol))
- main.music = music == "y"
- else
- main.setVolume(main.vol, true)
- end
- for k, mod in next, screens do
- if mod and mod.load then mod.load(arg) end
- end
- -- Bootstrap activation
- main.activate(screens.splash)
- -- Allow volume change with auto-repeat
- lk.setKeyRepeat(true)
- main.singlestep = false
- main.savedelay = false
- main.quitaccepted = false
- -- Dialog data
- main.dialogdata = {}
- main.dialogdata.waitrelease = false
- main.dialogactive = false
- end
- -- HACK: rather than altering love.run, we alter love.timer.sleep.
- -- This guarantees that it's run between lg.present and fetching
- -- events, giving maximum responsivity.
- main.sleep = lt.sleep
- main.lastTime = love.timer.getTime()
- function lt.sleep(t)
- main.sleep(main.lastTime + 0.02 - love.timer.getTime())
- main.lastTime = love.timer.getTime()
- end
- function love.update(dt)
- if main.savedelay then
- main.savedelay = main.savedelay - dt
- if main.savedelay <= 0 then
- main.savedelay = false
- end
- end
- if main.paused and not main.singlestep or main.dialogactive then return end
- if active.update then active.update(main.singlestep and 0.03125 or dt) end
- end
- local function roundrect(x1, y1, w, h, r)
- local nsegments = 8
- local x2, y2 = x1+w, y1+h
- local npoints = nsegments + 1 -- points per corner
- local poly = {}
- poly[1] = x2
- poly[2] = y2 - r
- poly[npoints*2-1] = x2 - r
- poly[npoints*2] = y2
- poly[npoints*2+1] = x1 + r
- poly[npoints*2+2] = y2
- poly[npoints*4-1] = x1
- poly[npoints*4] = y2 - r
- poly[npoints*4+1] = x1
- poly[npoints*4+2] = y1 + r
- poly[npoints*6-1] = x1 + r
- poly[npoints*6] = y1
- poly[npoints*6+1] = x2 - r
- poly[npoints*6+2] = y1
- poly[npoints*8-1] = x2
- poly[npoints*8] = y1 + r
- local angle, x, y
- for i = 2, (nsegments-1)*2, 2 do
- angle = math.pi * 0.25 * (i / nsegments)
- x = math.cos(angle) * r
- y = math.sin(angle) * r
- poly[i+1] = x2 - r + x
- poly[i+2] = y2 - r + y
- poly[npoints*4-i-1] = x1 + r - x
- poly[npoints*4-i] = y2 - r + y
- poly[npoints*4+i+1] = x1 + r - x
- poly[npoints*4+i+2] = y1 + r - y
- poly[npoints*8-i-1] = x2 - r + x
- poly[npoints*8-i] = y1 + r - y
- end
- lg.polygon("fill", poly)
- end
- function main.centertext(text, x, y)
- lg.print(text, math.floor(x-main.font:getWidth(text)/2), math.floor(y-main.font:getHeight()/2))
- end
- local function dummyfunction()
- end
- -- Show a modal yes/no dialog
- function main.dialog(text, cbyes, cbno)
- main.dialogdata.text = text
- main.dialogdata.cbyes = cbyes or dummyfunction
- main.dialogdata.cbno = cbno or dummyfunction
- main.dialogdata.waitrelease = false
- main.dialogactive = true
- -- If we're not in pause mode, pause now anyway since we're modal.
- if not main.paused and active.pause then active.pause(true) end
- end
- local function drawActive()
- -- HACK: Allow scroll-out in get ready screen, by not clearing the canvas
- if active ~= screens.getready then
- lg.clear()
- end
- active.draw()
- end
- function love.draw()
- lg.scale(main.pixelsize)
- if main.dialogactive then
- lg.setColor(85/DIV, 85/DIV, 85/DIV, 255/DIV)
- lg.rectangle("fill", 0, 0, main.ww, main.wh)
- lg.setColor(255/DIV, 255/DIV, 255/DIV, 120/DIV)
- lg.draw(canvas)
- lg.setColor(0/DIV, 0/DIV, 0/DIV, 200/DIV)
- local rectw = main.font:getWidth(main.dialogdata.text) + 68
- roundrect(main.wcx-rectw*0.5, main.wcy-50, rectw, 100, 7)
- lg.setColor(255/DIV, 255/DIV, 255/DIV, 255/DIV)
- main.centertext(main.dialogdata.text, main.wcx, main.wcy - 20)
- lg.setColor(160/DIV,160/DIV,160/DIV)
- roundrect(main.wcx-70, main.wcy+4, 60, 24, 4)
- roundrect(main.wcx+10, main.wcy+4, 60, 24, 4)
- lg.setColor(0/DIV, 0/DIV, 0/DIV, 255/DIV)
- main.centertext("YES", main.wcx-40, main.wcy+16)
- main.centertext("NO", main.wcx+40, main.wcy+16)
- lg.setColor(255/DIV, 255/DIV, 255/DIV, 255/DIV)
- else
- lg.setColor(255/DIV, 255/DIV, 255/DIV, 255/DIV)
- if main.paused and not main.singlestep then
- lg.draw(canvas)
- lg.setColor(64/DIV, 64/DIV, 64/DIV, 150/DIV)
- roundrect(main.wcx-60, main.wh-44, 120, 40, 6)
- lg.setColor(255/DIV, 255/DIV, 255/DIV, 255/DIV)
- main.centertext("PAUSED", main.wcx, main.wh - 24)
- else
- main.canvas = canvas
- if active.draw then
- lg.origin()
- canvas:renderTo(drawActive)
- lg.scale(main.pixelsize)
- end
- lg.draw(canvas)
- end
- if main.savedelay then
- lg.setColor(64/DIV, 64/DIV, 64/DIV, 200/DIV)
- roundrect(main.wcx-60, main.wh-44, 120, 40, 6)
- lg.setColor(255/DIV, 255/DIV, 255/DIV, 255/DIV)
- main.centertext("SAVED", main.wcx, main.wh - 24)
- end
- end
- if main.singlestep then main.singlestep = false end
- lg.origin()
- end
- local function quit_yes()
- main.quitaccepted = true
- le.quit()
- end
- function love.quit()
- if not main.quitaccepted then
- main.dialog("REALLY QUIT?", quit_yes)
- return true -- don't quit
- end
- end
- function love.mousepressed(x, y, b)
- x = x / main.pixelsize
- y = y / main.pixelsize
- if main.dialogactive then
- if x >= main.wcx-70 and x <= main.wcx-10
- and y >= main.wcy+4 and y <= main.wcy+28
- then
- main.dialogdata.cbyes()
- main.dialogactive = false
- elseif x >= main.wcx+10 and x <= main.wcx+70
- and y >= main.wcy+4 and y <= main.wcy+28
- then
- main.dialogdata.cbno()
- main.dialogactive = false
- end
- -- If we were not in pause mode then we paused because of the menu.
- -- If that's the case and we're closing the dialog, unpause now.
- if not main.dialogactive and not main.paused and active.pause then
- active.pause(false)
- end
- end
- end
- function love.keypressed(k, r, rr)
- -- 0.10+ compat
- if rr ~= nil then r = rr end
- if k == "kp-" or k == "kp+" then
- main.setVolume(main.vol + (k == "kp+" and 0.0625 or -0.0625), true)
- elseif r then
- -- nothing
- elseif main.dialogactive then
- if k == "y" then
- main.dialogdata.cbyes()
- main.dialogdata.waitrelease = true
- elseif k == "n" or k == "escape" then
- main.dialogdata.cbno()
- main.dialogdata.waitrelease = true
- end
- elseif k == "pause" and not main.dialogactive then
- main.paused = not main.paused
- if active.pause then active.pause(main.paused) end
- elseif active.keypressed and not main.paused then
- active.keypressed(k, r)
- end
- --[[ debug ]]
- --if k == "1" and main.paused then main.singlestep = true end
- --]]
- end
- function love.textinput(c)
- if not main.paused then
- if active.textinput then active.textinput(c) end
- end
- end
- function love.keyreleased(k)
- if main.dialogactive then
- if main.dialogdata.waitrelease then
- main.dialogdata.waitrelease = false
- main.dialogactive = false
- if not main.paused and active.pause then active.pause(false) end
- end
- elseif k ~= "pause" and not main.paused then
- if active.keyreleased then active.keyreleased(k) end
- end
- end
- function love.resize(neww, newh)
- main.ww = math.ceil(neww/main.pixelsize)
- main.wh = math.ceil(newh/main.pixelsize)
- main.wcx = main.ww/2
- main.wcy = main.wh/2
- local newcanvas = lg.newCanvas(main.ww, main.wh)
- local saveCanvas = lg.getCanvas()
- lg.setCanvas(newcanvas)
- main.drawaligned(5, canvas, main.wcx, main.wcy)
- lg.setCanvas(saveCanvas)
- canvas = newcanvas
- if not main.fullscreen then
- main.wwNFS = neww
- main.whNFS = newh
- main.saveScreen()
- end
- for k, mod in next, screens do
- if mod.resize then
- mod.resize(neww, newh)
- end
- end
- end
|