implemented duplicate
This commit is contained in:
parent
24294b9095
commit
00c0f6605b
3
.duplicate/clone/update-layout.sh
Executable file
3
.duplicate/clone/update-layout.sh
Executable file
|
@ -0,0 +1,3 @@
|
|||
target=$1
|
||||
location=$(dirname "$0")
|
||||
cp -rf $location/../../* $target
|
87
.duplicate/new-game.sh
Executable file
87
.duplicate/new-game.sh
Executable file
|
@ -0,0 +1,87 @@
|
|||
#!/bin/bash
|
||||
|
||||
help="new-game.sh: \n
|
||||
A tool for duplicating min-love2d-fennel. \n\
|
||||
\n\
|
||||
usage: \n\
|
||||
./new-game.sh [project-name] [opts]\n\
|
||||
\n
|
||||
opts:\n
|
||||
-o= (--output-directory=) Target parent directory. Defaults to PWD\n
|
||||
-l= (--layut=) Set the layout. Defaults to minimal layout.\n
|
||||
-f (--force) overwrite the target directory if it exists\n
|
||||
-h (--help) Display this text\n
|
||||
\n
|
||||
eg: \n\
|
||||
./new-game.sh sample-game -o=../ -f
|
||||
"
|
||||
|
||||
name=$1
|
||||
output_dir=$(pwd)
|
||||
gethelp=false
|
||||
location=$(dirname "$0")
|
||||
force=false
|
||||
layout="clone"
|
||||
|
||||
for i in "$@"
|
||||
do
|
||||
case $i in
|
||||
-o=*|--output-directory=*)
|
||||
output_dir="${i#*=}"
|
||||
;;
|
||||
-l=*|--layout=*)
|
||||
layout="${i#*=}"
|
||||
;;
|
||||
-f|--force)
|
||||
force=true
|
||||
;;
|
||||
-h|--help)
|
||||
gethelp=true
|
||||
;;
|
||||
*)
|
||||
# unknown option
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
|
||||
if [ ! -d $output_dir ]; then
|
||||
echo "output directory not found!"
|
||||
echo $output_dir
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d $location/$layout ]; then
|
||||
echo "\"$layout\" layout not found!"
|
||||
echo "Valid built in layouts are:"
|
||||
echo "clone"
|
||||
echo "seperate-source"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
target_dir=$output_dir/$name
|
||||
|
||||
if [ -d $target_dir ]; then
|
||||
if [ $force = true ]; then
|
||||
echo "Overwriting $target_dir."
|
||||
rm -rf $target_dir
|
||||
else
|
||||
echo "target directory already exists! Use -f or --force to overwrite this directory."
|
||||
echo $target_dir
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
copy-dir (){
|
||||
mkdir $target_dir
|
||||
./$location/$layout/update-layout.sh $target_dir
|
||||
rm -rf $location/.duplicate
|
||||
rm -rf $location/.git
|
||||
}
|
||||
|
||||
if [ $gethelp = true ]; then
|
||||
echo -e $help
|
||||
else
|
||||
copy-dir
|
||||
fi
|
||||
|
113
.duplicate/seperate-source/overwrite/lib/gamestate.lua
Normal file
113
.duplicate/seperate-source/overwrite/lib/gamestate.lua
Normal file
|
@ -0,0 +1,113 @@
|
|||
--[[
|
||||
Copyright (c) 2010-2013 Matthias Richter
|
||||
|
||||
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.
|
||||
|
||||
Except as contained in this notice, the name(s) of the above copyright holders
|
||||
shall not be used in advertising or otherwise to promote the sale, use or
|
||||
other dealings in this Software without prior written authorization.
|
||||
|
||||
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.
|
||||
]]--
|
||||
|
||||
local function __NULL__() end
|
||||
|
||||
-- default gamestate produces error on every callback
|
||||
local state_init = setmetatable({leave = __NULL__},
|
||||
{__index = function() error("Gamestate not initialized. Use Gamestate.switch()") end})
|
||||
local stack = {state_init}
|
||||
local initialized_states = setmetatable({}, {__mode = "k"})
|
||||
local state_is_dirty = true
|
||||
|
||||
local GS = {}
|
||||
function GS.new(t) return t or {} end -- constructor - deprecated!
|
||||
|
||||
local function change_state(stack_offset, to, ...)
|
||||
local pre = stack[#stack]
|
||||
|
||||
-- initialize only on first call
|
||||
;(initialized_states[to] or to.init or __NULL__)(to)
|
||||
initialized_states[to] = __NULL__
|
||||
|
||||
stack[#stack+stack_offset] = to
|
||||
state_is_dirty = true
|
||||
return (to.enter or __NULL__)(to, pre, ...)
|
||||
end
|
||||
|
||||
function GS.switch(to, ...)
|
||||
assert(to, "Missing argument: Gamestate to switch to")
|
||||
assert(to ~= GS, "Can't call switch with colon operator")
|
||||
;(stack[#stack].leave or __NULL__)(stack[#stack])
|
||||
return change_state(0, to, ...)
|
||||
end
|
||||
|
||||
function GS.push(to, ...)
|
||||
assert(to, "Missing argument: Gamestate to switch to")
|
||||
assert(to ~= GS, "Can't call push with colon operator")
|
||||
return change_state(1, to, ...)
|
||||
end
|
||||
|
||||
function GS.pop(...)
|
||||
assert(#stack > 1, "No more states to pop!")
|
||||
local pre, to = stack[#stack], stack[#stack-1]
|
||||
stack[#stack] = nil
|
||||
;(pre.leave or __NULL__)(pre)
|
||||
state_is_dirty = true
|
||||
return (to.resume or __NULL__)(to, pre, ...)
|
||||
end
|
||||
|
||||
function GS.current()
|
||||
return stack[#stack]
|
||||
end
|
||||
|
||||
-- XXX: don't overwrite love.errorhandler by default:
|
||||
-- this callback is different than the other callbacks
|
||||
-- (see http://love2d.org/wiki/love.errorhandler)
|
||||
-- overwriting thi callback can result in random crashes (issue #95)
|
||||
local all_callbacks = { 'draw', 'update' }
|
||||
|
||||
-- fetch event callbacks from love.handlers
|
||||
for k in pairs(love.handlers) do
|
||||
all_callbacks[#all_callbacks+1] = k
|
||||
end
|
||||
|
||||
function GS.registerEvents(callbacks)
|
||||
local registry = {}
|
||||
callbacks = callbacks or all_callbacks
|
||||
for _, f in ipairs(callbacks) do
|
||||
registry[f] = love[f] or __NULL__
|
||||
love[f] = function(...)
|
||||
registry[f](...)
|
||||
return GS[f](...)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- forward any undefined functions
|
||||
setmetatable(GS, {__index = function(_, func)
|
||||
-- call function only if at least one 'update' was called beforehand
|
||||
-- (see issue #46)
|
||||
if not state_is_dirty or func == 'update' then
|
||||
state_is_dirty = false
|
||||
return function(...)
|
||||
return (stack[#stack][func] or __NULL__)(stack[#stack], ...)
|
||||
end
|
||||
end
|
||||
return __NULL__
|
||||
end})
|
||||
|
||||
return GS
|
151
.duplicate/seperate-source/overwrite/lib/js.lua
Normal file
151
.duplicate/seperate-source/overwrite/lib/js.lua
Normal file
|
@ -0,0 +1,151 @@
|
|||
__requestQueue = {}
|
||||
_requestCount = 0
|
||||
_Request =
|
||||
{
|
||||
command = "",
|
||||
currentTime = 0,
|
||||
timeOut = 2,
|
||||
id = '0'
|
||||
}
|
||||
local os = love.system.getOS()
|
||||
local __defaultErrorFunction = nil
|
||||
local isDebugActive = false
|
||||
|
||||
JS = {}
|
||||
|
||||
function JS.callJS(funcToCall)
|
||||
if(os == "Web") then
|
||||
print("callJavascriptFunction " .. funcToCall)
|
||||
end
|
||||
end
|
||||
|
||||
--You can pass a set of commands here and, it is a syntactic sugar for executing many commands inside callJS, as it only calls a function
|
||||
--If you pass arguments to the func beyond the string, it will perform automatically string.format
|
||||
--Return statement is possible inside this structure
|
||||
--This will return a string containing a function to be called by JS.callJS
|
||||
local _unpack
|
||||
if(_VERSION == "Lua 5.1" or _VERSION == "LuaJIT") then
|
||||
_unpack = unpack
|
||||
else
|
||||
_unpack = table.unpack
|
||||
end
|
||||
function JS.stringFunc(str, ...)
|
||||
str = "(function(){"..str.."})()"
|
||||
if(#arg > 0) then
|
||||
str = str:format(_unpack(arg))
|
||||
end
|
||||
str = str:gsub("[\n\t]", "")
|
||||
return str
|
||||
end
|
||||
|
||||
--The call will store in the webDB the return value from the function passed
|
||||
--it timeouts
|
||||
local function retrieveJS(funcToCall, id)
|
||||
--Used for retrieveData function
|
||||
JS.callJS("FS.writeFile('"..love.filesystem.getSaveDirectory().."/__temp"..id.."', "..funcToCall..");")
|
||||
end
|
||||
|
||||
--Call JS.newRequest instead
|
||||
function _Request:new(isPromise, command, onDataLoaded, onError, timeout, id)
|
||||
local obj = {}
|
||||
setmetatable(obj, self)
|
||||
obj.command = command
|
||||
obj.onError = onError or __defaultErrorFunction
|
||||
if not isPromise then
|
||||
retrieveJS(command, id)
|
||||
else
|
||||
JS.callJS(command)
|
||||
end
|
||||
obj.onDataLoaded = onDataLoaded
|
||||
obj.timeOut = (timeout == nil) and obj.timeOut or timeout
|
||||
obj.id = id
|
||||
|
||||
|
||||
function obj:getData()
|
||||
--Try to read from webdb
|
||||
return love.filesystem.read("__temp"..self.id)
|
||||
end
|
||||
|
||||
function obj:purgeData()
|
||||
--Data must be purged for not allowing old data to be retrieved
|
||||
love.filesystem.remove("__temp"..self.id)
|
||||
end
|
||||
|
||||
function obj:update(dt)
|
||||
self.timeOut = self.timeOut - dt
|
||||
local retData = self:getData()
|
||||
|
||||
if((retData ~= nil and retData ~= "nil") or self.timeOut <= 0) then
|
||||
if(retData ~= nil and retData:match("ERROR") == nil) then
|
||||
if isDebugActive then
|
||||
print("Data has been retrieved "..retData)
|
||||
end
|
||||
self.onDataLoaded(retData)
|
||||
else
|
||||
self.onError(self.id, retData)
|
||||
end
|
||||
self:purgeData()
|
||||
return false
|
||||
else
|
||||
return true
|
||||
end
|
||||
end
|
||||
return obj
|
||||
end
|
||||
|
||||
--Place this function on love.update and set it to return if it returns false (This API is synchronous)
|
||||
function JS.retrieveData(dt)
|
||||
local isRetrieving = #__requestQueue ~= 0
|
||||
local deadRequests = {}
|
||||
for i = 1, #__requestQueue do
|
||||
local isUpdating =__requestQueue[i]:update(dt)
|
||||
if not isUpdating then
|
||||
table.insert(deadRequests, i)
|
||||
end
|
||||
end
|
||||
for i = 1, #deadRequests do
|
||||
if(isDebugActive) then
|
||||
print("Request died: "..deadRequests[i])
|
||||
end
|
||||
table.remove(__requestQueue, deadRequests[i])
|
||||
end
|
||||
return isRetrieving
|
||||
end
|
||||
|
||||
--May only be used for functions that don't return a promise
|
||||
function JS.newRequest(funcToCall, onDataLoaded, onError, timeout, optionalId)
|
||||
if(os ~= "Web") then
|
||||
return
|
||||
end
|
||||
table.insert(__requestQueue, _Request:new(false, funcToCall, onDataLoaded, onError, timeout or 5, optionalId or _requestCount))
|
||||
end
|
||||
|
||||
--This function can be handled manually (in JS code)
|
||||
--How to: add the function call when your events resolve: FS.writeFile("Put love.filesystem.getSaveDirectory here", "Pass a string here (NUMBER DONT WORK"))
|
||||
--Or it can be handled by Lua, it auto sets your data if you write the following command:
|
||||
-- _$_(yourStringOrFunctionHere)
|
||||
function JS.newPromiseRequest(funcToCall, onDataLoaded, onError, timeout, optionalId)
|
||||
if(os ~= "Web") then
|
||||
return
|
||||
end
|
||||
optionalId = optionalId or _requestCount
|
||||
funcToCall = funcToCall:gsub("_$_%(", "FS.writeFile('"..love.filesystem.getSaveDirectory().."/__temp"..optionalId.."', ")
|
||||
table.insert(__requestQueue, _Request:new(true, funcToCall, onDataLoaded, onError, timeout or 5, optionalId))
|
||||
end
|
||||
|
||||
|
||||
--It receives the ID from ther request
|
||||
--Don't try printing the request.command, as it will execute the javascript command
|
||||
function JS.setDefaultErrorFunction(func)
|
||||
__defaultErrorFunction = func
|
||||
end
|
||||
|
||||
JS.setDefaultErrorFunction(function(id, error)
|
||||
if( isDebugActive ) then
|
||||
local msg = "Data could not be loaded for id:'"..id.."'"
|
||||
if(error)then
|
||||
msg = msg.."\nError: "..error
|
||||
end
|
||||
print(msg)
|
||||
end
|
||||
end)
|
58
.duplicate/seperate-source/overwrite/main.lua
Normal file
58
.duplicate/seperate-source/overwrite/main.lua
Normal file
|
@ -0,0 +1,58 @@
|
|||
-- bootstrap the compiler
|
||||
fennel = require("lib.fennel")
|
||||
table.insert(package.loaders, fennel.make_searcher({correlate=true}))
|
||||
|
||||
fennel.path = love.filesystem.getSource() .. "/?.fnl;" ..
|
||||
love.filesystem.getSource() .. "/src/?.fnl;" ..
|
||||
love.filesystem.getSource() .. "/src/?/init.fnl;" ..
|
||||
fennel.path
|
||||
|
||||
debug_mode = true
|
||||
pp = function(x) print(fennel.view(x)) end
|
||||
db = function(x)
|
||||
if (debug_mode == true) then
|
||||
local debug_info = debug.getinfo(1)
|
||||
-- print debug.getinfo
|
||||
local currentline = debug_info.currentline
|
||||
-- local file = debug_info.source:match("^.+/(.+)$")
|
||||
local file = debug_info["short_src"] or ""
|
||||
local name = debug_info["namewhat"] or ""
|
||||
pp({"db", x})
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
js = (require "lib.js")
|
||||
lume = require("lib.lume")
|
||||
|
||||
local make_love_searcher = function(env)
|
||||
return function(module_name)
|
||||
local path = module_name:gsub("%.", "/") .. ".fnl"
|
||||
if love.filesystem.getInfo(path) then
|
||||
return function(...)
|
||||
local code = love.filesystem.read(path)
|
||||
return fennel.eval(code, {env=env}, ...)
|
||||
end, path
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(package.loaders, make_love_searcher(_G))
|
||||
table.insert(fennel["macro-searchers"], make_love_searcher("_COMPILER"))
|
||||
|
||||
require("lib.js")
|
||||
|
||||
-- taken from bump
|
||||
function rect_isIntersecting(x1,y1,w1,h1, x2,y2,w2,h2)
|
||||
return x1 < x2+w2 and x2 < x1+w1 and
|
||||
y1 < y2+h2 and y2 < y1+h1
|
||||
end
|
||||
|
||||
function pointWithin(px,py,x,y,w,h)
|
||||
return px < x + w and px > x and
|
||||
py < y + h and py > y
|
||||
end
|
||||
|
||||
math.randomseed( os.time() )
|
||||
|
||||
require("wrap")
|
95
.duplicate/seperate-source/overwrite/makefile
Normal file
95
.duplicate/seperate-source/overwrite/makefile
Normal file
|
@ -0,0 +1,95 @@
|
|||
VERSION=0.1.0
|
||||
LOVE_VERSION=11.4
|
||||
NAME=change-me
|
||||
ITCH_ACCOUNT=username
|
||||
URL=https://username.itch.io/change-me
|
||||
AUTHOR="Change Me"
|
||||
DESCRIPTION="Change Me"
|
||||
|
||||
HEIGHT=640
|
||||
WIDTH=880
|
||||
CANVAS_COLOUR="26,16,16"
|
||||
TEXT_COLOUR="250,248,240"
|
||||
|
||||
|
||||
LIBS_LUA := $(wildcard lib/*)
|
||||
LIBS_FNL := $(wildcard lib/*.fnl)
|
||||
LUA := $(wildcard *.lua)
|
||||
SRC := $(wildcard src/*.fnl)
|
||||
OUT := $(patsubst src/%.fnl,%.lua,$(SRC))
|
||||
OUT_LIBS := $(patsubst %.fnl,%.lua,$(LIBS_FNL))
|
||||
|
||||
run: $(OUT) $(OUT_ENTS); love .
|
||||
|
||||
count: ; cloc src/*.fnl --force-lang=clojure
|
||||
|
||||
clean: ; rm -rf releases/* $(OUT) $(OUT_LIBS)
|
||||
|
||||
cleansrc: ; rm -rf $(OUT) $(OUT_LIBS)
|
||||
|
||||
%.lua: src/%.fnl; lua lib/fennel --compile --correlate $< > $@
|
||||
|
||||
lib/%.lua: lib/%.fnl; lua lib/fennel --compile --correlate $< > $@
|
||||
|
||||
LOVEFILE=releases/$(NAME)-$(VERSION).love
|
||||
|
||||
$(LOVEFILE): $(LUA) $(OUT) $(OUT_LIBS) $(LIBS_LUA) assets #text
|
||||
mkdir -p releases/
|
||||
find $^ -type f | LC_ALL=C sort | env TZ=UTC zip -r -q -9 -X $@ -@
|
||||
|
||||
love: $(LOVEFILE)
|
||||
|
||||
# platform-specific distributables
|
||||
|
||||
REL=$(PWD)/buildtools/love-release.sh # https://p.hagelb.org/love-release.sh
|
||||
FLAGS=-a "$(AUTHOR)" --description $(DESCRIPTION) \
|
||||
--love $(LOVE_VERSION) --url $(URL) --version $(VERSION) --lovefile $(LOVEFILE)
|
||||
|
||||
releases/$(NAME)-$(VERSION)-x86_64.AppImage: $(LOVEFILE)
|
||||
cd buildtools/appimage && ./build.sh $(LOVE_VERSION) $(PWD)/$(LOVEFILE)
|
||||
mv buildtools/appimage/game-x86_64.AppImage $@
|
||||
|
||||
releases/$(NAME)-$(VERSION)-macos.zip: $(LOVEFILE)
|
||||
$(REL) $(FLAGS) -M
|
||||
mv releases/$(NAME)-macos.zip $@
|
||||
|
||||
releases/$(NAME)-$(VERSION)-win.zip: $(LOVEFILE)
|
||||
$(REL) $(FLAGS) -W32
|
||||
mv releases/$(NAME)-win32.zip $@
|
||||
|
||||
|
||||
releases/$(NAME)-$(VERSION)-web.zip: $(LOVEFILE)
|
||||
buildtools/love-js/love-js.sh releases/$(NAME)-$(VERSION).love $(NAME) -v=$(VERSION) -a=$(AUTHOR) -o=releases -x=$(WIDTH) -y=$(HEIGHT) -c=$(CANVAS_COLOUR) -t=$(TEXT_COLOUR)
|
||||
|
||||
releases/$(NAME)-$(VERSION)-source.zip:
|
||||
find assets buildtools lib ./conf.lua ./makefile ./license.txt ./main.lua ./readme.org src buildtools -type f | LC_ALL=C sort | env TZ=UTC zip -r -q -9 -X $@ -@
|
||||
|
||||
runweb: $(LOVEFILE)
|
||||
buildtools/love-js/love-js.sh releases/$(NAME)-$(VERSION).love $(NAME) -v=$(VERSION) -a=$(AUTHOR) -o=releases -x=$(WIDTH) -y=$(HEIGHT) -c=$(CANVAS_COLOUR) -t=$(TEXT_COLOUR) -r
|
||||
|
||||
linux: releases/$(NAME)-$(VERSION)-x86_64.AppImage
|
||||
mac: releases/$(NAME)-$(VERSION)-macos.zip
|
||||
windows: releases/$(NAME)-$(VERSION)-win.zip
|
||||
web: releases/$(NAME)-$(VERSION)-web.zip
|
||||
source: releases/$(NAME)-$(VERSION)-source.zip
|
||||
|
||||
# If you release on itch.io, you should install butler:
|
||||
# https://itch.io/docs/butler/installing.html
|
||||
|
||||
uploadlinux: releases/$(NAME)-$(VERSION)-x86_64.AppImage
|
||||
butler push $^ $(ITCH_ACCOUNT)/$(NAME):linux --userversion $(VERSION)
|
||||
uploadmac: releases/$(NAME)-$(VERSION)-macos.zip
|
||||
butler push $^ $(ITCH_ACCOUNT)/$(NAME):mac --userversion $(VERSION)
|
||||
uploadwindows: releases/$(NAME)-$(VERSION)-win.zip
|
||||
butler push $^ $(ITCH_ACCOUNT)/$(NAME):windows --userversion $(VERSION)
|
||||
uploadlove: releases/$(NAME)-$(VERSION).love
|
||||
butler push $^ $(ITCH_ACCOUNT)/$(NAME):love --userversion $(VERSION)
|
||||
uploadweb: releases/$(NAME)-$(VERSION)-web.zip
|
||||
butler push $^ $(ITCH_ACCOUNT)/$(NAME):web --userversion $(VERSION)
|
||||
uploadsource: releases/$(NAME)-$(VERSION)-source.zip
|
||||
butler push $^ $(ITCH_ACCOUNT)/$(NAME):source --userversion $(VERSION)
|
||||
|
||||
|
||||
upload: uploadlinux uploadmac uploadwindows uploadweb uploadlove cleansrc
|
||||
|
||||
release: linux web mac windows upload cleansrc
|
27
.duplicate/seperate-source/overwrite/src/mode-intro.fnl
Normal file
27
.duplicate/seperate-source/overwrite/src/mode-intro.fnl
Normal file
|
@ -0,0 +1,27 @@
|
|||
(import-macros {: incf} :sample-macros)
|
||||
|
||||
(var counter 0)
|
||||
(var time 0)
|
||||
|
||||
(love.graphics.setNewFont 30)
|
||||
|
||||
(local (major minor revision) (love.getVersion))
|
||||
|
||||
{:draw (fn draw [self message]
|
||||
(local (w h _flags) (love.window.getMode))
|
||||
(love.graphics.printf
|
||||
(: "Love Version: %s.%s.%s"
|
||||
:format major minor revision) 0 10 w :center)
|
||||
(love.graphics.printf
|
||||
(: "This window should close in %0.1f seconds"
|
||||
:format (math.max 0 (- 3 time)))
|
||||
0 (- (/ h 2) 15) w :center))
|
||||
:update (fn update [self dt set-mode]
|
||||
(if (< counter 65535)
|
||||
(set counter (+ counter 1))
|
||||
(set counter 0))
|
||||
(incf time dt)
|
||||
(when (> time 3)
|
||||
(love.event.quit)))
|
||||
:keypressed (fn keypressed [self key set-mode]
|
||||
(love.event.quit))}
|
9
.duplicate/seperate-source/overwrite/src/wrap.fnl
Normal file
9
.duplicate/seperate-source/overwrite/src/wrap.fnl
Normal file
|
@ -0,0 +1,9 @@
|
|||
(local gamestate (require :lib.gamestate))
|
||||
(local stdio (require :lib.stdio))
|
||||
|
||||
(fn love.load [args]
|
||||
(love.graphics.setDefaultFilter "nearest" "nearest" 0)
|
||||
(if (= :web (. args 1)) (global web true) (global web false))
|
||||
(gamestate.registerEvents)
|
||||
(gamestate.switch (require :mode-intro))
|
||||
(when (not web) (stdio.start)))
|
7
.duplicate/seperate-source/update-layout.sh
Executable file
7
.duplicate/seperate-source/update-layout.sh
Executable file
|
@ -0,0 +1,7 @@
|
|||
target=$1
|
||||
location=$(dirname "$0")
|
||||
cp -rf $location/../../* $target
|
||||
cp -rf $location/overwrite/* $target
|
||||
mv $target/sample-macros.fnl $target/src/
|
||||
rm -rf $target/*fnl
|
||||
mv -f $target/src/sample-macros.fnl $target
|
66
readme.org
66
readme.org
|
@ -1,30 +1,52 @@
|
|||
* Minimal Fennel Love2D Setup
|
||||
|
||||
In a lead up to the annual [[https://itch.io/jam/autumn-lisp-game-jam-2018][Autumn Lisp Game Jam]] I thought I'd look into Phil Hegelberg's approach to last Aprils Jam, using [[https://love2d.org/][love2d]] in concert with [[https://fennel-lang.org/][fennel]]. Phil outlines his approach on his [[https://technomancy.us/187][blog]].
|
||||
|
||||
The first step is to see how fennel works as a standalone execution environment, then slowly integrate some of the love2d API.
|
||||
In the lead up to the semi-annual [[https://itch.io/jam/autumn-lisp-game-jam-2018][Autumn Lisp Game Jam]] I thought I'd look into Phil Hegelberg's approach to last Aprils Jam, using [[https://love2d.org/][love2d]] in concert with [[https://fennel-lang.org/][fennel]]. Phil outlines his approach on his [[https://technomancy.us/187][blog]].
|
||||
|
||||
This repo contains the minimal viable setup to get started with Phil Hegelberg's game design process, plus some additional libraries.
|
||||
|
||||
* Getting Started
|
||||
The following commands will clone this project and duplicate its structure into a new folder =$PROJECT_NAME=
|
||||
|
||||
#+BEGIN_SRC bash
|
||||
PROJECT=project-name
|
||||
git clone https://gitlab.com/alexjgriffith/min-love2d-fennel.git $PROJECT
|
||||
cd $PROJECT
|
||||
rm -rf .git
|
||||
git clone https://gitlab.com/alexjgriffith/min-love2d-fennel.git
|
||||
./min-love2d-fennel/.duplicate/new-game.sh $PROJECT_NAME
|
||||
#+END_SRC
|
||||
|
||||
* Phil's Modal Callbacks (PMC)
|
||||
Check out the makefile and conf.lua files in =$PROJECT_NAME=, updating them with information relevant to your game.
|
||||
|
||||
Phil Hegelberg's [[https://gitlab.com/technomancy/exo-encounter-667/][exo-encounter-667]] is structured using a modal callback system. Each game state has a mode and each mode has a series of specific callbacks.
|
||||
You can enter =love .= in the =$PROJECT_NAME= directory to run your game, or =make run=.
|
||||
|
||||
If you design your game as a series of states in a very simple state machine, for example *start-screen*, *play* and *end*, with unidirectional progression, you can easily separate the logic for each state into state/mode specific callbacks. As an example, in order to have state dependant rendering that differs between start-screen,play and end you could provide a *draw* callback for each of those states. Similarly if we need state dependent logic and keyboard input we could provide *update* and *keyboard* callbacks. As you iterate you can add and remove callbacks and states/modes as needed with very little friction.
|
||||
The following lines with =Update= should be changed in the =makefile= and =love.conf= to reflect your game.
|
||||
|
||||
#+BEGIN_SRC makefile
|
||||
VERSION=0.1.0
|
||||
LOVE_VERSION=11.4
|
||||
NAME=change-me # Update
|
||||
ITCH_ACCOUNT=change-me-too # Update
|
||||
URL=https://gitlab.com/alexjgriffith/min-love2d-fennel # Update
|
||||
AUTHOR="Your Name" # Update
|
||||
DESCRIPTION="Minimal setup for trying out Phil Hagelberg's fennel/love game design process." # Update
|
||||
#+END_SRC
|
||||
|
||||
#+BEGIN_SRC lua
|
||||
love.conf = function(t)
|
||||
t.gammacorrect = true
|
||||
t.title, t.identity = "change-me", "Minimal" -- Update
|
||||
t.modules.joystick = false
|
||||
t.modules.physics = false
|
||||
t.window.width = 720
|
||||
t.window.height = 450
|
||||
t.window.vsync = false
|
||||
t.version = "11.4"
|
||||
end
|
||||
#+END_SRC
|
||||
|
||||
* Emacs Setup
|
||||
|
||||
Once you install the latest version of [[https://gitlab.com/technomancy/fennel-mode][fennel-mode]], you can run
|
||||
=C-u M-x fennel-repl= followed by =love .= to launch a repl.
|
||||
|
||||
* Project Structure
|
||||
* Default Project Structure
|
||||
|
||||
The =make= process as-is will only compile the contents of the root folder and the =lib/= folder+subfolders, so make sure to put your game files in either of those.
|
||||
|
||||
|
@ -32,6 +54,26 @@ Specifically, every =.fnl= file needed at runtime needs to be situated in the ro
|
|||
|
||||
In order to use macros, they have to be put in =.fnl= files inside =lib/=.
|
||||
|
||||
* Seperate your Code into a /src directory
|
||||
|
||||
If you want a more opinionated layout, you can use pass in a =--layout= parameter when creating your project.
|
||||
|
||||
#+BEGIN_SRC bash
|
||||
./min-love2d-fennel/.duplicate/new-game.sh $PROJECT_NAME --layout=seperate-source
|
||||
#+END_SRC
|
||||
|
||||
This build uses =gamestate= rather than Phil's approach to scene seperation and puts all your =.fnl= files into a =/src= directory. It also provides a seperate makefile that handles this layout.
|
||||
|
||||
Note, any macros will have to be placed in the root of the project or in the =lib= folder (this can be modified in =main.lua=)
|
||||
|
||||
Presently the only layouts are =clone= and =seperate-source=. If you want to make your own check out the =.duplicate= directory to see how they work.
|
||||
|
||||
* Release Process
|
||||
|
||||
Use =make linux=, =make windows=, or =make mac= to create executables for each platform, or =make release= to do all three. Make sure to read the makefile for more commands (and remember to edit your game data in it!)
|
||||
Use =make linux=, =make windows=, =make mac=, or =make web= to create targets for each platform, or =make release= to make all four. Check out the makefile for more commands, and remember to edit your game data in it!
|
||||
|
||||
* Phil's Modal Callbacks (PMC)
|
||||
|
||||
Phil Hegelberg's [[https://gitlab.com/technomancy/exo-encounter-667/][exo-encounter-667]] is structured using a modal callback system. Each game state has a mode and each mode has a series of specific callbacks.
|
||||
|
||||
If you design your game as a series of states in a very simple state machine, for example *start-screen*, *play* and *end*, with unidirectional progression, you can easily separate the logic for each state into state/mode specific callbacks. As an example, in order to have state dependant rendering that differs between start-screen,play and end you could provide a *draw* callback for each of those states. Similarly if we need state dependent logic and keyboard input we could provide *update* and *keyboard* callbacks. As you iterate you can add and remove callbacks and states/modes as needed with very little friction.
|
||||
|
|
Loading…
Reference in a new issue