Made the patcher all pretty and shit.

This commit is contained in:
shru 2018-12-15 21:14:48 -05:00
parent 58389f1872
commit 809f472a57
7 changed files with 369 additions and 18 deletions

View file

@ -1,9 +1,21 @@
Cave Story Randomizer
=====================
Todo
----
- Add all weapons.
- Add all items.
- Add instructions.
- Provide modified Cave.pxm. (When Bubbline or Fireball)
Issues
------
- 3 Life Capsules can not be replaced because they appear on maps with 2 capsules. Need label-aware replace.
- Hell Missile Upgrade uses a unique script and won't be easy to replace.
- Bubbler (and other weapons?) can't break blocks in the First Cave.
Credits
-------
- Font: https://datagoblin.itch.io/monogram

BIN
src/assets/background.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

View file

@ -2,9 +2,6 @@ if io then
io.stdout:setvbuf("no")
end
local seed = os.time()
math.randomseed(seed)
function love.conf(t)
t.window = {
title = "Cave Story Randomizer",

View file

@ -1,10 +1,19 @@
function weapon(t)
assert(t.name and t.map and t.id and t.ammo)
assert(t.name and t.map and t.id)
local ammo = t.ammo or "0000"
local names = t.name
if type(names) == 'string' then
names = {names}
end
local getText = {}
for _, name in ipairs(names) do
table.insert(getText, ("Got the =%s=!<WAI0160<NOD"):format(name))
end
return {
name = t.name,
name = names[1],
map = t.map,
getText = ("Got the =%s=!<WAI0160<NOD"):format(t.name),
command = ("<AM+00%s:%s"):format(t.id, t.ammo),
getText = getText,
command = ("<AM+00%s:%s"):format(t.id, ammo),
displayCmd = ("<GIT00%s"):format(t.id),
music = "<CMU0010",
kind = "weapon",
@ -58,17 +67,24 @@ function item(t)
end
return {
-------------------
-------------
-- WEAPONS --
-------------------
-------------
wPolarStar = weapon({
name = "Polar Star",
map = "Pole",
id = "02",
ammo = "0000",
}),
wBubbler = weapon({
name = "Bubbler",
wFireball = weapon({
name = "Fireball",
map = "Santa",
id = "03",
}),
wBubbline = weapon({
name = {
"Bubbline",
"Bubbler",
},
map = "Comu",
id = "07",
ammo = "0100",
@ -156,7 +172,7 @@ return {
mGrasslandsHut = missiles({
map = "WeedB",
}),
mEggRuined = missiles({
mEggCorridorRuined = missiles({
map = "Eggs2",
}),
mEggObservationRuined = missiles({
@ -173,6 +189,8 @@ return {
-----------
-- ITEMS --
-----------
-- - Map System
-- - Chako's Rouge
iPanties = item({
name = "Curly's Panties",
map = "CurlyS",
@ -183,6 +201,20 @@ return {
},
music = "",
}),
-- - Turbocharge
-- If you chose to take the Machine Gun from Curly in the Sand Zone, then you can
-- receive this for free from the Gaudi shopkeeper, Chaba, in the Labyrinth. It
-- speeds up the recovery rate of ammo for the Machine Gun.
-- - Arms Barrier
-- Halves weapon EXP lost when you take damage. Found the top part of the Camp,
-- accessible via a hidden passageway in the ceiling in the large Labyrinth room
-- from which you can access the normal Camp entrance and the Clinic nearby.
-- Unless you took the Machine Gun, you'll have to come back to this area with the
-- Booster to be able to reach it.
-- - Whimsical Star
-- A trinket that you can receive from Chaba, the Gaudi shopkeep in the Labyrinth,
-- if you talk to him with the Spur weapon in your posession. It will cause small
-- stars to float around you as a meagre shield when you charge the Spur to MAX.
iLifePot = item({
name = "Life Pot",
map = "Cent",
@ -194,10 +226,18 @@ return {
--[[
-- Weapons
<KEY<FLJ1640:0201<FL+1640<SOU0022<CNP0200:0021:0000
<MSGOpened the chest.<NOD<GIT0002<AM+0002:0000<CLR
<CMU0010Got the =Polar Star=!<WAI0160<NOD<GIT0000<CLO<RMU
#0500
...
Here, you can have this.<NOD<GIT0003<AM+0003:0000<CLR
<CMU0010Got the =Fireball=!<WAI0160<NOD<RMU<GIT0000<CLRYou're looking for someone?<NOD
#0301
<KEY<GIT1008<MSGDo you want to use the
=Jellyfish Juice=?<YNJ0000<CLO<GIT0000
@ -206,6 +246,8 @@ return {
ashes...<NOD<CLR<GIT0007<AM+0007:0100
<CMU0010Got the =Bubbler=!<WAI0160<NOD<CLO<RMU<DNP0300<END
-- Life Capsules
<PRI<SOU0022<DNP0400<CMU0016
<MSG<GIT1006Got a =Life Capsule=!<WAI0160<NOD<RMU<ML+0003
Max health increased by 3!<NOD<END

234
src/lib/terebi.lua Normal file
View file

@ -0,0 +1,234 @@
local Terebi = {
_VERSION = 'terebi v0.4.0',
_URL = 'https://github.com/oniietzschan/terebi',
_DESCRIPTION = 'Graphics scaling library for Love2D.',
_LICENSE = [[
Massachusecchu... Massachu... chu... chu... License!
Copyright (c) 1789 Retia Adolf
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 IZ, 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. PLEASE HAVE A FUN AND BE GENTLE WITH THIS SOFTWARE.
]]
}
function Terebi.initializeLoveDefaults()
love.graphics.setDefaultFilter('nearest', 'nearest')
love.graphics.setLineStyle('rough')
end
local Screen = {}
local ScreenMetaTable = {__index = Screen}
function Terebi.newScreen(...)
return setmetatable({}, ScreenMetaTable)
:initialize(...)
end
function Screen:initialize(width, height, scale)
assert(type(scale) == 'number')
return self
:setBackgroundColor(0, 0, 0)
:setDimensions(width, height, scale)
end
function Screen:getDimensions()
return self._width, self._height
end
function Screen:setDimensions(width, height, scale, isSkipWindowResize)
assert(type(width) == 'number')
assert(type(height) == 'number')
scale = scale or self._scale
self._width = width
self._height = height
self._canvas = love.graphics.newCanvas(width, height)
return self
:setScale(scale, isSkipWindowResize)
:_saveScale()
end
function Screen:setBackgroundColor(r, g, b)
assert(type(r) == 'number')
assert(type(g) == 'number')
assert(type(b) == 'number')
self._backgroundColor = {r, g, b}
return self
end
function Screen:getScale()
return self._scale
end
function Screen:setScale(scale, isSkipWindowResize)
assert(type(scale) == 'number')
self._scale = math.floor(math.max(1, math.min(scale, self:_getMaxScale())))
if isSkipWindowResize ~= true then
self:_resizeWindow()
end
return self:_updateDrawOffset()
end
function Screen:_resizeWindow()
local currentW, currentH, flags = love.window.getMode()
local newW, newH = love.window.fromPixels(self._width * self._scale, self._height * self._scale)
if not love.window.getFullscreen() and (currentW ~= newW or currentH ~= newH) then
love.window.setMode(newW, newH, flags)
end
return self
end
function Screen:_saveScale()
self._savedScale = self._scale
return self
end
function Screen:_restoreScale()
return self:setScale(self._savedScale)
end
function Screen:increaseScale(isSkipWindowResize)
return self
:setScale(self._scale + 1, isSkipWindowResize)
:_saveScale()
end
function Screen:decreaseScale(isSkipWindowResize)
return self
:setScale(self._scale - 1, isSkipWindowResize)
:_saveScale()
end
function Screen:toggleFullscreen()
if love.window.getFullscreen() then
love.window.setFullscreen(false)
self:_restoreScale()
else
self:_saveScale()
love.window.setFullscreen(true)
self
:setMaxScale()
:_updateDrawOffset()
end
return self
end
function Screen:setMaxScale()
return self:setScale(self:_getMaxScale())
end
function Screen:_getMaxScale()
local desktopW, desktopH = self:_getDesktopDimensions()
local maxScaleX = math.floor(desktopW / self._width)
local maxScaleY = math.floor(desktopH / self._height)
return math.min(maxScaleX, maxScaleY)
end
function Screen:handleResize()
return self
:setScale(self:_getMaxScaleForWindow(), true)
:_updateDrawOffset()
end
function Screen:_getMaxScaleForWindow()
local w, h = love.window.getMode()
return self:_getMaxScaleForDimensions(w, h)
end
function Screen:_getMaxScaleForDimensions(w, h)
local maxScaleX = math.floor(w / self._width)
local maxScaleY = math.floor(h / self._height)
return math.min(maxScaleX, maxScaleY)
end
function Screen:_updateDrawOffset()
local w, h = love.window.getMode()
local scaledWidth = self._width * self._scale
local scaledHeight = self._height * self._scale
self._drawOffsetX = math.floor((w - scaledWidth) / 2)
self._drawOffsetY = math.floor((h - scaledHeight) / 2)
return self
end
function Screen:_getDesktopDimensions()
return love.window.toPixels(love.window.getDesktopDimensions())
end
function Screen:draw(drawFunc, ...)
assert(type(drawFunc) == 'function', type(drawFunc))
love.graphics.push('all')
love.graphics.setCanvas(self._canvas)
love.graphics.clear()
drawFunc(...)
love.graphics.pop()
-- Draw background if it would be visible
if self._drawOffsetX ~= 0 or self._drawOffsetY ~= 0 then
local r, g, b = love.graphics.getColor()
love.graphics.setColor(unpack(self._backgroundColor))
love.graphics.rectangle('fill', 0, 0, love.graphics.getDimensions())
love.graphics.setColor(r, g, b)
end
-- Draw screen
love.graphics.draw(self._canvas, self._drawOffsetX, self._drawOffsetY, 0, self._scale, self._scale)
return self
end
function Screen:getMousePosition()
return self:windowToScreen(love.mouse.getPosition())
end
function Screen:windowToScreen(x, y)
assert(type(x) == 'number')
assert(type(y) == 'number')
return (x - self._drawOffsetX) / self._scale,
(y - self._drawOffsetY) / self._scale
end
function Screen:screenToWindow(x, y)
assert(type(x) == 'number')
assert(type(y) == 'number')
return x * self._scale + self._drawOffsetX,
y * self._scale + self._drawOffsetY
end
return Terebi

View file

@ -3,8 +3,10 @@ require 'lib.strict'
Class = require 'lib.classic'
_ = require 'lib.moses'
Serpent = require 'lib.serpent'
Terebi = require 'lib.terebi'
lf = love.filesystem
lg = love.graphics
local LOG_LEVEL = 3
local function _log(level, prefix, text, ...)
@ -29,15 +31,49 @@ do
end
end
local background
local font
local screen
local status
function love.load()
Terebi.initializeLoveDefaults()
screen = Terebi.newScreen(320, 240, 2)
background = lg.newImage('assets/background.png')
font = lg.newFont('assets/monogram_extended.ttf', 16)
font:setFilter('nearest', 'nearest', 1)
status = "Drag and drop your Cave Story folder here."
end
function love.directorydropped(path)
-- Mount.
local mountPath = 'mounted-data'
assert(lf.mount(path, mountPath))
local items = lf.getDirectoryItems('/' .. mountPath)
local containsStage = _.contains(items, 'Stage')
assert(containsStage)
local dirStage = '/' .. mountPath .. '/Stage'
local dirStage = '/' .. mountPath
do
local items = lf.getDirectoryItems(dirStage)
local containsData = _.contains(items, 'data')
if containsData then
dirStage = dirStage .. '/data'
end
end
do
local items = lf.getDirectoryItems(dirStage)
local containsStage = _.contains(items, 'Stage')
if containsStage then
dirStage = dirStage .. '/Stage'
else
status = "Could not find \"data\" subfolder.\n\nMaybe try dropping your Cave Story \"data\" folder in directly?"
return
end
end
-- Offer tribute to RNGesus.
local seed = tostring(os.time())
math.randomseed(seed)
logNotice(('Offering seed "%s" to RNGesus'):format(seed))
-- Create TscFile objects.
local tscFiles = {}
for _, filename in ipairs(TSC_FILES) do
local path = dirStage .. '/' .. filename
@ -75,6 +111,13 @@ function love.directorydropped(path)
-- Unmount.
assert(lf.unmount(path))
print("\n")
-- Update status
status = [[Randomized data successfully created!
Next overwrite the files in your copy of Cave Story with the versions in the newly created "data" folder. Don't forget to save a backup of the originals!
Then play and have a fun!]]
end
function love.keypressed(key)
@ -82,3 +125,26 @@ function love.keypressed(key)
love.event.push('quit')
end
end
local function _print(text, x, y, align)
align = align or 'center'
lg.setFont(font)
local limit = 320 - (x * 2)
lg.setColor(0, 0, 0)
lg.printf(text, x + 1, y + 1, limit, align)
lg.setColor(1, 1, 1)
lg.printf(text, x, y, limit, align)
end
local function _draw()
lg.draw(background, 0, 0)
_print('Cave Story Randomizer v0.1.0', 0, 10)
_print('by shru', 0, 22)
_print(status, 10, 65)
_print('shru.itch.io', 10, 220, 'left')
_print('@shruuu', 10, 220, 'right')
end
function love.draw()
screen:draw(_draw)
end