├── .gitignore
├── .travis.yml
├── Gruntfile.coffee
├── LICENSE
├── README.md
├── coffeescript
├── love.coffee
├── love.js
│ ├── audio.coffee
│ ├── audio
│ │ └── source.coffee
│ ├── color.coffee
│ ├── event.coffee
│ ├── exception.coffee
│ ├── filesystem.coffee
│ ├── filesystem
│ │ └── file_data.coffee
│ ├── graphics.coffee
│ ├── graphics
│ │ ├── Vera.ttf
│ │ ├── canvas_2d.coffee
│ │ ├── font.coffee
│ │ ├── image.coffee
│ │ └── quad.coffee
│ ├── image.coffee
│ ├── image
│ │ └── image_data.coffee
│ ├── keyboard.coffee
│ ├── math.coffee
│ ├── math
│ │ ├── bezier_curve.coffee
│ │ └── random_generator.coffee
│ ├── mouse.coffee
│ ├── system.coffee
│ ├── timer.coffee
│ ├── touch.coffee
│ └── window.coffee
└── punchdrunk.coffee
├── examples
├── arcs.html
├── arcs
│ └── main.lua
├── cube.html
├── cube
│ └── main.lua
├── falling_blocks.html
├── falling_blocks
│ └── main.lua
├── iyfct.html
├── iyfct
│ ├── LICENCE
│ ├── README.textile
│ ├── bird.lua
│ ├── cloud.lua
│ ├── conf.lua
│ ├── enemyShip.png
│ ├── gfx
│ │ ├── imgfont.png
│ │ ├── splash.png
│ │ ├── sprites.png
│ │ ├── terrain.png
│ │ └── trains.png
│ ├── gorge.lua
│ ├── main.lua
│ ├── menu.lua
│ ├── player.lua
│ ├── sfx
│ │ ├── bgm.ogg
│ │ ├── coffee.wav
│ │ ├── hit.wav
│ │ └── select.wav
│ ├── spritesheet.png
│ ├── table-save.lua
│ ├── terrain.lua
│ ├── train.lua
│ └── tunnel.lua
├── rolling_hills.html
├── rolling_hills
│ └── main.lua
├── touch.html
├── touch
│ └── main.lua
├── trails.html
└── trails
│ └── main.lua
├── index.html
├── js
├── boot.lua
├── debug
│ ├── debug.moonshine.js
│ ├── local.debug.moonshine.js
│ ├── remote.debug.moonshine.js
│ ├── server
│ │ ├── AbstractConnection.js
│ │ ├── AppConnection.js
│ │ ├── ConsoleConnection.js
│ │ ├── DebugServer.js
│ │ └── constants.js
│ └── ui
│ │ ├── css
│ │ └── debug.moonshine.css
│ │ ├── img
│ │ ├── application-dock.png
│ │ ├── applications-blue.png
│ │ ├── arrow-circle-315.png
│ │ ├── arrow-in.png
│ │ ├── arrow-out.png
│ │ ├── arrow-skip-270.png
│ │ ├── arrow-step-out.png
│ │ ├── arrow-step-over.png
│ │ ├── arrow-step.png
│ │ ├── control-double-270-small.png
│ │ ├── control-double-270.png
│ │ ├── control-pause.png
│ │ ├── control.png
│ │ ├── information-italic.png
│ │ ├── status-away.png
│ │ ├── status-busy.png
│ │ ├── status-offline.png
│ │ ├── status.png
│ │ ├── toggle-expand.png
│ │ └── toggle.png
│ │ ├── index.html
│ │ └── js
│ │ ├── debugger.js
│ │ ├── lib
│ │ └── ace
│ │ │ ├── ace.js
│ │ │ ├── ext-elastic_tabstops_lite.js
│ │ │ ├── ext-emmet.js
│ │ │ ├── ext-keybinding_menu.js
│ │ │ ├── ext-language_tools.js
│ │ │ ├── ext-modelist.js
│ │ │ ├── ext-options.js
│ │ │ ├── ext-searchbox.js
│ │ │ ├── ext-settings_menu.js
│ │ │ ├── ext-spellcheck.js
│ │ │ ├── ext-split.js
│ │ │ ├── ext-static_highlight.js
│ │ │ ├── ext-statusbar.js
│ │ │ ├── ext-textarea.js
│ │ │ ├── ext-themelist.js
│ │ │ ├── ext-whitespace.js
│ │ │ ├── keybinding-emacs.js
│ │ │ ├── keybinding-vim.js
│ │ │ ├── mode-lua.js
│ │ │ ├── snippets
│ │ │ └── lua.js
│ │ │ ├── theme-github.js
│ │ │ ├── theme-xcode.js
│ │ │ └── worker-lua.js
│ │ └── remote-debugger.js
├── long.js
├── moonshine.js
├── simplex-noise.js
└── sylvester.src.js
├── lua
├── baby.png
├── background.png
├── bubble.png
├── conf.lua
├── inspector.png
├── main.lua
└── text.png
├── package.json
└── test
├── assets
└── sprites.png
├── index.html
├── lib
├── chai.js
├── mocha.css
├── mocha.js
├── sinon-1.10.1.js
└── sinon-chai.js
└── src
├── audio.coffee
├── audio
└── source.coffee
├── color.coffee
├── event.coffee
├── filesystem.coffee
├── filesystem
└── file_data.coffee
├── graphics.coffee
├── graphics
├── canvas_2d.coffee
├── font.coffee
├── image.coffee
└── quad.coffee
├── image.coffee
├── image
└── image_data.coffee
├── keyboard.coffee
├── love.coffee
├── math.coffee
├── math
├── bezier_curve.coffee
└── random_generator.coffee
├── mouse.coffee
├── system.coffee
├── timer.coffee
├── touch.coffee
└── window.coffee
/.gitignore:
--------------------------------------------------------------------------------
1 | *.lua.json
2 | js/love.js
3 | js/punchdrunk.js
4 | test/tests.js
5 | node_modules
6 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - 0.10
4 | before_install: npm install -g grunt-cli moonshine
5 | install: npm install
6 | script: npm test
7 |
--------------------------------------------------------------------------------
/Gruntfile.coffee:
--------------------------------------------------------------------------------
1 | module.exports = (grunt) ->
2 |
3 | grunt.initConfig
4 | pkg: grunt.file.readJSON('package.json')
5 | banner: """/*! <%= pkg.name %> <%= pkg.version %> (<%= grunt.template.today('yyyy-mm-dd') %>) - <%= pkg.homepage %> */
6 | /*! <%= pkg.description %> */
7 | /*! <%= pkg.author %> */"""
8 | coffee:
9 | app:
10 | options:
11 | banner: '<%= banner %>'
12 | join: true
13 | files:
14 | 'js/love.js': [
15 | 'coffeescript/punchdrunk.coffee',
16 | 'coffeescript/love.coffee',
17 | 'coffeescript/love.js/*.coffee',
18 | 'coffeescript/love.js/audio/*.coffee',
19 | 'coffeescript/love.js/graphics/*.coffee',
20 | 'coffeescript/love.js/filesystem/*.coffee',
21 | 'coffeescript/love.js/image/*.coffee',
22 | 'coffeescript/love.js/math/*.coffee',
23 | ]
24 | tests:
25 | files:
26 | 'test/tests.js': [
27 | 'test/src/*.coffee',
28 | 'test/src/**/*.coffee'
29 | ]
30 | concat:
31 | punchdrunk:
32 | options:
33 | banner: '<%= banner %>'
34 | src: ['js/moonshine.js', 'js/sylvester.src.js', 'js/simplex-noise.js', 'js/long.js', 'js/love.js']
35 | dest: 'js/punchdrunk.js'
36 | watch:
37 | coffeescript:
38 | files: ['coffeescript/**/*.coffee', 'coffeescript/*.coffee']
39 | tasks: ['coffee:app', 'concat:punchdrunk']
40 | game:
41 | files: ['lua/*.lua']
42 | tasks: ['distil_game']
43 | bootstrap:
44 | files: ['js/boot.lua']
45 | tasks: ['distil_bootstrap']
46 | examples:
47 | files: ['examples/**/*.lua']
48 | tasks: ['distil_examples']
49 | connect:
50 | server:
51 | options:
52 | port: 8000
53 | mocha_phantomjs:
54 | all: ['test/*.html']
55 |
56 | grunt.loadNpmTasks('grunt-contrib-coffee')
57 | grunt.loadNpmTasks('grunt-contrib-watch')
58 | grunt.loadNpmTasks('grunt-contrib-concat')
59 | grunt.loadNpmTasks('grunt-contrib-connect')
60 | grunt.loadNpmTasks('grunt-mocha-phantomjs')
61 |
62 | grunt.registerTask 'default', ['compile', 'connect:server', 'watch']
63 | grunt.registerTask 'compile', ['coffee:app', 'concat', 'distil_game', 'distil_bootstrap', 'distil_examples']
64 | grunt.registerTask 'test', ['coffee', 'concat', 'mocha_phantomjs']
65 |
66 | distil = require('moonshine/bin/commands/distil.js')
67 | grunt.registerTask 'distil_game', ->
68 | done = @async()
69 | distil.parseCommand
70 | switches:
71 | outputFilename: ''
72 | outputPath: 'lua'
73 | jsonFormat: false
74 | packageMain: ''
75 | noRecursion: false
76 | stripDebugging: false
77 | watch: false
78 | filenames: [ 'lua' ], ->
79 | done()
80 |
81 | grunt.registerTask 'distil_bootstrap', ->
82 | done = @async()
83 | distil.parseCommand
84 | switches:
85 | outputFilename: 'js/boot.lua.json'
86 | outputPath: '.'
87 | jsonFormat: false
88 | packageMain: ''
89 | noRecursion: false
90 | stripDebugging: false
91 | watch: false
92 | filenames: [ 'js/boot.lua' ], ->
93 | done()
94 |
95 | grunt.registerTask 'distil_examples', ->
96 | done = @async()
97 | distil.parseCommand
98 | switches:
99 | outputFilename: ''
100 | outputPath: 'examples'
101 | jsonFormat: false
102 | packageMain: ''
103 | noRecursion: false
104 | stripDebugging: false
105 | watch: false
106 | filenames: [ 'examples' ], ->
107 | done()
108 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Tanner Rogalsky
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | punchdrunk.js
2 | ================
3 |
4 | #DEPRECATED
5 | This project has been replaced by [Love.js](https://github.com/TannerRogalsky/love.js). It is a direct port of LÖVE and is more feature-complete and performant.
6 |
7 | Punchdrunk = [Moonshine](http://moonshinejs.org/) + [LÖVE](http://love2d.org/)
8 |
9 | Demo: http://tannerrogalsky.com/punchdrunk/
10 |
11 | Tests: http://tannerrogalsky.com/punchdrunk/test/
12 |
13 | [](https://travis-ci.org/TannerRogalsky/punchdrunk)
14 |
15 | # Get This Running
16 |
17 | 1. Install [node.js](http://nodejs.org/)
18 | 2. Install [Lua](http://www.lua.org/) and make sure that it's in your path.
19 | 3. Install top-level dependencies: `npm install grunt-cli -g`
20 | 4. Install the project dependencies: `npm install`
21 | 5. `grunt` will watch and compile the source as well as run a simple web server on port 8000.
22 | 6. Open `localhost:8000` in your browser.
23 |
24 | # Caveats
25 |
26 | Some things don't work (and some may never work):
27 | - love.image
28 | - love.joystick
29 | - love.physics
30 | - love.thread
31 | - love.filesystem
32 |
33 | Pretty much everything else has partial support. I'm not going to make a long list of everything that doesn't work right now because there's still too much that doesn't. I think you'll find that all the most basic elements of the API are functional. Those that aren't should be stubbed out in the hopes that they aren't crucial to the game that is being ported.
34 |
35 | # FAQ
36 |
37 | - Can I run my own game in the browser?
38 |
39 | Sure! Just delete what's in the `lua` folder, replace it with your source code and run `grunt`. It should compile the Lua code in JSON-ized bytecode which should then just work, if you aren't using anything that hasn't been implemented yet. You may also have to preload your graphics and audio by putting tags for them in `index.html`.
40 |
41 | - This is cool! How can I help?
42 |
43 | Everything that I'm working on should be [logged as an issue](https://github.com/TannerRogalsky/punchdrunk/issues). If you find something that doesn't work, please create a new issue for it. If you're interested in picking one of the existing issues up, please comment on it first so that I can let you know if I've already done any work on it.
44 |
45 | - Why is your CoffeScript so weird?
46 |
47 | The interop between Lua and CoffeeScript has necessitated some CoffeeScript that is atypical. Mainly, you'll notice two things: heavy use of the 'fat-arrow notation' for function binding and a lot of passing the object being operated on to the function instead of using proper JavaScript context. Both of these are because the differences between how JavaScript and Lua handle function context. You can read more about it [here](https://github.com/gamesys/moonshine/issues/12).
48 |
--------------------------------------------------------------------------------
/coffeescript/love.coffee:
--------------------------------------------------------------------------------
1 | class @Love
2 | constructor: (element = null, window_conf = {}, module_conf = {}) ->
3 | Love.element = element
4 | @graphics = new Love.Graphics(window_conf.width, window_conf.height)
5 | @window = new Love.Window(@graphics)
6 | @timer = new Love.Timer()
7 | @event = new Love.EventQueue()
8 | @keyboard = new Love.Keyboard(@event, Love.element)
9 | @mouse = new Love.Mouse(@event, Love.element)
10 | @touch = new Love.Touch(@event, Love.element)
11 | @filesystem = new Love.FileSystem()
12 | @audio = new Love.Audio()
13 | @system = new Love.System()
14 | @image = new Love.ImageModule()
15 | @math = new Love.Math()
16 |
17 | window.addEventListener "beforeunload", () =>
18 | @quit.call()
19 |
20 | run: () =>
21 | @timer.step()
22 |
23 | @load.call()
24 |
25 | game_loop = =>
26 | for e in @event.internalQueue
27 | @[e.eventType].call(null, e.arg1, e.arg2, e.arg3, e.arg4)
28 | @event.clear()
29 |
30 | @timer.step()
31 |
32 | @update.call(null, @timer.getDelta())
33 |
34 | @graphics.origin()
35 | @graphics.clear()
36 | @draw.call()
37 |
38 | @timer.nextFrame(game_loop)
39 |
40 | @timer.nextFrame(game_loop)
41 |
42 | # default functions to be overwritten by main.lua
43 | load: (args) ->
44 | update: (dt) ->
45 | mousepressed: (x, y, button) ->
46 | mousereleased: (x, y, button) ->
47 | touchpressed: (id, x, y) ->
48 | touchreleased: (id, x, y) ->
49 | touchmoved: (id, x, y) ->
50 | keypressed: (key, unicode) ->
51 | keyreleased: (key, unicode) ->
52 | draw: () ->
53 | quit: () ->
54 |
55 | Love.root = "lua"
56 | Love.element = null
57 |
--------------------------------------------------------------------------------
/coffeescript/love.js/audio.coffee:
--------------------------------------------------------------------------------
1 | class Love.Audio
2 | constructor: () ->
3 |
4 | getDistanceModel: () =>
5 |
6 | getOrientation: () =>
7 |
8 | getPosition: () =>
9 |
10 | getSourceCount: () =>
11 |
12 | getVelocity: () =>
13 |
14 | getVolume: () =>
15 |
16 | newSource: (filename, type) =>
17 | new Love.Audio.Source(filename, type)
18 |
19 | pause: (source) =>
20 | source.pause(source)
21 |
22 | play: (source) =>
23 | source.play(source)
24 |
25 | resume: (source) =>
26 | source.play(source)
27 |
28 | rewind: (source) =>
29 | source.rewind(source)
30 |
31 | setDistanceModel: () =>
32 |
33 | setOrientation: () =>
34 |
35 | setPosition: () =>
36 |
37 | setVelocity: () =>
38 |
39 | setVolume: () =>
40 |
41 | stop: (source) =>
42 | source.stop(source)
43 |
--------------------------------------------------------------------------------
/coffeescript/love.js/audio/source.coffee:
--------------------------------------------------------------------------------
1 | class Love.Audio.Source
2 | constructor: (@filename, @type) ->
3 | @element = document.createElement("audio")
4 | @element.setAttribute("src", Love.root + "/" + filename)
5 |
6 | # one would think that you should use preload=none for a stream type asset
7 | # except that that doesn't really work for our use-case
8 | @element.setAttribute("preload", "auto")
9 |
10 | clone: (self) ->
11 | new Source(self.filename, self.type)
12 |
13 | getAttenuationDistances: (self) ->
14 |
15 | getChannels: (self) ->
16 |
17 | getCone: (self) ->
18 |
19 | getDirection: (self) ->
20 |
21 | getPitch: (self) ->
22 |
23 | getPosition: (self) ->
24 |
25 | getRolloff: (self) ->
26 |
27 | getVelocity: (self) ->
28 |
29 | getVolume: (self) ->
30 | self.element.volume
31 |
32 | getVolumeLimits: (self) ->
33 |
34 | isLooping: (self) ->
35 | !!self.element.getAttribute("loop")
36 |
37 | isPaused: (self) ->
38 | self.element.paused
39 |
40 | isPlaying: (self) ->
41 | !self.element.paused
42 |
43 | isRelative: (self) ->
44 |
45 | isStatic: (self) ->
46 |
47 | isStopped: (self) ->
48 | self.isPaused(self) and self.currentTime == 0
49 |
50 | pause: (self) ->
51 | self.element.pause()
52 |
53 | play: (self) ->
54 | self.element.play()
55 |
56 | resume: (self) ->
57 | self.element.play()
58 |
59 | rewind: (self) ->
60 | self.element.currentTime = 0
61 |
62 | seek: (self, offset, time_unit = "seconds") ->
63 | switch time_unit
64 | when "seconds" then self.element.currentTime = offset
65 |
66 | setAttenuationDistances: (self) ->
67 |
68 | setCone: (self) ->
69 |
70 | setDirection: (self) ->
71 |
72 | setLooping: (self, looping) ->
73 | self.element.setAttribute("loop", looping)
74 |
75 | setPitch: (self) ->
76 |
77 | setPosition: (self) ->
78 |
79 | setRelative: (self) ->
80 |
81 | setRolloff: (self) ->
82 |
83 | setVelocity: (self) ->
84 |
85 | setVolume: (self, volume) ->
86 | self.element.volume = volume
87 |
88 | setVolumeLimits: (self) ->
89 |
90 | stop: (self) ->
91 | self.element.load()
92 |
93 | tell: (self, time_unit = "seconds") ->
94 | switch time_unit
95 | when "seconds" then self.element.currentTime
96 | when "samples" then 0
97 |
--------------------------------------------------------------------------------
/coffeescript/love.js/color.coffee:
--------------------------------------------------------------------------------
1 | class Love.Color
2 | constructor: (@r, @g, @b, @a = 255) ->
3 | @html_code = "rgb(#{@r}, #{@g}, #{@b})"
4 |
5 | unpack: ->
6 | [@r, @g, @b, @a]
7 |
8 | Color = Love.Color
9 |
--------------------------------------------------------------------------------
/coffeescript/love.js/event.coffee:
--------------------------------------------------------------------------------
1 | class Love.EventQueue
2 | constructor: () ->
3 | @internalQueue = []
4 |
5 | clear: () =>
6 | @internalQueue = []
7 |
8 | # TODO: this will require some understanding custom iterators in JS and Lua
9 | poll: () =>
10 |
11 | # This doesn't need to do anything in JS.
12 | pump: () =>
13 |
14 | push: (eventType, args...) =>
15 | newEvent = new Event(eventType, args...)
16 | @internalQueue.push(newEvent)
17 |
18 | quit: () =>
19 | @internalQueue.push(new Event("quit"))
20 |
21 | wait: () =>
22 |
23 | # PRIVATE
24 | class Event
25 | constructor: (@eventType, @arg1, @arg2, @arg3, @arg4) ->
26 |
--------------------------------------------------------------------------------
/coffeescript/love.js/exception.coffee:
--------------------------------------------------------------------------------
1 | class Love.Exception
2 | constructor: (@message) ->
3 | @name = "Love Error"
4 |
--------------------------------------------------------------------------------
/coffeescript/love.js/filesystem.coffee:
--------------------------------------------------------------------------------
1 | class Love.FileSystem
2 | constructor: () ->
3 |
4 | append: () =>
5 |
6 | createDirectory: () =>
7 |
8 | exists: (filename) =>
9 | localStorage.getItem(filename) != null
10 |
11 | getAppdataDirectory: () =>
12 |
13 | getDirectoryItems: () =>
14 |
15 | getIdentity: () =>
16 |
17 | getLastModified: () =>
18 |
19 | getSaveDirectory: () =>
20 |
21 | getSize: () =>
22 |
23 | getUserDirectory: () =>
24 |
25 | getWorkingDirectory: () =>
26 |
27 | init: () =>
28 |
29 | isDirectory: () =>
30 |
31 | isFile: () =>
32 |
33 | isFused: () =>
34 |
35 | lines: () =>
36 |
37 | load: () =>
38 |
39 | mount: () =>
40 |
41 | newFile: () =>
42 |
43 | newFileData: (contents, name, decoder) =>
44 | new Love.FileSystem.FileData(contents, name, decoder)
45 |
46 | read: (filename) =>
47 | localStorage.getItem(filename)
48 |
49 | remove: (filename) =>
50 | localStorage.removeItem(filename)
51 |
52 | setIdentity: () =>
53 |
54 | setSource: () =>
55 |
56 | unmount: () =>
57 |
58 | write: (filename, data) =>
59 | localStorage.setItem(filename, data)
60 |
--------------------------------------------------------------------------------
/coffeescript/love.js/filesystem/file_data.coffee:
--------------------------------------------------------------------------------
1 | class Love.FileSystem.FileData
2 | constructor: (@contents, @name, decoder) ->
3 | @extension = @name.match("\\.(.*)")[1]
4 |
5 | getPointer: (self) ->
6 | getSize: (self) ->
7 | getString: (self) ->
8 | self.contents
9 |
10 | getExtension: (self) ->
11 | self.extension
12 |
13 | getFilename: (self) ->
14 | self.name
15 |
--------------------------------------------------------------------------------
/coffeescript/love.js/graphics.coffee:
--------------------------------------------------------------------------------
1 | class Love.Graphics
2 | constructor: (width = 800, height = 600) ->
3 | if Love.element
4 | @canvas = new Love.Graphics.Canvas2D(width, height, Love.element)
5 | else
6 | @canvas = new Love.Graphics.Canvas2D(width, height)
7 | document.body.appendChild(@canvas.element)
8 | Love.element = @canvas.element
9 |
10 | @default_canvas = @canvas
11 | @default_font = new Love.Graphics.Font("Vera", 12)
12 |
13 | @setColor(255, 255, 255)
14 | @setBackgroundColor(0, 0, 0)
15 | @setFont(@default_font)
16 |
17 | # DRAWING
18 | arc: (mode, x, y, radius, startAngle, endAngle, segments) =>
19 | @canvas.arc(mode, x, y, radius, startAngle, endAngle, segments)
20 |
21 | circle: (mode, x, y, radius, segments) =>
22 | @canvas.circle(mode, x, y, radius, segments)
23 |
24 | clear: () =>
25 | [r, g, b, a] = @getBackgroundColor()
26 | @canvas.clear(@canvas, r, g, b, a)
27 |
28 | draw: (args...) =>
29 | @canvas.draw(args...)
30 |
31 | line: (points...) =>
32 | @canvas.line(points...)
33 |
34 | point: (x, y) =>
35 | @canvas.point(x, y)
36 |
37 | polygon: (mode, points...) =>
38 | @canvas.polygon(mode, points...)
39 |
40 | print: (text, x, y) =>
41 | @canvas.print(text, x, y)
42 |
43 | # TODO: word wrap? UGH
44 | printf: (text, x, y, limit, align = "left") =>
45 | @canvas.printf(text, x, y, limit, align)
46 |
47 | rectangle: (mode, x, y, width, height) =>
48 | @canvas.rectangle(mode, x, y, width, height)
49 |
50 | # OBJECT CREATION
51 | newCanvas: (width = @getWidth(@), height = @getHeight(@)) =>
52 | new Love.Graphics.Canvas2D(width, height)
53 |
54 | newFont: (filename, size = 12) =>
55 | new Love.Graphics.Font(filename, size)
56 |
57 | newImage: (data) =>
58 | new Love.Graphics.Image(data)
59 |
60 | newImageFont: =>
61 |
62 | newMesh: () =>
63 | newParticleSystem: () =>
64 |
65 | newQuad: (x, y, width, height, sw, sh) =>
66 | new Love.Graphics.Quad(x, y, width, height, sw, sh)
67 |
68 | newScreenshot: =>
69 | newShader: () =>
70 | newSpriteBatch: () =>
71 |
72 | setNewFont: (filename, size) =>
73 | font = @newFont(filename, size)
74 | @setFont(font)
75 |
76 | # STATE
77 | getBackgroundColor: () =>
78 | @canvas.getBackgroundColor()
79 |
80 | getBlendMode: () =>
81 | @canvas.getBlendMode()
82 |
83 | getCanvas: () =>
84 | @canvas
85 |
86 | getColor: () =>
87 | @canvas.getColor()
88 |
89 | getColorMask: () =>
90 | @canvas.getColorMask()
91 |
92 | getDefaultFilter: () =>
93 | @canvas.getDefaultFilter()
94 |
95 | getFont: () =>
96 | @canvas.getFont()
97 |
98 | getLineJoin: () =>
99 | @canvas.getLineJoin()
100 |
101 | getLineStyle: () =>
102 | @canvas.getLineStyle()
103 |
104 | getLineWidth: () =>
105 | @canvas.getLineWidth()
106 |
107 | getMaxImageSize: () =>
108 | @canvas.getMaxImageSize()
109 |
110 | getMaxPointSize: () =>
111 | @canvas.getMaxPointSize()
112 |
113 | getPointSize: () =>
114 | @canvas.getPointSize()
115 |
116 | getPointStyle: () =>
117 | @canvas.getPointStyle()
118 |
119 | getRendererInfo: () =>
120 | @canvas.getRendererInfo()
121 |
122 | getScissor: () =>
123 | @canvas.getScissor()
124 |
125 | getShader: () =>
126 | @canvas.getShader()
127 |
128 | getSystemLimit: () =>
129 | @canvas.getSystemLimit()
130 |
131 | isSupported: () =>
132 |
133 | isWireframe: () =>
134 | @canvas.isWireframe()
135 |
136 | reset: () =>
137 | @setCanvas()
138 | @origin()
139 |
140 | setBackgroundColor: (r, g, b, a = 255) =>
141 | @canvas.setBackgroundColor(r, g, b, a)
142 |
143 | setBlendMode: (mode) =>
144 | @canvas.setBlendMode(mode)
145 |
146 | setCanvas: (canvas) =>
147 | if canvas == undefined or canvas == null
148 | @default_canvas.copyContext(@canvas.context)
149 | @canvas = @default_canvas
150 | else
151 | canvas.copyContext(@canvas.context)
152 | @canvas = canvas
153 |
154 | setColor: (r, g, b, a = 255) =>
155 | @canvas.setColor(r, g, b, a)
156 |
157 | setFont: (font) =>
158 | @canvas.setFont(font)
159 |
160 | setColorMask: (r, g, b, a) =>
161 | @canvas.setColorMask(r, g, b, a)
162 |
163 | setDefaultFilter: (min, mag, anisotropy) =>
164 | @canvas.setDefaultFilter(min, mag, anisotropy)
165 |
166 | setInvertedStencil: (callback) =>
167 | @canvas.setInvertedStencil(callback)
168 |
169 | setLineJoin: (join) =>
170 | @canvas.setLineJoin(join)
171 |
172 | setLineStyle: (style) =>
173 | @canvas.setLineStyle(style)
174 |
175 | setLineWidth: (width) =>
176 | @canvas.setLineWidth(width)
177 |
178 | setPointSize: (size) =>
179 | @canvas.setPointSize(size)
180 |
181 | setPointStyle: (style) =>
182 | @canvas.setPointStyle(style)
183 |
184 | setScissor: (x, y, width, height) =>
185 | @canvas.setScissor(x, y, width, height)
186 |
187 | setShader: (shader) =>
188 | @canvas.setShader(shader)
189 |
190 | setStencil: (callback) =>
191 | @canvas.setStencil(callback)
192 |
193 | setWireframe: (enable) =>
194 | @canvas.setWireframe(enable)
195 |
196 | # COORDINATE SYSTEM
197 | origin: () =>
198 | @canvas.origin()
199 |
200 | pop: () =>
201 | @canvas.pop()
202 |
203 | push: () =>
204 | @canvas.push()
205 |
206 | rotate: (r) =>
207 | @canvas.rotate(r)
208 |
209 | scale: (sx, sy = sx) =>
210 | @canvas.scale(sx, sy)
211 |
212 | shear: (kx, ky) =>
213 | @canvas.shear(kx, ky)
214 |
215 | translate: (dx, dy) =>
216 | @canvas.translate(dx, dy)
217 |
218 | # WINDOW
219 | getDimensions: () =>
220 | [@getWidth(), @getHeight()]
221 |
222 | getHeight: () =>
223 | @default_canvas.getHeight(@default_canvas)
224 |
225 | getWidth: () =>
226 | @default_canvas.getWidth(@default_canvas)
227 |
--------------------------------------------------------------------------------
/coffeescript/love.js/graphics/Vera.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TannerRogalsky/punchdrunk/a2835d4963289734ad402e1fc4ceb284d0e1ee91/coffeescript/love.js/graphics/Vera.ttf
--------------------------------------------------------------------------------
/coffeescript/love.js/graphics/font.coffee:
--------------------------------------------------------------------------------
1 | class Love.Graphics.Font
2 | constructor: (@filename, @size) ->
3 | @html_code = "#{@size}px #{@filename}"
4 |
5 | getAscent: (self) ->
6 | getBaseline: (self) ->
7 | getDescent: (self) ->
8 | getFilter: (self) ->
9 | getHeight: (self) ->
10 | getLineHeight: (self) ->
11 | getWidth: (self) ->
12 | getWrap: (self) ->
13 | hasGlyphs: (self) ->
14 | setFilter: (self) ->
15 | setLineHeight: (self) ->
16 |
--------------------------------------------------------------------------------
/coffeescript/love.js/graphics/image.coffee:
--------------------------------------------------------------------------------
1 | class Love.Graphics.Image
2 | constructor: (data) ->
3 | if data instanceof Love.ImageModule.ImageData
4 | @element = document.createElement("img")
5 | @element.setAttribute("src", data.getString(data))
6 | else
7 | filename = data
8 | @element = document.getElementById(filename)
9 |
10 | if @element == null
11 | @element = document.createElement("img")
12 | @element.setAttribute("src", Love.root + "/" + filename)
13 |
14 | getData: (self) ->
15 |
16 | getDimensions: (self) ->
17 | [self.element.width, self.element.height]
18 |
19 | getFilter: (self) ->
20 |
21 | getHeight: (self) ->
22 | self.element.height
23 |
24 | getMipmapFilter: (self) ->
25 |
26 | getWidth: (self) ->
27 | self.element.width
28 |
29 | getWrap: (self) ->
30 |
31 | isCompressed: (self) ->
32 |
33 | refresh: (self) ->
34 |
35 | setFilter: (self) ->
36 |
37 | setMipmapFilter: (self) ->
38 |
39 | setWrap: (self) ->
40 |
41 | # PRIVATE
42 |
--------------------------------------------------------------------------------
/coffeescript/love.js/graphics/quad.coffee:
--------------------------------------------------------------------------------
1 | class Love.Graphics.Quad
2 | constructor: (@x, @y, @width, @height, @sw, @sh) ->
3 |
4 | getViewport: (self) ->
5 | [self.x, self.y, self.width, self.height]
6 |
7 | setViewport: (self, x, y, width, height) ->
8 | self.x = x
9 | self.y = y
10 | self.width = width
11 | self.height = height
12 |
--------------------------------------------------------------------------------
/coffeescript/love.js/image.coffee:
--------------------------------------------------------------------------------
1 | class Love.ImageModule
2 | constructor: () ->
3 |
4 | isCompressed: =>
5 | newCompressedData: () =>
6 | newImageData: (filedata) =>
7 | new Love.ImageModule.ImageData(filedata)
8 |
--------------------------------------------------------------------------------
/coffeescript/love.js/image/image_data.coffee:
--------------------------------------------------------------------------------
1 | # TODO: this should probably be backed by the actual ImageData object
2 | # https://developer.mozilla.org/en/docs/Web/API/ImageData
3 | class Love.ImageModule.ImageData
4 | constructor: (filedata) ->
5 | @contents = "data:image/#{filedata.getExtension(filedata)};base64,#{filedata.getString(filedata)}"
6 |
7 | getString: (self) ->
8 | @contents
9 |
10 | encode: (self) ->
11 |
12 | getDimensions: (self) ->
13 |
14 | getHeight: (self) ->
15 |
16 | getPixel: (self) ->
17 |
18 | getWidth: (self) ->
19 |
20 | mapPixel: (self) ->
21 |
22 | paste: (self) ->
23 |
24 | setPixel: (self) ->
25 |
26 |
--------------------------------------------------------------------------------
/coffeescript/love.js/keyboard.coffee:
--------------------------------------------------------------------------------
1 | class Love.Keyboard
2 | constructor: (eventQueue, canvas) ->
3 | @keysDown = {}
4 |
5 | canvas.setAttribute("tabindex", "0")
6 |
7 | keydown = (evt) =>
8 | evt.preventDefault()
9 | evt.stopPropagation()
10 |
11 | key = getKeyFromEvent(evt)
12 | @keysDown[key] = true
13 |
14 | eventQueue.push("keypressed", key, evt.which)
15 | canvas.addEventListener "keydown", keydown, true
16 |
17 | keyup = (evt) =>
18 | evt.preventDefault()
19 | evt.stopPropagation()
20 |
21 | key = getKeyFromEvent(evt)
22 | @keysDown[key] = false
23 |
24 | eventQueue.push("keyreleased", key, evt.which)
25 | canvas.addEventListener "keyup", keyup, true
26 |
27 | isDown: (key, others...) =>
28 | if !@keysDown[key]
29 | return false
30 | else
31 | if others.length == 0
32 | return true
33 | else
34 | return @isDown(others...)
35 |
36 | # ## key names
37 | #
38 | # This object contains the names used for each key code. Notice that this is not a
39 | # comprehensive list; common ASCII characters like 'a', which can be calculated via
40 | # `String.fromCharCode`, are not included here.
41 | keys = {
42 | 8: "backspace",
43 | 9: "tab",
44 | 13: "return",
45 | 16: "shift",
46 | 17: "ctrl",
47 | 18: "alt",
48 | 19: "pause", 20: "capslock", 27: "escape",
49 | 33: "pageup", 34: "pagedown", 35: "end", 36: "home", 45: "insert", 46: "delete",
50 | 37: "left", 38: "up", 39: "right", 40: "down",
51 | 91: "lmeta", 92: "rmeta", 93: "mode",
52 | 96: "kp0", 97: "kp1", 98: "kp2", 99: "kp3", 100: "kp4", 101: "kp5",
53 | 102: "kp6", 103: "kp7", 104: "kp8", 105: "kp9",
54 | 106: "kp*", 107: "kp+", 109: "kp-", 110: "kp.", 111: "kp/",
55 | 112: "f1", 113: "f2", 114: "f3", 115: "f4", 116: "f5", 117: "f6", 118: "f7",
56 | 119: "f8", 120: "f9", 121: "f10", 122: "f11", 123: "f12",
57 | 144: "numlock", 145: "scrolllock",
58 | 186: ",", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`",
59 | 219: "[", 220: "\\",221: "]", 222: "'"
60 | }
61 |
62 | # ## Shifted key names
63 | #
64 | # These names will get passed to onPress and onRelease instead of the default ones
65 | # if one of the two "shift" keys is pressed.
66 | shiftedKeys = {
67 | 192:"~", 48:")", 49:"!", 50:"@", 51:"#", 52:"$", 53:"%", 54:"^", 55:"&", 56:"*", 57:"(", 109:"_", 61:"+",
68 | 219:"{", 221:"}", 220:"|", 59:":", 222:"\"", 188:"<", 189:">", 191:"?",
69 | 96:"insert", 97:"end", 98:"down", 99:"pagedown", 100:"left", 102:"right", 103:"home", 104:"up", 105:"pageup"
70 | }
71 |
72 | # ## Right keys
73 | #
74 | # luv.js will attempt to differentiate rshift from shift, rctrl from ctrl, and
75 | # ralt from alt. This is browser-dependent though, and not completely supported.
76 | rightKeys = {
77 | 16: "rshift", 17: "rctrl", 18: "ralt"
78 | }
79 |
80 | getKeyFromEvent = (event) ->
81 | code = event.which;
82 | if event.location and event.location > 1
83 | key = rightKeys[code]
84 | else if event.shiftKey
85 | key = shiftedKeys[code] or keys[code]
86 | else
87 | key = keys[code]
88 |
89 | if typeof key == "undefined"
90 | key = String.fromCharCode(code);
91 | if not event.shiftKey
92 | key = key.toLowerCase()
93 |
94 | return key;
95 |
--------------------------------------------------------------------------------
/coffeescript/love.js/math.coffee:
--------------------------------------------------------------------------------
1 | class Love.Math
2 | constructor: () ->
3 | @random_generator = new Love.Math.RandomGenerator()
4 | simplex_r = new Love.Math.RandomGenerator()
5 | @simplex = new SimplexNoise(simplex_r.random.bind(simplex_r, simplex_r))
6 |
7 | gammaToLinear: (gamma_colors...) =>
8 | gamma_colors = getGammaArgs(gamma_colors)
9 |
10 | for c in gamma_colors
11 | c /= 255
12 | if c > 1
13 | c = 1
14 | else if c < 0
15 | c = 0
16 | else if c < 0.0031308
17 | c *= 12.92
18 | else
19 | c = 1.055 * window.Math.pow(c, 0.41666) - 0.055
20 | c *= 255
21 |
22 | getRandomSeed: =>
23 | @random_generator.getSeed(@random_generator)
24 |
25 | isConvex: (vertices...) =>
26 | polygon = toPolygon(vertices)
27 |
28 | i = polygon.length - 2
29 | j = polygon.length - 1
30 | k = 0
31 |
32 | p =
33 | x: polygon[j].x - polygon[i].x
34 | y: polygon[j].y - polygon[i].y
35 | q =
36 | x: polygon[k].x - polygon[j].x
37 | y: polygon[k].y - polygon[j].y
38 | winding = p.x * q.y - p.y * q.x
39 |
40 | while k + 1 < polygon.length
41 | i = j
42 | j = k
43 | k++
44 | p.x = polygon[j].x - polygon[i].x
45 | p.y = polygon[j].y - polygon[i].y
46 | q.x = polygon[k].x - polygon[j].x
47 | q.y = polygon[k].y - polygon[j].y
48 |
49 | if (p.x * q.y - p.y * q.x) * winding < 0
50 | return false
51 | return true
52 |
53 |
54 | linearToGamma: (linear_colors...) =>
55 | linear_colors = getGammaArgs(linear_colors)
56 |
57 | for c in linear_colors
58 | c /= 255
59 | if c > 1
60 | c = 1
61 | else if c < 0
62 | c = 0
63 | else if c <= 0.04045
64 | c /= 12.92
65 | else
66 | c = window.Math.pow((c + 0.055) / 1.055, 2.4)
67 | c *= 255
68 |
69 | newBezierCurve: (vertices...) =>
70 | if vertices.length == 1
71 | vertices = if vertices[0].__shine
72 | # make up for lua being one-indexed with the slice
73 | vertices[0].__shine.numValues.slice(1, vertices[0].__shine.numValues.length)
74 | else
75 | vertices[0]
76 |
77 | controlPoints = for i in [0...vertices.length] by 2
78 | x: vertices[i]
79 | y: vertices[i + 1]
80 |
81 | new @constructor.BezierCurve(controlPoints)
82 |
83 | newRandomGenerator: (low, high) =>
84 | r = new Love.Math.RandomGenerator()
85 | if low
86 | r.setSeed(r, low, high)
87 | return r
88 |
89 | noise: (dimensions...) =>
90 | switch dimensions.length
91 | when 1 then @simplex.noise1D(dimensions[0])
92 | when 2 then @simplex.noise2D(dimensions[0], dimensions[1])
93 | when 3 then @simplex.noise3D(dimensions[0], dimensions[1], dimensions[2])
94 | when 4 then @simplex.noise4D(dimensions[0], dimensions[1], dimensions[2], dimensions[3])
95 |
96 | random: (min, max) =>
97 | @random_generator.random(@random_generator, min, max)
98 |
99 | randomNormal: (stddev = 1, mean = 0) =>
100 | @random_generator.randomNormal(@random_generator, stddev, mean)
101 |
102 | setRandomSeed: (low, high) =>
103 | @random_generator.setSeed(@random_generator, low, high)
104 |
105 | triangulate: (vertices...) =>
106 | polygon = toPolygon(vertices)
107 |
108 | next_idx = new Array(polygon.length)
109 | prev_idx = new Array(polygon.length)
110 | idx_lm = 0
111 |
112 | for i in [0...polygon.length]
113 | lm = polygon[idx_lm]
114 | p = polygon[i]
115 | if p.x < lm.x or (p.x == lm.x and p.y < lm.y)
116 | idx_lm = i
117 | next_idx[i] = i+1
118 | prev_idx[i] = i-1
119 | next_idx[next_idx.length-1] = 0
120 | prev_idx[0] = prev_idx.length-1
121 |
122 | if !is_oriented_ccw(polygon[prev_idx[idx_lm]], polygon[idx_lm], polygon[next_idx[idx_lm]])
123 | [next_idx, prev_idx] = [prev_idx, next_idx]
124 |
125 | concave_vertices = []
126 | for i in [0...polygon.length]
127 | if !is_oriented_ccw(polygon[prev_idx[i]], polygon[i], polygon[next_idx[i]])
128 | concave_vertices.push(polygon[i])
129 |
130 | triangles = []
131 | n_vertices = polygon.length
132 | [current, skipped, next, prev] = [1, 0]
133 | while n_vertices > 3
134 | next = next_idx[current]
135 | prev = prev_idx[current]
136 | a = polygon[prev]
137 | b = polygon[current]
138 | c = polygon[next]
139 |
140 | if is_ear(a,b,c, concave_vertices)
141 | triangles.push([a,b,c])
142 | next_idx[prev] = next
143 | prev_idx[next] = prev
144 | concave_vertices.splice(concave_vertices.indexOf(b), 1)
145 | --n_vertices
146 | skipped = 0
147 | else if ++skipped > n_vertices
148 | console.log "Cannot triangulate polygon."
149 |
150 | current = next
151 |
152 | next = next_idx[current]
153 | prev = prev_idx[current]
154 |
155 | triangles.push([polygon[prev], polygon[current], polygon[next]])
156 |
157 | return triangles
158 |
159 | getGammaArgs = (colors) ->
160 | if colors.length == 1 and colors[0] instanceof Object
161 | colors = if colors[0].__shine
162 | # make up for lua being one-indexed with the slice
163 | colors[0].__shine.numValues.slice(1, colors[0].__shine.numValues.length)
164 | else
165 | colors[0]
166 | else
167 | colors
168 |
169 | toPolygon = (vertices) ->
170 | if vertices.length == 1
171 | vertices = if vertices[0].__shine
172 | # make up for lua being one-indexed with the slice
173 | vertices[0].__shine.numValues.slice(1, vertices[0].__shine.numValues.length)
174 | else
175 | vertices[0]
176 |
177 | for i in [0...vertices.length] by 2
178 | x: vertices[i]
179 | y: vertices[i + 1]
180 |
181 | # check if an angle is oriented counter clockwise
182 | is_oriented_ccw = (a, b, c) ->
183 | return ((b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x)) >= 0
184 |
185 | # check if a and b are on the same side of the line c->d
186 | on_same_side = (a, b, c, d) ->
187 | px = d.x - c.x
188 | py = d.y - c.y
189 | l = px * (a.y - c.y) - py * (a.x - c.x)
190 | m = px * (b.y - c.y) - py * (b.x - c.x)
191 | return l * m >= 0
192 |
193 | # checks is p is contained in the triangle abc
194 | point_in_triangle = (p, a, b, c) ->
195 | return on_same_side(p,a, b,c) and on_same_side(p,b, a,c) and on_same_side(p,c, a,b)
196 |
197 | # checks if any vertex in `vertices` is in the triangle abc.
198 | any_point_in_triangle = (vertices, a, b, c) ->
199 | for p in vertices
200 | if (p.x != a.x and p.y != a.y) and (p.x != b.x and p.y != a.y) and (p.x != c.x and p.y != a.y) and point_in_triangle(p, a,b,c)
201 | true
202 | false
203 |
204 | is_ear = (a, b, c, vertices) ->
205 | is_oriented_ccw(a,b,c) and !any_point_in_triangle(vertices, a,b,c)
206 |
--------------------------------------------------------------------------------
/coffeescript/love.js/math/bezier_curve.coffee:
--------------------------------------------------------------------------------
1 | class Love.Math.BezierCurve
2 | constructor: (@controlPoints) ->
3 |
4 | evaluate: (self, t) ->
5 | if t < 0 or t > 1
6 | throw new Love.Exception("Invalid evaluation parameter: must be between 0 and 1")
7 | if self.controlPoints.length < 2
8 | throw new Love.Exception("Invalid Bezier curve: Not enough control points.")
9 |
10 | # de casteljau
11 | points = self.controlPoints.slice(0)
12 | for step in [1...self.controlPoints.length]
13 | for i in [0...(self.controlPoints.length - step)]
14 | points[i] =
15 | x: points[i].x * (1-t) + points[i+1].x * t
16 | y: points[i].y * (1-t) + points[i+1].y * t
17 |
18 | [points[0].x, points[0].y]
19 |
20 | getControlPoint: (self, i) ->
21 | if i < 0
22 | i += self.controlPoints.length
23 |
24 | if i < 0 or i >= self.controlPoints.length
25 | throw new Love.Exception("Invalid control point index")
26 |
27 | [self.controlPoints[i].x, self.controlPoints[i].y]
28 |
29 | getControlPointCount: (self) ->
30 | self.controlPoints.length
31 |
32 | getDegree: (self) ->
33 | self.controlPoints.length - 1
34 |
35 | getDerivative: (self) ->
36 | if self.getDegree(self) < 1
37 | throw new Love.Exception("Cannot derive a curve of degree < 1.")
38 |
39 | forward_differences = new Array()
40 | degree = self.getDegree(self)
41 | for i in [0...self.controlPoints.length - 1]
42 | forward_differences.push
43 | x: (self.controlPoints[i+1].x - self.controlPoints[i].x) * degree
44 | y: (self.controlPoints[i+1].y - self.controlPoints[i].y) * degree
45 |
46 | new self.constructor(forward_differences)
47 |
48 |
49 | insertControlPoint: (self, x, y, pos = -1) ->
50 | if pos < 0
51 | pos += self.controlPoints.length + 1
52 |
53 | if pos < 0 or pos > self.controlPoints.length
54 | throw new Love.Exception("Invalid control point index")
55 |
56 | self.controlPoints.splice pos, 0,
57 | x: x
58 | y: y
59 |
60 | render: (self, depth = 5) ->
61 | if self.controlPoints.length < 2
62 | throw new Love.Exception("Invalid Bezier curve: Not enough control points.")
63 | vertices = self.controlPoints.slice(0)
64 | subdivide(vertices, depth)
65 | results = []
66 | for vertice in vertices
67 | results.push(vertice.x)
68 | results.push(vertice.y)
69 | return results
70 |
71 | rotate: (self, angle, ox = 0, oy = 0) ->
72 | c = Math.cos(angle)
73 | s = Math.sin(angle)
74 | for controlPoint in self.controlPoints
75 | v =
76 | x: controlPoint.x - ox
77 | y: controlPoint.y - oy
78 | controlPoint.x = c * v.x - s * v.y + ox
79 | controlPoint.y = s * v.x + c * v.y + oy
80 |
81 | scale: (self, s, ox = 0, oy = 0) ->
82 | for controlPoint in self.controlPoints
83 | controlPoint.x = (controlPoint.x - ox) * s + ox
84 | controlPoint.y = (controlPoint.y - oy) * s + oy
85 |
86 | setControlPoint: (self, i, x, y) ->
87 | if i < 0
88 | i += self.controlPoints.length
89 |
90 | if i < 0 or i >= self.controlPoints.length
91 | throw new Love.Exception("Invalid control point index")
92 |
93 | self.controlPoints[i] =
94 | x: x
95 | y: y
96 |
97 | translate: (self, dx, dy) ->
98 | for controlPoint in self.controlPoints
99 | controlPoint.x += dx
100 | controlPoint.y += dy
101 |
102 | subdivide = (points, k) ->
103 | if k <= 0
104 | return
105 |
106 | left = []
107 | right = []
108 |
109 | for step in [1...points.length]
110 | left.push(points[0])
111 | right.push(points[points.length - step])
112 | for i in [0...(points.length - step)]
113 | points[i] = (points[i] + points[i+1]) * .5
114 | left.push(points[0])
115 | right.push(points[0])
116 |
117 | subdivide(left, k - 1)
118 | subdivide(right, k - 1)
119 |
120 | for i in [0...left.length]
121 | points[i] = left[i]
122 | for i in [0...right.length]
123 | points[i-1 + left.length] = right[right.length - i - 1]
124 | return points
125 |
--------------------------------------------------------------------------------
/coffeescript/love.js/math/random_generator.coffee:
--------------------------------------------------------------------------------
1 | class Love.Math.RandomGenerator
2 | constructor: ->
3 | @last_random_normal = Number.POSITIVE_INFINITY
4 | seed = new Long(0xCBBF7A44, 0x0139408D)
5 | @setSeed(@, seed)
6 |
7 | rand: ->
8 | @rng_state = @rng_state.xor(@rng_state.shiftLeft(13))
9 | @rng_state = @rng_state.xor(@rng_state.shiftRight(7))
10 | @rng_state = @rng_state.xor(@rng_state.shiftLeft(17))
11 |
12 | random: (self, min, max) ->
13 | if min == undefined && max == undefined
14 | return Math.abs(self.rand().toNumber() / MAX_VALUE)
15 |
16 | if max == undefined
17 | max = min
18 | return self.random(self) * max
19 |
20 | self.random(self) * (max - min) + min
21 |
22 | randomNormal: (self, stddev = 1, mean = 0) ->
23 | if self.last_random_normal != Number.POSITIVE_INFINITY
24 | r = self.last_random_normal
25 | self.last_random_normal = Number.POSITIVE_INFINITY
26 | return r * stddev + mean
27 |
28 | r = Math.sqrt(-2.0 * Math.log(1 - self.random(self)))
29 | phi = 2 * Math.PI * (1 - self.random(self))
30 |
31 | self.last_random_normal = r * Math.cos(phi)
32 | return r * Math.sin(phi) * stddev + mean
33 |
34 | setSeed: (self, low, high) ->
35 | if high
36 | self.seed = new Long(low, high)
37 | else
38 | self.seed = Long.fromNumber(low)
39 |
40 | self.rng_state = self.seed
41 | for i in [0..2]
42 | self.rand()
43 |
44 | getSeed: (self) ->
45 | [self.seed.getLowBits(), self.seed.getHighBits()]
46 |
47 | getState: (self) ->
48 | [low, high] = self.getSeed()
49 | padding = '00000000' # eight zeros
50 | ss = '0x'
51 | low_string = low.toString(16) # hex string
52 | high_string = high.toString(16) # hex string
53 | ss += padding.substring(0, padding.length - low_string.length) + low_string
54 | ss += padding.substring(0, padding.length - high_string.length) + high_string
55 | return ss
56 |
57 | setState: (self, state_string) ->
58 | low = parseInt(state_string.substring(2, 10), 16)
59 | high = parseInt(state_string.substring(10, 18), 16)
60 | self.rng_state = new Long(low, high)
61 |
62 | Long = goog.math.Long
63 | MAX_VALUE = Long.fromNumber(Number.MAX_VALUE).toNumber()
64 |
--------------------------------------------------------------------------------
/coffeescript/love.js/mouse.coffee:
--------------------------------------------------------------------------------
1 | class Love.Mouse
2 | @WHEEL_TIMEOUT = 0.02
3 |
4 | constructor: (eventQueue, canvas) ->
5 | @x = 0
6 | @y = 0
7 | @buttonsDown = {}
8 | @wheelTimeOuts = {}
9 |
10 | handlePress = (button) =>
11 | @buttonsDown[button] = true
12 | eventQueue.push("mousepressed", @x, @y, button)
13 |
14 | handleRelease = (button) =>
15 | @buttonsDown[button] = false
16 | eventQueue.push("mousereleased", @x, @y, button)
17 |
18 | handleWheel = (evt) =>
19 | evt.preventDefault()
20 | button = getWheelButtonFromEvent(evt)
21 | # The 'wheel has stopped scrolling' event is triggered via setTimeout, since
22 | # browsers don't provide a native 'stopped scrolling' event
23 | clearTimeout(@wheelTimeOuts[button])
24 | @wheelTimeOuts[button] = setTimeout =>
25 | handleRelease(button)
26 | , @constructor.WHEEL_TIMEOUT * 1000
27 | handlePress(button)
28 |
29 | canvas.addEventListener 'mousemove', (evt) =>
30 | rect = Love.element.getBoundingClientRect()
31 | @x = evt.pageX - rect.left
32 | @y = evt.pageY - rect.top
33 |
34 | canvas.addEventListener 'mousedown', (evt) =>
35 | handlePress(getButtonFromEvent(evt))
36 |
37 | canvas.addEventListener 'mouseup', (evt) =>
38 | handleRelease(getButtonFromEvent(evt))
39 |
40 | canvas.addEventListener('DOMMouseScroll', handleWheel); # firefox
41 | canvas.addEventListener('mousewheel', handleWheel); # everyone else
42 |
43 | getCursor: =>
44 | null
45 |
46 | getPosition: =>
47 | [@x, @y]
48 |
49 | getSystemCursor: =>
50 | null
51 |
52 | getX: =>
53 | @x
54 |
55 | getY: =>
56 | @y
57 |
58 | isDown: (button, others...) =>
59 | if !@buttonsDown[button]
60 | return false
61 | else
62 | if others.length == 0
63 | return true
64 | else
65 | return @isDown(others...)
66 |
67 | isGrabbed: =>
68 | false
69 |
70 | isVisible: =>
71 | true
72 |
73 | newCursor: =>
74 | null
75 |
76 | setCursor: (cursor) =>
77 |
78 | setGrabbed: (grab) =>
79 |
80 | setPosition: (x, y) =>
81 | @setX(x)
82 | @setY(y)
83 |
84 | setVisible: (visible) =>
85 |
86 | setX: (x) =>
87 |
88 | setY: (y) =>
89 |
90 | mouseButtonNames =
91 | 1: "l"
92 | 2: "m"
93 | 3: "r"
94 |
95 | getButtonFromEvent = (evt) ->
96 | mouseButtonNames[evt.which]
97 |
98 | getWheelButtonFromEvent = (evt) ->
99 | delta = Math.max(-1, Math.min(1, (evt.wheelDelta or -evt.detail)))
100 | if delta == 1 then 'wu' else 'wd'
101 |
--------------------------------------------------------------------------------
/coffeescript/love.js/system.coffee:
--------------------------------------------------------------------------------
1 | class Love.System
2 | constructor: () ->
3 |
4 | getClipboardText: () =>
5 |
6 | getOS: () =>
7 | window.navigator.appVersion
8 |
9 | getPowerInfo: () =>
10 | battery = window.navigator.battery
11 | if battery
12 | state = if battery.charging then "charging" else "unknown"
13 | percent = battery.level * 100
14 | seconds = battery.dischargingTime
15 | [state, percent, seconds]
16 | else
17 | ["unknown", null, null]
18 |
19 | getProcessorCount: () =>
20 | window.navigator.hardwareConcurrency or 1
21 |
22 | openURL: (url) =>
23 | window.open(url)
24 |
25 | setClipboardText: (text) =>
26 |
27 |
--------------------------------------------------------------------------------
/coffeescript/love.js/timer.coffee:
--------------------------------------------------------------------------------
1 | class Love.Timer
2 | constructor: () ->
3 | # The time that has passed since the timer was created, in milliseconds
4 | @microTime = performance.now()
5 |
6 | # The time that has passed between the last two frames, in seconds
7 | @deltaTime = 0
8 |
9 | # The upper value that deltaTime can have, in seconds. Defaults to 0.25.
10 | # Can be changed via `setDeltaTimeLimit`.
11 | # Note that this does *not* magically make a game go faster. If a game has
12 | # very low FPS, this makes sure that the delta time is not too great (its bad
13 | # for things like physics simulations, etc).
14 | @deltaTimeLimit = 0.25
15 |
16 | @events = {}
17 | @maxEventId = 0
18 |
19 | nextFrame: (callback) =>
20 | requestAnimationFrame(callback)
21 |
22 | getDelta: () =>
23 | @deltaTime
24 |
25 | getFPS: () =>
26 | if @deltaTime == 0 then 0 else 1 / @deltaTime
27 |
28 | getTime: () =>
29 | @microTime
30 |
31 | sleep: () =>
32 |
33 | step: () =>
34 | dt = (performance.now() - @microTime) / 1000
35 | @deltaTime = Math.max(0, Math.min(@deltaTimeLimit, dt))
36 | @microTime += dt * 1000
37 |
38 | performance = window.performance || Date
39 | performance.now = performance.now ||
40 | performance.msNow ||
41 | performance.mozNow ||
42 | performance.webkitNow ||
43 | Date.now
44 |
45 | lastTime = 0
46 | requestAnimationFrame =
47 | window.requestAnimationFrame ||
48 | window.msRequestAnimationFrame ||
49 | window.mozRequestAnimationFrame ||
50 | window.webkitRequestAnimationFrame ||
51 | window.oRequestAnimationFrame ||
52 | (callback) ->
53 | currTime = performance.now()
54 | timeToCall = Math.max(0, 16 - (currTime - lastTime))
55 | delay = -> callback(currTime + timeToCall)
56 | lastTime = currTime + timeToCall
57 | setTimeout(delay, timeToCall)
58 |
--------------------------------------------------------------------------------
/coffeescript/love.js/touch.coffee:
--------------------------------------------------------------------------------
1 | class Love.Touch
2 | constructor: (eventQueue, canvas) ->
3 | @fingers = []
4 |
5 | preventDefault = (evt) ->
6 | evt.preventDefault()
7 | evt.stopPropagation()
8 |
9 | canvas.addEventListener('gesturestart', preventDefault)
10 | canvas.addEventListener('gesturechange', preventDefault)
11 | canvas.addEventListener('gestureend', preventDefault)
12 |
13 | canvas.addEventListener 'touchstart', (evt) =>
14 | preventDefault(evt);
15 |
16 | for t in evt.targetTouches
17 | index = getFingerIndex(@fingers, t.identifier)
18 | if index == -1
19 | rect = Love.element.getBoundingClientRect()
20 | finger = new Finger(t.identifier, t.pageX - rect.left, t.pageY - rect.top)
21 | @fingers.push finger
22 | eventQueue.push('touchpressed', finger.identifier, finger.x, finger.y)
23 |
24 | touchend = (evt) =>
25 | preventDefault(evt)
26 | for t in evt.changedTouches
27 | index = getFingerIndex(@fingers, t.identifier)
28 | if index >= 0
29 | finger = @fingers[index]
30 | @fingers.splice(index, 1)
31 | eventQueue.push('touchreleased', finger.identifier, finger.x, finger.y)
32 | canvas.addEventListener('touchend', touchend)
33 | canvas.addEventListener('touchleave', touchend)
34 | canvas.addEventListener('touchcancel', touchend)
35 |
36 | canvas.addEventListener 'touchmove', (evt) =>
37 | preventDefault(evt)
38 |
39 | for t in evt.targetTouches
40 | index = getFingerIndex(@fingers, t.identifier)
41 | if index >= 0
42 | finger = @fingers[index]
43 | rect = Love.element.getBoundingClientRect()
44 | finger.x = t.pageX - rect.left
45 | finger.y = t.pageY - rect.top
46 | eventQueue.push('touchmoved', finger.identifier, finger.x, finger.y)
47 |
48 |
49 | getTouch: (id) =>
50 | finger = @fingers[id]
51 | if finger
52 | [finger.identifier, finger.x, finger.y, 1]
53 | else
54 | null
55 |
56 | getTouchCount: =>
57 | Object.keys(@fingers).length
58 |
59 | getFingerIndex = (fingers, id) ->
60 | for index in [0...fingers.length]
61 | finger = fingers[index]
62 | if finger.identifier == id
63 | return index
64 | return -1
65 |
66 | class Finger
67 | constructor: (@identifier, @x, @y) ->
68 |
--------------------------------------------------------------------------------
/coffeescript/love.js/window.coffee:
--------------------------------------------------------------------------------
1 | class Love.Window
2 | constructor: (@graphics) ->
3 | @fullscreen = false
4 |
5 | getDesktopDimensions: =>
6 | [window.screen.width, window.screen.height]
7 |
8 | getDimensions: =>
9 | [@getWidth(), @getHeight()]
10 |
11 | getDisplayCount: () =>
12 |
13 | getFullscreen: =>
14 | @fullscreen
15 |
16 | getFullscreenModes: () =>
17 | []
18 |
19 | getHeight: =>
20 | @graphics.getHeight()
21 |
22 | getIcon: () =>
23 |
24 | getMode: () =>
25 |
26 | getPixelScale: =>
27 | window.devicePixelRatio
28 |
29 | getTitle: =>
30 | window.document.title
31 |
32 | getWidth: =>
33 | @graphics.getWidth()
34 |
35 | hasFocus: =>
36 | document.activeElement == Love.element
37 |
38 | hasMouseFocus: () =>
39 |
40 | isCreated: () =>
41 |
42 | isVisible: () =>
43 |
44 | setFullscreen: (@fullscreen) =>
45 | @fullscreen = false
46 |
47 | setIcon: () =>
48 |
49 | setMode: (width, height, flags) =>
50 | @graphics.default_canvas.setDimensions(width, height)
51 |
52 | setTitle: (title) =>
53 | window.document.title = title
54 |
55 |
56 |
--------------------------------------------------------------------------------
/coffeescript/punchdrunk.coffee:
--------------------------------------------------------------------------------
1 | class @Punchdrunk
2 | constructor: (config = {}) ->
3 | game_root = config["game_root"] or "lua"
4 | game_code = config["game_code"]
5 | element = config["canvas"] or null
6 |
7 | conf = {
8 | window: {},
9 | modules: {}
10 | }
11 |
12 | if game_code
13 | # if we were passed compiled game code directly, run that instead of the boot code
14 | Love.root = game_root
15 | love = new Love(element, conf.window, conf.modules)
16 |
17 | vm = new shine.VM({
18 | love: love
19 | })
20 | vm.load(game_code)
21 | love.run()
22 | else
23 | new shine.FileManager().load "#{game_root}/conf.lua.json", (_, file) ->
24 | if file
25 | conf_env = {love: {}}
26 | conf_vm = new shine.VM(conf_env)
27 | conf_vm.execute(null, file)
28 | conf_env.love.conf.call(null, conf)
29 |
30 | Love.root = game_root
31 | love = new Love(element, conf.window, conf.modules)
32 |
33 | vm = new shine.VM({
34 | love: love
35 | })
36 |
37 | vm._globals['package'].path = "#{game_root}/?.lua.json;#{game_root}/?.json;" + vm._globals['package'].path
38 |
39 | # this is boot.lua.json
40 | # it's convenient to have it embeded here because then we don't need to know its path
41 | vm.load({"sourceName":"@js/boot.lua","lineDefined":0,"lastLineDefined":0,"upvalueCount":0,"paramCount":0,"is_vararg":2,"maxStackSize":2,"instructions":[5,0,0,0,1,1,1,0,28,0,2,1,5,0,2,0,6,0,0,259,28,0,1,1,30,0,1,0],"constants":["require","main","love","run"],"functions":[],"linePositions":[1,1,1,3,3,3,3],"locals":[],"upvalues":[],"sourcePath":"js/boot.lua"})
42 |
43 | # Forward print() messages to the console
44 | shine.stdout.write = () ->
45 | console.log.apply(console, arguments)
46 |
--------------------------------------------------------------------------------
/examples/arcs.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
15 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/examples/arcs/main.lua:
--------------------------------------------------------------------------------
1 | function love.draw()
2 | local g = love.graphics
3 |
4 | g.setColor(255, 255, 255)
5 | g.arc("line", 100, 100, -100, 0, math.pi)
6 | g.setColor(255, 0, 0)
7 | g.arc("line", 150, 150, 100, 0, -math.pi)
8 |
9 | g.setColor(255, 255, 255)
10 | g.arc("line", 100, 200, 50, math.rad(-35), math.rad(-12))
11 | g.setColor(255, 0, 0)
12 | g.arc("line", 150, 200, -50, math.rad(-35), math.rad(-12))
13 |
14 | g.setColor(255, 255, 255)
15 | g.arc("line", 200, 200, -50, math.rad(-90), math.rad(20))
16 | g.setColor(255, 0, 0)
17 | g.arc("line", 200, 200, 50, math.rad(-90), math.rad(20))
18 |
19 | g.setColor(255, 255, 255)
20 | g.arc("line", 50, 250, -50, math.rad(1000), math.rad(950))
21 | g.setColor(255, 0, 0)
22 | g.arc("line", 50, 250, 50, math.rad(1000), math.rad(950))
23 | end
24 |
--------------------------------------------------------------------------------
/examples/cube.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
15 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/examples/cube/main.lua:
--------------------------------------------------------------------------------
1 | --[[ CODE DOODLE #2 by DaedalusYoung ]]--
2 | --[[ Cube ]]--
3 |
4 | local colour = {
5 | left = { 1, 1, 1 },
6 | side = { 2, 2, 2 },
7 | right = { 3, 3, 3 },
8 | white = { 255, 255, 255 },
9 | black = { 0, 0, 0 },
10 | }
11 | local width, height = 800, 600
12 | local timer = 0
13 | local direction = 10
14 | local mathsin = math.sin
15 | local mathcos = math.cos
16 | local mathpi = math.pi
17 |
18 | function colour.alpha(t, a)
19 | local r, g, b = unpack(t)
20 | a = a or 127
21 | return { r, g, b, a }
22 | end
23 |
24 | local function randomcolour()
25 | colour.left = { math.random(255), math.random(255), math.random(255) }
26 | colour.right = { math.random(255), math.random(255), math.random(255) }
27 | colour.side = { math.random(255), math.random(255), math.random(255) }
28 | end
29 |
30 | function love.load()
31 | width, height = love.window.getWidth(), love.window.getHeight()
32 | randomcolour()
33 | end
34 |
35 | function love.update(dt)
36 | if love.keyboard.isDown("left", "a") then
37 | direction = direction - 1
38 | elseif love.keyboard.isDown("right", "d") then
39 | direction = direction + 1
40 | end
41 | timer = timer + (direction * dt)
42 | end
43 |
44 | function love.draw()
45 | local xmax = (width / 40) + 1
46 | local ymax = (height / 40) - 2
47 | for x = 1, xmax do
48 | for y = 2, ymax do
49 | local xp = ((timer + (x * 40)) % (width + 40)) - 20
50 | local alpha = (xp + 32) / ((width + 64) / 255)
51 | local xh = (((xp - (width/2)) / xmax) ^ 3) / 70
52 | xp = xp + xh
53 | local yh = math.abs(xh) * ((y - (ymax / 2)) / 6)
54 | local sh = math.abs(xh) / 10
55 | love.graphics.setColor(colour.left)
56 | love.graphics.circle('fill', xp, (y * 40) + yh, 16 + sh, 16)
57 | love.graphics.setColor(colour.alpha(colour.right, alpha))
58 | love.graphics.circle('fill', xp, (y * 40) + yh, 16 + sh, 16)
59 | end
60 | end
61 | love.graphics.setColor(colour.black)
62 | local boxheight = 170
63 | local boxtimer = timer / 100
64 | local ax, bx, cx, dx = (width / 2) + (mathcos(boxtimer) * 128), (width / 2) + (mathcos(boxtimer + (mathpi / 2)) * 128), (width / 2) + (mathcos(boxtimer + mathpi) * 128), (width / 2) + (mathcos(boxtimer + (mathpi * 1.5)) * 128)
65 | local ay, by, cy, dy = (height / 2) + (mathsin(boxtimer) * 64) - 64, (height / 2) + (mathsin(boxtimer + (mathpi / 2)) * 64) - 64, (height / 2) + (mathsin(boxtimer + mathpi) * 64) - 64, (height / 2) + (mathsin(boxtimer + (mathpi * 1.5)) * 64) - 64
66 | love.graphics.polygon('fill', { ax, ay, bx, by, cx, cy, dx, dy})
67 | love.graphics.setColor(colour.alpha(colour.side, 191))
68 | love.graphics.polygon('fill', { ax, ay, bx, by, cx, cy, dx, dy})
69 | love.graphics.setColor(colour.white)
70 | love.graphics.polygon('line', { ax, ay, bx, by, cx, cy, dx, dy})
71 | if ax > bx then
72 | love.graphics.setColor(colour.black)
73 | love.graphics.polygon('fill', { ax, ay, bx, by, bx, by + boxheight, ax, ay + boxheight} )
74 | love.graphics.setColor(colour.alpha(colour.side, ax - bx))
75 | love.graphics.polygon('fill', { ax, ay, bx, by, bx, by + boxheight, ax, ay + boxheight} )
76 | love.graphics.setColor(colour.white)
77 | love.graphics.polygon('line', { ax, ay, bx, by, bx, by + boxheight, ax, ay + boxheight} )
78 | end
79 | if bx > cx then
80 | love.graphics.setColor(colour.black)
81 | love.graphics.polygon('fill', { bx, by, cx, cy, cx, cy + boxheight, bx, by + boxheight} )
82 | love.graphics.setColor(colour.alpha(colour.side, bx - cx))
83 | love.graphics.polygon('fill', { bx, by, cx, cy, cx, cy + boxheight, bx, by + boxheight} )
84 | love.graphics.setColor(colour.white)
85 | love.graphics.polygon('line', { bx, by, cx, cy, cx, cy + boxheight, bx, by + boxheight} )
86 | end
87 | if cx > dx then
88 | love.graphics.setColor(colour.black)
89 | love.graphics.polygon('fill', { cx, cy, dx, dy, dx, dy + boxheight, cx, cy + boxheight} )
90 | love.graphics.setColor(colour.alpha(colour.side, cx - dx))
91 | love.graphics.polygon('fill', { cx, cy, dx, dy, dx, dy + boxheight, cx, cy + boxheight} )
92 | love.graphics.setColor(colour.white)
93 | love.graphics.polygon('line', { cx, cy, dx, dy, dx, dy + boxheight, cx, cy + boxheight} )
94 | end
95 | if dx > ax then
96 | love.graphics.setColor(colour.black)
97 | love.graphics.polygon('fill', { dx, dy, ax, ay, ax, ay + boxheight, dx, dy + boxheight} )
98 | love.graphics.setColor(colour.alpha(colour.side, dx - ax))
99 | love.graphics.polygon('fill', { dx, dy, ax, ay, ax, ay + boxheight, dx, dy + boxheight} )
100 | love.graphics.setColor(colour.white)
101 | love.graphics.polygon('line', { dx, dy, ax, ay, ax, ay + boxheight, dx, dy + boxheight} )
102 | end
103 | end
104 |
105 | function love.keypressed(key)
106 | if key == "down" or key == "s" then
107 | direction = 10
108 | elseif key == " " then
109 | randomcolour()
110 | end
111 | end
112 |
--------------------------------------------------------------------------------
/examples/falling_blocks.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
15 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/examples/iyfct.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
15 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/examples/iyfct/LICENCE:
--------------------------------------------------------------------------------
1 | The source code for this game is released under GPLv3 licence.
2 | See: http://www.gnu.org/licenses/gpl.txt
3 |
4 | All assets including the background music is released under a CC BY 3.0 licence.
5 | See: http://creativecommons.org/licenses/by/3.0/
6 |
7 | The background music is 'The rb Sequence' by Fighter X from 'Little Fighter X', and
8 | can be downloaded for free here:
9 | http://www.crunchyco.com/wp-content/plugins/download-monitor/download.php?id=9
10 |
11 | IYFCT uses the LÖVE framework which is released under the ZLIB licence.
12 | See: http://love2d.org/wiki/License
13 |
--------------------------------------------------------------------------------
/examples/iyfct/README.textile:
--------------------------------------------------------------------------------
1 | h1. In Your Face City Trains
2 |
3 | h2. Instructions
4 |
5 | The goal of the game is to survive as long as you can.
6 | Jump over trains with closed doors and try (as much as possible) to run through trains with open doors to avoid birds and tunnels. Some open trains will give you coffee. When your coffee-meter is full, you have one extra life.
7 |
8 | h2. Controls
9 |
10 | * Space: Jump
11 | * R: Restart
12 | * 1-4: Set zoom
13 | * P: Pause game
14 | * M: Mute sound
--------------------------------------------------------------------------------
/examples/iyfct/bird.lua:
--------------------------------------------------------------------------------
1 | Bird = {}
2 | Bird.__index = Bird
3 |
4 | bird_frames = {}
5 | bird_frames[0] = love.graphics.newQuad(0,64,11,8,128,128)
6 | bird_frames[1] = love.graphics.newQuad(11,64,11,8,128,128)
7 | bird_frames[2] = love.graphics.newQuad(22,64,11,8,128,128)
8 | bird_frames[3] = love.graphics.newQuad(33,64,11,8,128,128)
9 |
10 | function Bird.create()
11 | local self = {}
12 | setmetatable(self,Bird)
13 | self.x = WIDTH
14 | self.y = math.random(10,40)
15 | self.alive = true
16 | self.speed = math.random(180,210)
17 | self.frame = 0
18 | return self
19 | end
20 |
21 | function Bird:update(dt)
22 | self.x = self.x - dt * self.speed * global_speed
23 | self.frame = (self.frame + 10*dt) % 4
24 | if self.x < -12 then
25 | self.alive = false
26 | end
27 | end
28 |
29 | function Bird:draw()
30 | love.graphics.drawq(imgSprites,bird_frames[math.floor(self.frame)],self.x,self.y)
31 | end
32 |
33 | function spawnBirds(dt)
34 | next_bird = next_bird - dt
35 | if next_bird <= 0 then
36 | if train.type ~= 1 and train.x < 200 and gorge.x < 100 then
37 | table.insert(birds,Bird.create())
38 | end
39 | next_bird = math.random()/2
40 | end
41 | end
42 |
--------------------------------------------------------------------------------
/examples/iyfct/cloud.lua:
--------------------------------------------------------------------------------
1 | MAX_CLOUDS = 32
2 | Cloud = {}
3 | Cloud.__index = Cloud
4 |
5 | function Cloud.create(y,size,type)
6 | local self = {}
7 | setmetatable(self,Cloud)
8 | self.x = WIDTH
9 | self.y = y
10 | self.size = size
11 | self.speed = math.random(80,130)
12 | self.type = type
13 | self.alive = true
14 | return self
15 | end
16 |
17 | function Cloud:update(dt)
18 | -- self.x = self.x - dt*self.speed OLD way
19 | self.x = self.x - global_speed * dt * self.speed
20 | end
21 |
22 | function Cloud:draw()
23 | local quad = nil
24 | if self.size == 1 then
25 | quad = love.graphics.newQuad(self.type*16,32,16,16,128,128)
26 | elseif self.size == 2 then
27 | quad = love.graphics.newQuad(48+self.type*32,32,32,16,128,128)
28 | end
29 | if quad ~= nil then
30 | love.graphics.drawq(imgSprites,quad,self.x,self.y)
31 | end
32 | end
33 |
34 | function spawnClouds(dt)
35 | next_cloud = next_cloud - dt
36 | if next_cloud <= 0 then
37 | if #clouds < MAX_CLOUDS then
38 | if math.random(2) == 1 then -- small cloud
39 | table.insert(clouds,Cloud.create(math.random(32),1,math.random(0,2)))
40 | else -- large cloud
41 | table.insert(clouds,Cloud.create(math.random(32),2,math.random(0,1)))
42 | end
43 | end
44 | next_cloud = math.random()
45 | end
46 | end
47 |
--------------------------------------------------------------------------------
/examples/iyfct/conf.lua:
--------------------------------------------------------------------------------
1 | function love.conf(t)
2 | t.title = "In Your Face City Trains"
3 | t.author = "Simon Larsen"
4 | t.identity = "iyfct"
5 | t.window.width = 900
6 | t.window.height = 300
7 | t.modules.joystick = false
8 | t.modules.audio = true
9 | t.modules.keyboard = true
10 | t.modules.event = true
11 | t.modules.image = true
12 | t.modules.graphics = true
13 | t.modules.timer = true
14 | t.modules.mouse = false
15 | t.modules.sound = true
16 | t.modules.physics = false
17 | end
18 |
--------------------------------------------------------------------------------
/examples/iyfct/enemyShip.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TannerRogalsky/punchdrunk/a2835d4963289734ad402e1fc4ceb284d0e1ee91/examples/iyfct/enemyShip.png
--------------------------------------------------------------------------------
/examples/iyfct/gfx/imgfont.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TannerRogalsky/punchdrunk/a2835d4963289734ad402e1fc4ceb284d0e1ee91/examples/iyfct/gfx/imgfont.png
--------------------------------------------------------------------------------
/examples/iyfct/gfx/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TannerRogalsky/punchdrunk/a2835d4963289734ad402e1fc4ceb284d0e1ee91/examples/iyfct/gfx/splash.png
--------------------------------------------------------------------------------
/examples/iyfct/gfx/sprites.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TannerRogalsky/punchdrunk/a2835d4963289734ad402e1fc4ceb284d0e1ee91/examples/iyfct/gfx/sprites.png
--------------------------------------------------------------------------------
/examples/iyfct/gfx/terrain.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TannerRogalsky/punchdrunk/a2835d4963289734ad402e1fc4ceb284d0e1ee91/examples/iyfct/gfx/terrain.png
--------------------------------------------------------------------------------
/examples/iyfct/gfx/trains.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TannerRogalsky/punchdrunk/a2835d4963289734ad402e1fc4ceb284d0e1ee91/examples/iyfct/gfx/trains.png
--------------------------------------------------------------------------------
/examples/iyfct/gorge.lua:
--------------------------------------------------------------------------------
1 | Gorge = {}
2 | Gorge.__index = Gorge
3 |
4 | gorge_quad = love.graphics.newQuad(208,0,128,8,512,512)
5 |
6 | function Gorge.create()
7 | local self = {}
8 | setmetatable(self,Gorge)
9 | self.x = WIDTH+10
10 | self.alive = true
11 | return self
12 | end
13 |
14 | function Gorge:update(dt)
15 | if self.alive == false then return end
16 |
17 | self.x = self.x - global_speed * dt * TRACK_SPEED
18 |
19 | if self.x < -130 then
20 | self.alive = false
21 | end
22 | end
23 |
24 | function Gorge:draw()
25 | love.graphics.drawq(imgTerrain,gorge_quad,self.x-7,92)
26 | end
27 |
--------------------------------------------------------------------------------
/examples/iyfct/menu.lua:
--------------------------------------------------------------------------------
1 | menu_difficulties = {"normal","hard","oh god!"}
2 |
3 | function updateMenu(dt)
4 | updateTerrain(dt)
5 | updateTracks(dt)
6 | end
7 |
8 | function drawMenu()
9 | drawTerrain()
10 | drawTracks()
11 |
12 | if submenu == 0 then
13 | love.graphics.draw(imgSplash,86,0)
14 | elseif submenu == 1 then
15 | elseif submenu == 2 then
16 | love.graphics.printf("select difficulty",0,22,WIDTH,"center")
17 | if selection > 2 then selection = 0
18 | elseif selection < 0 then selection = 2 end
19 |
20 | for i = 0,2 do
21 | if i == selection then
22 | love.graphics.printf("* "..menu_difficulties[i+1].." *",0,42+i*13,WIDTH,"center")
23 | else
24 | love.graphics.printf(menu_difficulties[i+1],0,42+i*13,WIDTH,"center")
25 | end
26 | end
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/examples/iyfct/player.lua:
--------------------------------------------------------------------------------
1 | Player = {}
2 | Player.__index = Player
3 |
4 | JUMP_POWER = -300
5 | GRAVITY = 1000
6 | PLAYER_WIDTH = 14
7 | PLAYER_HEIGHT = 21
8 | PLAYER_START_X = 54
9 |
10 | player_frames = {}
11 | for i=0,5 do
12 | player_frames[i] = love.graphics.newQuad(15*i,0,15,21,128,128)
13 | end
14 |
15 | function Player.create()
16 | local self = {}
17 | setmetatable(self,Player)
18 | self:reset()
19 | return self
20 | end
21 |
22 | function Player:reset()
23 | self.frame = 0
24 | self.x = PLAYER_START_X
25 | self.y = 71
26 | self.yspeed = 0
27 | self.onGround = true
28 | self.status = 0
29 | self.alive = true
30 | self.invul = true
31 | self.invultime = 1
32 | end
33 |
34 | function Player:update(dt)
35 | -- Check keyboard input
36 | if love.keyboard.isDown(' ') and self.onGround == true then
37 | self.yspeed = JUMP_POWER
38 | self.onGround = false
39 | end
40 |
41 | self.onGround = false
42 |
43 | -- Update position
44 | self.yspeed = self.yspeed + dt*GRAVITY
45 |
46 | if self.status == 0 then -- normal ourside
47 | self.y = self.y + self.yspeed*dt
48 | if self.y > 71 then
49 | self.y = 71
50 | self.yspeed = 0
51 | self.onGround = true
52 | end
53 |
54 | elseif self.status == 3 then -- inside train
55 | self.y = self.y + self.yspeed*dt
56 | if self.y > 66 then
57 | self.y = 66
58 | self.yspeed = 0
59 | self.onGround = true
60 | elseif self.y < 60 then
61 | self.y = 60
62 | self.yspeed = 0
63 | end
64 |
65 | elseif self.status == 1 then -- hit by train
66 | self.y = self.y + self.yspeed*dt
67 | self.x = self.x - dt*300
68 |
69 | elseif self.status == 4 then -- falling through ground
70 | self.y = self.y + 150*dt
71 | if self.y > HEIGHT+10 then
72 | scrn_shake = 0.25
73 | auHit:stop() auHit:play()
74 | self.status = 1
75 | end
76 |
77 | elseif self.status == 5 then -- hit by mountain
78 | self.x = self.x - global_speed * dt * TRACK_SPEED * 1.5
79 | end
80 |
81 | -- Update walk frame
82 | self.frame = (self.frame + 20*dt) % 6
83 |
84 | -- Update invulnerability
85 | if self.invultime > 0 then
86 | self.invultime = self.invultime - dt
87 | if self.invultime <= 0 then
88 | self.invul = false
89 | end
90 | end
91 | end
92 |
93 | function Player:draw()
94 | if self.status == 0 then
95 | if self.invul == false or math.floor(self.frame) % 2 == 0 then
96 | love.graphics.drawq(imgSprites,player_frames[math.floor(self.frame)],self.x,self.y)
97 | end
98 |
99 | elseif self.status == 1 or self.status == 5 then
100 | love.graphics.drawq(imgSprites,player_frames[math.floor(self.frame)],self.x,self.y, -self.x/10, 1,1,7,10)
101 |
102 | else -- default case
103 | love.graphics.drawq(imgSprites,player_frames[math.floor(self.frame)],self.x,self.y)
104 | end
105 | end
106 |
107 | function Player:kill(status)
108 | if self.invul == true or self.alive == false then
109 | return
110 | end
111 |
112 | if status ~= 4 then
113 | scrn_shake = 0.25
114 | auHit:stop() auHit:play()
115 | end
116 |
117 | if coffee >= 5 then
118 | self.invul = true
119 | self.invultime = 1
120 | coffee = 0
121 | else
122 | self.alive = false
123 | self.status = status
124 | if status == 1 then
125 | if self.yspeed > -100 then
126 | self.yspeed = -100
127 | end
128 | end
129 | end
130 |
131 | score = math.floor(score)
132 | if score > highscore[difficulty] then
133 | highscore[difficulty] = score
134 | end
135 | end
136 |
137 | function Player:collideWithTrain()
138 | if train.alive == false then
139 | return
140 | end
141 |
142 | if self.status == 0 then
143 | -- check collision with front of train
144 | if Player.collideWithPoint(self,train.x+4,train.y+10) or
145 | Player.collideWithPoint(self,train.x+2,train.y+24) then
146 | if train.type == 1 then -- hit by closed train
147 | self:kill(1)
148 |
149 | elseif train.type == 2 then -- hit by open train
150 | if self.yspeed >= 0 then
151 | self.status = 3
152 | else
153 | self:kill(1)
154 | end
155 | end
156 | -- check if landed on train
157 | elseif train.x < self.x and train.x+125 > self.x and self.yspeed > 0 then
158 | if self.y > 35 then
159 | self.y = 35
160 | self.yspeed = 0
161 | self.onGround = true
162 | end
163 | end
164 | end
165 |
166 | if self.status == 3 then -- inside open train
167 | if self.x > train.x+135 then
168 | self.status = 0
169 | elseif train.hasCoffee == true and
170 | self:collideWithPoint(train.x+57,train.y+20) then
171 | train.hasCoffee = false
172 | coffee = coffee + 1
173 | if coffee > 5 then coffee = 5 end
174 | auCoffee:stop() auCoffee:play()
175 | end
176 | end
177 | end
178 |
179 | function Player:collideWithTunnel()
180 | if tunnel.alive == false then
181 | return
182 | end
183 |
184 | if self.status == 0 then
185 | if self.y < 47 and self.x < tunnel.x and
186 | self.x > tunnel.x-16 then
187 | self:kill(5)
188 | end
189 | end
190 | end
191 |
192 | function Player:collideWithBirds()
193 | for i,v in ipairs(birds) do
194 | if Player.collideWithPoint(self,v.x+5.5,v.y+5) then
195 | self:kill(1)
196 | return
197 | end
198 | end
199 | end
200 |
201 | function Player:collideWithGorge()
202 | if self.y >= 71 and self.x > gorge.x+2 and self.x < gorge.x+92 then
203 | self:kill(4)
204 | end
205 | end
206 |
207 | function Player:collideWithPoint(x,y)
208 | if x > self.x and x < self.x+PLAYER_WIDTH
209 | and y > self.y and y < self.y+PLAYER_HEIGHT then
210 | return true
211 | else
212 | return false
213 | end
214 | end
215 |
216 | --[[ Status values:
217 | 0 = alive
218 | 1 = hit by train
219 | 2 = hit by bird
220 | 3 = inside train
221 | 4 = falling through ground?
222 | 5 = hit by mountain
223 | --]]
224 |
--------------------------------------------------------------------------------
/examples/iyfct/sfx/bgm.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TannerRogalsky/punchdrunk/a2835d4963289734ad402e1fc4ceb284d0e1ee91/examples/iyfct/sfx/bgm.ogg
--------------------------------------------------------------------------------
/examples/iyfct/sfx/coffee.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TannerRogalsky/punchdrunk/a2835d4963289734ad402e1fc4ceb284d0e1ee91/examples/iyfct/sfx/coffee.wav
--------------------------------------------------------------------------------
/examples/iyfct/sfx/hit.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TannerRogalsky/punchdrunk/a2835d4963289734ad402e1fc4ceb284d0e1ee91/examples/iyfct/sfx/hit.wav
--------------------------------------------------------------------------------
/examples/iyfct/sfx/select.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TannerRogalsky/punchdrunk/a2835d4963289734ad402e1fc4ceb284d0e1ee91/examples/iyfct/sfx/select.wav
--------------------------------------------------------------------------------
/examples/iyfct/spritesheet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TannerRogalsky/punchdrunk/a2835d4963289734ad402e1fc4ceb284d0e1ee91/examples/iyfct/spritesheet.png
--------------------------------------------------------------------------------
/examples/iyfct/table-save.lua:
--------------------------------------------------------------------------------
1 | --[[
2 | Save Table to File/Stringtable
3 | Load Table from File/Stringtable
4 | v 0.94
5 |
6 | Lua 5.1 compatible
7 |
8 | Userdata and indices of these are not saved
9 | Functions are saved via string.dump, so make sure it has no upvalues
10 | References are saved
11 | ----------------------------------------------------
12 | table.save( table [, filename] )
13 |
14 | Saves a table so it can be called via the table.load function again
15 | table must a object of type 'table'
16 | filename is optional, and may be a string representing a filename or true/1
17 |
18 | table.save( table )
19 | on success: returns a string representing the table (stringtable)
20 | (uses a string as buffer, ideal for smaller tables)
21 | table.save( table, true or 1 )
22 | on success: returns a string representing the table (stringtable)
23 | (uses io.tmpfile() as buffer, ideal for bigger tables)
24 | table.save( table, "filename" )
25 | on success: returns 1
26 | (saves the table to file "filename")
27 | on failure: returns as second argument an error msg
28 | ----------------------------------------------------
29 | table.load( filename or stringtable )
30 |
31 | Loads a table that has been saved via the table.save function
32 |
33 | on success: returns a previously saved table
34 | on failure: returns as second argument an error msg
35 | ----------------------------------------------------
36 |
37 | chillcode, http://lua-users.org/wiki/SaveTableToFile
38 | Licensed under the same terms as Lua itself.
39 | ]]--
40 | do
41 | -- declare local variables
42 | --// exportstring( string )
43 | --// returns a "Lua" portable version of the string
44 | local function exportstring( s )
45 | s = string.format( "%q",s )
46 | -- to replace
47 | s = string.gsub( s,"\\\n","\\n" )
48 | s = string.gsub( s,"\r","\\r" )
49 | s = string.gsub( s,string.char(26),"\"..string.char(26)..\"" )
50 | return s
51 | end
52 | --// The Save Function
53 | function table.save( tbl,filename )
54 | local charS,charE = " ","\n"
55 | local file,err
56 | -- create a pseudo file that writes to a string and return the string
57 | if not filename then
58 | file = { write = function( self,newstr ) self.str = self.str..newstr end, str = "" }
59 | charS,charE = "",""
60 | -- write table to tmpfile
61 | elseif filename == true or filename == 1 then
62 | charS,charE,file = "","",io.tmpfile()
63 | -- write table to file
64 | -- use io.open here rather than io.output, since in windows when clicking on a file opened with io.output will create an error
65 | else
66 | file,err = io.open( filename, "w" )
67 | if err then return _,err end
68 | end
69 | -- initiate variables for save procedure
70 | local tables,lookup = { tbl },{ [tbl] = 1 }
71 | file:write( "return {"..charE )
72 | for idx,t in ipairs( tables ) do
73 | if filename and filename ~= true and filename ~= 1 then
74 | file:write( "-- Table: {"..idx.."}"..charE )
75 | end
76 | file:write( "{"..charE )
77 | local thandled = {}
78 | for i,v in ipairs( t ) do
79 | thandled[i] = true
80 | -- escape functions and userdata
81 | if type( v ) ~= "userdata" then
82 | -- only handle value
83 | if type( v ) == "table" then
84 | if not lookup[v] then
85 | table.insert( tables, v )
86 | lookup[v] = #tables
87 | end
88 | file:write( charS.."{"..lookup[v].."},"..charE )
89 | elseif type( v ) == "function" then
90 | file:write( charS.."loadstring("..exportstring(string.dump( v )).."),"..charE )
91 | else
92 | local value = ( type( v ) == "string" and exportstring( v ) ) or tostring( v )
93 | file:write( charS..value..","..charE )
94 | end
95 | end
96 | end
97 | for i,v in pairs( t ) do
98 | -- escape functions and userdata
99 | if (not thandled[i]) and type( v ) ~= "userdata" then
100 | -- handle index
101 | if type( i ) == "table" then
102 | if not lookup[i] then
103 | table.insert( tables,i )
104 | lookup[i] = #tables
105 | end
106 | file:write( charS.."[{"..lookup[i].."}]=" )
107 | else
108 | local index = ( type( i ) == "string" and "["..exportstring( i ).."]" ) or string.format( "[%d]",i )
109 | file:write( charS..index.."=" )
110 | end
111 | -- handle value
112 | if type( v ) == "table" then
113 | if not lookup[v] then
114 | table.insert( tables,v )
115 | lookup[v] = #tables
116 | end
117 | file:write( "{"..lookup[v].."},"..charE )
118 | elseif type( v ) == "function" then
119 | file:write( "loadstring("..exportstring(string.dump( v )).."),"..charE )
120 | else
121 | local value = ( type( v ) == "string" and exportstring( v ) ) or tostring( v )
122 | file:write( value..","..charE )
123 | end
124 | end
125 | end
126 | file:write( "},"..charE )
127 | end
128 | file:write( "}" )
129 | -- Return Values
130 | -- return stringtable from string
131 | if not filename then
132 | -- set marker for stringtable
133 | return file.str.."--|"
134 | -- return stringttable from file
135 | elseif filename == true or filename == 1 then
136 | file:seek ( "set" )
137 | -- no need to close file, it gets closed and removed automatically
138 | -- set marker for stringtable
139 | return file:read( "*a" ).."--|"
140 | -- close file and return 1
141 | else
142 | file:close()
143 | return 1
144 | end
145 | end
146 |
147 | --// The Load Function
148 | function table.load( sfile )
149 | -- catch marker for stringtable
150 | if string.sub( sfile,-3,-1 ) == "--|" then
151 | tables,err = loadstring( sfile )
152 | else
153 | tables,err = loadfile( sfile )
154 | end
155 | if err then return _,err
156 | end
157 | tables = tables()
158 | for idx = 1,#tables do
159 | local tolinkv,tolinki = {},{}
160 | for i,v in pairs( tables[idx] ) do
161 | if type( v ) == "table" and tables[v[1]] then
162 | table.insert( tolinkv,{ i,tables[v[1]] } )
163 | end
164 | if type( i ) == "table" and tables[i[1]] then
165 | table.insert( tolinki,{ i,tables[i[1]] } )
166 | end
167 | end
168 | -- link values, first due to possible changes of indices
169 | for _,v in ipairs( tolinkv ) do
170 | tables[idx][v[1]] = v[2]
171 | end
172 | -- link indices
173 | for _,v in ipairs( tolinki ) do
174 | tables[idx][v[2]],tables[idx][v[1]] = tables[idx][v[1]],nil
175 | end
176 | end
177 | return tables[1]
178 | end
179 | -- close do
180 | end
--------------------------------------------------------------------------------
/examples/iyfct/terrain.lua:
--------------------------------------------------------------------------------
1 | front_x = 0
2 | back_x = 0
3 |
4 | back_terrain = love.graphics.newQuad(0,112,300,94,512,512)
5 | front_terrain = love.graphics.newQuad(0,224,300,94,512,512)
6 | track_quad = love.graphics.newQuad(0,48,121,5,128,128)
7 |
8 | function updateTerrain(dt)
9 | front_x = (front_x + 65*dt) % WIDTH
10 | back_x = (back_x + 40*dt) % WIDTH
11 | end
12 |
13 | function drawTerrain()
14 | love.graphics.drawq(imgTerrain,back_terrain,0-back_x,0)
15 | love.graphics.drawq(imgTerrain,back_terrain,WIDTH-back_x,0)
16 | love.graphics.drawq(imgTerrain,front_terrain,0-front_x,0)
17 | love.graphics.drawq(imgTerrain,front_terrain,WIDTH-front_x,0)
18 | end
19 |
20 | function updateTracks(dt)
21 | track_frame = track_frame + global_speed * dt * TRACK_SPEED
22 | if track_frame >= 11 then
23 | track_frame = track_frame % 11
24 | end
25 | end
26 |
27 | function drawTracks()
28 | for i=0,2 do
29 | love.graphics.drawq(imgSprites,track_quad,i*121 - track_frame,92)
30 | end
31 | end
32 |
--------------------------------------------------------------------------------
/examples/iyfct/train.lua:
--------------------------------------------------------------------------------
1 | Train = {}
2 | Train.__index = Train
3 |
4 | normal_train_quad = love.graphics.newQuad(0,0,132,36,256,256)
5 | open_train_quad = love.graphics.newQuad(0,48,146,36,256,256)
6 | inside_train_quad = love.graphics.newQuad(0,96,146,36,256,256)
7 |
8 | coffee_cup_quad = love.graphics.newQuad(96,0,26,23,128,128)
9 |
10 | TRAIN_MIN_SPEED = 160
11 | TRAIN_MAX_SPEED = 200
12 |
13 | function Train.createRandom()
14 | if math.random(1,3) == 1 then
15 | return Train.create(2)
16 | else
17 | return Train.create(1)
18 | end
19 | end
20 |
21 | function Train.create(type)
22 | local self = {}
23 | setmetatable(self,Train)
24 | self.speed = math.random(TRAIN_MIN_SPEED,TRAIN_MAX_SPEED)
25 | self.x = WIDTH
26 | self.y = 56
27 | self.type = type
28 | self.alive = true
29 | self.hasCoffee = false
30 | if self.type == 2 then
31 | if math.random(2) == 1 then
32 | self.hasCoffee = true
33 | end
34 | end
35 | return self
36 | end
37 |
38 | function Train:update(dt)
39 | if self.alive == false then
40 | return
41 | end
42 |
43 | self.x = self.x - self.speed*dt*global_speed
44 | if self.x < -146 then
45 | self.alive = false
46 | end
47 | end
48 |
49 | function Train:draw()
50 | if self.alive == false then
51 | return
52 | end
53 |
54 | if self.type == 1 then -- closed train
55 | love.graphics.drawq(imgTrains,normal_train_quad,self.x,self.y)
56 | elseif self.type == 2 then -- open train
57 | if pl.status == 3 then -- inside
58 | love.graphics.drawq(imgTrains,inside_train_quad,self.x-7,self.y)
59 | if self.hasCoffee == true then
60 | love.graphics.drawq(imgSprites,coffee_cup_quad,self.x+54,self.y+8)
61 | end
62 | else -- outside
63 | love.graphics.drawq(imgTrains,open_train_quad,self.x-7,self.y)
64 | end
65 | end
66 | end
67 |
--------------------------------------------------------------------------------
/examples/iyfct/tunnel.lua:
--------------------------------------------------------------------------------
1 | Tunnel = {}
2 | Tunnel.__index = Tunnel
3 |
4 | tunnel_start_back = love.graphics.newQuad(0,0,58,100,512,512)
5 | tunnel_end = love.graphics.newQuad(58,0,133,100,512,512)
6 |
7 | function Tunnel.create()
8 | local self = {}
9 | setmetatable(self,Tunnel)
10 | self.x = WIDTH+58
11 | self.alive = true
12 | return self
13 | end
14 |
15 | function Tunnel:update(dt)
16 | if self.alive == false then
17 | return
18 | end
19 |
20 | self.x = self.x - global_speed * dt * TRACK_SPEED
21 |
22 | if self.x < -150 then
23 | self.alive = false
24 | end
25 | end
26 |
27 | function Tunnel:drawBack()
28 | if self.alive == true then
29 | love.graphics.drawq(imgTerrain,tunnel_start_back,self.x-58,0)
30 | end
31 | end
32 |
33 | function Tunnel:drawFront()
34 | if self.alive == true then
35 | love.graphics.drawq(imgTerrain,tunnel_end,self.x,0)
36 | end
37 | end
38 |
--------------------------------------------------------------------------------
/examples/rolling_hills.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
15 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/examples/rolling_hills/main.lua:
--------------------------------------------------------------------------------
1 | --[[ CODE DOODLE #5 by DaedalusYoung ]]--
2 | --[[ Rolling Hills ]]--
3 |
4 | local map = {}
5 | local width = 16
6 | local height = 16
7 | local timer = 0
8 | local waveheight = 50
9 | local wavesize = 5
10 | local blockmode = false
11 | local keytimer = 0
12 | local keyinput = true
13 |
14 | function love.load()
15 | love.window.setTitle("Rolling Hills")
16 | for y = 1, height do
17 | map[y] = {}
18 | for x = 1, width do
19 | map[y][x] = 0
20 | end
21 | end
22 | end
23 |
24 | function love.update(dt)
25 | timer = timer + dt
26 | if not keyinput then
27 | keytimer = keytimer + dt
28 | if keytimer >= 0.04 then
29 | keytimer = 0
30 | keyinput = true
31 | end
32 | else
33 | if love.keyboard.isDown("a", "d", "q", "e") then
34 | keyinput = false
35 | end
36 | if love.keyboard.isDown("a") then
37 | waveheight = math.floor(waveheight - 0.5)
38 | end
39 | if love.keyboard.isDown("d") then
40 | waveheight = math.ceil(waveheight + 0.5)
41 | end
42 | waveheight = math.min(100, math.max(1, waveheight))
43 | if love.keyboard.isDown("q") then
44 | wavesize = math.floor((wavesize * 10) - 0.5) / 10
45 | end
46 | if love.keyboard.isDown("e") then
47 | wavesize = math.ceil((wavesize * 10) + 0.5) / 10
48 | end
49 | wavesize = math.min(15, math.max(1, wavesize))
50 | end
51 | for y = 1, height do
52 | map[y] = {}
53 | for x = 1, width do
54 | map[y][x] = love.math.noise(x / wavesize, y / wavesize, timer / 4)
55 | end
56 | end
57 | end
58 |
59 | function love.draw()
60 | for y = 1, height do
61 | for x = 1, width do
62 | local x1 = (x * 24) - (y * 24) + 400
63 | local x2 = x1 + 22
64 | local x3 = x1
65 | local x4 = x1 - 22
66 | local val = map[y][x] * waveheight
67 | if blockmode then
68 | val = val - ((val + 10) % 20)
69 | end
70 | local y1 = (((y * 24) + (x * 24)) / 2) + (100 - val)
71 | local y2 = y1 + 11
72 | local y3 = y1 + 22
73 | local y4 = y1 + 11
74 | --left side
75 | love.graphics.setColor(32, 147, 32)
76 | love.graphics.polygon('fill', x3, y3, x4, y4, x4, y4 + 4, x3, y3 + 4)
77 | love.graphics.setColor(104, 68, 33)
78 | love.graphics.polygon('fill', x3, y3 + 4, x4, y4 + 4, x4, y4 + 20, x3, y3 + 20)
79 | love.graphics.setColor(33, 33, 33)
80 | love.graphics.polygon('fill', x3, y3 + 20, x4, y4 + 20, x4, y4 + 80, x3, y3 + 80)
81 | love.graphics.setColor(68, 68, 68)
82 | love.graphics.polygon('fill', x3, y3 + 21, x4, y4 + 21, x4, y4 + 40, x3, y3 + 40)
83 | love.graphics.polygon('fill', x3, y3 + 41, x4, y4 + 41, x4, y4 + 60, x3, y3 + 60)
84 | --right side
85 | love.graphics.setColor(25, 116, 25)
86 | love.graphics.polygon('fill', x2, y2, x3, y3, x3, y3 + 4, x2, y2 + 4)
87 | love.graphics.setColor(76, 50, 25)
88 | love.graphics.polygon('fill', x2, y2 + 4, x3, y3 + 4, x3, y3 + 20, x2, y2 + 20)
89 | love.graphics.setColor(25, 25, 25)
90 | love.graphics.polygon('fill', x2, y2 + 20, x3, y3 + 20, x3, y3 + 80, x2, y2 + 80)
91 | love.graphics.setColor(50, 50, 50)
92 | love.graphics.polygon('fill', x2, y2 + 21, x3, y3 + 21, x3, y3 + 40, x2, y2 + 40)
93 | love.graphics.polygon('fill', x2, y2 + 41, x3, y3 + 41, x3, y3 + 60, x2, y2 + 60)
94 | --top side
95 | love.graphics.setColor(43, 198, 43)
96 | love.graphics.polygon('fill', x1, y1, x2, y2, x3, y3, x4, y4)
97 | love.graphics.setColor(25, 116, 25)
98 | love.graphics.polygon('line', x1, y1, x2, y2, x3, y3, x4, y4)
99 | end
100 | end
101 | love.graphics.setColor(255, 255, 255)
102 | love.graphics.print("Wave size: " .. wavesize .. " [Q] and [E] to change\nWave height: " .. waveheight .. " [A] and [D] to change\n[B] to toggle Block Mode", 16, 16)
103 | end
104 |
105 | function love.keypressed(k)
106 | if k == "b" then
107 | blockmode = not blockmode
108 | end
109 | end
110 |
--------------------------------------------------------------------------------
/examples/touch.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
15 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/examples/touch/main.lua:
--------------------------------------------------------------------------------
1 | local g = love.graphics
2 |
3 | function love.load()
4 | canvas = g.newCanvas()
5 | color_index = 1
6 | colors = {
7 | {255, 255, 255},
8 | {255, 0, 0},
9 | {0, 255, 0},
10 | {0, 0, 255},
11 | }
12 | end
13 |
14 | function love.draw()
15 | g.setColor(255, 255, 255)
16 | g.draw(canvas, 0, 0)
17 | end
18 |
19 | function love.update(dt)
20 | g.setCanvas(canvas)
21 | g.setColor(colors[color_index])
22 | if love.touch then
23 | for i=0,love.touch.getTouchCount() do
24 | local id, x, y, pressure = love.touch.getTouch(i)
25 | g.circle("fill", x, y, 20)
26 | end
27 | end
28 | if love.mouse then
29 | if love.mouse.isDown("l") then
30 | local x, y = love.mouse.getPosition()
31 | g.circle("fill", x, y, 20)
32 | end
33 | end
34 | g.setCanvas()
35 | end
36 |
37 | function love.touchpressed(id, x, y)
38 | print("touchpressed", id, x, y)
39 | end
40 |
41 | function love.touchreleased(id, x, y)
42 | print("touchreleased", id, x, y)
43 | color_index = color_index % #colors + 1
44 | end
45 |
46 | function love.mousepressed(x, y, button)
47 | print("mousepressed", button, x, y)
48 | end
49 |
50 | function love.mousereleased(x, y, button)
51 | print("mousereleased", button, x, y)
52 | color_index = color_index % #colors + 1
53 | end
54 |
--------------------------------------------------------------------------------
/examples/trails.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
15 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/examples/trails/main.lua:
--------------------------------------------------------------------------------
1 | function love.load()
2 | math.randomseed(os.time(),love.mouse.getX(),love.mouse.getY())
3 | head = {x=400,y=300}
4 | tail = {}
5 | colors = {}
6 | directions = {}
7 | for i=1,100 do
8 | table.insert(tail,{x=-6000+math.random(12000),y=-6000+math.random(12000)})
9 | table.insert(colors,{math.random(255),math.random(255),math.random(255)})
10 | table.insert(directions,math.random(math.pi*2))
11 | end
12 |
13 | angle = 0
14 |
15 | timer = 0.2
16 |
17 |
18 | end
19 |
20 | function love.update(dt)
21 |
22 | if love.keyboard.isDown(" ") then
23 | for i,v in ipairs(tail) do
24 | v.x = v.x + math.cos(directions[i]) * 500 * dt
25 | v.y = v.y + math.sin(directions[i]) * 500 * dt
26 | end
27 | else
28 |
29 | for i,v in ipairs(tail) do
30 | if i == 1 then
31 | if getDistance(head,v) > 30 then
32 | local ang = math.atan2(head.y-v.y,head.x-v.x)
33 | v.x = v.x + math.cos(ang) * 500 * dt
34 | v.y = v.y + math.sin(ang) * 500 * dt
35 | end
36 | else
37 | local j = i-1
38 | if getDistance(tail[j],v) > 15 then
39 | local ang = math.atan2(tail[j].y-v.y,tail[j].x-v.x)
40 | v.x = v.x + math.cos(ang) * 500 * dt
41 | v.y = v.y + math.sin(ang) * 500 * dt
42 | end
43 | end
44 | end
45 | end
46 |
47 | if love.keyboard.isDown("left") then
48 | angle = angle - 8 * dt
49 | end
50 |
51 | if love.keyboard.isDown("right") then
52 | angle = angle + 8 * dt
53 | end
54 |
55 | if love.keyboard.isDown("up") then
56 | head.x = head.x + 500 * math.cos(angle) * dt
57 | head.y = head.y + 500 * math.sin(angle) * dt
58 |
59 | end
60 |
61 | if head.x > 800 then
62 | head.x = 0
63 | end
64 | if head.y > 600 then
65 | head.y = 0
66 | end
67 |
68 | if head.x < 0 then
69 | head.x = 800
70 | end
71 | if head.y < 0 then
72 | head.y = 600
73 | end
74 |
75 | timer = timer - dt
76 |
77 | if timer < 0 then
78 | local temp = colors[1]
79 | table.remove(colors,1)
80 | table.insert(colors,temp)
81 | timer = 0.05
82 | end
83 |
84 |
85 |
86 |
87 | end
88 |
89 | function love.draw()
90 |
91 | for i,v in ipairs(tail) do
92 | love.graphics.setColor(colors[i][1],colors[i][2],colors[i][3])
93 | if i == 1 then
94 | love.graphics.line(v.x,v.y,head.x,head.y)
95 | else
96 | love.graphics.line(v.x,v.y,tail[i-1].x,tail[i-1].y)
97 | end
98 | love.graphics.circle("fill", v.x, v.y, 3, 10)
99 | end
100 |
101 | love.graphics.setColor(255,255,255)
102 | love.graphics.push()
103 | love.graphics.translate(head.x, head.y)
104 | love.graphics.rotate(angle)
105 | love.graphics.polygon("fill", 0, 0, -30, -10, -30,10)
106 | love.graphics.pop()
107 |
108 | end
109 |
110 | function getDistance(a,b)
111 | return math.sqrt((a.x-b.x)^2+(a.y-b.y)^2)
112 | end
113 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
8 |
43 |
44 |
45 |
46 |
47 |
Punchdrunk = Moonshine + LÖVE
48 |
49 |
50 |
51 |
52 |
53 |
54 |
Download
55 | Grab the latest release of Punchdrunk from the releases page!
56 |
57 |
About
58 |
Have you ever made a Love game and wished that you could easily show it to people without them having to download an executable? Well now they can! ... maybe. Punchdrunk is a project that aims to replicate most part of the Love API in the browser and enable to you to run your original Lua code in the browser via a Lua Virtual Machine named Moonshine.
59 |
60 |
Punchdrunk is still very early alpha software. It works well with a limited subset of Love's API. If it doesn't work with the game you made, please consider sending me your game to test against or forking the project. I am working to expand which portions of the Love API that Punchdrunk supports but I will prioritize feature requests from actual users when possible. However, certain elements of the API will require a lot of additional work (i.e. Shaders) or be altogether impossible (full filesystem integration).
61 |
62 |
Please check out the Github page for more information on how to get your own game running in the browser.
63 |
64 |