├── .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 | [![Build Status](https://travis-ci.org/TannerRogalsky/punchdrunk.svg?branch=tests)](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 | Fork me on GitHub 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 |

Examples

65 | 73 |
74 | 75 | 76 | 77 | 78 | 79 |
80 |
81 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /js/boot.lua: -------------------------------------------------------------------------------- 1 | require("main") 2 | 3 | love.run() 4 | -------------------------------------------------------------------------------- /js/debug/local.debug.moonshine.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Moonshine - a Lua virtual machine. 3 | * 4 | * Copyright (C) 2013 Gamesys Limited, 5 | * 10 Piccadilly, London W1J 0DD 6 | * Email: moonshine@gamesys.co.uk 7 | * http://moonshinejs.org 8 | * 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | * along with this program. If not, see . 21 | */ 22 | 23 | /** 24 | * @fileOverview Local UI for debug engine. 25 | * @author Paul Cuthbertson 26 | */ 27 | 28 | 29 | var shine = shine || {}; 30 | shine.debug = shine.debug || {}; 31 | 32 | 33 | shine.debug.LOCAL_UI_URL = (function () { 34 | var script = document.querySelector('script[src$="local.debug.moonshine.js"]'); 35 | return shine.debug.LOCAL_UI_URL || (script? script.src.substr(0, script.src.length - 25) : './js/moonshine/extensions/debug') + '/ui'; 36 | })(); 37 | 38 | 39 | 40 | 41 | shine.debug.ui = { 42 | 43 | name: 'local', 44 | 45 | init: function () { 46 | 47 | var me = this, 48 | iframe = this.iframe = document.createElement('iframe'); 49 | 50 | iframe.src = shine.debug.LOCAL_UI_URL; 51 | iframe.style.position = 'fixed'; 52 | iframe.style.top = '0'; 53 | iframe.style.right = '20px'; 54 | iframe.style.width = '270px'; 55 | iframe.style.height = '30px'; 56 | iframe.style.overflow = 'hidden'; 57 | iframe.style.border = 'none'; 58 | 59 | 60 | function append () { 61 | document.body.appendChild(iframe); 62 | 63 | iframe.contentWindow.addEventListener('load', function () { 64 | me._initIFrame(iframe); 65 | }); 66 | } 67 | 68 | if (document.body) { 69 | append(); 70 | } else { 71 | window.addEventListener('load', append, false); 72 | } 73 | }, 74 | 75 | 76 | 77 | 78 | _initIFrame: function (iframe) { 79 | var doc = iframe.contentDocument, 80 | toggle = document.createElement('button'); 81 | 82 | // Toggle size; 83 | toggle.className = 'toggle'; 84 | toggle.title = 'Toggle size'; 85 | toggle.textContent = 'Size'; 86 | 87 | 88 | function toggleExpanded () { 89 | var expand = toggle.className == 'toggle'; 90 | 91 | if (expand) { 92 | iframe.style.width = '50%'; 93 | iframe.style.right = '0'; 94 | iframe.style.height = '100%'; 95 | toggle.className = 'toggle expanded'; 96 | 97 | } else { 98 | iframe.style.right = '20px'; 99 | iframe.style.width = '270px'; 100 | iframe.style.height = '30px'; 101 | toggle.className = 'toggle'; 102 | } 103 | 104 | if (sessionStorage) sessionStorage.setItem('expanded', expand? '1' : ''); 105 | } 106 | 107 | toggle.addEventListener('click', toggleExpanded); 108 | if (sessionStorage && sessionStorage.getItem('expanded')) toggleExpanded(); 109 | 110 | 111 | iframe.contentDocument.querySelector('.buttons').appendChild(toggle); 112 | iframe.contentWindow.registerDebugEngine(shine.debug); 113 | shine.debug._clearLoadQueue(); 114 | } 115 | 116 | }; 117 | 118 | 119 | 120 | 121 | shine.debug.ui.init(); 122 | 123 | -------------------------------------------------------------------------------- /js/debug/server/AbstractConnection.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Moonshine - a Lua virtual machine. 3 | * 4 | * Copyright (C) 2013 Gamesys Limited, 5 | * 10 Piccadilly, London W1J 0DD 6 | * Email: moonshine@gamesys.co.uk 7 | * http://moonshinejs.org 8 | * 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | * along with this program. If not, see . 21 | */ 22 | 23 | 24 | var http = require('http'), 25 | express = require('express'), 26 | socketio = require('socket.io'), 27 | EventEmitter = require('../../../vm/src/EventEmitter').EventEmitter; 28 | 29 | 30 | 31 | 32 | AbstractConnection = function () { 33 | EventEmitter.apply(this); 34 | 35 | this.connected = false; 36 | if (this._port) this._open(this._port); 37 | }; 38 | 39 | 40 | AbstractConnection.prototype = new EventEmitter; 41 | AbstractConnection.prototype.constructor = AbstractConnection; 42 | 43 | 44 | 45 | 46 | AbstractConnection.prototype._open = function (port) { 47 | var me = this; 48 | 49 | this._app = express(); 50 | this._server = http.createServer(this._app).listen(port); 51 | this._io = socketio.listen(this._server, { 'log level': 0 }); 52 | 53 | this._onReady(); 54 | 55 | 56 | this._io.on('connection', function (socket) { 57 | 58 | if (me.connected) { 59 | var errorMessage = me._onFailedConnection(); 60 | socket.emit('error', { message: errorMessage || 'Already connected to another instance.' }); 61 | 62 | return; 63 | } 64 | 65 | 66 | socket.on('disconnect', function () { 67 | delete me._socket; 68 | 69 | me.connected = false; 70 | me._onDisconnect(); 71 | me._trigger('disconnect'); 72 | }); 73 | 74 | 75 | socket.on('message', function (data, callback) { 76 | me._processMessage(data[0], data[1], callback); 77 | }); 78 | 79 | 80 | me._socket = socket; 81 | me.connected = true; 82 | 83 | me._onConnection(); 84 | }); 85 | 86 | }; 87 | 88 | 89 | 90 | 91 | AbstractConnection.prototype.disconnect = function () { 92 | this._socket.disconnect(); 93 | }; 94 | 95 | 96 | 97 | 98 | AbstractConnection.prototype._send = function (event, data, callback) { 99 | var socket = this._socket; 100 | if (socket) socket.emit(event, data, callback); 101 | }; 102 | 103 | 104 | 105 | 106 | AbstractConnection.prototype._sendStatus = function (status) { 107 | this._send('status', status); 108 | }; 109 | 110 | 111 | 112 | 113 | AbstractConnection.prototype._onReady = function () { 114 | // Override 115 | }; 116 | 117 | 118 | 119 | 120 | AbstractConnection.prototype._onConnection = function () { 121 | // Override 122 | }; 123 | 124 | 125 | 126 | 127 | AbstractConnection.prototype._onFailedConnection = function () { 128 | // Override 129 | // Return error message. 130 | }; 131 | 132 | 133 | 134 | 135 | AbstractConnection.prototype._onDisconnect = function () { 136 | // Override 137 | }; 138 | 139 | 140 | 141 | 142 | AbstractConnection.prototype._processMessage = function (event, data, callback) { 143 | // Override 144 | }; 145 | 146 | 147 | 148 | 149 | module.exports = AbstractConnection; 150 | 151 | -------------------------------------------------------------------------------- /js/debug/server/AppConnection.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Moonshine - a Lua virtual machine. 3 | * 4 | * Copyright (C) 2013 Gamesys Limited, 5 | * 10 Piccadilly, London W1J 0DD 6 | * Email: moonshine@gamesys.co.uk 7 | * http://moonshinejs.org 8 | * 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | * along with this program. If not, see . 21 | */ 22 | 23 | 24 | var AbstractConnection = require('./AbstractConnection'), 25 | MESSAGE_TYPES = require('./constants').MESSAGE_TYPES, 26 | COLORS = require('./constants').COLORS, 27 | os = require('os'); 28 | 29 | 30 | 31 | 32 | AppConnection = function (config) { 33 | config = config || {}; 34 | 35 | this._port = config.port || 1959; 36 | this._reset(); 37 | 38 | AbstractConnection.apply(this, arguments); 39 | }; 40 | 41 | 42 | AppConnection.prototype = new AbstractConnection(); 43 | AppConnection.prototype.constructor = AppConnection; 44 | 45 | 46 | 47 | 48 | AppConnection.prototype._onReady = function () { 49 | var interfaces = os.networkInterfaces(), 50 | device, 51 | ip, 52 | i, j; 53 | 54 | for (i in interfaces) { 55 | device = interfaces[i] 56 | 57 | for (j in device) { 58 | if (device[j].family == 'IPv4') { 59 | ip = device[j].address; 60 | break; 61 | } 62 | } 63 | } 64 | 65 | ip = ip? ' IP ' + COLORS.CYAN + ip + COLORS.RESET : ''; 66 | console.log('Debug server listening for app on:' + ip + ' Port ' + COLORS.CYAN + this._port + COLORS.RESET); 67 | }; 68 | 69 | 70 | 71 | 72 | AppConnection.prototype._onConnection = function () { 73 | var me = this; 74 | 75 | console.log ('App found.'); 76 | 77 | this._send(MESSAGE_TYPES.GET_STATE, undefined, function (state) { 78 | console.log ('App connected.'); 79 | 80 | me.state = state; 81 | me._trigger('connect'); 82 | me._trigger('engine-state-updated', state); 83 | }); 84 | }; 85 | 86 | 87 | 88 | 89 | AppConnection.prototype._onDisconnect = function () { 90 | console.log ('App disconnected.'); 91 | this._reset(); 92 | this._trigger('disconnect'); 93 | }; 94 | 95 | 96 | 97 | 98 | AppConnection.prototype._reset = function () { 99 | this.state = { 100 | loaded: {}, 101 | breakpoints: {}, 102 | stopAtBreakpoints: false, 103 | errorLog: [], 104 | engine: { 105 | state: 'disconnected', 106 | data: {} 107 | } 108 | }; 109 | 110 | this._trigger('engine-state-changed', this.state); 111 | }; 112 | 113 | 114 | 115 | 116 | AppConnection.prototype._processMessage = function (type, data, callback) { 117 | 118 | switch (type) { 119 | 120 | case MESSAGE_TYPES.ENGINE_STATE_CHANGED: 121 | this.state.engine = { 122 | state: data[0], 123 | data: data[1] 124 | }; 125 | 126 | this._trigger('engine-state-changed', data); 127 | break; 128 | 129 | 130 | case MESSAGE_TYPES.LUA_LOADED: 131 | this.state.loaded[data[0]] = { 132 | filename: data[1], 133 | source: data[2] 134 | }; 135 | 136 | this._trigger('lua-loaded', data); 137 | 138 | break; 139 | 140 | 141 | case MESSAGE_TYPES.LUA_LOAD_FAILED: 142 | this.state.loaded[data[0]] = { 143 | filename: data[1], 144 | source: false 145 | }; 146 | 147 | this._trigger('lua-load-failed', data); 148 | 149 | break; 150 | 151 | 152 | case MESSAGE_TYPES.BREAKPOINTS_UPDATED: 153 | this.state.breakpoints = data; 154 | this._trigger('breakpoints-updated', data); 155 | 156 | break; 157 | 158 | 159 | case MESSAGE_TYPES.BREAKPOINT_UPDATED: 160 | var breakpoints = this.state.breakpoints; 161 | if (breakpoints[data[0]] === undefined) breakpoints[data[0]] = []; 162 | 163 | breakpoints[data[0]][data[1]] = data[2]; 164 | this._trigger('breakpoint-updated', data); 165 | 166 | break; 167 | 168 | 169 | case MESSAGE_TYPES.STOP_AT_BREAKPOINTS_UPDATED: 170 | this.state.stopAtBreakpoints = data[0]; 171 | this._trigger('stop-at-breakpoints-updated', data); 172 | 173 | break; 174 | 175 | 176 | case MESSAGE_TYPES.ERROR: 177 | this.state.errorLog.push(data); 178 | this._trigger('error', data); 179 | 180 | break; 181 | 182 | } 183 | }; 184 | 185 | 186 | 187 | 188 | AppConnection.prototype.toggleBreakpoint = function (jsonUrl, line) { 189 | this._send(MESSAGE_TYPES.TOGGLE_BREAKPOINT, [jsonUrl, line]); 190 | }; 191 | 192 | 193 | 194 | 195 | AppConnection.prototype.toggleStopAtBreakpoints = function (stops) { 196 | this._send(MESSAGE_TYPES.TOGGLE_STOPS_AT_BREAKPOINTS, stops); 197 | }; 198 | 199 | 200 | 201 | 202 | AppConnection.prototype.autoStep = function () { 203 | this._send(MESSAGE_TYPES.TOGGLE_AUTO_STEP); 204 | }; 205 | 206 | 207 | 208 | 209 | AppConnection.prototype.stepIn = function () { 210 | this._send(MESSAGE_TYPES.STEP_IN); 211 | }; 212 | 213 | 214 | 215 | 216 | AppConnection.prototype.stepOver = function () { 217 | this._send(MESSAGE_TYPES.STEP_OVER); 218 | }; 219 | 220 | 221 | 222 | 223 | AppConnection.prototype.stepOut = function () { 224 | this._send(MESSAGE_TYPES.STEP_OUT); 225 | }; 226 | 227 | 228 | 229 | 230 | AppConnection.prototype.pause = function () { 231 | this._send(MESSAGE_TYPES.PAUSE); 232 | }; 233 | 234 | 235 | 236 | 237 | AppConnection.prototype.resume = function () { 238 | this._send(MESSAGE_TYPES.RESUME); 239 | }; 240 | 241 | 242 | 243 | 244 | AppConnection.prototype.reload = function () { 245 | this._send(MESSAGE_TYPES.RELOAD); 246 | }; 247 | 248 | 249 | 250 | 251 | module.exports = AppConnection; 252 | 253 | -------------------------------------------------------------------------------- /js/debug/server/ConsoleConnection.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Moonshine - a Lua virtual machine. 3 | * 4 | * Copyright (C) 2013 Gamesys Limited, 5 | * 10 Piccadilly, London W1J 0DD 6 | * Email: moonshine@gamesys.co.uk 7 | * http://moonshinejs.org 8 | * 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | * along with this program. If not, see . 21 | */ 22 | 23 | 24 | var fs = require('fs'), 25 | express = require('express'), 26 | AbstractConnection = require('./AbstractConnection'), 27 | MESSAGE_TYPES = require('./constants').MESSAGE_TYPES, 28 | COLORS = require('./constants').COLORS; 29 | 30 | 31 | 32 | 33 | ConsoleConnection = function (config) { 34 | config = config || {}; 35 | 36 | this._port = config.port || 1969; 37 | AbstractConnection.apply(this, arguments); 38 | }; 39 | 40 | 41 | ConsoleConnection.prototype = new AbstractConnection(); 42 | ConsoleConnection.prototype.constructor = ConsoleConnection; 43 | 44 | 45 | 46 | 47 | ConsoleConnection.prototype._onReady = function () { 48 | 49 | 50 | this._app.get('/', function (req, res) { 51 | console.log ('app get') 52 | fs.readFile(__dirname + '/../ui/index.html', function (err, data) { 53 | if (err) throw err; 54 | 55 | var placeholder = '', 56 | scriptTag = ''; 57 | 58 | data = data.toString().replace(placeholder, scriptTag); 59 | 60 | res.set('Content-Type', 'text/html'); 61 | res.send(data); 62 | }) 63 | '' 64 | }); 65 | 66 | this._app.use(express.static(__dirname + '/../ui')); 67 | 68 | console.log('To view the console, go to: ' + COLORS.CYAN + 'http://127.0.0.1:' + this._port + COLORS.RESET); 69 | }; 70 | 71 | 72 | 73 | 74 | ConsoleConnection.prototype._onConnection = function () { 75 | console.log ('Console connected.'); 76 | this._trigger('connect'); 77 | }; 78 | 79 | 80 | 81 | 82 | ConsoleConnection.prototype._onDisconnect = function () { 83 | console.log ('Console disconnected.'); 84 | }; 85 | 86 | 87 | 88 | 89 | ConsoleConnection.prototype._processMessage = function (type, data, callback) { 90 | 91 | switch (type) { 92 | 93 | case MESSAGE_TYPES.GET_STATE: 94 | this._trigger('get-state-request', callback); 95 | break; 96 | 97 | case MESSAGE_TYPES.TOGGLE_BREAKPOINT: 98 | this._trigger('toggle-breakpoint-request', data); 99 | break; 100 | 101 | case MESSAGE_TYPES.TOGGLE_STOP_AT_BREAKPOINTS: 102 | this._trigger('toggle-stop-at-breakpoints-request'); 103 | break; 104 | 105 | case MESSAGE_TYPES.TOGGLE_AUTO_STEP: 106 | this._trigger('auto-step-request'); 107 | break; 108 | 109 | case MESSAGE_TYPES.STEP_IN: 110 | this._trigger('step-in-request'); 111 | break; 112 | 113 | case MESSAGE_TYPES.STEP_IN: 114 | this._trigger('step-in-request'); 115 | break; 116 | 117 | case MESSAGE_TYPES.STEP_OVER: 118 | this._trigger('step-over-request'); 119 | break; 120 | 121 | case MESSAGE_TYPES.STEP_OUT: 122 | this._trigger('step-out-request'); 123 | break; 124 | 125 | case MESSAGE_TYPES.PAUSE: 126 | this._trigger('pause-request'); 127 | break; 128 | 129 | case MESSAGE_TYPES.RESUME: 130 | this._trigger('resume-request'); 131 | break; 132 | 133 | case MESSAGE_TYPES.RELOAD: 134 | this._trigger('reload-request'); 135 | break; 136 | } 137 | }; 138 | 139 | 140 | 141 | 142 | ConsoleConnection.prototype.updateState = function (state, data) { 143 | this._send(MESSAGE_TYPES.ENGINE_STATE_CHANGED, [state, data]); 144 | }; 145 | 146 | 147 | 148 | 149 | ConsoleConnection.prototype.luaLoaded = function (jsonUrl, url, code) { 150 | this._send(MESSAGE_TYPES.LUA_LOADED, [jsonUrl, url, code]); 151 | }; 152 | 153 | 154 | 155 | 156 | ConsoleConnection.prototype.luaLoadFailed = function (jsonUrl, url) { 157 | this._send(MESSAGE_TYPES.LUA_LOAD_FAILED, [jsonUrl, url]); 158 | }; 159 | 160 | 161 | 162 | 163 | ConsoleConnection.prototype.updateBreakpoints = function (data) { 164 | this._send(MESSAGE_TYPES.BREAKPOINTS_UPDATED, [data]); 165 | }; 166 | 167 | 168 | 169 | 170 | ConsoleConnection.prototype.updateBreakpoint = function (jsonUrl, lineNumber, breakOn) { 171 | this._send(MESSAGE_TYPES.BREAKPOINT_UPDATED, [jsonUrl, lineNumber, breakOn]); 172 | }; 173 | 174 | 175 | 176 | 177 | ConsoleConnection.prototype.updateStopAtBreakpoints = function (stops) { 178 | this._send(MESSAGE_TYPES.STOP_AT_BREAKPOINTS_UPDATED, stops); 179 | }; 180 | 181 | 182 | 183 | 184 | ConsoleConnection.prototype.handleError = function (error) { 185 | this._send(MESSAGE_TYPES.ERROR, error); 186 | }; 187 | 188 | 189 | 190 | 191 | module.exports = ConsoleConnection; 192 | -------------------------------------------------------------------------------- /js/debug/server/constants.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Moonshine - a Lua virtual machine. 3 | * 4 | * Copyright (C) 2013 Gamesys Limited, 5 | * 10 Piccadilly, London W1J 0DD 6 | * Email: moonshine@gamesys.co.uk 7 | * http://moonshinejs.org 8 | * 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | * along with this program. If not, see . 21 | */ 22 | 23 | 24 | 'use strict'; 25 | 26 | 27 | var ESC = String.fromCharCode(27); 28 | 29 | 30 | module.exports = { 31 | 32 | MESSAGE_TYPES: { 33 | ENGINE_STATE_CHANGED: 0, 34 | LUA_LOADED: 1, 35 | LUA_LOAD_FAILED: 2, 36 | BREAKPOINTS_UPDATED: 3, 37 | BREAKPOINT_UPDATED: 4, 38 | STOP_AT_BREAKPOINTS_UPDATED: 5, 39 | ERROR: 6, 40 | 41 | GET_STATE: 100, 42 | TOGGLE_BREAKPOINT: 101, 43 | TOGGLE_STOP_AT_BREAKPOINTS: 102, 44 | STEP_IN: 103, 45 | STEP_OVER: 104, 46 | STEP_OUT: 105, 47 | PAUSE: 106, 48 | RESUME: 107, 49 | RELOAD: 108, 50 | TOGGLE_AUTO_STEP: 109 51 | }, 52 | 53 | 54 | COLORS: { 55 | RED: ESC + '[31m', 56 | GREEN: ESC + '[32m', 57 | CYAN: ESC + '[36m', 58 | WHITE: ESC + '[37m', 59 | RESET: ESC + '[0m' 60 | } 61 | 62 | }; 63 | 64 | -------------------------------------------------------------------------------- /js/debug/ui/img/application-dock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TannerRogalsky/punchdrunk/a2835d4963289734ad402e1fc4ceb284d0e1ee91/js/debug/ui/img/application-dock.png -------------------------------------------------------------------------------- /js/debug/ui/img/applications-blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TannerRogalsky/punchdrunk/a2835d4963289734ad402e1fc4ceb284d0e1ee91/js/debug/ui/img/applications-blue.png -------------------------------------------------------------------------------- /js/debug/ui/img/arrow-circle-315.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TannerRogalsky/punchdrunk/a2835d4963289734ad402e1fc4ceb284d0e1ee91/js/debug/ui/img/arrow-circle-315.png -------------------------------------------------------------------------------- /js/debug/ui/img/arrow-in.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TannerRogalsky/punchdrunk/a2835d4963289734ad402e1fc4ceb284d0e1ee91/js/debug/ui/img/arrow-in.png -------------------------------------------------------------------------------- /js/debug/ui/img/arrow-out.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TannerRogalsky/punchdrunk/a2835d4963289734ad402e1fc4ceb284d0e1ee91/js/debug/ui/img/arrow-out.png -------------------------------------------------------------------------------- /js/debug/ui/img/arrow-skip-270.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TannerRogalsky/punchdrunk/a2835d4963289734ad402e1fc4ceb284d0e1ee91/js/debug/ui/img/arrow-skip-270.png -------------------------------------------------------------------------------- /js/debug/ui/img/arrow-step-out.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TannerRogalsky/punchdrunk/a2835d4963289734ad402e1fc4ceb284d0e1ee91/js/debug/ui/img/arrow-step-out.png -------------------------------------------------------------------------------- /js/debug/ui/img/arrow-step-over.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TannerRogalsky/punchdrunk/a2835d4963289734ad402e1fc4ceb284d0e1ee91/js/debug/ui/img/arrow-step-over.png -------------------------------------------------------------------------------- /js/debug/ui/img/arrow-step.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TannerRogalsky/punchdrunk/a2835d4963289734ad402e1fc4ceb284d0e1ee91/js/debug/ui/img/arrow-step.png -------------------------------------------------------------------------------- /js/debug/ui/img/control-double-270-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TannerRogalsky/punchdrunk/a2835d4963289734ad402e1fc4ceb284d0e1ee91/js/debug/ui/img/control-double-270-small.png -------------------------------------------------------------------------------- /js/debug/ui/img/control-double-270.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TannerRogalsky/punchdrunk/a2835d4963289734ad402e1fc4ceb284d0e1ee91/js/debug/ui/img/control-double-270.png -------------------------------------------------------------------------------- /js/debug/ui/img/control-pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TannerRogalsky/punchdrunk/a2835d4963289734ad402e1fc4ceb284d0e1ee91/js/debug/ui/img/control-pause.png -------------------------------------------------------------------------------- /js/debug/ui/img/control.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TannerRogalsky/punchdrunk/a2835d4963289734ad402e1fc4ceb284d0e1ee91/js/debug/ui/img/control.png -------------------------------------------------------------------------------- /js/debug/ui/img/information-italic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TannerRogalsky/punchdrunk/a2835d4963289734ad402e1fc4ceb284d0e1ee91/js/debug/ui/img/information-italic.png -------------------------------------------------------------------------------- /js/debug/ui/img/status-away.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TannerRogalsky/punchdrunk/a2835d4963289734ad402e1fc4ceb284d0e1ee91/js/debug/ui/img/status-away.png -------------------------------------------------------------------------------- /js/debug/ui/img/status-busy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TannerRogalsky/punchdrunk/a2835d4963289734ad402e1fc4ceb284d0e1ee91/js/debug/ui/img/status-busy.png -------------------------------------------------------------------------------- /js/debug/ui/img/status-offline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TannerRogalsky/punchdrunk/a2835d4963289734ad402e1fc4ceb284d0e1ee91/js/debug/ui/img/status-offline.png -------------------------------------------------------------------------------- /js/debug/ui/img/status.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TannerRogalsky/punchdrunk/a2835d4963289734ad402e1fc4ceb284d0e1ee91/js/debug/ui/img/status.png -------------------------------------------------------------------------------- /js/debug/ui/img/toggle-expand.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TannerRogalsky/punchdrunk/a2835d4963289734ad402e1fc4ceb284d0e1ee91/js/debug/ui/img/toggle-expand.png -------------------------------------------------------------------------------- /js/debug/ui/img/toggle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TannerRogalsky/punchdrunk/a2835d4963289734ad402e1fc4ceb284d0e1ee91/js/debug/ui/img/toggle.png -------------------------------------------------------------------------------- /js/debug/ui/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Moonshine Debugger 6 | 7 | 8 | 9 | 10 |
11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 22 |
23 | 24 |

25 |
26 | 27 |
28 |
31 |
32 |
Globals
33 |
34 |
35 | 36 |
37 |
Upvalues
38 |
39 |
40 | 41 |
42 |
Locals
43 |
44 |
45 | 46 |
47 |
Call stack
48 |
49 |
50 | 51 |
52 |
Error log
53 |
54 |
55 |
56 | 57 |

58 |
59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /js/debug/ui/js/lib/ace/ext-elastic_tabstops_lite.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/ext/elastic_tabstops_lite",["require","exports","module","ace/editor","ace/config"],function(e,t,n){var r=function(e){this.$editor=e;var t=this,n=[],r=!1;this.onAfterExec=function(){r=!1,t.processRows(n),n=[]},this.onExec=function(){r=!0},this.onChange=function(e){var t=e.data.range;r&&(n.indexOf(t.start.row)==-1&&n.push(t.start.row),t.end.row!=t.start.row&&n.push(t.end.row))}};(function(){this.processRows=function(e){this.$inChange=!0;var t=[];for(var n=0,r=e.length;n-1)continue;var s=this.$findCellWidthsForBlock(i),o=this.$setBlockCellWidthsToMax(s.cellWidths),u=s.firstRow;for(var a=0,f=o.length;a=0){n=this.$cellWidthsForRow(r);if(n.length==0)break;t.unshift(n),r--}var i=r+1;r=e;var s=this.$editor.session.getLength();while(r0&&(this.$editor.session.getDocument().insertInLine({row:e,column:f+1},Array(l+1).join(" ")+" "),this.$editor.session.getDocument().removeInLine(e,f,f+1),r+=l),l<0&&p>=-l&&(this.$editor.session.getDocument().removeInLine(e,f+l,f),r+=l)}},this.$izip_longest=function(e){if(!e[0])return[];var t=e[0].length,n=e.length;for(var r=1;rt&&(t=i)}var s=[];for(var o=0;o=t.length?t.length:e.length,r=[];for(var i=0;i'+t.command+" : "+''+t.key+""},"");s.id="kbshortcutmenu",s.innerHTML="

Keyboard Shortcuts

"+o+"",n(t,s,"0","0","0",null)}}var r=e("ace/editor").Editor;n.exports.init=function(e){r.prototype.showKeyboardShortcuts=function(){i(this)},e.commands.addCommands([{name:"showKeyboardShortcuts",bindKey:{win:"Ctrl-Alt-h",mac:"Command-Alt-h"},exec:function(e,t){e.showKeyboardShortcuts()}}])}}),ace.define("ace/ext/menu_tools/overlay_page",["require","exports","module","ace/lib/dom"],function(e,t,n){var r=e("../../lib/dom"),i="#ace_settingsmenu, #kbshortcutmenu {background-color: #F7F7F7;color: black;box-shadow: -5px 4px 5px rgba(126, 126, 126, 0.55);padding: 1em 0.5em 2em 1em;overflow: auto;position: absolute;margin: 0;bottom: 0;right: 0;top: 0;z-index: 9991;cursor: default;}.ace_dark #ace_settingsmenu, .ace_dark #kbshortcutmenu {box-shadow: -20px 10px 25px rgba(126, 126, 126, 0.25);background-color: rgba(255, 255, 255, 0.6);color: black;}.ace_optionsMenuEntry:hover {background-color: rgba(100, 100, 100, 0.1);-webkit-transition: all 0.5s;transition: all 0.3s}.ace_closeButton {background: rgba(245, 146, 146, 0.5);border: 1px solid #F48A8A;border-radius: 50%;padding: 7px;position: absolute;right: -8px;top: -8px;z-index: 1000;}.ace_closeButton{background: rgba(245, 146, 146, 0.9);}.ace_optionsMenuKey {color: darkslateblue;font-weight: bold;}.ace_optionsMenuCommand {color: darkcyan;font-weight: normal;}";r.importCssString(i),n.exports.overlayPage=function(t,n,i,s,o,u){function l(e){e.keyCode===27&&a.click()}i=i?"top: "+i+";":"",o=o?"bottom: "+o+";":"",s=s?"right: "+s+";":"",u=u?"left: "+u+";":"";var a=document.createElement("div"),f=document.createElement("div");a.style.cssText="margin: 0; padding: 0; position: fixed; top:0; bottom:0; left:0; right:0;z-index: 9990; background-color: rgba(0, 0, 0, 0.3);",a.addEventListener("click",function(){document.removeEventListener("keydown",l),a.parentNode.removeChild(a),t.focus(),a=null}),document.addEventListener("keydown",l),f.style.cssText=i+s+o+u,f.addEventListener("click",function(e){e.stopPropagation()});var c=r.createElement("div");c.style.position="relative";var h=r.createElement("div");h.className="ace_closeButton",h.addEventListener("click",function(){a.click()}),c.appendChild(h),f.appendChild(c),f.appendChild(n),a.appendChild(f),document.body.appendChild(a),t.blur()}}),ace.define("ace/ext/menu_tools/get_editor_keyboard_shortcuts",["require","exports","module","ace/lib/keys"],function(e,t,n){var r=e("../../lib/keys");n.exports.getEditorKeybordShortcuts=function(e){var t=r.KEY_MODS,n=[],i={};return e.keyBinding.$handlers.forEach(function(e){var r=e.commmandKeyBinding;for(var s in r){var o=parseInt(s);o==-1?o="":isNaN(o)?o=s:o=""+(o&t.command?"Cmd-":"")+(o&t.ctrl?"Ctrl-":"")+(o&t.alt?"Alt-":"")+(o&t.shift?"Shift-":"");for(var u in r[s]){var a=r[s][u];typeof a!="string"&&(a=a.name),i[a]?i[a].key+="|"+o+u:(i[a]={key:o+u,command:a},n.push(i[a]))}}}),n}}) -------------------------------------------------------------------------------- /js/debug/ui/js/lib/ace/ext-modelist.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/ext/modelist",["require","exports","module"],function(e,t,n){function i(e){var t=a.text,n=e.split(/[\/\\]/).pop();for(var i=0;i"),e.forEach(function(e){}),t.push("","",'',"",'',"",""),t.push(""),n};o(i)}) -------------------------------------------------------------------------------- /js/debug/ui/js/lib/ace/ext-spellcheck.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/ext/spellcheck",["require","exports","module","ace/lib/event","ace/editor","ace/config"],function(e,t,n){var r=e("../lib/event");t.contextMenuHandler=function(e){var t=e.target,n=t.textInput.getElement();if(!t.selection.isEmpty())return;var i=t.getCursorPosition(),s=t.session.getWordRange(i.row,i.column),o=t.session.getTextRange(s);t.session.tokenRe.lastIndex=0;if(!t.session.tokenRe.test(o))return;var u="",a=o+" "+u;n.value=a,n.setSelectionRange(o.length+1,o.length+1),n.setSelectionRange(0,0);var f=!1;r.addListener(n,"keydown",function l(){r.removeListener(n,"keydown",l),f=!0}),t.textInput.setInputHandler(function(e){console.log(e,a,n.selectionStart,n.selectionEnd);if(e==a)return"";if(e.lastIndexOf(a,0)===0)return e.slice(a.length);if(e.substr(n.selectionEnd)==a)return e.slice(0,-a.length);if(e.slice(-2)==u){var r=e.slice(0,-2);if(r.slice(-1)==" ")return f?r.substring(0,n.selectionEnd):(r=r.slice(0,-1),t.session.replace(s,r),"")}return e})};var i=e("../editor").Editor;e("../config").defineOptions(i.prototype,"editor",{spellcheck:{set:function(e){var n=this.textInput.getElement();n.spellcheck=!!e,e?this.on("nativecontextmenu",t.contextMenuHandler):this.removeListener("nativecontextmenu",t.contextMenuHandler)},value:!0}})}) -------------------------------------------------------------------------------- /js/debug/ui/js/lib/ace/ext-split.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/ext/split",["require","exports","module","ace/split"],function(e,t,n){n.exports=e("../split")}),ace.define("ace/split",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/lib/event_emitter","ace/editor","ace/virtual_renderer","ace/edit_session"],function(e,t,n){function l(e,t){this.$u=e,this.$doc=t}var r=e("./lib/oop"),i=e("./lib/lang"),s=e("./lib/event_emitter").EventEmitter,o=e("./editor").Editor,u=e("./virtual_renderer").VirtualRenderer,a=e("./edit_session").EditSession,f=function(e,t,n){this.BELOW=1,this.BESIDE=0,this.$container=e,this.$theme=t,this.$splits=0,this.$editorCSS="",this.$editors=[],this.$orientation=this.BESIDE,this.setSplits(n||1),this.$cEditor=this.$editors[0],this.on("focus",function(e){this.$cEditor=e}.bind(this))};(function(){r.implement(this,s),this.$createEditor=function(){var e=document.createElement("div");e.className=this.$editorCSS,e.style.cssText="position: absolute; top:0px; bottom:0px",this.$container.appendChild(e);var t=new o(new u(e,this.$theme));return t.on("focus",function(){this._emit("focus",t)}.bind(this)),this.$editors.push(t),t.setFontSize(this.$fontSize),t},this.setSplits=function(e){var t;if(e<1)throw"The number of splits have to be > 0!";if(e==this.$splits)return;if(e>this.$splits){while(this.$splitse)t=this.$editors[this.$splits-1],this.$container.removeChild(t.container),this.$splits--;this.resize()},this.getSplits=function(){return this.$splits},this.getEditor=function(e){return this.$editors[e]},this.getCurrentEditor=function(){return this.$cEditor},this.focus=function(){this.$cEditor.focus()},this.blur=function(){this.$cEditor.blur()},this.setTheme=function(e){this.$editors.forEach(function(t){t.setTheme(e)})},this.setKeyboardHandler=function(e){this.$editors.forEach(function(t){t.setKeyboardHandler(e)})},this.forEach=function(e,t){this.$editors.forEach(e,t)},this.$fontSize="",this.setFontSize=function(e){this.$fontSize=e,this.forEach(function(t){t.setFontSize(e)})},this.$cloneSession=function(e){var t=new a(e.getDocument(),e.getMode()),n=e.getUndoManager();if(n){var r=new l(n,t);t.setUndoManager(r)}return t.$informUndoManager=i.delayedCall(function(){t.$deltas=[]}),t.setTabSize(e.getTabSize()),t.setUseSoftTabs(e.getUseSoftTabs()),t.setOverwrite(e.getOverwrite()),t.setBreakpoints(e.getBreakpoints()),t.setUseWrapMode(e.getUseWrapMode()),t.setUseWorker(e.getUseWorker()),t.setWrapLimitRange(e.$wrapLimitRange.min,e.$wrapLimitRange.max),t.$foldData=e.$cloneFoldData(),t},this.setSession=function(e,t){var n;t==null?n=this.$cEditor:n=this.$editors[t];var r=this.$editors.some(function(t){return t.session===e});return r&&(e=this.$cloneSession(e)),n.setSession(e),e},this.getOrientation=function(){return this.$orientation},this.setOrientation=function(e){if(this.$orientation==e)return;this.$orientation=e,this.resize()},this.resize=function(){var e=this.$container.clientWidth,t=this.$container.clientHeight,n;if(this.$orientation==this.BESIDE){var r=e/this.$splits;for(var i=0;i"),u||l.push(""+(h+o)+""),f.$renderLine(l,h,!0,!1),l.push("");var p="
"+"
"+l.join("")+"
"+"
";return f.destroy(),{css:s+n.cssText,html:p}}}) -------------------------------------------------------------------------------- /js/debug/ui/js/lib/ace/ext-statusbar.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/ext/statusbar",["require","exports","module","ace/lib/dom","ace/lib/lang"],function(e,t,n){var r=e("ace/lib/dom"),i=e("ace/lib/lang"),s=function(e,t){this.element=r.createElement("div"),this.element.className="ace_status-indicator",this.element.style.cssText="display: inline-block;",t.appendChild(this.element);var n=i.delayedCall(function(){this.updateStatus(e)}.bind(this));e.on("changeStatus",function(){n.schedule(100)}),e.on("changeSelection",function(){n.schedule(100)})};(function(){this.updateStatus=function(e){function n(e,n){e&&t.push(e,n||"|")}var t=[];e.$vimModeHandler?n(e.$vimModeHandler.getStatusText()):e.commands.recording&&n("REC");var r=e.selection.lead;n(r.row+":"+r.column," ");if(!e.selection.isEmpty()){var i=e.getSelectionRange();n("("+(i.end.row-i.start.row)+":"+(i.end.column-i.start.column)+")")}t.pop(),this.element.textContent=t.join("")}}).call(s.prototype),t.StatusBar=s}) -------------------------------------------------------------------------------- /js/debug/ui/js/lib/ace/ext-textarea.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/ext/textarea",["require","exports","module","ace/lib/event","ace/lib/useragent","ace/lib/net","ace/ace","ace/theme/textmate","ace/mode/text"],function(e,t,n){function a(e,t){for(var n in t)e.style[n]=t[n]}function f(e,t){if(e.type!="textarea")throw"Textarea required!";var n=e.parentNode,i=document.createElement("div"),s=function(){var t="position:relative;";["margin-top","margin-left","margin-right","margin-bottom"].forEach(function(n){t+=n+":"+u(e,i,n)+";"});var n=u(e,i,"width")||e.clientWidth+"px",r=u(e,i,"height")||e.clientHeight+"px";t+="height:"+r+";width:"+n+";",t+="display:inline-block;",i.setAttribute("style",t)};r.addListener(window,"resize",s),s(),n.insertBefore(i,e.nextSibling);while(n!==document){if(n.tagName.toUpperCase()==="FORM"){var o=n.onsubmit;n.onsubmit=function(n){e.value=t(),o&&o.call(this,n)};break}n=n.parentNode}return i}function l(t,n,r){s.loadScript(t,function(){e([n],r)})}function c(n,r,i,s,o,u){function c(e){return e==="true"||e==1}var a=n.getSession(),f=n.renderer;return u=u||l,n.setDisplaySettings=function(e){e==null&&(e=i.style.display=="none"),e?(i.style.display="block",i.hideButton.focus(),n.on("focus",function t(){n.removeListener("focus",t),i.style.display="none"})):n.focus()},n.$setOption=n.setOption,n.setOption=function(t,i){if(o[t]==i)return;switch(t){case"mode":i!="text"?u("mode-"+i+".js","ace/mode/"+i,function(){var t=e("../mode/"+i).Mode;a.setMode(new t)}):a.setMode(new(e("../mode/text").Mode));break;case"theme":i!="textmate"?u("theme-"+i+".js","ace/theme/"+i,function(){n.setTheme("ace/theme/"+i)}):n.setTheme("ace/theme/textmate");break;case"fontSize":r.style.fontSize=i;break;case"keybindings":switch(i){case"vim":n.setKeyboardHandler("ace/keyboard/vim");break;case"emacs":n.setKeyboardHandler("ace/keyboard/emacs");break;default:n.setKeyboardHandler(null)}break;case"softWrap":switch(i){case"off":a.setUseWrapMode(!1),f.setPrintMarginColumn(80);break;case"40":a.setUseWrapMode(!0),a.setWrapLimitRange(40,40),f.setPrintMarginColumn(40);break;case"80":a.setUseWrapMode(!0),a.setWrapLimitRange(80,80),f.setPrintMarginColumn(80);break;case"free":a.setUseWrapMode(!0),a.setWrapLimitRange(null,null),f.setPrintMarginColumn(80)}break;default:n.$setOption(t,c(i))}o[t]=i},n.getOption=function(e){return o[e]},n.getOptions=function(){return o},n.setOptions(t.options),n}function h(e,t,n,i){function f(e,t,n,r){if(!n){e.push("");return}e.push("")}var s=null,o={mode:"Mode:",gutter:"Display Gutter:",theme:"Theme:",fontSize:"Font Size:",softWrap:"Soft Wrap:",keybindings:"Keyboard",showPrintMargin:"Show Print Margin:",useSoftTabs:"Use Soft Tabs:",showInvisibles:"Show Invisibles"},u={mode:{text:"Plain",javascript:"JavaScript",xml:"XML",html:"HTML",css:"CSS",scss:"SCSS",python:"Python",php:"PHP",java:"Java",ruby:"Ruby",c_cpp:"C/C++",coffee:"CoffeeScript",json:"json",perl:"Perl",clojure:"Clojure",ocaml:"OCaml",csharp:"C#",haxe:"haXe",svg:"SVG",textile:"Textile",groovy:"Groovy",liquid:"Liquid",Scala:"Scala"},theme:{clouds:"Clouds",clouds_midnight:"Clouds Midnight",cobalt:"Cobalt",crimson_editor:"Crimson Editor",dawn:"Dawn",eclipse:"Eclipse",idle_fingers:"Idle Fingers",kr_theme:"Kr Theme",merbivore:"Merbivore",merbivore_soft:"Merbivore Soft",mono_industrial:"Mono Industrial",monokai:"Monokai",pastel_on_dark:"Pastel On Dark",solarized_dark:"Solarized Dark",solarized_light:"Solarized Light",textmate:"Textmate",twilight:"Twilight",vibrant_ink:"Vibrant Ink"},gutter:s,fontSize:{"10px":"10px","11px":"11px","12px":"12px","14px":"14px","16px":"16px"},softWrap:{off:"Off",40:"40",80:"80",free:"Free"},keybindings:{ace:"ace",vim:"vim",emacs:"emacs"},showPrintMargin:s,useSoftTabs:s,showInvisibles:s},a=[];a.push("");for(var l in i)a.push(""),a.push("");a.push("
SettingValue
",o[l],""),f(a,l,u[l],i[l]),a.push("
"),e.innerHTML=a.join("");var c=function(e){var t=e.currentTarget;n.setOption(t.title,t.value)},h=function(e){var t=e.currentTarget;n.setOption(t.title,t.checked)},p=e.getElementsByTagName("select");for(var d=0;d0&&!(s%c)&&!(l%c)&&(r[c]=(r[c]||0)+1),n[l]=(n[l]||0)+1}s=l;while(a[a.length-1]=="\\")a=e[u++]}var p=r.reduce(function(e,t){return e+t},0),d={score:0,length:0},v=0;for(var u=1;u<12;u++){if(u==1){v=h(u);var m=1}else var m=h(u)/v;r[u]&&(m+=r[u]/p),m>d.score&&(d={score:m,length:u})}if(d.score&&d.score>1.4)var g=d.length;if(i>v+1)return{ch:" ",length:g};if(v+1>i)return{ch:" ",length:g}},t.detectIndentation=function(e){var n=e.getLines(0,1e3),r=t.$detectIndentation(n)||{};return r.ch&&e.setUseSoftTabs(r.ch==" "),r.length&&e.setTabSize(r.length),r},t.trimTrailingSpace=function(e){var t=e.getDocument(),n=t.getAllLines();for(var r=0,i=n.length;r span {font-weight: normal !important;}.ace-github .ace_marker-layer .ace_step {background: rgb(252, 255, 0);}.ace-github .ace_marker-layer .ace_stack {background: rgb(164, 229, 101);}.ace-github .ace_marker-layer .ace_bracket {margin: -1px 0 0 -1px;border: 1px solid rgb(192, 192, 192);}.ace-github .ace_gutter-active-line {background-color : rgba(0, 0, 0, 0.07);}.ace-github .ace_marker-layer .ace_selected-word {background: rgb(250, 250, 255);border: 1px solid rgb(200, 200, 250);}.ace-github .ace_print-margin {width: 1px;background: #e8e8e8;}.ace-github .ace_indent-guide {background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==") right repeat-y;}';var r=e("../lib/dom");r.importCssString(t.cssText,t.cssClass)}) -------------------------------------------------------------------------------- /js/debug/ui/js/lib/ace/theme-xcode.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/theme/xcode",["require","exports","module","ace/lib/dom"],function(e,t,n){t.isDark=!1,t.cssClass="ace-xcode",t.cssText="/* THIS THEME WAS AUTOGENERATED BY Theme.tmpl.css (UUID: EE3AD170-2B7F-4DE1-B724-C75F13FE0085) */.ace-xcode .ace_gutter {background: #e8e8e8;color: #333}.ace-xcode .ace_print-margin {width: 1px;background: #e8e8e8}.ace-xcode {background-color: #FFFFFF;color: #000000}.ace-xcode .ace_cursor {border-left: 2px solid #000000}.ace-xcode .ace_overwrite-cursors .ace_cursor {border-left: 0px;border-bottom: 1px solid #000000}.ace-xcode .ace_marker-layer .ace_selection {background: #B5D5FF}.ace-xcode.ace_multiselect .ace_selection.ace_start {box-shadow: 0 0 3px 0px #FFFFFF;border-radius: 2px}.ace-xcode .ace_marker-layer .ace_step {background: rgb(198, 219, 174)}.ace-xcode .ace_marker-layer .ace_bracket {margin: -1px 0 0 -1px;border: 1px solid #BFBFBF}.ace-xcode .ace_marker-layer .ace_active-line {background: rgba(0, 0, 0, 0.071)}.ace-xcode .ace_gutter-active-line {background-color: rgba(0, 0, 0, 0.071)}.ace-xcode .ace_marker-layer .ace_selected-word {border: 1px solid #B5D5FF}.ace-xcode .ace_constant.ace_language,.ace-xcode .ace_keyword,.ace-xcode .ace_meta,.ace-xcode .ace_variable.ace_language {color: #C800A4}.ace-xcode .ace_invisible {color: #BFBFBF}.ace-xcode .ace_constant.ace_character,.ace-xcode .ace_constant.ace_other {color: #275A5E}.ace-xcode .ace_constant.ace_numeric {color: #3A00DC}.ace-xcode .ace_entity.ace_other.ace_attribute-name,.ace-xcode .ace_support.ace_constant,.ace-xcode .ace_support.ace_function {color: #450084}.ace-xcode .ace_fold {background-color: #C800A4;border-color: #000000}.ace-xcode .ace_entity.ace_name.ace_tag,.ace-xcode .ace_support.ace_class,.ace-xcode .ace_support.ace_type {color: #790EAD}.ace-xcode .ace_storage {color: #C900A4}.ace-xcode .ace_string {color: #DF0002}.ace-xcode .ace_comment {color: #008E00}.ace-xcode .ace_indent-guide {background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==) right repeat-y}";var r=e("../lib/dom");r.importCssString(t.cssText,t.cssClass)}) -------------------------------------------------------------------------------- /js/debug/ui/js/remote-debugger.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Moonshine - a Lua virtual machine. 3 | * 4 | * Copyright (C) 2013 Gamesys Limited, 5 | * 10 Piccadilly, London W1J 0DD 6 | * Email: moonshine@gamesys.co.uk 7 | * http://moonshinejs.org 8 | * 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | * along with this program. If not, see . 21 | */ 22 | 23 | 24 | (function () { 25 | 26 | 27 | var api = {}, 28 | socket, 29 | listeners = {}, 30 | statusElement = document.querySelector('#remote-status'), 31 | 32 | 33 | messageTypes = { 34 | ENGINE_STATE_CHANGED: 0, 35 | LUA_LOADED: 1, 36 | LUA_LOAD_FAILED: 2, 37 | BREAKPOINTS_UPDATED: 3, 38 | BREAKPOINT_UPDATED: 4, 39 | STOP_AT_BREAKPOINTS_UPDATED: 5, 40 | ERROR: 6, 41 | 42 | GET_STATE: 100, 43 | TOGGLE_BREAKPOINT: 101, 44 | TOGGLE_STOP_AT_BREAKPOINTS: 102, 45 | STEP_IN: 103, 46 | STEP_OVER: 104, 47 | STEP_OUT: 105, 48 | PAUSE: 106, 49 | RESUME: 107, 50 | RELOAD: 108, 51 | TOGGLE_AUTO_STEP: 109 52 | }; 53 | 54 | 55 | 56 | api.toggleBreakpoint = function (jsonUrl, lineNumber) { 57 | send(messageTypes.TOGGLE_BREAKPOINT, [jsonUrl, lineNumber]); 58 | }; 59 | 60 | 61 | 62 | 63 | api.toggleStopAtBreakpoints = function () { 64 | send(messageTypes.TOGGLE_STOP_AT_BREAKPOINTS); 65 | }; 66 | 67 | 68 | 69 | 70 | api.autoStep = function () { 71 | send(messageTypes.TOGGLE_AUTO_STEP); 72 | }; 73 | 74 | 75 | 76 | 77 | api.stepIn = function () { 78 | send(messageTypes.STEP_IN); 79 | }; 80 | 81 | 82 | 83 | 84 | api.stepOver = function () { 85 | send(messageTypes.STEP_OVER); 86 | }; 87 | 88 | 89 | 90 | 91 | api.stepOut = function () { 92 | send(messageTypes.STEP_OUT); 93 | }; 94 | 95 | 96 | 97 | 98 | api.resume = function () { 99 | send(messageTypes.RESUME); 100 | }; 101 | 102 | 103 | 104 | 105 | api.pause = function () { 106 | send(messageTypes.PAUSE); 107 | }; 108 | 109 | 110 | 111 | 112 | api.reload = function () { 113 | send(messageTypes.RELOAD); 114 | }; 115 | 116 | 117 | 118 | 119 | api.getCurrentState = function (callback) { 120 | send(messageTypes.GET_STATE, undefined, function (state) { 121 | callback(state); 122 | api._trigger('state-updated', [state.engine.state, state.engine.data]); 123 | }); 124 | }; 125 | 126 | 127 | 128 | 129 | api.on = function (event, listener) { 130 | listeners[event] = listener; 131 | }; 132 | 133 | 134 | 135 | 136 | api._trigger = function (event, data) { 137 | if (listeners[event]) listeners[event].apply(this, data); 138 | }; 139 | 140 | 141 | 142 | 143 | function init () { 144 | document.body.className += 'remote'; 145 | document.querySelector('button.reload').addEventListener('click', api.reload); 146 | 147 | var script = document.createElement('script'); 148 | script.src = '/socket.io/socket.io.js'; 149 | document.head.appendChild(script); 150 | 151 | script.addEventListener('load', function () { 152 | socket = io.connect(); 153 | 154 | socket.on('connect', function () { 155 | updateRemoteStatus('waiting', 'Debug server connected. Waiting for remote app...'); 156 | }); 157 | 158 | 159 | socket.on('disconnect', function () { 160 | updateRemoteStatus('disconnected', 'Debug server disconnected.'); 161 | 162 | var state = { 163 | loaded: {}, 164 | breakpoints: {}, 165 | stopAtBreakpoints: false, 166 | errorLog: [] 167 | }; 168 | 169 | api._trigger('reset', [api, state]); 170 | }); 171 | 172 | 173 | socket.on('status', function (status) { 174 | if (status.appConnected) { 175 | updateRemoteStatus('connected', 'Connected to remote app.'); 176 | } else { 177 | updateRemoteStatus('waiting', 'Lost connection to remote app.'); 178 | } 179 | 180 | api._trigger('reset', [api]); 181 | }); 182 | 183 | 184 | socket.on('error', function (data) { 185 | console.error('Error from Moonshine debug server:', data.message); 186 | }); 187 | 188 | 189 | socket.on(messageTypes.ENGINE_STATE_CHANGED, function (data) { 190 | api._trigger('state-updated', data); 191 | }); 192 | 193 | 194 | socket.on(messageTypes.LUA_LOADED, function (data) { 195 | api._trigger('lua-loaded', data); 196 | }); 197 | 198 | 199 | socket.on(messageTypes.LUA_LOAD_FAILED, function (data) { 200 | api._trigger('lua-load-failed', data); 201 | }); 202 | 203 | 204 | socket.on(messageTypes.BREAKPOINT_UPDATED, function (data) { 205 | api._trigger('breakpoint-updated', data); 206 | }); 207 | 208 | 209 | socket.on(messageTypes.STOP_AT_BREAKPOINTS_UPDATED, function (stops) { 210 | api._trigger('stop-at-breakpoints-updated', [stops]); 211 | }); 212 | 213 | 214 | socket.on(messageTypes.ERROR, function (error) { 215 | api._trigger('error', [error]); 216 | }); 217 | 218 | 219 | registerDebugEngine(api); 220 | }); 221 | 222 | } 223 | 224 | 225 | 226 | 227 | function send (event, data, callback) { 228 | if (socket) socket.emit('message', [event, data], callback); 229 | } 230 | 231 | 232 | 233 | 234 | function updateRemoteStatus (status, caption) { 235 | statusElement.className = status; 236 | statusElement.textContent = caption; 237 | } 238 | 239 | 240 | 241 | 242 | init(); 243 | 244 | 245 | })(); -------------------------------------------------------------------------------- /lua/baby.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TannerRogalsky/punchdrunk/a2835d4963289734ad402e1fc4ceb284d0e1ee91/lua/baby.png -------------------------------------------------------------------------------- /lua/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TannerRogalsky/punchdrunk/a2835d4963289734ad402e1fc4ceb284d0e1ee91/lua/background.png -------------------------------------------------------------------------------- /lua/bubble.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TannerRogalsky/punchdrunk/a2835d4963289734ad402e1fc4ceb284d0e1ee91/lua/bubble.png -------------------------------------------------------------------------------- /lua/conf.lua: -------------------------------------------------------------------------------- 1 | function love.conf(t) 2 | t.title = "Hello, Punchdrunk" 3 | t.author = "Tanner Rogalsky" 4 | t.identity = "punchdrunk" 5 | t.window.width = 900 6 | t.window.height = 300 7 | t.modules.joystick = true 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 = true 15 | t.modules.sound = true 16 | t.modules.physics = true 17 | end 18 | -------------------------------------------------------------------------------- /lua/inspector.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TannerRogalsky/punchdrunk/a2835d4963289734ad402e1fc4ceb284d0e1ee91/lua/inspector.png -------------------------------------------------------------------------------- /lua/main.lua: -------------------------------------------------------------------------------- 1 | local inspector = {} 2 | local background = {} 3 | local bubble = {} 4 | local text = {} 5 | local rain = {} 6 | local g_time = 0 7 | 8 | function love.load() 9 | love.graphics.setBackgroundColor(11, 88, 123) 10 | 11 | local win_w = love.graphics.getWidth() 12 | local win_h = love.graphics.getHeight() 13 | 14 | inspector.image = love.graphics.newImage("inspector.png") 15 | inspector.img_w = inspector.image:getWidth() 16 | inspector.img_h = inspector.image:getHeight() 17 | inspector.x = win_w * 0.45 18 | inspector.y = win_h * 0.55 19 | 20 | background.image = love.graphics.newImage("background.png") 21 | background.img_w = background.image:getWidth() 22 | background.img_h = background.image:getHeight() 23 | background.x = 0 24 | background.y = 0 25 | 26 | bubble.image = love.graphics.newImage("bubble.png") 27 | bubble.img_w = bubble.image:getWidth() 28 | bubble.img_h = bubble.image:getHeight() 29 | bubble.x = 140 30 | bubble.y = -80 31 | 32 | text.image = love.graphics.newImage("text.png") 33 | text.x = 25 34 | text.y = 9 35 | 36 | -- Baby Rain 37 | rain.spacing_x = 110 38 | rain.spacing_y = 80 39 | rain.image = love.graphics.newImage("baby.png") 40 | rain.img_w = rain.image:getWidth() 41 | rain.img_h = rain.image:getHeight() 42 | rain.ox = -rain.img_w / 2 43 | rain.oy = -rain.img_h / 2 44 | rain.t = 0 45 | 46 | create_rain() 47 | end 48 | 49 | function create_rain() 50 | local sx = rain.spacing_x 51 | local sy = rain.spacing_y 52 | local ox = rain.ox 53 | local oy = rain.oy 54 | 55 | local m = 1 56 | local batch_w = 2 * math.ceil(m * love.graphics.getWidth() / sx) + 2 57 | local batch_h = 2 * math.ceil(m * love.graphics.getHeight() / sy) + 2 58 | 59 | rain.canvas = love.graphics.newCanvas(love.graphics.getWidth(), love.graphics.getHeight() + sy) 60 | rain.small_canvas = love.graphics.newCanvas(love.graphics.getWidth(), love.graphics.getHeight() + sy) 61 | 62 | love.graphics.setCanvas(rain.canvas) 63 | for i = 0, batch_h - 1 do 64 | for j = 0, batch_w - 1 do 65 | local is_even = (j % 2) == 0 66 | local offset_y = is_even and 0 or sy / 2 67 | local x = ox + j * sx 68 | local y = oy + i * sy + offset_y 69 | love.graphics.draw(rain.image, x, y) 70 | end 71 | end 72 | 73 | love.graphics.setCanvas(rain.small_canvas) 74 | ox = ox / 2 75 | for i = 0, (batch_h - 1) * 4 do 76 | for j = 0, (batch_w - 1) * 4 do 77 | local is_even = (j % 2) == 0 78 | local offset_y = is_even and 0 or sy / 4 79 | local x = ox + j * sx / 2 80 | local y = oy + i * sy / 2 + offset_y 81 | love.graphics.draw(rain.image, x, y, 0, 0.5, 0.5) 82 | end 83 | end 84 | 85 | love.graphics.setCanvas() 86 | end 87 | 88 | local function update_rain(t) 89 | rain.t = t 90 | end 91 | 92 | function love.update(dt) 93 | g_time = g_time + dt / 2 94 | local int, frac = math.modf(g_time) 95 | update_rain(frac) 96 | local scale = 1 97 | inspector.x = love.graphics.getWidth() * 0.45 / scale 98 | inspector.y = love.graphics.getHeight() * 0.55 / scale 99 | end 100 | 101 | local function draw_grid() 102 | local y = rain.spacing_y * rain.t 103 | 104 | local small_y = -rain.spacing_y + y / 2 105 | local big_y = -rain.spacing_y + y 106 | 107 | love.graphics.setBlendMode("additive") 108 | love.graphics.setColor(255, 255, 255, 128) 109 | love.graphics.draw(rain.small_canvas, 0, small_y) 110 | 111 | love.graphics.setBlendMode("alpha") 112 | love.graphics.setColor(255, 255, 255, 255) 113 | love.graphics.draw(rain.canvas, 0, big_y) 114 | end 115 | 116 | local function draw_text(x, y) 117 | local int, frac = math.modf(g_time) 118 | if frac < 0.5 then 119 | return 120 | end 121 | local tx = x + text.x 122 | local ty = y + text.y 123 | love.graphics.draw(text.image, tx, ty, 0, 1, 1, 70, 70) 124 | end 125 | 126 | local function draw_background(x, y) 127 | local intensity = (math.sin(math.pi * g_time * 2) + 1) / 2 128 | 129 | local bx = x 130 | local by = y 131 | 132 | love.graphics.setColor(255, 255, 255, 64 + 16*intensity) 133 | love.graphics.draw(background.image, bx, by, 0, 0.7, 0.7, 256, 256) 134 | love.graphics.setColor(255, 255, 255, 32 + 16*intensity) 135 | love.graphics.draw(background.image, bx, by, 0, 0.65, 0.65, 256, 256) 136 | love.graphics.setBlendMode("additive") 137 | love.graphics.setColor(255, 255, 255, 16 + 16*intensity) 138 | love.graphics.draw(background.image, bx, by, 0, 0.6, 0.6, 256, 256) 139 | end 140 | 141 | local function draw_bubble(x, y) 142 | local osc = 10 * math.sin(math.pi * g_time) 143 | local bx = x + bubble.x 144 | local by = y + bubble.y + osc 145 | love.graphics.draw(bubble.image, bx, by, 0, 1, 1, 70, 70) 146 | 147 | draw_text(bx, by) 148 | end 149 | 150 | local function draw_inspector() 151 | local x, y = inspector.x, inspector.y 152 | local ox, oy = inspector.img_w / 2, inspector.img_h / 2 153 | draw_background(x, y) 154 | love.graphics.setColor(255, 255, 255, 255) 155 | love.graphics.setBlendMode("alpha") 156 | love.graphics.draw(inspector.image, x, y, 0, 1, 1, ox, oy) 157 | draw_bubble(x, y) 158 | end 159 | 160 | function love.draw() 161 | love.graphics.setColor(255, 255, 255) 162 | 163 | draw_grid() 164 | draw_inspector() 165 | end 166 | -------------------------------------------------------------------------------- /lua/text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TannerRogalsky/punchdrunk/a2835d4963289734ad402e1fc4ceb284d0e1ee91/lua/text.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "punchdrunk", 3 | "version": "0.0.4", 4 | "author": "Tanner Rogalsky", 5 | "description": "An attempt to replicate the Love API in JavaScript", 6 | "homepage": "https://github.com/TannerRogalsky/punchdrunk", 7 | "bugs": "https://github.com/TannerRogalsky/punchdrunk/issues", 8 | "scripts": { 9 | "test": "grunt test" 10 | }, 11 | "main": "./js/punchdrunk.js", 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/TannerRogalsky/punchdrunk" 15 | }, 16 | "keywords": [ 17 | "game", 18 | "html5" 19 | ], 20 | "dependencies": {}, 21 | "devDependencies": { 22 | "coffee-script": "1.7.x", 23 | "moonshine": "git+https://github.com/TannerRogalsky/moonshine.git#0c897b793", 24 | "grunt": "0.4.x", 25 | "grunt-contrib-concat": "0.1.x", 26 | "grunt-contrib-watch": "~0.2.x", 27 | "grunt-contrib-coffee": "0.10.x", 28 | "grunt-contrib-connect": "v0.7.x", 29 | "grunt-mocha-phantomjs": "v0.4.x" 30 | }, 31 | "license": "MIT" 32 | } 33 | -------------------------------------------------------------------------------- /test/assets/sprites.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TannerRogalsky/punchdrunk/a2835d4963289734ad402e1fc4ceb284d0e1ee91/test/assets/sprites.png -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 26 | 27 | 28 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /test/lib/mocha.css: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | 3 | body { 4 | margin:0; 5 | } 6 | 7 | #mocha { 8 | font: 20px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif; 9 | margin: 60px 50px; 10 | } 11 | 12 | #mocha ul, 13 | #mocha li { 14 | margin: 0; 15 | padding: 0; 16 | } 17 | 18 | #mocha ul { 19 | list-style: none; 20 | } 21 | 22 | #mocha h1, 23 | #mocha h2 { 24 | margin: 0; 25 | } 26 | 27 | #mocha h1 { 28 | margin-top: 15px; 29 | font-size: 1em; 30 | font-weight: 200; 31 | } 32 | 33 | #mocha h1 a { 34 | text-decoration: none; 35 | color: inherit; 36 | } 37 | 38 | #mocha h1 a:hover { 39 | text-decoration: underline; 40 | } 41 | 42 | #mocha .suite .suite h1 { 43 | margin-top: 0; 44 | font-size: .8em; 45 | } 46 | 47 | #mocha .hidden { 48 | display: none; 49 | } 50 | 51 | #mocha h2 { 52 | font-size: 12px; 53 | font-weight: normal; 54 | cursor: pointer; 55 | } 56 | 57 | #mocha .suite { 58 | margin-left: 15px; 59 | } 60 | 61 | #mocha .test { 62 | margin-left: 15px; 63 | overflow: hidden; 64 | } 65 | 66 | #mocha .test.pending:hover h2::after { 67 | content: '(pending)'; 68 | font-family: arial, sans-serif; 69 | } 70 | 71 | #mocha .test.pass.medium .duration { 72 | background: #c09853; 73 | } 74 | 75 | #mocha .test.pass.slow .duration { 76 | background: #b94a48; 77 | } 78 | 79 | #mocha .test.pass::before { 80 | content: '✓'; 81 | font-size: 12px; 82 | display: block; 83 | float: left; 84 | margin-right: 5px; 85 | color: #00d6b2; 86 | } 87 | 88 | #mocha .test.pass .duration { 89 | font-size: 9px; 90 | margin-left: 5px; 91 | padding: 2px 5px; 92 | color: #fff; 93 | -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 94 | -moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 95 | box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 96 | -webkit-border-radius: 5px; 97 | -moz-border-radius: 5px; 98 | -ms-border-radius: 5px; 99 | -o-border-radius: 5px; 100 | border-radius: 5px; 101 | } 102 | 103 | #mocha .test.pass.fast .duration { 104 | display: none; 105 | } 106 | 107 | #mocha .test.pending { 108 | color: #0b97c4; 109 | } 110 | 111 | #mocha .test.pending::before { 112 | content: '◦'; 113 | color: #0b97c4; 114 | } 115 | 116 | #mocha .test.fail { 117 | color: #c00; 118 | } 119 | 120 | #mocha .test.fail pre { 121 | color: black; 122 | } 123 | 124 | #mocha .test.fail::before { 125 | content: '✖'; 126 | font-size: 12px; 127 | display: block; 128 | float: left; 129 | margin-right: 5px; 130 | color: #c00; 131 | } 132 | 133 | #mocha .test pre.error { 134 | color: #c00; 135 | max-height: 300px; 136 | overflow: auto; 137 | } 138 | 139 | /** 140 | * (1): approximate for browsers not supporting calc 141 | * (2): 42 = 2*15 + 2*10 + 2*1 (padding + margin + border) 142 | * ^^ seriously 143 | */ 144 | #mocha .test pre { 145 | display: block; 146 | float: left; 147 | clear: left; 148 | font: 12px/1.5 monaco, monospace; 149 | margin: 5px; 150 | padding: 15px; 151 | border: 1px solid #eee; 152 | max-width: 85%; /*(1)*/ 153 | max-width: calc(100% - 42px); /*(2)*/ 154 | word-wrap: break-word; 155 | border-bottom-color: #ddd; 156 | -webkit-border-radius: 3px; 157 | -webkit-box-shadow: 0 1px 3px #eee; 158 | -moz-border-radius: 3px; 159 | -moz-box-shadow: 0 1px 3px #eee; 160 | border-radius: 3px; 161 | } 162 | 163 | #mocha .test h2 { 164 | position: relative; 165 | } 166 | 167 | #mocha .test a.replay { 168 | position: absolute; 169 | top: 3px; 170 | right: 0; 171 | text-decoration: none; 172 | vertical-align: middle; 173 | display: block; 174 | width: 15px; 175 | height: 15px; 176 | line-height: 15px; 177 | text-align: center; 178 | background: #eee; 179 | font-size: 15px; 180 | -moz-border-radius: 15px; 181 | border-radius: 15px; 182 | -webkit-transition: opacity 200ms; 183 | -moz-transition: opacity 200ms; 184 | transition: opacity 200ms; 185 | opacity: 0.3; 186 | color: #888; 187 | } 188 | 189 | #mocha .test:hover a.replay { 190 | opacity: 1; 191 | } 192 | 193 | #mocha-report.pass .test.fail { 194 | display: none; 195 | } 196 | 197 | #mocha-report.fail .test.pass { 198 | display: none; 199 | } 200 | 201 | #mocha-report.pending .test.pass, 202 | #mocha-report.pending .test.fail { 203 | display: none; 204 | } 205 | #mocha-report.pending .test.pass.pending { 206 | display: block; 207 | } 208 | 209 | #mocha-error { 210 | color: #c00; 211 | font-size: 1.5em; 212 | font-weight: 100; 213 | letter-spacing: 1px; 214 | } 215 | 216 | #mocha-stats { 217 | position: fixed; 218 | top: 15px; 219 | right: 10px; 220 | font-size: 12px; 221 | margin: 0; 222 | color: #888; 223 | z-index: 1; 224 | } 225 | 226 | #mocha-stats .progress { 227 | float: right; 228 | padding-top: 0; 229 | } 230 | 231 | #mocha-stats em { 232 | color: black; 233 | } 234 | 235 | #mocha-stats a { 236 | text-decoration: none; 237 | color: inherit; 238 | } 239 | 240 | #mocha-stats a:hover { 241 | border-bottom: 1px solid #eee; 242 | } 243 | 244 | #mocha-stats li { 245 | display: inline-block; 246 | margin: 0 5px; 247 | list-style: none; 248 | padding-top: 11px; 249 | } 250 | 251 | #mocha-stats canvas { 252 | width: 40px; 253 | height: 40px; 254 | } 255 | 256 | #mocha code .comment { color: #ddd; } 257 | #mocha code .init { color: #2f6fad; } 258 | #mocha code .string { color: #5890ad; } 259 | #mocha code .keyword { color: #8a6343; } 260 | #mocha code .number { color: #2f6fad; } 261 | 262 | @media screen and (max-device-width: 480px) { 263 | #mocha { 264 | margin: 60px 0px; 265 | } 266 | 267 | #mocha #stats { 268 | position: absolute; 269 | } 270 | } 271 | -------------------------------------------------------------------------------- /test/lib/sinon-chai.js: -------------------------------------------------------------------------------- 1 | (function (sinonChai) { 2 | "use strict"; 3 | 4 | // Module systems magic dance. 5 | 6 | if (typeof require === "function" && typeof exports === "object" && typeof module === "object") { 7 | // NodeJS 8 | module.exports = sinonChai; 9 | } else if (typeof define === "function" && define.amd) { 10 | // AMD 11 | define(function () { 12 | return sinonChai; 13 | }); 14 | } else { 15 | // Other environment (usually