├── .gitignore ├── media ├── logo.xcf ├── pixel.png ├── pixel.xcf └── logo-lg.png ├── lib ├── weltmeister │ ├── arrow.png │ ├── collisiontiles-64.png │ ├── nopache-modules │ │ ├── weltmeister-api-router.js │ │ ├── weltmeister.js │ │ └── weltmeister-api.js │ ├── entities.js │ ├── evented-input.js │ ├── modal-dialogs.js │ ├── select-file-dropdown.js │ ├── config.js │ ├── tile-select.js │ ├── undo.js │ ├── edit-map.js │ ├── weltmeister.css │ ├── edit-entities.js │ ├── jquery-ui-1.8.1.custom.min.js │ └── weltmeister.js ├── game │ ├── levels │ │ └── splash.js │ ├── entities │ │ ├── logo.js │ │ ├── flames.js │ │ └── particle.js │ └── main.js └── css │ └── style.css ├── tools ├── bake.sh ├── bake.bat ├── bake.php └── jsmin.php ├── package.json ├── views ├── index.html └── weltmeister.html ├── server.js ├── LICENSE.txt └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .git 2 | node_modules 3 | lib/impact 4 | npm-debug.log 5 | -------------------------------------------------------------------------------- /media/logo.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheMaverickProgrammer/flare-impact/HEAD/media/logo.xcf -------------------------------------------------------------------------------- /media/pixel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheMaverickProgrammer/flare-impact/HEAD/media/pixel.png -------------------------------------------------------------------------------- /media/pixel.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheMaverickProgrammer/flare-impact/HEAD/media/pixel.xcf -------------------------------------------------------------------------------- /media/logo-lg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheMaverickProgrammer/flare-impact/HEAD/media/logo-lg.png -------------------------------------------------------------------------------- /lib/weltmeister/arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheMaverickProgrammer/flare-impact/HEAD/lib/weltmeister/arrow.png -------------------------------------------------------------------------------- /lib/weltmeister/collisiontiles-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheMaverickProgrammer/flare-impact/HEAD/lib/weltmeister/collisiontiles-64.png -------------------------------------------------------------------------------- /lib/game/levels/splash.js: -------------------------------------------------------------------------------- 1 | ig.module( 'game.levels.splash' ) 2 | .requires( 'impact.image','game.entities.logo' ) 3 | .defines(function(){ 4 | LevelSplash=/*JSON[*/{ 5 | "entities": [ 6 | { 7 | "type": "EntityLogo", 8 | "x": 280, 9 | "y": 116 10 | } 11 | ], 12 | "layer": [] 13 | }/*]JSON*/; 14 | }); -------------------------------------------------------------------------------- /tools/bake.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Path to impact.js and your game's main .js 4 | IMPACT_LIBRARY=lib/impact/impact.js 5 | GAME=lib/game/main.js 6 | 7 | # Output file 8 | OUTPUT_FILE=game.min.js 9 | 10 | 11 | # Change CWD to Impact's base dir and bake! 12 | cd .. 13 | php tools/bake.php $IMPACT_LIBRARY $GAME $OUTPUT_FILE -------------------------------------------------------------------------------- /lib/game/entities/logo.js: -------------------------------------------------------------------------------- 1 | ig.module( 2 | 'game.entities.logo' 3 | ) 4 | .requires( 5 | 'impact.entity' 6 | ) 7 | .defines(function(){ 8 | EntityLogo = ig.Entity.extend({ 9 | size: {x: 400, y: 300}, 10 | 11 | animSheet: new ig.AnimationSheet( 'media/logo-lg.png', 400, 200 ), 12 | 13 | init: function( x, y, settings ) { 14 | this.parent( x, y, settings ); 15 | 16 | // Add the animations 17 | this.addAnim( 'idle', 1, [0] ); 18 | } 19 | }); 20 | // end EntityLogo 21 | }); 22 | -------------------------------------------------------------------------------- /tools/bake.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | :: Path to impact.js and your game's main .js 4 | SET IMPACT_LIBRARY=lib/impact/impact.js 5 | SET GAME=lib/game/main.js 6 | 7 | :: Output file 8 | SET OUTPUT_FILE=game.min.js 9 | 10 | 11 | :: Change CWD to Impact's base dir 12 | cd ../ 13 | 14 | 15 | :: Bake! 16 | php tools/bake.php %IMPACT_LIBRARY% %GAME% %OUTPUT_FILE% 17 | 18 | :: If you dont have the php.exe in your PATH uncomment the 19 | :: following line and point it to your php.exe 20 | 21 | ::c:/php/php.exe tools/bake.php %IMPACT_LIBRARY% %GAME% %OUTPUT_FILE% 22 | 23 | pause -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flare-impact", 3 | "version": "0.0.1", 4 | "description": "Nodejs impact solution complete with weltmeister", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "nodemon ./server.js" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/TheMaverickProgrammer/flare-impact" 12 | }, 13 | "author": "Maverick Peppers", 14 | "license": "BSD-2-Clause", 15 | "dependencies": { 16 | "body-parser": "^1.15.2", 17 | "braces": "^1.8.5", 18 | "chalk": "^1.1.3", 19 | "express": "~4.13.4", 20 | "glob": "^7.0.5", 21 | "nodemon": "~1.9.2" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /views/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | F L A R E 5 | 6 | 7 | 8 | 9 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | var express = require('express'), 2 | bodyParser = require('body-parser'), 3 | port = 8080, 4 | app = express(); 5 | 6 | app.use(bodyParser.urlencoded({ 7 | extended: true, 8 | limit: '50mb' 9 | })); 10 | 11 | app.use(bodyParser.json({limit: '50mb'})); 12 | 13 | // Use node-based weltmeister api 14 | var wm = require('./lib/weltmeister/nopache-modules/weltmeister'); 15 | wm({rootDir: __dirname}, app); 16 | 17 | app.set('views', __dirname + '/views'); 18 | 19 | app.use('/lib', express.static(__dirname + '/lib')); 20 | app.use('/media', express.static(__dirname + '/media')); 21 | 22 | app.get('/', function(req, res){ 23 | res.sendFile(__dirname + '/views/index.html'); 24 | }); 25 | 26 | app.get('/wm', function(req, res){ 27 | res.sendFile(__dirname + '/views/weltmeister.html'); 28 | }); 29 | 30 | app.listen(port); 31 | 32 | console.log('app listening on port', port); 33 | -------------------------------------------------------------------------------- /lib/css/style.css: -------------------------------------------------------------------------------- 1 | html,body { 2 | 3 | background-color: #111; 4 | color: #fff; 5 | font-family: helvetica, arial, sans-serif; 6 | margin: 0; 7 | padding: 0; 8 | font-size: 12pt; 9 | } 10 | 11 | /* 12 | Canvas is confifugred to benefit the pixel art 13 | Anti-aliasing made the pixel art blurry as the canvas scaled 14 | to fit the device 15 | */ 16 | #canvas { 17 | position: absolute; 18 | left: 0; 19 | top: 0; 20 | margin: auto; 21 | image-rendering: optimizeSpeed; /* Older versions of FF */ 22 | image-rendering: -moz-crisp-edges; /* FF 6.0+ */ 23 | image-rendering: -webkit-optimize-contrast; /* Webkit (non standard naming) */ 24 | image-rendering: -o-crisp-edges; /* OS X & Windows Opera (12.02+) */ 25 | image-rendering: crisp-edges; /* Possible future browsers. */ 26 | -ms-interpolation-mode: nearest-neighbor; /* IE (non standard naming) */ 27 | image-rendering: pixelated; /* Chrome 41 */ 28 | } 29 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [2016] [Maverick Peppers] 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. 22 | -------------------------------------------------------------------------------- /lib/weltmeister/nopache-modules/weltmeister-api-router.js: -------------------------------------------------------------------------------- 1 | /* 2 | Author: Maverick Peppers 3 | Github: TheMaverickProgrammer 4 | Webstite: maverickpeppers.com 5 | Module: Weltmeister-api-router 6 | 7 | This module exports an object that passes weltmeister configurations 8 | to the callback objects. Then it maps the objects to api endpoints. 9 | The endpoints return JSON object results. 10 | 11 | GET Browse -- /browse 12 | GET Glob -- /glob 13 | POST Save -- /save 14 | */ 15 | module.exports = function(conf) { 16 | var express = require('express'); 17 | var router = express.Router(); 18 | 19 | // weltmeister api specific 20 | var wm = require('./weltmeister-api')(conf); 21 | var browse = wm.Browse; 22 | var glob = wm.Glob; 23 | var save = wm.Save; 24 | 25 | // Get Browse 26 | router.get('/browse', function(req, res) { 27 | res.send(browse(req)); 28 | }); 29 | 30 | // GET Glob 31 | router.get('/glob', function(req, res) { 32 | res.send(glob(req)); 33 | }); 34 | 35 | // POST Save 36 | router.post('/save', function(req, res) { 37 | res.send(save(req)); 38 | }); 39 | 40 | return router; 41 | } 42 | -------------------------------------------------------------------------------- /lib/weltmeister/nopache-modules/weltmeister.js: -------------------------------------------------------------------------------- 1 | /* 2 | Author: Maverick Peppers 3 | Github: TheMaverickProgrammer 4 | Webstite: maverickpeppers.com 5 | Module: Weltmeister 6 | Dependencies: 7 | - glob 8 | - braces 9 | - chalk 10 | - fs 11 | - express 12 | 13 | This module exports an object that accepts weltmeister configurations 14 | and the express server application object. The object is set to use a specific 15 | endpoint '/wm/api' as found in the lib/weltmeister/config.js file. 16 | 17 | Further work can be done to make the configuration between the two files 18 | seemless. 19 | */ 20 | module.exports = function(opts, app) { 21 | // Create a config object on the options 22 | var conf = { 23 | // Use the current file directory as the basis for fetching objects 24 | // Or, if supplied, use another directory as specified in the options object. 25 | WM_ROOT_DIR:(typeof opts.rootDir == 'undefined')? "" + __dirname + "/" : "" + opts.rootDir + "/" 26 | } 27 | 28 | // instantiate the router 29 | var wmRouter = require('./weltmeister-api-router')(conf); 30 | 31 | // Use the router 32 | app.use('/wm/api', wmRouter); 33 | } 34 | -------------------------------------------------------------------------------- /lib/game/entities/flames.js: -------------------------------------------------------------------------------- 1 | ig.module( 2 | 'game.entities.flames' 3 | ) 4 | .requires( 5 | 'impact.entity', 6 | 'game.entities.particle' 7 | ) 8 | .defines(function(){ 9 | Flames = EntityParticle.extend({ 10 | // longer lifetime 11 | lifetime: 3.0, 12 | fadetime: 1.5, 13 | 14 | // velocity value to be set 15 | vel: null, 16 | 17 | gravityFactor: 0, 18 | 19 | // bounce a little less 20 | bounciness: 0.6, 21 | 22 | // add animation sheet of sprites 23 | animSheet: new ig.AnimationSheet('media/pixel.png',1,1), 24 | 25 | init: function( x, y, settings ){ 26 | // add ember animation 27 | this.addAnim( 'idle', 0.3, [254, 127, 0] ); 28 | 29 | // update random velocity to create starburst effect 30 | this.vel = { x: (Math.random() < 0.5 ? -1 : 1)*Math.random()*100, 31 | y: (Math.random() < 0.5 ? -1 : 1)*Math.random()*100 }; 32 | 33 | // send to parent 34 | this.parent( x, y, settings ); 35 | }, 36 | 37 | update: function() { 38 | this.vel.y -= 10; // particles move up 39 | this.pos.x += Math.sin((this.pos.y*(22/7))/100); // make it swirl upwards 40 | 41 | this.parent(); 42 | } 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /lib/game/entities/particle.js: -------------------------------------------------------------------------------- 1 | ig.module( 2 | 'game.entities.particle' 3 | ) 4 | .requires( 5 | 'impact.entity' 6 | ) 7 | .defines(function(){ 8 | EntityParticle = ig.Entity.extend({ 9 | // single pixel sprites 10 | size: { x:1, y:1 }, 11 | offset: { x:0, y:0 }, 12 | 13 | // particle will collide but not effect other entities 14 | type: ig.Entity.TYPE.NONE, 15 | checkAgainst: ig.Entity.TYPE.NONE, 16 | collides: ig.Entity.COLLIDES.LITE, 17 | 18 | // default particle lifetime & fadetime 19 | lifetime: 5, 20 | fadetime: 1, 21 | 22 | // particles will bounce off other entities when it collides 23 | minBounceVelocity: 0, 24 | bounciness: 1.0, 25 | friction: { x:0, y:0 }, 26 | 27 | init: function( x, y, settings ){ 28 | this.parent( x, y, settings ); 29 | 30 | // take velocity and add randomness to vel 31 | var vx = this.vel.x; var vy = this.vel.y; 32 | this.vel.x = (Math.random()*2 - 1)*vx; 33 | this.vel.y = (Math.random()*2 - 1)*vy; 34 | 35 | // creates a flicker effect 36 | this.currentAnim.gotoRandomFrame(); 37 | 38 | // init timer for fadetime 39 | this.idleTimer = new ig.Timer(); 40 | }, 41 | 42 | update: function(){ 43 | // check if particle has existed past lifetime 44 | // if so, remove particle 45 | if(this.idleTimer.delta() > this.lifetime){ 46 | this.kill(); 47 | return; 48 | } 49 | 50 | // fade the particle effect using the aplha blend 51 | this.currentAnim.alpha = this.idleTimer.delta().map( this.lifetime - this.fadetime, this.lifetime, 1, 0 ); 52 | this.parent(); 53 | } 54 | 55 | }); 56 | }); 57 | -------------------------------------------------------------------------------- /lib/weltmeister/entities.js: -------------------------------------------------------------------------------- 1 | ig.module( 2 | 'weltmeister.entityLoader' 3 | ) 4 | .requires( 5 | 'weltmeister.config' 6 | ) 7 | .defines(function(){ "use strict"; 8 | 9 | // Load the list of entity files via AJAX PHP glob 10 | var path = wm.config.api.glob + '?', 11 | globs = typeof wm.config.project.entityFiles == 'string' ? 12 | [wm.config.project.entityFiles] : 13 | wm.config.project.entityFiles; 14 | 15 | for (var i = 0; i < globs.length; i++) { 16 | path += 'glob[]=' + encodeURIComponent(globs[i]) + '&'; 17 | } 18 | 19 | path += 'nocache=' + Math.random(); 20 | 21 | var req = $.ajax({ 22 | url: path, 23 | method: 'get', 24 | dataType: 'json', 25 | 26 | // MUST load synchronous, as the engine would otherwise determine that it 27 | // can't resolve dependencies to weltmeister.entities when there are 28 | // no more files to load and weltmeister.entities is still not defined 29 | // because the ajax request hasn't finished yet. 30 | // FIXME FFS! 31 | async: false, 32 | success: function(files) { 33 | 34 | // File names to Module names 35 | var moduleNames = []; 36 | var modules = {}; 37 | for( var i = 0; i < files.length; i++ ) { 38 | var name = files[i] 39 | .replace(new RegExp("^"+ig.lib+"|\\.js$", "g"), '') 40 | .replace(/\//g, '.'); 41 | moduleNames.push( name ); 42 | modules[name] = files[i]; 43 | } 44 | 45 | // Define a Module that requires all entity Modules 46 | ig.module('weltmeister.entities') 47 | .requires.apply(ig, moduleNames) 48 | .defines(function(){ wm.entityModules = modules; }); 49 | }, 50 | error: function( xhr, status, error ){ 51 | throw( 52 | "Failed to load entity list via glob.php: " + error + "\n" + 53 | xhr.responseText 54 | ); 55 | } 56 | }); 57 | 58 | }); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | updated 11/18/2017 3 | 4 | # README v0.0.2 5 | > A nopache (apache-free) nodejs server for [Impactjs](http://impactjs.com) complete with weltmeister support. 6 | Part of the F L A R E development suite. 7 | 8 | 9 | 10 | # Features 11 | - **NEW** BoomSheets sprite mapping and management tool 12 | - Easy installation 13 | - 100% nodejs 14 | - Scripts watch for any changes in project and relaunch server immediately 15 | - All server-side weltmeister exceptions are caught and printed in the console 16 | - Server body handles request up to **50 mb** of level data from the editor! 17 | - FREE 18 | 19 | ## Usage 20 | - Download and install [node](http://nodejs.org) 21 | - Close all ports running on ```8080``` 22 | - Clone/fork/download this git project 23 | - ```npm install --save``` to install project dependencies 24 | - drop your purchased ```/impact``` source folder to ```flare-impact/lib``` 25 | - ```npm start``` to launch server 26 | - **TO RUN GAME:** Navigate browser to ```localhost:8080/``` 27 | - You should see the logo with flares swirling upward 28 | - **TO LAUNCH WELTMEISTER:** Navigate browser to ```localhost:8080/wm``` 29 | 30 | ## Follow 31 | - [First announcement](http://impactjs.com/forums/code/f-l-a-r-e-development-suite-for-impact/page/1) for public use. Includes node weltmeister only. 32 | - [First release of BoomSheets](http://impactjs.com/forums/code/boomsheets-sprite-mapping-tool-for-impactjs) Includes Mac and Linux 64 binaries. 33 | 34 | ## Contribute 35 | Contributions are always welcome! Discovered a bug? Have an idea? File a [new issue](https://github.com/TheMaverickProgrammer/flare-impact/issues) on github. 36 | 37 | ## License 38 | 39 | ![MIT](https://dl.dropbox.com/s/dmnb84n9s6sn55e/mit.png) 40 | -------------------------------------------------------------------------------- /lib/weltmeister/evented-input.js: -------------------------------------------------------------------------------- 1 | ig.module( 2 | 'weltmeister.evented-input' 3 | ) 4 | .requires( 5 | 'impact.input' 6 | ) 7 | .defines(function(){ "use strict"; 8 | 9 | wm.EventedInput = ig.Input.extend({ 10 | mousemoveCallback: null, 11 | keyupCallback: null, 12 | keydownCallback: null, 13 | 14 | delayedKeyup: {push:function(){},length: 0}, 15 | 16 | 17 | keydown: function( event ) { 18 | var tag = event.target.tagName; 19 | if( tag == 'INPUT' || tag == 'TEXTAREA' ) { return; } 20 | 21 | var code = event.type == 'keydown' 22 | ? event.keyCode 23 | : (event.button == 2 ? ig.KEY.MOUSE2 : ig.KEY.MOUSE1); 24 | var action = this.bindings[code]; 25 | if( action ) { 26 | if( !this.actions[action] ) { 27 | this.actions[action] = true; 28 | if( this.keydownCallback ) { 29 | this.keydownCallback( action ); 30 | } 31 | } 32 | event.stopPropagation(); 33 | event.preventDefault(); 34 | } 35 | }, 36 | 37 | 38 | keyup: function( event ) { 39 | var tag = event.target.tagName; 40 | if( tag == 'INPUT' || tag == 'TEXTAREA' ) { return; } 41 | 42 | var code = event.type == 'keyup' 43 | ? event.keyCode 44 | : (event.button == 2 ? ig.KEY.MOUSE2 : ig.KEY.MOUSE1); 45 | var action = this.bindings[code]; 46 | if( action ) { 47 | this.actions[action] = false; 48 | if( this.keyupCallback ) { 49 | this.keyupCallback( action ); 50 | } 51 | event.stopPropagation(); 52 | event.preventDefault(); 53 | } 54 | }, 55 | 56 | 57 | mousewheel: function( event ) { 58 | var delta = event.wheelDelta ? event.wheelDelta : (event.detail * -1); 59 | var code = delta > 0 ? ig.KEY.MWHEEL_UP : ig.KEY.MWHEEL_DOWN; 60 | var action = this.bindings[code]; 61 | if( action ) { 62 | if( this.keyupCallback ) { 63 | this.keyupCallback( action ); 64 | } 65 | event.stopPropagation(); 66 | event.preventDefault(); 67 | } 68 | }, 69 | 70 | 71 | mousemove: function( event ) { 72 | this.parent( event ); 73 | if( this.mousemoveCallback ) { 74 | this.mousemoveCallback(); 75 | } 76 | } 77 | }); 78 | 79 | }); -------------------------------------------------------------------------------- /lib/game/main.js: -------------------------------------------------------------------------------- 1 | ig.module( 2 | 'game.main' 3 | ) 4 | .requires( 5 | 'impact.game', 6 | 'impact.font', 7 | 'impact.debug.debug', 8 | 9 | // game specific dependencies 10 | 'game.entities.flames', 11 | 'game.levels.splash' 12 | ) 13 | .defines(function(){ 14 | MyGame = ig.Game.extend({ 15 | 16 | // create a timer for lares 17 | fireTimer: new ig.Timer(), 18 | 19 | init: function() { 20 | this.loadLevel(LevelSplash); 21 | this.fireTimer.set(1); 22 | }, 23 | 24 | update: function() { 25 | this.parent(); 26 | 27 | // Use timer to spawn fireworks every second 28 | if( this.fireTimer.delta() > 0 ){ 29 | for (var i = 0; i <= 2; i++){ 30 | randPos = {x: Math.random()*150, y: Math.random()*50}; 31 | randPos.x += (640-380); 32 | randPos.y += (480-210); 33 | 34 | ig.game.spawnEntity( Flames, randPos.x, randPos.y ); 35 | } 36 | this.fireTimer.reset(); 37 | } 38 | }, 39 | 40 | draw: function() { 41 | // Draw all entities and backgroundMaps 42 | this.parent(); 43 | } 44 | }); 45 | 46 | var scale = (window.innerWidth < 640) ? 2 : 0.5; 47 | 48 | 49 | // We want to run the game in "fullscreen", so let's use the window's size 50 | // directly as the canvas' style size. 51 | var canvas = document.getElementById('canvas'); 52 | canvas.style.width = window.innerWidth + 'px'; 53 | canvas.style.height = window.innerHeight + 'px'; 54 | 55 | 56 | // Listen to the window's 'resize' event and set the canvas' size each time 57 | // it changes. 58 | window.addEventListener('resize', function(){ 59 | // If the game hasn't started yet, there's nothing to do here 60 | if( !ig.system ) { return; } 61 | 62 | // Resize the canvas style and tell Impact to resize the canvas itself; 63 | canvas.style.width = window.innerWidth + 'px'; 64 | canvas.style.height = window.innerHeight + 'px'; 65 | ig.system.resize( window.innerWidth * scale, window.innerHeight * scale ); 66 | 67 | // Also repositon the touch buttons, if we have any 68 | if( window.myTouchButtons ) { 69 | window.myTouchButtons.align(); 70 | } 71 | }, false); 72 | 73 | 74 | // Finally, start the game into MyGame and use the ImpactSplashLoader plugin 75 | // as our loading screen 76 | var width = window.innerWidth * scale, 77 | height = window.innerHeight * scale; 78 | 79 | ig.main( '#canvas', MyGame, 60, width, height, 1, ig.ImpactSplashLoader ); 80 | 81 | }); 82 | -------------------------------------------------------------------------------- /lib/weltmeister/modal-dialogs.js: -------------------------------------------------------------------------------- 1 | ig.module( 2 | 'weltmeister.modal-dialogs' 3 | ) 4 | .requires( 5 | 'weltmeister.select-file-dropdown' 6 | ) 7 | .defines(function(){ "use strict"; 8 | 9 | wm.ModalDialog = ig.Class.extend({ 10 | onOk: null, 11 | onCancel: null, 12 | 13 | text: '', 14 | okText: '', 15 | cancelText: '', 16 | 17 | background: null, 18 | dialogBox: null, 19 | buttonDiv: null, 20 | 21 | init: function( text, okText, cancelText ) { 22 | this.text = text; 23 | this.okText = okText || 'OK'; 24 | this.cancelText = cancelText || 'Cancel'; 25 | 26 | this.background = $('
', {'class':'modalDialogBackground'}); 27 | this.dialogBox = $('
', {'class':'modalDialogBox'}); 28 | this.background.append( this.dialogBox ); 29 | $('body').append( this.background ); 30 | 31 | this.initDialog( text ); 32 | }, 33 | 34 | 35 | initDialog: function() { 36 | this.buttonDiv = $('
', {'class': 'modalDialogButtons'} ); 37 | var okButton = $('', {'type': 'button', 'class':'button', 'value': this.okText}); 38 | var cancelButton = $('', {'type': 'button', 'class':'button', 'value': this.cancelText}); 39 | 40 | okButton.bind( 'click', this.clickOk.bind(this) ); 41 | cancelButton.bind( 'click', this.clickCancel.bind(this) ); 42 | 43 | this.buttonDiv.append( okButton ).append( cancelButton ); 44 | 45 | this.dialogBox.html('
' + this.text + '
' ); 46 | this.dialogBox.append( this.buttonDiv ); 47 | }, 48 | 49 | 50 | clickOk: function() { 51 | if( this.onOk ) { this.onOk(this); } 52 | this.close(); 53 | }, 54 | 55 | 56 | clickCancel: function() { 57 | if( this.onCancel ) { this.onCancel(this); } 58 | this.close(); 59 | }, 60 | 61 | 62 | open: function() { 63 | this.background.fadeIn(100); 64 | }, 65 | 66 | 67 | close: function() { 68 | this.background.fadeOut(100); 69 | } 70 | }); 71 | 72 | 73 | 74 | wm.ModalDialogPathSelect = wm.ModalDialog.extend({ 75 | pathDropdown: null, 76 | pathInput: null, 77 | fileType: '', 78 | 79 | init: function( text, okText, type ) { 80 | this.fileType = type || ''; 81 | this.parent( text, (okText || 'Select') ); 82 | }, 83 | 84 | 85 | setPath: function( path ) { 86 | var dir = path.replace(/\/[^\/]*$/, ''); 87 | this.pathInput.val( path ); 88 | this.pathDropdown.loadDir( dir ); 89 | }, 90 | 91 | 92 | initDialog: function() { 93 | this.parent(); 94 | this.pathInput = $('', {'type': 'text', 'class': 'modalDialogPath'} ); 95 | this.buttonDiv.before( this.pathInput ); 96 | this.pathDropdown = new wm.SelectFileDropdown( this.pathInput, wm.config.api.browse, this.fileType ); 97 | }, 98 | 99 | 100 | clickOk: function() { 101 | if( this.onOk ) { 102 | this.onOk(this, this.pathInput.val()); 103 | } 104 | this.close(); 105 | } 106 | }); 107 | 108 | }); -------------------------------------------------------------------------------- /lib/weltmeister/select-file-dropdown.js: -------------------------------------------------------------------------------- 1 | ig.module( 2 | 'weltmeister.select-file-dropdown' 3 | ) 4 | .defines(function(){ "use strict"; 5 | 6 | wm.SelectFileDropdown = ig.Class.extend({ 7 | input: null, 8 | boundShow: null, 9 | div: null, 10 | filelistPHP: '', 11 | filetype: '', 12 | 13 | init: function( elementId, filelistPHP, filetype ) { 14 | this.filetype = filetype || ''; 15 | this.filelistPHP = filelistPHP; 16 | this.input = $(elementId); 17 | this.boundHide = this.hide.bind(this); 18 | this.input.bind('focus', this.show.bind(this) ); 19 | 20 | this.div = $('
', {'class':'selectFileDialog'}); 21 | this.input.after( this.div ); 22 | this.div.bind('mousedown', this.noHide.bind(this) ); 23 | 24 | this.loadDir( '' ); 25 | }, 26 | 27 | 28 | loadDir: function( dir ) { 29 | var path = this.filelistPHP + '?dir=' + encodeURIComponent( dir || '' ) + '&type=' + this.filetype; 30 | var req = $.ajax({ 31 | url:path, 32 | dataType: 'json', 33 | async: false, 34 | success:this.showFiles.bind(this) 35 | }); 36 | }, 37 | 38 | 39 | selectDir: function( event ) { 40 | this.loadDir( $(event.target).attr('href') ); 41 | return false; 42 | }, 43 | 44 | 45 | selectFile: function( event ) { 46 | this.input.val( $(event.target).attr('href') ); 47 | this.input.blur(); 48 | this.hide(); 49 | return false; 50 | }, 51 | 52 | 53 | showFiles: function( data ) { 54 | this.div.empty(); 55 | if( data.parent !== false ) { 56 | var parentDir = $('', {'class':'dir', href:data.parent, html: '…parent directory'}); 57 | parentDir.bind( 'click', this.selectDir.bind(this) ); 58 | this.div.append( parentDir ); 59 | } 60 | for( var i = 0; i < data.dirs.length; i++ ) { 61 | var name = data.dirs[i].match(/[^\/]*$/)[0] + '/'; 62 | var dir = $('', {'class':'dir', href:data.dirs[i], html: name, title: name}); 63 | dir.bind( 'click', this.selectDir.bind(this) ); 64 | this.div.append( dir ); 65 | } 66 | for( var i = 0; i < data.files.length; i++ ) { 67 | var name = data.files[i].match(/[^\/]*$/)[0]; 68 | var file = $('', {'class':'file', href:data.files[i], html: name, title: name}); 69 | file.bind( 'click', this.selectFile.bind(this) ); 70 | this.div.append( file ); 71 | } 72 | }, 73 | 74 | 75 | noHide: function(event) { 76 | event.stopPropagation(); 77 | }, 78 | 79 | 80 | show: function( event ) { 81 | var inputPos = this.input.position();//this.input.getPosition(this.input.getOffsetParent()); 82 | var inputHeight = parseInt(this.input.innerHeight()) + parseInt(this.input.css('margin-top')); 83 | var inputWidth = this.input.innerWidth(); 84 | $(document).bind( 'mousedown', this.boundHide ); 85 | this.div.css({ 86 | 'top': inputPos.top + inputHeight + 1, 87 | 'left': inputPos.left, 88 | 'width': inputWidth 89 | }).slideDown(100); 90 | }, 91 | 92 | 93 | hide: function() { 94 | $(document).unbind( 'mousedown', this.boundHide ); 95 | this.div.slideUp(100); 96 | } 97 | }); 98 | 99 | }); -------------------------------------------------------------------------------- /views/weltmeister.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Weltmeister 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 |
27 |
28 | 29 | 30 | 31 | 93 | 94 |
1x
95 | 96 |
97 | 98 | 99 | -------------------------------------------------------------------------------- /tools/bake.php: -------------------------------------------------------------------------------- 1 | \n"; 4 | echo "e.g. bake.php lib/impact/impact.js lib/game/game.js mygame-baked.js\n"; 5 | die; 6 | } 7 | 8 | $inFiles = array_slice( $argv, 1, -1 ); 9 | $outFile = $argv[ count($argv)-1 ]; 10 | 11 | $baker = new Baker( Baker::MINIFIED ); 12 | $baker->bake( $inFiles, $outFile ); 13 | 14 | 15 | class Baker { 16 | const PLAIN = 0; 17 | const MINIFIED = 1; 18 | const GZIPPED = 2; 19 | 20 | protected $base = 'lib/'; 21 | protected $format = 0; 22 | protected $loaded = array(); 23 | protected $currentInput = 'Command Line'; 24 | protected $fileCount = 0, $bytesIn = 0, $bytesOut = 0; 25 | 26 | public function __construct( $format = 0 ) { 27 | $this->format = $format; 28 | if( $this->format & self::MINIFIED ) { 29 | require_once( 'jsmin.php' ); 30 | } 31 | } 32 | 33 | 34 | public function bake( $inFiles, $outFile ) { 35 | $this->fileCount = 0; 36 | $this->bytesIn = 0; 37 | $out = "/*! Built with IMPACT - impactjs.com */\n\n"; 38 | 39 | foreach( $inFiles as $f ) { 40 | $out .= $this->load( $f ); 41 | } 42 | 43 | $bytesOut = strlen($out); 44 | $bytesOutZipped = 0; 45 | 46 | echo "writing $outFile\n"; 47 | @file_put_contents( $outFile, $out ) or 48 | die("ERROR: Couldn't write to $outFile\n"); 49 | 50 | if( $this->format & self::GZIPPED ) { 51 | $gzFile = "$outFile.gz"; 52 | echo "writing $gzFile\n"; 53 | $fh = gzopen( $gzFile, 'w9' ) or 54 | die("ERROR: Couldn't write to $gzFile\n"); 55 | 56 | gzwrite( $fh, $out ); 57 | gzclose( $fh ); 58 | $bytesOutZipped = filesize( $gzFile ); 59 | } 60 | 61 | 62 | echo 63 | "\nbaked {$this->fileCount} files: ". 64 | round($this->bytesIn/1024,1)."kb -> ".round($bytesOut/1024,1)."kb" . 65 | ( $this->format & self::GZIPPED 66 | ? " (".round($bytesOutZipped/1024,1)."kb gzipped)\n" 67 | : "\n" 68 | ); 69 | } 70 | 71 | 72 | protected function load( $path ) { 73 | if( isset($this->loaded[$path]) ) { 74 | return ''; 75 | } 76 | 77 | if( !file_exists($path) ) { 78 | die("ERROR: Couldn't load $path required from {$this->currentInput}\n"); 79 | } 80 | 81 | echo "loading $path \n"; 82 | $this->loaded[$path] = true; 83 | $this->currentInput = $path; 84 | 85 | $code = file_get_contents( $path ); 86 | $this->bytesIn += strlen($code); 87 | $this->fileCount++; 88 | if( $this->format & self::MINIFIED ) { 89 | $code = trim(JSMin::minify($code)); 90 | } 91 | 92 | 93 | // Naively probe the file for 'ig.module().requires().defines()' code; 94 | // the 'requries()' part will be handled by the regexp callback 95 | $this->definesModule = false; 96 | $code = preg_replace_callback( 97 | '/ig\s* 98 | \.\s*module\s*\((.*?)\)\s* 99 | (\.\s*requires\s*\((.*?)\)\s*)? 100 | \.\s*defines\s*\( 101 | /smx', 102 | array($this,'loadCallback'), 103 | $code 104 | ); 105 | 106 | // All files should define a module; maybe we just missed it? Print a 107 | // friendly reminder :) 108 | if( !$this->definesModule ) { 109 | echo "WARNING: file $path seems to define no module!\n"; 110 | } 111 | 112 | return $code; 113 | } 114 | 115 | 116 | protected function loadCallback( $matches ) { 117 | $currentInput = $this->currentInput; 118 | $this->definesModule = true; 119 | 120 | $moduleName = $matches[1]; 121 | $requiredFiles = isset($matches[3]) ? $matches[3] : ''; 122 | $requiredCode = ''; 123 | 124 | if( $requiredFiles ) { 125 | // Explode the module names and map them to file names. Ignore the 126 | // dom.ready module if present 127 | $moduleFiles = array_diff( 128 | explode( 129 | ',', 130 | preg_replace( 131 | '/[\s\'"]|\/\/.*|\/\*.*\*\//', // strip quotes and spaces 132 | '', 133 | str_replace('.', '/', $requiredFiles ) // . to / 134 | ) 135 | ), 136 | array('dom/ready') 137 | ); 138 | 139 | foreach( $moduleFiles as $f ) { 140 | $requiredCode .= $this->load( $this->base . $f.'.js' ); 141 | } 142 | } 143 | 144 | return 145 | $requiredCode . 146 | "\n\n// $currentInput\n" . 147 | 'ig.baked=true;' . 148 | 'ig.module('.$moduleName.')' . 149 | ( $requiredFiles 150 | ? '.requires('.$requiredFiles.')' 151 | : '' 152 | ) . 153 | '.defines('; 154 | } 155 | } 156 | 157 | ?> -------------------------------------------------------------------------------- /lib/weltmeister/config.js: -------------------------------------------------------------------------------- 1 | ig.module( 2 | 'weltmeister.config' 3 | ) 4 | .defines(function(){ "use strict"; 5 | 6 | wm.config = { 7 | 8 | project: { 9 | // The prefix path of your game's source code. You only have to change 10 | // this if you use the 'ImpactPrefix' in your dev environment. 11 | 'modulePath': 'lib/', 12 | 13 | // This "glob" tells Weltmeister where to load the entity files 14 | // from. If you want to load entities from several directories, 15 | // you can specify an array here. E.g.: 16 | 'entityFiles': ['lib/game/entities/*.js'], 17 | 18 | // The default path for the level file selection box 19 | 'levelPath': 'lib/game/levels/', 20 | 21 | // Whether to save levels as plain JSON or wrapped in a module. If 22 | // you want to load levels asynchronously via AJAX, saving as plain 23 | // JSON can be helpful. 24 | 'outputFormat': 'module', // 'module' or 'json' 25 | 26 | // Whether to pretty print the JSON data in level files. If you have 27 | // any issues with your levels, it's usually a good idea to turn this 28 | // on and look at the saved level files with a text editor. 29 | 'prettyPrint': true 30 | }, 31 | 32 | 33 | // Plugins for Weltmeister: an array of module names to load 34 | plugins: [], 35 | 36 | 37 | // Default settings when creating new layers in Weltmeister. Change these 38 | // as you like 39 | 'layerDefaults': { 40 | 'width': 30, 41 | 'height': 20, 42 | 'tilesize': 8 43 | }, 44 | 45 | // Whether to ask before closing Weltmeister when there are unsaved changes 46 | 'askBeforeClose': true, 47 | 48 | // Whether to attempt to load the last opened level on startup 49 | 'loadLastLevel': true, 50 | 51 | // Size of the "snap" grid when moving entities 52 | 'entityGrid': 4, 53 | 54 | // Number of undo levels. You may want to increase this if you use 'undo' 55 | // frequently. 56 | 'undoLevels': 50, 57 | 58 | // Mouse and Key bindings in Weltmeister. Some function are bound to 59 | // several keys. See the documentation of ig.Input for a list of available 60 | // key names. 61 | 'binds': { 62 | 'MOUSE1': 'draw', 63 | 'MOUSE2': 'drag', 64 | 'SHIFT': 'select', 65 | 'CTRL': 'drag', 66 | 'SPACE': 'menu', 67 | 'DELETE': 'delete', 68 | 'BACKSPACE': 'delete', 69 | 'G': 'grid', 70 | 'C': 'clone', 71 | 'Z': 'undo', 72 | 'Y': 'redo', 73 | 'MWHEEL_UP': 'zoomin', 74 | 'PLUS': 'zoomin', 75 | 'MWHEEL_DOWN': 'zoomout', 76 | 'MINUS': 'zoomout' 77 | }, 78 | 79 | // Whether to enable unidirectional scrolling for touchpads; this 80 | // automatically unbinds the MWHEEL_UP and MWHEEL_DOWN actions. 81 | 'touchScroll': false, 82 | 83 | // View settings. You can change the default Zoom level and whether 84 | // to show the grid on startup here. 85 | 'view': { 86 | 'zoom': 1, 87 | 'zoomMax': 4, 88 | 'zoomMin': 0.125, 89 | 'grid': false 90 | }, 91 | 92 | // Font face and size for entity labels and the grid coordinates 93 | 'labels': { 94 | 'draw': true, 95 | 'step': 32, 96 | 'font': '10px Bitstream Vera Sans Mono, Monaco, sans-serif' 97 | }, 98 | 99 | // Colors to use for the background, selection boxes, text and the grid 100 | 'colors': { 101 | 'clear': '#000000', // Background Color 102 | 'highlight': '#ceff36', // Currently selected tile or entity 103 | 'primary': '#ffffff', // Labels and layer bounds 104 | 'secondary': '#555555', // Grid and tile selection bounds 105 | 'selection': '#ff9933' // Selection cursor box on tile maps 106 | }, 107 | 108 | // Settings for the Collision tiles. You shouldn't need to change these. 109 | // The tilesize only specifies the size in the image - resizing to final 110 | // size for each layer happens in Weltmeister. 111 | 'collisionTiles': { 112 | 'path': 'lib/weltmeister/collisiontiles-64.png', 113 | 'tilesize': 64 114 | }, 115 | 116 | // API paths for saving levels and browsing directories. If you use a 117 | // different backend (i.e. not the official PHP backend), you may have 118 | // to change these. 119 | 120 | /** DEPRICATED **/ 121 | /* 'api': { 122 | 'save': 'lib/weltmeister/api/save.php', 123 | 'browse': 'lib/weltmeister/api/browse.php', 124 | 'glob': 'lib/weltmeister/api/glob.php' 125 | } */ 126 | 127 | // use flashly new nopache-impact weltmeister api 128 | 'api': { 129 | 'save': '/wm/api/save', 130 | 'browse': '/wm/api/browse', 131 | 'glob': '/wm/api/glob' 132 | } 133 | }; 134 | 135 | }); 136 | -------------------------------------------------------------------------------- /lib/weltmeister/tile-select.js: -------------------------------------------------------------------------------- 1 | ig.module( 2 | 'weltmeister.tile-select' 3 | ) 4 | .defines(function(){ "use strict"; 5 | 6 | wm.TileSelect = ig.Class.extend({ 7 | 8 | pos: {x:0, y:0}, 9 | 10 | layer: null, 11 | selectionBegin: null, 12 | 13 | init: function( layer ) { 14 | this.layer = layer; 15 | }, 16 | 17 | 18 | getCurrentTile: function() { 19 | var b = this.layer.brush; 20 | if( b.length == 1 && b[0].length == 1 ) { 21 | return b[0][0] - 1; 22 | } 23 | else { 24 | return -1; 25 | } 26 | }, 27 | 28 | 29 | setPosition: function( x, y ) { 30 | this.selectionBegin = null; 31 | var tile = this.getCurrentTile(); 32 | this.pos.x = 33 | Math.floor( x / this.layer.tilesize ) * this.layer.tilesize 34 | - Math.floor( tile * this.layer.tilesize ) % this.layer.tiles.width; 35 | 36 | this.pos.y = 37 | Math.floor( y / this.layer.tilesize ) * this.layer.tilesize 38 | - Math.floor( tile * this.layer.tilesize / this.layer.tiles.width ) * this.layer.tilesize 39 | - (tile == -1 ? this.layer.tilesize : 0); 40 | 41 | this.pos.x = this.pos.x.limit( 0, ig.system.width - this.layer.tiles.width - (ig.system.width % this.layer.tilesize) ); 42 | this.pos.y = this.pos.y.limit( 0, ig.system.height - this.layer.tiles.height - (ig.system.height % this.layer.tilesize) ); 43 | }, 44 | 45 | 46 | beginSelecting: function( x, y ) { 47 | this.selectionBegin = {x:x, y:y}; 48 | }, 49 | 50 | 51 | endSelecting: function( x, y ) { 52 | var r = this.getSelectionRect( x, y); 53 | 54 | var mw = Math.floor( this.layer.tiles.width / this.layer.tilesize ); 55 | var mh = Math.floor( this.layer.tiles.height / this.layer.tilesize ); 56 | 57 | var brush = []; 58 | for( var ty = r.y; ty < r.y+r.h; ty++ ) { 59 | var row = []; 60 | for( var tx = r.x; tx < r.x+r.w; tx++ ) { 61 | if( tx < 0 || ty < 0 || tx >= mw || ty >= mh) { 62 | row.push( 0 ); 63 | } 64 | else { 65 | row.push( ty * Math.floor(this.layer.tiles.width / this.layer.tilesize) + tx + 1 ); 66 | } 67 | } 68 | brush.push( row ); 69 | } 70 | this.selectionBegin = null; 71 | return brush; 72 | }, 73 | 74 | 75 | getSelectionRect: function( x, y ) { 76 | var sx = this.selectionBegin ? this.selectionBegin.x : x, 77 | sy = this.selectionBegin ? this.selectionBegin.y : y; 78 | 79 | var 80 | txb = Math.floor( (sx - this.pos.x) / this.layer.tilesize ), 81 | tyb = Math.floor( (sy - this.pos.y) / this.layer.tilesize ), 82 | txe = Math.floor( (x - this.pos.x) / this.layer.tilesize ), 83 | tye = Math.floor( (y - this.pos.y) / this.layer.tilesize ); 84 | 85 | return { 86 | x: Math.min( txb, txe ), 87 | y: Math.min( tyb, tye ), 88 | w: Math.abs( txb - txe) + 1, 89 | h: Math.abs( tyb - tye) + 1 90 | } 91 | }, 92 | 93 | 94 | draw: function() { 95 | ig.system.clear( "rgba(0,0,0,0.8)" ); 96 | if( !this.layer.tiles.loaded ) { 97 | return; 98 | } 99 | 100 | // Tileset 101 | ig.system.context.lineWidth = 1; 102 | ig.system.context.strokeStyle = wm.config.colors.secondary; 103 | ig.system.context.fillStyle = wm.config.colors.clear; 104 | ig.system.context.fillRect( 105 | this.pos.x * ig.system.scale, 106 | this.pos.y * ig.system.scale, 107 | this.layer.tiles.width * ig.system.scale, 108 | this.layer.tiles.height * ig.system.scale 109 | ); 110 | ig.system.context.strokeRect( 111 | this.pos.x * ig.system.scale - 0.5, 112 | this.pos.y * ig.system.scale - 0.5, 113 | this.layer.tiles.width * ig.system.scale + 1, 114 | this.layer.tiles.height * ig.system.scale + 1 115 | ); 116 | 117 | this.layer.tiles.draw( this.pos.x, this.pos.y ); 118 | 119 | // Selected Tile 120 | var tile = this.getCurrentTile(); 121 | var tx = Math.floor( tile * this.layer.tilesize ) % this.layer.tiles.width + this.pos.x; 122 | var ty = 123 | Math.floor( tile * this.layer.tilesize / this.layer.tiles.width ) 124 | * this.layer.tilesize + this.pos.y 125 | + (tile == -1 ? this.layer.tilesize : 0); 126 | 127 | ig.system.context.lineWidth = 1; 128 | ig.system.context.strokeStyle = wm.config.colors.highlight; 129 | ig.system.context.strokeRect( 130 | tx * ig.system.scale - 0.5, 131 | ty * ig.system.scale - 0.5, 132 | this.layer.tilesize * ig.system.scale + 1, 133 | this.layer.tilesize * ig.system.scale + 1 134 | ); 135 | }, 136 | 137 | 138 | drawCursor: function( x, y ) { 139 | var cx = Math.floor( x / this.layer.tilesize ) * this.layer.tilesize; 140 | var cy = Math.floor( y / this.layer.tilesize ) * this.layer.tilesize; 141 | 142 | var r = this.getSelectionRect( x, y); 143 | 144 | ig.system.context.lineWidth = 1; 145 | ig.system.context.strokeStyle = wm.config.colors.selection; 146 | ig.system.context.strokeRect( 147 | (r.x * this.layer.tilesize + this.pos.x) * ig.system.scale - 0.5, 148 | (r.y * this.layer.tilesize + this.pos.y) * ig.system.scale - 0.5, 149 | r.w * this.layer.tilesize * ig.system.scale + 1, 150 | r.h * this.layer.tilesize * ig.system.scale + 1 151 | ); 152 | } 153 | }); 154 | 155 | }); -------------------------------------------------------------------------------- /lib/weltmeister/nopache-modules/weltmeister-api.js: -------------------------------------------------------------------------------- 1 | /* 2 | Author: Maverick Peppers 3 | Github: TheMaverickProgrammer 4 | Webstite: maverickpeppers.com 5 | Module: Weltmeister-api 6 | 7 | This module exports an object that contain the functions used at the endpoints 8 | at localhost/wm/api/browse, localhost/wm/api/glob, localhost/wm/api/save 9 | which are configured by the Welmeister-api-router module. 10 | */ 11 | module.exports = function(conf) { 12 | 13 | // Make available for all functions 14 | var public = {conf: conf}; 15 | 16 | // dependencies 17 | var glob = require('glob'); 18 | var braces = require('braces'); 19 | var fs = require('fs'); 20 | var chalk = require('chalk'); 21 | 22 | // aux function 23 | // references https://github.com/kvz/locutus/blob/master/src/php/var/empty.js 24 | function empty(mixedVar) { 25 | var undef 26 | var key 27 | var i 28 | var len 29 | var emptyValues = [undef, null, false, 0, '', '0'] 30 | 31 | for (i = 0, len = emptyValues.length; i < len; i++) { 32 | if (mixedVar === emptyValues[i]) { 33 | return true 34 | } 35 | } 36 | 37 | if (typeof mixedVar === 'object') { 38 | for (key in mixedVar) { 39 | if (mixedVar.hasOwnProperty(key)) { 40 | return false 41 | } 42 | } 43 | return true 44 | } 45 | 46 | return false 47 | } 48 | 49 | /* 50 | Browse fetches all files within a directory and is filtered by type. 51 | The type is read from req.query.dir and can have one of the following values: 52 | 1 - images (.png, .gif, .jpg, .jpeg) 53 | 2 - scripts (.js) 54 | */ 55 | this.Browse = function(req) { 56 | var dir = ""; 57 | if(!empty(req.query.dir)) { 58 | req.query.dir = String(req.query.dir); 59 | dir = req.query.dir.replace( '..', ''); 60 | } 61 | 62 | if( dir[dir.length-1] != '/' ) { 63 | dir += '/'; 64 | } 65 | 66 | var find = '*.*'; 67 | switch(req.query.type) { 68 | case 'images': 69 | find = braces('*.{png,gif,jpg,jpeg}'); 70 | break; 71 | case 'scripts': 72 | find = ['*.js']; 73 | break; 74 | } 75 | 76 | var dirs = []; 77 | try { 78 | dirs = glob.sync(dir + "*/", {mark: true}); 79 | } catch(e) { 80 | console.log(chalk.yellow("Welmeister caught an exception: ") + e); 81 | dirs = []; 82 | } 83 | 84 | var files = []; 85 | for(var f = 0; f < find.length; f++) { 86 | try { 87 | var matchingFiles = glob.sync(dir + find[f]); 88 | files = files.concat(matchingFiles); 89 | } catch(e) { 90 | console.log(chalk.yellow("Welmeister caught an exception: ") + e); 91 | files = []; 92 | } 93 | } 94 | 95 | /* 96 | var fileRootLength = public.conf.WM_ROOT_DIR.length; 97 | for(var i = 0; i < files.length; i++) { 98 | files[i] = files[i].substring(0,fileRootLength); 99 | } 100 | */ 101 | 102 | for(var j = 0; j < dirs.length; j++) { 103 | // ! This is needed ! 104 | dirs[j] = dirs[j].substr(0, dirs[j].length-1); // drop the '/' character 105 | } 106 | 107 | // substr at second-to-last '/' 108 | var parent = req.query.dir.substring(0, req.query.dir.lastIndexOf('/')); 109 | 110 | var result = { 111 | 'parent': empty(req.query.dir)? false : parent, 112 | 'dirs': dirs, 113 | 'files': files 114 | }; 115 | 116 | return JSON.stringify(result); 117 | } 118 | 119 | /* 120 | Glob fetches the entity files used in the requested level 121 | */ 122 | this.Glob = function(req) { 123 | var files = []; 124 | 125 | if(!empty(req.query.glob)) { 126 | var globs = Array.isArray(req.query.glob)? req.query.glob : [req.query.glob]; 127 | 128 | for(var g = 0; g < globs.length; g++) { 129 | pattern = "" + globs[g].replace('..', ''); 130 | try { 131 | var currentFiles = glob.sync(pattern); // glob returns array 132 | files = files.concat(currentFiles); 133 | } catch(e) { 134 | currentFiles = false; 135 | console.log(chalk.yellow("Welmeister caught an exception: ") + e); 136 | } 137 | } 138 | } 139 | //console.log("JSON.stringify(files): " + JSON.stringify(files)); 140 | return JSON.stringify(files); 141 | } 142 | 143 | /* 144 | Save writes level data to a file. Will return errors if: 145 | 1 - The file failed to write (directory doesn't exist) 146 | 2 - File does not have a .js suffix 147 | 3 - No data is found in the level workspace 148 | 4 - No path was specified 149 | */ 150 | this.Save = function(req) { 151 | result = {'error': 0}; 152 | 153 | if( !empty(req.body.path) && !empty(req.body.data) ) { 154 | var path = public.conf.WM_ROOT_DIR + String(req.body.path).replace('..', ''); 155 | 156 | if(path.match(/.*\.js/g)) { 157 | try { 158 | success = fs.writeFileSync( path, req.body.data ); 159 | } catch(e) { 160 | success = false; 161 | console.log(chalk.yellow("Welmeister caught an exception: ") + e); 162 | } 163 | 164 | if(success === false) { 165 | result = { 166 | 'error': '2', 167 | 'msg': "Couldn't write to file: " + path 168 | }; 169 | } 170 | } else { // end if match js 171 | result = { 172 | 'error': '3', 173 | 'msg': "File must have a .js suffix" 174 | }; 175 | } 176 | } else { // end if path or data has value 177 | result = { 178 | 'error': '1', 179 | 'msg': "No Data or Path specified" 180 | }; 181 | } 182 | return(JSON.stringify(result)); 183 | } 184 | return this; 185 | } 186 | -------------------------------------------------------------------------------- /lib/weltmeister/undo.js: -------------------------------------------------------------------------------- 1 | ig.module( 2 | 'weltmeister.undo' 3 | ) 4 | .requires( 5 | 'weltmeister.config' 6 | ) 7 | .defines(function(){ "use strict"; 8 | 9 | 10 | wm.Undo = ig.Class.extend({ 11 | levels: 10, 12 | chain: [], 13 | rpos: 0, 14 | currentAction: null, 15 | 16 | init: function( levels ) { 17 | this.levels = levels || 10; 18 | }, 19 | 20 | 21 | clear: function() { 22 | this.chain = []; 23 | this.currentAction = null; 24 | }, 25 | 26 | 27 | commit: function( action ) { 28 | if( this.rpos ) { 29 | this.chain.splice( this.chain.length - this.rpos, this.rpos ); 30 | this.rpos = 0; 31 | } 32 | action.activeLayer = ig.game.activeLayer ? ig.game.activeLayer.name : ''; 33 | this.chain.push( action ); 34 | if( this.chain.length > this.levels ) { 35 | this.chain.shift(); 36 | } 37 | }, 38 | 39 | 40 | undo: function() { 41 | var action = this.chain[ this.chain.length - this.rpos - 1 ]; 42 | if( !action ) { 43 | return; 44 | } 45 | this.rpos++; 46 | 47 | 48 | ig.game.setActiveLayer( action.activeLayer ); 49 | 50 | if( action.type == wm.Undo.MAP_DRAW ) { 51 | for( var i = 0; i < action.changes.length; i++ ) { 52 | var change = action.changes[i]; 53 | change.layer.setTile( change.x, change.y, change.old ); 54 | } 55 | } 56 | else if( action.type == wm.Undo.ENTITY_EDIT ) { 57 | action.entity.pos.x = action.old.x; 58 | action.entity.pos.y = action.old.y; 59 | action.entity.size.x = action.old.w; 60 | action.entity.size.y = action.old.h; 61 | ig.game.entities.selectEntity( action.entity ); 62 | ig.game.entities.loadEntitySettings(); 63 | } 64 | else if( action.type == wm.Undo.ENTITY_CREATE ) { 65 | ig.game.entities.removeEntity( action.entity ); 66 | ig.game.entities.selectEntity( null ); 67 | } 68 | else if( action.type == wm.Undo.ENTITY_DELETE ) { 69 | ig.game.entities.entities.push( action.entity ); 70 | if( action.entity.name ) { 71 | this.namedEntities[action.entity.name] = action.entity; 72 | } 73 | ig.game.entities.selectEntity( action.entity ); 74 | } 75 | 76 | ig.game.setModified(); 77 | }, 78 | 79 | 80 | redo: function() { 81 | if( !this.rpos ) { 82 | return; 83 | } 84 | 85 | var action = this.chain[ this.chain.length - this.rpos ]; 86 | if( !action ) { 87 | return; 88 | } 89 | this.rpos--; 90 | 91 | 92 | ig.game.setActiveLayer( action.activeLayer ); 93 | 94 | if( action.type == wm.Undo.MAP_DRAW ) { 95 | for( var i = 0; i < action.changes.length; i++ ) { 96 | var change = action.changes[i]; 97 | change.layer.setTile( change.x, change.y, change.current ); 98 | } 99 | } 100 | else if( action.type == wm.Undo.ENTITY_EDIT ) { 101 | action.entity.pos.x = action.current.x; 102 | action.entity.pos.y = action.current.y; 103 | action.entity.size.x = action.current.w; 104 | action.entity.size.y = action.current.h; 105 | ig.game.entities.selectEntity( action.entity ); 106 | ig.game.entities.loadEntitySettings(); 107 | } 108 | else if( action.type == wm.Undo.ENTITY_CREATE ) { 109 | ig.game.entities.entities.push( action.entity ); 110 | if( action.entity.name ) { 111 | this.namedEntities[action.entity.name] = action.entity; 112 | } 113 | ig.game.entities.selectEntity( action.entity ); 114 | } 115 | else if( action.type == wm.Undo.ENTITY_DELETE ) { 116 | ig.game.entities.removeEntity( action.entity ); 117 | ig.game.entities.selectEntity( null ); 118 | } 119 | 120 | ig.game.setModified(); 121 | }, 122 | 123 | 124 | // ------------------------------------------------------------------------- 125 | // Map changes 126 | 127 | beginMapDraw: function( layer ) { 128 | this.currentAction = { 129 | type: wm.Undo.MAP_DRAW, 130 | time: Date.now(), 131 | changes: [] 132 | }; 133 | }, 134 | 135 | pushMapDraw: function( layer, x, y, oldTile, currentTile ) { 136 | if( !this.currentAction ) { 137 | return; 138 | } 139 | 140 | this.currentAction.changes.push({ 141 | layer: layer, 142 | x: x, 143 | y: y, 144 | old: oldTile, 145 | current: currentTile 146 | }); 147 | }, 148 | 149 | endMapDraw: function() { 150 | if( !this.currentAction || !this.currentAction.changes.length ) { 151 | return; 152 | } 153 | 154 | this.commit( this.currentAction ); 155 | this.currentAction = null; 156 | }, 157 | 158 | 159 | // ------------------------------------------------------------------------- 160 | // Entity changes 161 | 162 | beginEntityEdit: function( entity ) { 163 | this.currentAction = { 164 | type: wm.Undo.ENTITY_EDIT, 165 | time: Date.now(), 166 | entity: entity, 167 | old: { 168 | x: entity.pos.x, 169 | y: entity.pos.y, 170 | w: entity.size.x, 171 | h: entity.size.y 172 | }, 173 | current: { 174 | x: entity.pos.x, 175 | y: entity.pos.y, 176 | w: entity.size.x, 177 | h: entity.size.y 178 | } 179 | }; 180 | }, 181 | 182 | pushEntityEdit: function( entity ) { 183 | if( !this.currentAction ) { 184 | return; 185 | } 186 | 187 | this.currentAction.current = { 188 | x: entity.pos.x, 189 | y: entity.pos.y, 190 | w: entity.size.x, 191 | h: entity.size.y 192 | }; 193 | }, 194 | 195 | 196 | endEntityEdit: function() { 197 | var a = this.currentAction; 198 | 199 | if( !a || ( 200 | a.old.x == a.current.x && a.old.y == a.current.y && 201 | a.old.w == a.current.w && a.old.h == a.current.h 202 | )) { 203 | return; 204 | } 205 | 206 | this.commit( this.currentAction ); 207 | this.currentAction = null; 208 | }, 209 | 210 | 211 | commitEntityCreate: function( entity ) { 212 | this.commit({ 213 | type: wm.Undo.ENTITY_CREATE, 214 | time: Date.now(), 215 | entity: entity 216 | }); 217 | }, 218 | 219 | 220 | commitEntityDelete: function( entity ) { 221 | this.commit({ 222 | type: wm.Undo.ENTITY_DELETE, 223 | time: Date.now(), 224 | entity: entity 225 | }); 226 | } 227 | }); 228 | 229 | wm.Undo.MAP_DRAW = 1; 230 | wm.Undo.ENTITY_EDIT = 2; 231 | wm.Undo.ENTITY_CREATE = 3; 232 | wm.Undo.ENTITY_DELETE = 4; 233 | 234 | }); -------------------------------------------------------------------------------- /tools/jsmin.php: -------------------------------------------------------------------------------- 1 | 41 | * @copyright 2002 Douglas Crockford (jsmin.c) 42 | * @copyright 2008 Ryan Grove (PHP port) 43 | * @license http://opensource.org/licenses/mit-license.php MIT License 44 | * @version 1.1.1 (2008-03-02) 45 | * @link http://code.google.com/p/jsmin-php/ 46 | */ 47 | 48 | class JSMin { 49 | const ORD_LF = 10; 50 | const ORD_SPACE = 32; 51 | 52 | protected $a = ''; 53 | protected $b = ''; 54 | protected $input = ''; 55 | protected $inputIndex = 0; 56 | protected $inputLength = 0; 57 | protected $lookAhead = null; 58 | protected $output = ''; 59 | 60 | // -- Public Static Methods -------------------------------------------------- 61 | 62 | public static function minify($js) { 63 | $jsmin = new JSMin($js); 64 | return $jsmin->min(); 65 | } 66 | 67 | // -- Public Instance Methods ------------------------------------------------ 68 | 69 | public function __construct($input) { 70 | $this->input = str_replace("\r\n", "\n", $input); 71 | $this->inputLength = strlen($this->input); 72 | } 73 | 74 | // -- Protected Instance Methods --------------------------------------------- 75 | 76 | protected function action($d) { 77 | switch($d) { 78 | case 1: 79 | $this->output .= $this->a; 80 | 81 | case 2: 82 | $this->a = $this->b; 83 | 84 | if ($this->a === "'" || $this->a === '"') { 85 | for (;;) { 86 | $this->output .= $this->a; 87 | $this->a = $this->get(); 88 | 89 | if ($this->a === $this->b) { 90 | break; 91 | } 92 | 93 | if (ord($this->a) <= self::ORD_LF) { 94 | throw new JSMinException('Unterminated string literal.'); 95 | } 96 | 97 | if ($this->a === '\\') { 98 | $this->output .= $this->a; 99 | $this->a = $this->get(); 100 | } 101 | } 102 | } 103 | 104 | case 3: 105 | $this->b = $this->next(); 106 | 107 | if ($this->b === '/' && ( 108 | $this->a === '(' || $this->a === ',' || $this->a === '=' || 109 | $this->a === ':' || $this->a === '[' || $this->a === '!' || 110 | $this->a === '&' || $this->a === '|' || $this->a === '?')) { 111 | 112 | $this->output .= $this->a . $this->b; 113 | 114 | for (;;) { 115 | $this->a = $this->get(); 116 | 117 | if ($this->a === '/') { 118 | break; 119 | } elseif ($this->a === '\\') { 120 | $this->output .= $this->a; 121 | $this->a = $this->get(); 122 | } elseif (ord($this->a) <= self::ORD_LF) { 123 | throw new JSMinException('Unterminated regular expression '. 124 | 'literal.'); 125 | } 126 | 127 | $this->output .= $this->a; 128 | } 129 | 130 | $this->b = $this->next(); 131 | } 132 | } 133 | } 134 | 135 | protected function get() { 136 | $c = $this->lookAhead; 137 | $this->lookAhead = null; 138 | 139 | if ($c === null) { 140 | if ($this->inputIndex < $this->inputLength) { 141 | $c = $this->input[$this->inputIndex]; 142 | $this->inputIndex += 1; 143 | } else { 144 | $c = null; 145 | } 146 | } 147 | 148 | if ($c === "\r") { 149 | return "\n"; 150 | } 151 | 152 | if ($c === null || $c === "\n" || ord($c) >= self::ORD_SPACE) { 153 | return $c; 154 | } 155 | 156 | return ' '; 157 | } 158 | 159 | protected function isAlphaNum($c) { 160 | return ord($c) > 126 || $c === '\\' || preg_match('/^[\w\$]$/', $c) === 1; 161 | } 162 | 163 | protected function min() { 164 | $this->a = "\n"; 165 | $this->action(3); 166 | 167 | while ($this->a !== null) { 168 | switch ($this->a) { 169 | case ' ': 170 | if ($this->isAlphaNum($this->b)) { 171 | $this->action(1); 172 | } else { 173 | $this->action(2); 174 | } 175 | break; 176 | 177 | case "\n": 178 | switch ($this->b) { 179 | case '{': 180 | case '[': 181 | case '(': 182 | case '+': 183 | case '-': 184 | $this->action(1); 185 | break; 186 | 187 | case ' ': 188 | $this->action(3); 189 | break; 190 | 191 | default: 192 | if ($this->isAlphaNum($this->b)) { 193 | $this->action(1); 194 | } 195 | else { 196 | $this->action(2); 197 | } 198 | } 199 | break; 200 | 201 | default: 202 | switch ($this->b) { 203 | case ' ': 204 | if ($this->isAlphaNum($this->a)) { 205 | $this->action(1); 206 | break; 207 | } 208 | 209 | $this->action(3); 210 | break; 211 | 212 | case "\n": 213 | switch ($this->a) { 214 | case '}': 215 | case ']': 216 | case ')': 217 | case '+': 218 | case '-': 219 | case '"': 220 | case "'": 221 | $this->action(1); 222 | break; 223 | 224 | default: 225 | if ($this->isAlphaNum($this->a)) { 226 | $this->action(1); 227 | } 228 | else { 229 | $this->action(3); 230 | } 231 | } 232 | break; 233 | 234 | default: 235 | $this->action(1); 236 | break; 237 | } 238 | } 239 | } 240 | 241 | return $this->output; 242 | } 243 | 244 | protected function next() { 245 | $c = $this->get(); 246 | 247 | if ($c === '/') { 248 | switch($this->peek()) { 249 | case '/': 250 | for (;;) { 251 | $c = $this->get(); 252 | 253 | if (ord($c) <= self::ORD_LF) { 254 | return $c; 255 | } 256 | } 257 | 258 | case '*': 259 | $this->get(); 260 | 261 | for (;;) { 262 | switch($this->get()) { 263 | case '*': 264 | if ($this->peek() === '/') { 265 | $this->get(); 266 | return ' '; 267 | } 268 | break; 269 | 270 | case null: 271 | throw new JSMinException('Unterminated comment.'); 272 | } 273 | } 274 | 275 | default: 276 | return $c; 277 | } 278 | } 279 | 280 | return $c; 281 | } 282 | 283 | protected function peek() { 284 | $this->lookAhead = $this->get(); 285 | return $this->lookAhead; 286 | } 287 | } 288 | 289 | // -- Exceptions --------------------------------------------------------------- 290 | class JSMinException extends Exception {} 291 | ?> -------------------------------------------------------------------------------- /lib/weltmeister/edit-map.js: -------------------------------------------------------------------------------- 1 | ig.module( 2 | 'weltmeister.edit-map' 3 | ) 4 | .requires( 5 | 'impact.background-map', 6 | 'weltmeister.tile-select' 7 | ) 8 | .defines(function(){ "use strict"; 9 | 10 | wm.EditMap = ig.BackgroundMap.extend({ 11 | name: '', 12 | visible: true, 13 | active: true, 14 | linkWithCollision: false, 15 | 16 | div: null, 17 | brush: [[0]], 18 | oldData: null, 19 | hotkey: -1, 20 | ignoreLastClick: false, 21 | tileSelect: null, 22 | 23 | isSelecting: false, 24 | selectionBegin: null, 25 | 26 | init: function( name, tilesize, tileset, foreground ) { 27 | this.name = name; 28 | this.parent( tilesize, [[0]], tileset || '' ); 29 | this.foreground = foreground; 30 | 31 | this.div = $( '
', { 32 | 'class': 'layer layerActive', 33 | 'id': ('layer_' + name), 34 | 'mouseup': this.click.bind(this) 35 | }); 36 | this.setName( name ); 37 | if( this.foreground ) { 38 | $('#layers').prepend( this.div ); 39 | } 40 | else { 41 | $('#layerEntities').after( this.div ); 42 | } 43 | 44 | this.tileSelect = new wm.TileSelect( this ); 45 | }, 46 | 47 | 48 | getSaveData: function() { 49 | return { 50 | name: this.name, 51 | width: this.width, 52 | height: this.height, 53 | linkWithCollision: this.linkWithCollision, 54 | visible: this.visible, 55 | tilesetName: this.tilesetName, 56 | repeat: this.repeat, 57 | preRender: this.preRender, 58 | distance: this.distance, 59 | tilesize: this.tilesize, 60 | foreground: this.foreground, 61 | data: this.data 62 | }; 63 | }, 64 | 65 | 66 | resize: function( newWidth, newHeight ) { 67 | var newData = new Array( newHeight ); 68 | for( var y = 0; y < newHeight; y++ ) { 69 | newData[y] = new Array( newWidth ); 70 | for( var x = 0; x < newWidth; x++ ) { 71 | newData[y][x] = (x < this.width && y < this.height) ? this.data[y][x] : 0; 72 | } 73 | } 74 | this.data = newData; 75 | this.width = newWidth; 76 | this.height = newHeight; 77 | 78 | this.resetDiv(); 79 | }, 80 | 81 | beginEditing: function() { 82 | this.oldData = ig.copy(this.data); 83 | }, 84 | 85 | getOldTile: function( x, y ) { 86 | var tx = Math.floor( x / this.tilesize ); 87 | var ty = Math.floor( y / this.tilesize ); 88 | if( 89 | (tx >= 0 && tx < this.width) && 90 | (ty >= 0 && ty < this.height) 91 | ) { 92 | return this.oldData[ty][tx]; 93 | } 94 | else { 95 | return 0; 96 | } 97 | }, 98 | 99 | setTileset: function( tileset ) { 100 | if( this.name == 'collision' ) { 101 | this.setCollisionTileset(); 102 | } 103 | else { 104 | this.parent( tileset ); 105 | } 106 | }, 107 | 108 | 109 | setCollisionTileset: function() { 110 | var path = wm.config.collisionTiles.path; 111 | var scale = this.tilesize / wm.config.collisionTiles.tilesize; 112 | this.tiles = new ig.AutoResizedImage( path, scale ); 113 | }, 114 | 115 | 116 | 117 | 118 | 119 | // ------------------------------------------------------------------------- 120 | // UI 121 | 122 | setHotkey: function( hotkey ) { 123 | this.hotkey = hotkey; 124 | this.setName( this.name ); 125 | }, 126 | 127 | 128 | setName: function( name ) { 129 | this.name = name.replace(/[^0-9a-zA-Z]/g, '_'); 130 | this.resetDiv(); 131 | }, 132 | 133 | 134 | resetDiv: function() { 135 | var visClass = this.visible ? ' checkedVis' : ''; 136 | this.div.html( 137 | '' + 138 | '' + this.name + '' + 139 | ' (' + this.width + 'x' + this.height + ')' 140 | ); 141 | this.div.attr('title', 'Select Layer ('+this.hotkey+')' ); 142 | this.div.children('.visible').bind('mousedown', this.toggleVisibilityClick.bind(this) ); 143 | }, 144 | 145 | 146 | setActive: function( active ) { 147 | this.active = active; 148 | if( active ) { 149 | this.div.addClass( 'layerActive' ); 150 | } else { 151 | this.div.removeClass( 'layerActive' ); 152 | } 153 | }, 154 | 155 | 156 | toggleVisibility: function() { 157 | this.visible ^= 1; 158 | this.resetDiv(); 159 | if( this.visible ) { 160 | this.div.children('.visible').addClass('checkedVis'); 161 | } else { 162 | this.div.children('.visible').removeClass('checkedVis'); 163 | } 164 | ig.game.draw(); 165 | }, 166 | 167 | 168 | toggleVisibilityClick: function( event ) { 169 | if( !this.active ) { 170 | this.ignoreLastClick = true; 171 | } 172 | this.toggleVisibility() 173 | }, 174 | 175 | 176 | click: function() { 177 | if( this.ignoreLastClick ) { 178 | this.ignoreLastClick = false; 179 | return; 180 | } 181 | ig.editor.setActiveLayer( this.name ); 182 | }, 183 | 184 | 185 | destroy: function() { 186 | this.div.remove(); 187 | }, 188 | 189 | 190 | 191 | // ------------------------------------------------------------------------- 192 | // Selecting 193 | 194 | beginSelecting: function( x, y ) { 195 | this.isSelecting = true; 196 | this.selectionBegin = {x:x, y:y}; 197 | }, 198 | 199 | 200 | endSelecting: function( x, y ) { 201 | var r = this.getSelectionRect( x, y); 202 | 203 | var brush = []; 204 | for( var ty = r.y; ty < r.y+r.h; ty++ ) { 205 | var row = []; 206 | for( var tx = r.x; tx < r.x+r.w; tx++ ) { 207 | if( tx < 0 || ty < 0 || tx >= this.width || ty >= this.height ) { 208 | row.push( 0 ); 209 | } 210 | else { 211 | row.push( this.data[ty][tx] ); 212 | } 213 | } 214 | brush.push( row ); 215 | } 216 | this.isSelecting = false; 217 | this.selectionBegin = null; 218 | return brush; 219 | }, 220 | 221 | 222 | getSelectionRect: function( x, y ) { 223 | var sx = this.selectionBegin ? this.selectionBegin.x : x, 224 | sy = this.selectionBegin ? this.selectionBegin.y : y; 225 | 226 | var 227 | txb = Math.floor( (sx + this.scroll.x) / this.tilesize ), 228 | tyb = Math.floor( (sy + this.scroll.y) / this.tilesize ), 229 | txe = Math.floor( (x + this.scroll.x) / this.tilesize ), 230 | tye = Math.floor( (y + this.scroll.y) / this.tilesize ); 231 | 232 | return { 233 | x: Math.min( txb, txe ), 234 | y: Math.min( tyb, tye ), 235 | w: Math.abs( txb - txe) + 1, 236 | h: Math.abs( tyb - tye) + 1 237 | } 238 | }, 239 | 240 | 241 | 242 | 243 | // ------------------------------------------------------------------------- 244 | // Drawing 245 | 246 | draw: function() { 247 | // For performance reasons, repeated background maps are not drawn 248 | // when zoomed out 249 | if( this.visible && !(wm.config.view.zoom < 1 && this.repeat) ) { 250 | this.drawTiled(); 251 | } 252 | 253 | // Grid 254 | if( this.active && wm.config.view.grid ) { 255 | 256 | var x = -ig.system.getDrawPos(this.scroll.x % this.tilesize) - 0.5; 257 | var y = -ig.system.getDrawPos(this.scroll.y % this.tilesize) - 0.5; 258 | var step = this.tilesize * ig.system.scale; 259 | 260 | ig.system.context.beginPath(); 261 | for( x; x < ig.system.realWidth; x += step ) { 262 | ig.system.context.moveTo( x, 0 ); 263 | ig.system.context.lineTo( x, ig.system.realHeight ); 264 | } 265 | for( y; y < ig.system.realHeight; y += step ) { 266 | ig.system.context.moveTo( 0, y ); 267 | ig.system.context.lineTo( ig.system.realWidth, y ); 268 | } 269 | ig.system.context.strokeStyle = wm.config.colors.secondary; 270 | ig.system.context.stroke(); 271 | ig.system.context.closePath(); 272 | 273 | // Not calling beginPath() again has some weird performance issues 274 | // in Firefox 5. closePath has no effect. So to make it happy: 275 | ig.system.context.beginPath(); 276 | } 277 | 278 | // Bounds 279 | if( this.active ) { 280 | ig.system.context.lineWidth = 1; 281 | ig.system.context.strokeStyle = wm.config.colors.primary; 282 | ig.system.context.strokeRect( 283 | -ig.system.getDrawPos(this.scroll.x) - 0.5, 284 | -ig.system.getDrawPos(this.scroll.y) - 0.5, 285 | this.width * this.tilesize * ig.system.scale + 1, 286 | this.height * this.tilesize * ig.system.scale + 1 287 | ); 288 | } 289 | }, 290 | 291 | getCursorOffset: function() { 292 | var w = this.brush[0].length; 293 | var h = this.brush.length; 294 | 295 | //return {x:0, y:0}; 296 | return { 297 | x: (w/2-0.5).toInt() * this.tilesize, 298 | y: (h/2-0.5).toInt() * this.tilesize 299 | } 300 | }, 301 | 302 | drawCursor: function( x, y ) { 303 | if( this.isSelecting ) { 304 | var r = this.getSelectionRect( x, y); 305 | 306 | ig.system.context.lineWidth = 1; 307 | ig.system.context.strokeStyle = wm.config.colors.selection; 308 | ig.system.context.strokeRect( 309 | (r.x * this.tilesize - this.scroll.x) * ig.system.scale - 0.5, 310 | (r.y * this.tilesize - this.scroll.y) * ig.system.scale - 0.5, 311 | r.w * this.tilesize * ig.system.scale + 1, 312 | r.h * this.tilesize * ig.system.scale + 1 313 | ); 314 | } 315 | else { 316 | var w = this.brush[0].length; 317 | var h = this.brush.length; 318 | 319 | var co = this.getCursorOffset(); 320 | 321 | var cx = Math.floor( (x+this.scroll.x) / this.tilesize ) * this.tilesize - this.scroll.x - co.x; 322 | var cy = Math.floor( (y+this.scroll.y) / this.tilesize ) * this.tilesize - this.scroll.y - co.y; 323 | 324 | ig.system.context.lineWidth = 1; 325 | ig.system.context.strokeStyle = wm.config.colors.primary; 326 | ig.system.context.strokeRect( 327 | ig.system.getDrawPos(cx)-0.5, 328 | ig.system.getDrawPos(cy)-0.5, 329 | w * this.tilesize * ig.system.scale + 1, 330 | h * this.tilesize * ig.system.scale + 1 331 | ); 332 | 333 | ig.system.context.globalAlpha = 0.5; 334 | for( var ty = 0; ty < h; ty++ ) { 335 | for( var tx = 0; tx < w; tx++ ) { 336 | var t = this.brush[ty][tx]; 337 | if( t ) { 338 | var px = cx + tx * this.tilesize; 339 | var py = cy + ty * this.tilesize; 340 | this.tiles.drawTile( px, py, t-1, this.tilesize ); 341 | } 342 | } 343 | } 344 | ig.system.context.globalAlpha = 1; 345 | } 346 | } 347 | }); 348 | 349 | 350 | ig.AutoResizedImage = ig.Image.extend({ 351 | internalScale: 1, 352 | 353 | staticInstantiate: function() { 354 | return null; // Never cache! 355 | }, 356 | 357 | init: function( path, internalScale ) { 358 | this.internalScale = internalScale; 359 | this.parent( path ); 360 | }, 361 | 362 | onload: function( event ) { 363 | this.width = Math.ceil(this.data.width * this.internalScale); 364 | this.height = Math.ceil(this.data.height * this.internalScale); 365 | 366 | if( this.internalScale != 1 ) { 367 | var scaled = ig.$new('canvas'); 368 | scaled.width = this.width; 369 | scaled.height = this.height; 370 | var scaledCtx = scaled.getContext('2d'); 371 | 372 | scaledCtx.drawImage( this.data, 0, 0, this.data.width, this.data.height, 0, 0, this.width , this.height ); 373 | this.data = scaled; 374 | } 375 | 376 | this.loaded = true; 377 | if( ig.system.scale != 1 ) { 378 | this.resize( ig.system.scale ); 379 | } 380 | 381 | if( this.loadCallback ) { 382 | this.loadCallback( this.path, true ); 383 | } 384 | } 385 | }); 386 | 387 | 388 | }); -------------------------------------------------------------------------------- /lib/weltmeister/weltmeister.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #000; 3 | color: #fff; 4 | font-family: sans-serif; 5 | font-size: 10pt; 6 | margin: 0px; 7 | overflow: hidden; 8 | text-shadow: 0px 1px 1px rgba(0,0,0,0.5); 9 | -webkit-font-smoothing: antialiased; 10 | -webkit-user-select: none; 11 | } 12 | 13 | h2 { 14 | margin: 0 0 4px 0; 15 | padding: 4px 0 4px 6px; 16 | background-color: #000; 17 | font-size: 100%; 18 | color: #555; 19 | xtext-transform: uppercase; 20 | xborder-bottom: 1px solid #555; 21 | } 22 | 23 | h3 { 24 | margin: 0; 25 | font-size: 100%; 26 | display: block; 27 | } 28 | 29 | dt { 30 | margin: 0; 31 | padding: 4px 0 0 6px; 32 | display:inline; 33 | float:left; 34 | margin-right:5px; 35 | } 36 | 37 | dd { 38 | margin: 0; 39 | padding: 2px 0 8px 6px; 40 | } 41 | 42 | dl { 43 | margin:0; 44 | } 45 | 46 | div.clear { 47 | clear: both; 48 | } 49 | 50 | label { 51 | cursor: pointer; 52 | } 53 | 54 | /* --- Input ------------------------------------------------------------------ */ 55 | 56 | input { 57 | background-color: rgba(0,0,0,0.5); 58 | border: 1px solid rgb(50,50,50); 59 | color: #fff; 60 | margin: 0; 61 | font-family: sans-serif; 62 | -webkit-font-smoothing: antialiased; 63 | font-size: 10pt; 64 | outline: none; 65 | text-shadow: 0px 1px 1px rgba(0,0,0,0.5); 66 | 67 | } 68 | 69 | input:focus{ 70 | border: 1px solid rgb(200,200,200); 71 | } 72 | 73 | input.text { 74 | padding: 1px; 75 | margin: 0; 76 | } 77 | 78 | input.number { 79 | width: 30px; 80 | text-align: right; 81 | } 82 | 83 | input.button { 84 | font-size: 90%; 85 | padding-left: 13px; 86 | padding-right: 13px; 87 | padding-top: 5px; 88 | padding-bottom: 5px; 89 | color: #fff; 90 | font-weight: bold; 91 | background-color: rgba(255,255,255,0.1); 92 | border:none; 93 | border-top: 1px solid rgba(255,255,255,0.1); 94 | border-bottom: 1px solid rgba(0,0,0,0.1); 95 | cursor: pointer; 96 | -webkit-transition: 0.1s linear; 97 | } 98 | 99 | input.button:hover { 100 | background-color: rgba(255,255,255,0.2); 101 | } 102 | 103 | input.button:active { 104 | background-color: rgba(255,255,255,0.3); 105 | } 106 | 107 | input.text#layerName { 108 | width:140px; 109 | } 110 | 111 | input.text#layerTileset { 112 | width:138px; 113 | } 114 | 115 | input#levelSaveAs { margin-right: 10px; } 116 | input#levelLoad { margin-right: 10px; } 117 | input#reloadImages {margin-right: 10px;} 118 | 119 | input:disabled { 120 | background-color: #555; 121 | color: #888; 122 | } 123 | 124 | /* --- Layout ------------------------------------------------------------------ */ 125 | 126 | #editor { 127 | margin: 0px; 128 | position: relative; 129 | } 130 | 131 | #canvas { 132 | image-rendering: optimizeSpeed; 133 | -webkit-interpolation-mode: nearest-neighbor; 134 | } 135 | 136 | #menu { 137 | width: 200px; 138 | float: right; 139 | position:absolute; 140 | top:0px; 141 | right:0px; 142 | } 143 | 144 | /* --- Layers ------------------------------------------------------------------ */ 145 | 146 | #layerContainer { 147 | background-color: rgba(0,0,0,0.95); 148 | padding-right:2px; 149 | } 150 | 151 | #layers { 152 | max-height: 200px; 153 | overflow: auto; 154 | } 155 | 156 | #layerButtons div.button#buttonAddLayer { 157 | position:absolute; 158 | right: 0px; 159 | top:-5px; 160 | cursor: pointer; 161 | padding: 10px; 162 | color:rgba(255,255,255,0.5); 163 | font-weight: bold; 164 | font-size:110%; 165 | margin-left:1px; 166 | -webkit-transition: 0.1s linear; 167 | -webkit-font-smoothing: none; 168 | z-index:10; 169 | } 170 | #layerButtons div.button#buttonAddLayer:hover { color:rgba(255,255,255,1);} 171 | #layerButtons div.button#buttonAddLayer:active { color:rgba(255,255,255,1); text-shadow:none;} 172 | 173 | .layer { 174 | padding: 6px 4px; 175 | cursor: pointer; 176 | -webkit-transition: background-color 0.1s linear; 177 | border-top: 1px solid rgba(255,255,255,0); 178 | border-bottom: 1px solid rgba(255,255,255,0); 179 | } 180 | 181 | .layer:hover { 182 | background-color: rgba(255,255,255,0.1); 183 | border-top: 1px solid rgba(255,255,255,0.1); 184 | border-bottom: 1px solid rgba(255,255,255,0.1); 185 | } 186 | 187 | .layer:active { 188 | background-color: rgba(255,255,255,0.2); 189 | border-top: 1px solid rgba(255,255,255,0.2); 190 | border-bottom: 1px solid rgba(255,255,255,0.2); 191 | } 192 | 193 | .layerActive { 194 | background-color: rgba(255,255,255,0.1); 195 | border-top: 1px solid rgba(255,255,255,0.2); 196 | border-bottom: 1px solid rgba(255,255,255,0.2) !important; 197 | } 198 | 199 | 200 | #layerEntities { border-bottom: 1px solid #000; } 201 | 202 | .layer .visible { 203 | background-color: rgba(255,255,255,0.2); 204 | text-indent: -99999px; 205 | display: inline-block; 206 | width: 10px; 207 | height: 10px; 208 | margin-right: 7px; 209 | margin-left: 4px; 210 | -webkit-transition: 0.1s linear; 211 | } 212 | 213 | .layer .visible.specialVis{ 214 | margin-right: 2px; 215 | } 216 | 217 | .layer .checkedVis{ background-color: rgba(255,255,255,1); } 218 | .layer span.size { font-size: 75%; color: rgba(255,255,255,0.7); } 219 | 220 | #layerSettings { 221 | background-color: rgba(0,0,0,0.95); 222 | padding-top: 5px; 223 | margin-top: 1px; 224 | display: none; 225 | } 226 | 227 | /* --- Entities ------------------------------------------------------------------ */ 228 | h3#entityClass { 229 | border-bottom: 1px solid rgba(255,255,255,0.2); 230 | padding: 5px; 231 | padding-left: 10px; 232 | } 233 | 234 | #entitySettings { 235 | background-color: rgba(0,0,0,0.95); 236 | margin-top: 1px; 237 | display: none; 238 | } 239 | 240 | #entityDefinitions { 241 | max-height: 220px; 242 | overflow: auto; 243 | } 244 | 245 | div.entityDefinition { 246 | color: #aaa; 247 | padding: 2px 0; 248 | border-bottom: 1px solid rgba(255,255,255,0.2); 249 | cursor: pointer; 250 | } 251 | 252 | div.entityDefinition:hover { 253 | background-color: rgba(255,255,255,0.1); 254 | } 255 | 256 | div.entityDefinition .key { 257 | width: 50%; 258 | display: block; 259 | float: left; 260 | margin: 0 0px 0 0; 261 | padding: 0; 262 | text-align: right; 263 | color: #fff; 264 | overflow: hidden; 265 | } 266 | 267 | div.entityDefinition .value { 268 | padding: 0 0 0 2px; 269 | color: #fff; 270 | } 271 | 272 | dl#entityDefinitionInput { 273 | padding: 8px 0; 274 | } 275 | 276 | dl#entityDefinitionInput dt { 277 | width: 40px; 278 | display: block; 279 | float: left; 280 | margin: 0 4px 0 0; 281 | padding: 4px 0 0 0; 282 | text-align: right; 283 | } 284 | 285 | dl#entityDefinitionInput dd { 286 | display: block; 287 | margin: 0; 288 | padding: 2px 0; 289 | } 290 | 291 | #entityKey, #entityValue { 292 | } 293 | 294 | #entityMenu { 295 | background-color: rgba(0,0,0,0.9); 296 | display: none; 297 | position: absolute; 298 | min-width: 100px; 299 | max-height:300px; 300 | overflow-y: scroll; 301 | z-index: 1000; 302 | } 303 | 304 | #entityMenu div { 305 | padding: 3px; 306 | padding-left: 8px; 307 | color: #fff; 308 | cursor: pointer; 309 | border-top: 1px solid rgba(255,255,255,0); 310 | border-bottom: 1px solid rgba(255,255,255,0); 311 | -webkit-transition: 0.1s linear; 312 | } 313 | 314 | #entityMenu div:hover { 315 | background-color: rgba(255,255,255,0.2); 316 | border-top: 1px solid rgba(255,255,255,0.2); 317 | border-bottom: 1px solid rgba(255,255,255,0.2); 318 | } 319 | 320 | /* --- Dialogs ------------------------------------------------------------------ */ 321 | 322 | .selectFileDialog { 323 | background-color: rgba(0,0,0,0.9); 324 | border: 1px solid white; 325 | border-top: 1px solid rgba(255,255,255,0.4); 326 | display: none; 327 | position: absolute; 328 | overflow: hidden; 329 | -webkit-box-shadow: 0px 0px 10px rgba(0,0,0,1); 330 | 331 | max-height: 300px; 332 | overflow-y: scroll; 333 | } 334 | 335 | .selectFileDialog a { 336 | padding: 4px; 337 | color: #fff; 338 | display: block; 339 | text-decoration: none; 340 | border-top: 1px solid rgba(255,255,255,0); 341 | border-bottom: 1px solid rgba(255,255,255,0); 342 | } 343 | 344 | .selectFileDialog a:hover { 345 | background-color: rgba(255,255,255,0.2); 346 | border-top: 1px solid rgba(255,255,255,0.2); 347 | border-bottom: 1px solid rgba(255,255,255,0.2); 348 | } 349 | 350 | div.modalDialogBackground { 351 | background-color: rgba(0,0,0,0.7); 352 | width: 100%; 353 | height: 100%; 354 | position: fixed; 355 | top: 0; 356 | left: 0; 357 | display: none; 358 | z-index: 100; 359 | } 360 | 361 | div.modalDialogBox { 362 | width: 300px; 363 | margin-left: -170px; 364 | background-color: rgba(0,0,0,0.9); 365 | border: 1px solid rgb(100,100,100); 366 | -webkit-box-shadow: 0px 0px 10px rgba(0,0,0,1); 367 | position: absolute; 368 | top: 20%; 369 | left: 50%; 370 | padding: 20px; 371 | } 372 | 373 | div.modalDialogText { 374 | font-size: 180%; 375 | font-weight: bold; 376 | } 377 | 378 | div.modalDialogButtons { 379 | margin-top: 20px; 380 | text-align: right; 381 | } 382 | 383 | div.modalDialogButtons input.button { 384 | min-width: 100px; 385 | text-align: center; 386 | margin-left: 10px; 387 | } 388 | 389 | input.modalDialogPath { 390 | margin-top: 20px; 391 | width: 100%; 392 | outline: none; 393 | } 394 | 395 | input.modalDialogPath:focus { 396 | outline: none; 397 | border: 1px solid rgb(100,100,100); 398 | } 399 | 400 | #headerMenu { 401 | position:relative; 402 | z-index:10; 403 | height:47px; 404 | width:100%; 405 | background: #131314; 406 | background: -webkit-gradient(linear, left bottom, left top, color-stop(0,#000000), color-stop(1,#2e3033)); 407 | background: -moz-linear-gradient(center bottom, #000000 0%, #2e3033 100%); 408 | background: -o-linear-gradient(#2e3033, #000000); 409 | } 410 | 411 | #headerMenu span.headerTitle { 412 | display:inline-block; 413 | font-weight:bold; 414 | font-size:200%; 415 | padding-left:20px; 416 | padding-top:7px; 417 | color:rgba(0,0,0,0.1); 418 | text-shadow:0px -1px 0px rgba(255,255,255,0.4); 419 | } 420 | 421 | #headerMenu span.unsavedTitle { 422 | display:inline-block; 423 | font-weight:bold; 424 | font-size:240%; 425 | padding-left:0px; 426 | color:#cc0000; 427 | text-shadow:0px 1px 1px rgba(0,0,0,0.1); 428 | } 429 | 430 | #headerMenu span.headerFloat { 431 | float: right; 432 | } 433 | 434 | div#zoomIndicator { 435 | font-weight: bold; 436 | font-size: 300%; 437 | position: absolute; 438 | left: 50px; 439 | top: 30px; 440 | color: #fff; 441 | display: none; 442 | } 443 | 444 | input#toggleSidebar { 445 | width: 200px; 446 | height: 47px; 447 | text-indent: -99999px; 448 | background: url(arrow.png) 50% -37px no-repeat; 449 | -webkit-transition: 0s linear; 450 | opacity: 0.25; 451 | padding: 0px; 452 | } 453 | 454 | input#toggleSidebar.active{ 455 | background-position: 50% 10px; 456 | } 457 | 458 | input[type="checkbox"] { 459 | position: relative; 460 | margin: 0; 461 | border: 0; 462 | width: 10px; 463 | height: 10px; 464 | display: inline-block; 465 | -webkit-appearance: none; 466 | -webkit-transition: 0.1s linear; 467 | } 468 | input[type="checkbox"] { 469 | background-color:rgba(255,255,255,0.2); 470 | } 471 | input[type="checkbox"]:checked { 472 | background-color: rgba(255,255,255,1); 473 | } 474 | input[type="checkbox"]:hover { 475 | cursor: pointer; 476 | } 477 | input[type="checkbox"]:disabled { 478 | background-color: rgba(255,255,255,0.1); 479 | } 480 | 481 | ::-webkit-scrollbar { width: 2px; } 482 | ::-webkit-scrollbar-button:start:decrement, 483 | ::-webkit-scrollbar-button:end:increment { display: block; height: 2px; } 484 | ::-webkit-scrollbar-button:vertical:increment { background-color: transparent; } 485 | ::-webkit-scrollbar-track-piece { background-color: rgba(0,0,0,0); } 486 | ::-webkit-scrollbar-thumb:vertical { background-color: rgba(255,255,255,1); } 487 | -------------------------------------------------------------------------------- /lib/weltmeister/edit-entities.js: -------------------------------------------------------------------------------- 1 | ig.module( 2 | 'weltmeister.edit-entities' 3 | ) 4 | .requires( 5 | 'impact.game', 6 | 'impact.background-map', 7 | 'weltmeister.config', 8 | 'weltmeister.tile-select', 9 | 'weltmeister.entities' 10 | ) 11 | .defines(function(){ "use strict"; 12 | 13 | wm.EditEntities = ig.Class.extend({ 14 | visible: true, 15 | active: true, 16 | 17 | div: null, 18 | hotkey: -1, 19 | ignoreLastClick: false, 20 | name: 'entities', 21 | 22 | entities: [], 23 | namedEntities: {}, 24 | selectedEntity: null, 25 | entityClasses: {}, 26 | menuDiv: null, 27 | selector: {size:{x:2, y:2}, pos:{x:0,y:0}, offset:{x:0,y:0}}, 28 | wasSelectedOnScaleBorder: false, 29 | gridSize: wm.config.entityGrid, 30 | entityDefinitions: null, 31 | 32 | 33 | 34 | init: function( div ) { 35 | this.div = div; 36 | div.bind( 'mouseup', this.click.bind(this) ); 37 | this.div.children('.visible').bind( 'mousedown', this.toggleVisibilityClick.bind(this) ); 38 | 39 | this.menu = $('#entityMenu'); 40 | this.importEntityClass( wm.entityModules ); 41 | this.entityDefinitions = $('#entityDefinitions'); 42 | 43 | $('#entityKey').bind( 'keydown', function(ev){ 44 | if( ev.which == 13 ){ 45 | $('#entityValue').focus(); 46 | return false; 47 | } 48 | return true; 49 | }); 50 | $('#entityValue').bind( 'keydown', this.setEntitySetting.bind(this) ); 51 | }, 52 | 53 | 54 | clear: function() { 55 | this.entities = []; 56 | this.selectEntity( null ); 57 | }, 58 | 59 | 60 | sort: function() { 61 | this.entities.sort( ig.Game.SORT.Z_INDEX ); 62 | }, 63 | 64 | 65 | 66 | 67 | // ------------------------------------------------------------------------- 68 | // Loading, Saving 69 | 70 | 71 | fileNameToClassName: function( name ) { 72 | var typeName = '-' + name.replace(/^.*\/|\.js/g,''); 73 | typeName = typeName.replace(/-(\w)/g, function( m, a ) { 74 | return a.toUpperCase(); 75 | }); 76 | return 'Entity' + typeName; 77 | }, 78 | 79 | 80 | importEntityClass: function( modules ) { 81 | var unloadedClasses = []; 82 | for( var m in modules ) { 83 | var className = this.fileNameToClassName(modules[m]); 84 | var entityName = className.replace(/^Entity/, ''); 85 | 86 | // ig.global[className] should be the actual class object 87 | if( className && ig.global[className] ) { 88 | 89 | // Ignore entities that have the _wmIgnore flag 90 | if( !ig.global[className].prototype._wmIgnore ) { 91 | var a = $( '
', { 92 | 'id': className, 93 | 'href': '#', 94 | 'html': entityName, 95 | 'mouseup': this.newEntityClick.bind(this) 96 | }); 97 | this.menu.append( a ); 98 | this.entityClasses[className] = m; 99 | } 100 | } 101 | else { 102 | unloadedClasses.push( modules[m] + ' (expected name: ' + className + ')' ); 103 | } 104 | } 105 | 106 | if( unloadedClasses.length > 0 ) { 107 | var warning = 'The following entity classes were not loaded due to\n' 108 | + 'file and class name mismatches: \n\n' 109 | + unloadedClasses.join( '\n' ); 110 | alert( warning ); 111 | } 112 | }, 113 | 114 | 115 | getEntityByName: function( name ) { 116 | return this.namedEntities[name]; 117 | }, 118 | 119 | 120 | getSaveData: function() { 121 | var ents = []; 122 | for( var i = 0; i < this.entities.length; i++ ) { 123 | var ent = this.entities[i]; 124 | var type = ent._wmClassName; 125 | var data = {type:type,x:ent.pos.x,y:ent.pos.y}; 126 | 127 | var hasSettings = false; 128 | for( var p in ent._wmSettings ) { 129 | hasSettings = true; 130 | } 131 | if( hasSettings ) { 132 | data.settings = ent._wmSettings; 133 | } 134 | 135 | ents.push( data ); 136 | } 137 | return ents; 138 | }, 139 | 140 | 141 | 142 | 143 | // ------------------------------------------------------------------------- 144 | // Selecting 145 | 146 | 147 | selectEntityAt: function( x, y ) { 148 | this.selector.pos = { x: x, y: y }; 149 | 150 | // Find all possible selections 151 | var possibleSelections = []; 152 | for( var i = 0; i < this.entities.length; i++ ) { 153 | if( this.entities[i].touches(this.selector) ) { 154 | possibleSelections.push( this.entities[i] ); 155 | } 156 | } 157 | 158 | // Nothing found? Early out. 159 | if( !possibleSelections.length ) { 160 | this.selectEntity( null ); 161 | return false; 162 | } 163 | 164 | // Find the 'next' selection 165 | var selectedIndex = possibleSelections.indexOf(this.selectedEntity); 166 | var nextSelection = (selectedIndex + 1) % possibleSelections.length; 167 | var ent = possibleSelections[nextSelection]; 168 | 169 | // Select it! 170 | this.selector.offset = { 171 | x: (x - ent.pos.x + ent.offset.x), 172 | y: (y - ent.pos.y + ent.offset.y) 173 | }; 174 | this.selectEntity( ent ); 175 | this.wasSelectedOnScaleBorder = this.isOnScaleBorder( ent, this.selector ); 176 | return ent; 177 | }, 178 | 179 | 180 | selectEntity: function( entity ) { 181 | if( entity && entity != this.selectedEntity ) { 182 | this.selectedEntity = entity; 183 | $('#entitySettings').fadeOut(100,(function(){ 184 | this.loadEntitySettings(); 185 | $('#entitySettings').fadeIn(100); 186 | }).bind(this)); 187 | } 188 | else if( !entity ) { 189 | $('#entitySettings').fadeOut(100); 190 | $('#entityKey').blur(); 191 | $('#entityValue').blur(); 192 | } 193 | 194 | this.selectedEntity = entity; 195 | $('#entityKey').val(''); 196 | $('#entityValue').val(''); 197 | }, 198 | 199 | 200 | 201 | 202 | // ------------------------------------------------------------------------- 203 | // Creating, Deleting, Moving 204 | 205 | 206 | deleteSelectedEntity: function() { 207 | if( !this.selectedEntity ) { 208 | return false; 209 | } 210 | 211 | ig.game.undo.commitEntityDelete( this.selectedEntity ); 212 | 213 | this.removeEntity( this.selectedEntity ); 214 | this.selectEntity( null ); 215 | return true; 216 | }, 217 | 218 | 219 | removeEntity: function( ent ) { 220 | if( ent.name ) { 221 | delete this.namedEntities[ent.name]; 222 | } 223 | this.entities.erase( ent ); 224 | }, 225 | 226 | 227 | cloneSelectedEntity: function() { 228 | if( !this.selectedEntity ) { 229 | return false; 230 | } 231 | 232 | var className = this.selectedEntity._wmClassName; 233 | var settings = ig.copy(this.selectedEntity._wmSettings); 234 | if( settings.name ) { 235 | settings.name = settings.name + '_clone'; 236 | } 237 | var x = this.selectedEntity.pos.x + this.gridSize; 238 | var y = this.selectedEntity.pos.y; 239 | var newEntity = this.spawnEntity( className, x, y, settings ); 240 | newEntity._wmSettings = settings; 241 | this.selectEntity( newEntity ); 242 | 243 | ig.game.undo.commitEntityCreate( newEntity ); 244 | 245 | return true; 246 | }, 247 | 248 | 249 | dragOnSelectedEntity: function( x, y ) { 250 | if( !this.selectedEntity ) { 251 | return false; 252 | } 253 | 254 | 255 | // scale or move? 256 | if( this.selectedEntity._wmScalable && this.wasSelectedOnScaleBorder ) { 257 | this.scaleSelectedEntity( x, y ); 258 | } 259 | else { 260 | this.moveSelectedEntity( x, y ) 261 | } 262 | 263 | ig.game.undo.pushEntityEdit( this.selectedEntity ); 264 | return true; 265 | }, 266 | 267 | 268 | moveSelectedEntity: function( x, y ) { 269 | x = 270 | Math.round( (x - this.selector.offset.x ) / this.gridSize ) 271 | * this.gridSize + this.selectedEntity.offset.x; 272 | y = 273 | Math.round( (y - this.selector.offset.y ) / this.gridSize ) 274 | * this.gridSize + this.selectedEntity.offset.y; 275 | 276 | // new position? 277 | if( this.selectedEntity.pos.x != x || this.selectedEntity.pos.y != y ) { 278 | $('#entityDefinitionPosX').text( x ); 279 | $('#entityDefinitionPosY').text( y ); 280 | 281 | this.selectedEntity.pos.x = x; 282 | this.selectedEntity.pos.y = y; 283 | } 284 | }, 285 | 286 | 287 | scaleSelectedEntity: function( x, y ) { 288 | var scale = this.wasSelectedOnScaleBorder; 289 | 290 | var w = Math.round( x / this.gridSize ) * this.gridSize - this.selectedEntity.pos.x; 291 | 292 | if( !this.selectedEntity._wmSettings.size ) { 293 | this.selectedEntity._wmSettings.size = {}; 294 | } 295 | 296 | if( scale == 'n' ) { 297 | var h = this.selectedEntity.pos.y - Math.round( y / this.gridSize ) * this.gridSize; 298 | if( this.selectedEntity.size.y + h <= this.gridSize ) { 299 | h = (this.selectedEntity.size.y - this.gridSize) * -1; 300 | } 301 | this.selectedEntity.size.y += h; 302 | this.selectedEntity.pos.y -= h; 303 | } 304 | else if( scale == 's' ) { 305 | var h = Math.round( y / this.gridSize ) * this.gridSize - this.selectedEntity.pos.y; 306 | this.selectedEntity.size.y = Math.max( this.gridSize, h ); 307 | } 308 | else if( scale == 'e' ) { 309 | var w = Math.round( x / this.gridSize ) * this.gridSize - this.selectedEntity.pos.x; 310 | this.selectedEntity.size.x = Math.max( this.gridSize, w ); 311 | } 312 | else if( scale == 'w' ) { 313 | var w = this.selectedEntity.pos.x - Math.round( x / this.gridSize ) * this.gridSize; 314 | if( this.selectedEntity.size.x + w <= this.gridSize ) { 315 | w = (this.selectedEntity.size.x - this.gridSize) * -1; 316 | } 317 | this.selectedEntity.size.x += w; 318 | this.selectedEntity.pos.x -= w; 319 | } 320 | this.selectedEntity._wmSettings.size.x = this.selectedEntity.size.x; 321 | this.selectedEntity._wmSettings.size.y = this.selectedEntity.size.y; 322 | 323 | this.loadEntitySettings(); 324 | }, 325 | 326 | 327 | newEntityClick: function( ev ) { 328 | this.hideMenu(); 329 | var newEntity = this.spawnEntity( ev.target.id, 0, 0, {} ); 330 | this.selectEntity( newEntity ); 331 | this.moveSelectedEntity( this.selector.pos.x, this.selector.pos.y ); 332 | ig.editor.setModified(); 333 | 334 | ig.game.undo.commitEntityCreate( newEntity ); 335 | }, 336 | 337 | 338 | spawnEntity: function( className, x, y, settings ) { 339 | settings = settings || {}; 340 | var entityClass = ig.global[ className ]; 341 | if( entityClass ) { 342 | var newEntity = new (entityClass)( x, y, settings ); 343 | newEntity._wmInEditor = true; 344 | newEntity._wmClassName = className; 345 | newEntity._wmSettings = {}; 346 | for( var s in settings ) { 347 | newEntity._wmSettings[s] = settings[s]; 348 | } 349 | this.entities.push( newEntity ); 350 | if( settings.name ) { 351 | this.namedEntities[settings.name] = newEntity; 352 | } 353 | this.sort(); 354 | return newEntity; 355 | } 356 | return null; 357 | }, 358 | 359 | 360 | isOnScaleBorder: function( entity, selector ) { 361 | var border = 2; 362 | var w = selector.pos.x - entity.pos.x; 363 | var h = selector.pos.y - entity.pos.y; 364 | 365 | if( w < border ) return 'w'; 366 | if( w > entity.size.x - border ) return 'e'; 367 | 368 | if( h < border ) return 'n'; 369 | if( h > entity.size.y - border ) return 's'; 370 | 371 | return false; 372 | }, 373 | 374 | 375 | 376 | 377 | // ------------------------------------------------------------------------- 378 | // Settings 379 | 380 | 381 | loadEntitySettings: function(ent) { 382 | 383 | if( !this.selectedEntity ) { 384 | return; 385 | } 386 | var html = 387 | '
x:'+this.selectedEntity.pos.x+'
' 388 | + '
y:'+this.selectedEntity.pos.y+'
'; 389 | 390 | html += this.loadEntitySettingsRecursive( this.selectedEntity._wmSettings ); 391 | this.entityDefinitions.html( html ); 392 | 393 | var className = this.selectedEntity._wmClassName.replace(/^Entity/, ''); 394 | $('#entityClass').text( className ); 395 | 396 | $('.entityDefinition').bind( 'mouseup', this.selectEntitySetting ); 397 | }, 398 | 399 | 400 | loadEntitySettingsRecursive: function( settings, path ) { 401 | path = path || ""; 402 | var html = ""; 403 | for( var key in settings ) { 404 | var value = settings[key]; 405 | if( typeof(value) == 'object' ) { 406 | html += this.loadEntitySettingsRecursive( value, path + key + "." ); 407 | } 408 | else { 409 | html += '
'+path+key+':'+value+'
'; 410 | } 411 | } 412 | 413 | return html; 414 | }, 415 | 416 | 417 | setEntitySetting: function( ev ) { 418 | if( ev.which != 13 ) { 419 | return true; 420 | } 421 | var key = $('#entityKey').val(); 422 | var value = $('#entityValue').val(); 423 | var floatVal = parseFloat(value); 424 | if( value == floatVal ) { 425 | value = floatVal; 426 | } 427 | 428 | if( key == 'name' ) { 429 | if( this.selectedEntity.name ) { 430 | delete this.namedEntities[this.selectedEntity.name]; 431 | } 432 | this.namedEntities[ value ] = this.selectedEntity; 433 | } 434 | 435 | if( key == 'x' ) { 436 | this.selectedEntity.pos.x = Math.round(value); 437 | } 438 | else if( key == 'y' ) { 439 | this.selectedEntity.pos.y = Math.round(value); 440 | } 441 | else { 442 | this.writeSettingAtPath( this.selectedEntity._wmSettings, key, value ); 443 | ig.merge( this.selectedEntity, this.selectedEntity._wmSettings ); 444 | } 445 | 446 | this.sort(); 447 | 448 | ig.game.setModified(); 449 | ig.game.draw(); 450 | 451 | $('#entityKey').val(''); 452 | $('#entityValue').val(''); 453 | $('#entityValue').blur(); 454 | this.loadEntitySettings(); 455 | 456 | $('#entityKey').focus(); 457 | return false; 458 | }, 459 | 460 | 461 | writeSettingAtPath: function( root, path, value ) { 462 | path = path.split('.'); 463 | var cur = root; 464 | for( var i = 0; i < path.length; i++ ) { 465 | var n = path[i]; 466 | if( i < path.length-1 && typeof(cur[n]) != 'object' ) { 467 | cur[n] = {}; 468 | } 469 | 470 | if( i == path.length-1 ) { 471 | cur[n] = value; 472 | } 473 | cur = cur[n]; 474 | } 475 | 476 | this.trimObject( root ); 477 | }, 478 | 479 | 480 | trimObject: function( obj ) { 481 | var isEmpty = true; 482 | for( var i in obj ) { 483 | if( 484 | (obj[i] === "") || 485 | (typeof(obj[i]) == 'object' && this.trimObject(obj[i])) 486 | ) { 487 | delete obj[i]; 488 | } 489 | 490 | if( typeof(obj[i]) != 'undefined' ) { 491 | isEmpty = false; 492 | } 493 | } 494 | 495 | return isEmpty; 496 | }, 497 | 498 | 499 | selectEntitySetting: function( ev ) { 500 | $('#entityKey').val( $(this).children('.key').text() ); 501 | $('#entityValue').val( $(this).children('.value').text() ); 502 | $('#entityValue').select(); 503 | }, 504 | 505 | 506 | 507 | 508 | 509 | 510 | // ------------------------------------------------------------------------- 511 | // UI 512 | 513 | setHotkey: function( hotkey ) { 514 | this.hotkey = hotkey; 515 | this.div.attr('title', 'Select Layer ('+this.hotkey+')' ); 516 | }, 517 | 518 | 519 | showMenu: function( x, y ) { 520 | this.selector.pos = { 521 | x: Math.round( (x + ig.editor.screen.x) / this.gridSize ) * this.gridSize, 522 | y: Math.round( (y + ig.editor.screen.y) / this.gridSize ) * this.gridSize 523 | }; 524 | this.menu.css({top: (y * ig.system.scale + 2), left: (x * ig.system.scale + 2) }); 525 | this.menu.show(); 526 | }, 527 | 528 | 529 | hideMenu: function( x, y ) { 530 | ig.editor.mode = ig.editor.MODE.DEFAULT; 531 | this.menu.hide(); 532 | }, 533 | 534 | 535 | setActive: function( active ) { 536 | this.active = active; 537 | if( active ) { 538 | this.div.addClass( 'layerActive' ); 539 | } else { 540 | this.div.removeClass( 'layerActive' ); 541 | } 542 | }, 543 | 544 | 545 | toggleVisibility: function() { 546 | this.visible ^= 1; 547 | if( this.visible ) { 548 | this.div.children('.visible').addClass('checkedVis'); 549 | } else { 550 | this.div.children('.visible').removeClass('checkedVis'); 551 | } 552 | ig.game.draw(); 553 | }, 554 | 555 | 556 | toggleVisibilityClick: function( ev ) { 557 | if( !this.active ) { 558 | this.ignoreLastClick = true; 559 | } 560 | this.toggleVisibility() 561 | }, 562 | 563 | 564 | click: function() { 565 | if( this.ignoreLastClick ) { 566 | this.ignoreLastClick = false; 567 | return; 568 | } 569 | ig.editor.setActiveLayer( 'entities' ); 570 | }, 571 | 572 | 573 | mousemove: function( x, y ) { 574 | this.selector.pos = { x: x, y: y }; 575 | 576 | if( this.selectedEntity ) { 577 | if( this.selectedEntity._wmScalable && this.selectedEntity.touches(this.selector) ) { 578 | var scale = this.isOnScaleBorder( this.selectedEntity, this.selector ); 579 | if( scale == 'n' || scale == 's' ) { 580 | $('body').css('cursor', 'n-resize'); 581 | return; 582 | } 583 | else if( scale == 'e' || scale == 'w' ) { 584 | $('body').css('cursor', 'e-resize'); 585 | return; 586 | } 587 | } 588 | } 589 | 590 | $('body').css('cursor', 'default'); 591 | }, 592 | 593 | 594 | 595 | 596 | 597 | 598 | // ------------------------------------------------------------------------- 599 | // Drawing 600 | 601 | 602 | draw: function() { 603 | if( this.visible ) { 604 | for( var i = 0; i < this.entities.length; i++ ) { 605 | this.drawEntity( this.entities[i] ); 606 | } 607 | } 608 | }, 609 | 610 | 611 | drawEntity: function( ent ) { 612 | 613 | // entity itself 614 | ent.draw(); 615 | 616 | // box 617 | if( ent._wmDrawBox ) { 618 | ig.system.context.fillStyle = ent._wmBoxColor || 'rgba(128, 128, 128, 0.9)'; 619 | ig.system.context.fillRect( 620 | ig.system.getDrawPos(ent.pos.x - ig.game.screen.x), 621 | ig.system.getDrawPos(ent.pos.y - ig.game.screen.y), 622 | ent.size.x * ig.system.scale, 623 | ent.size.y * ig.system.scale 624 | ); 625 | } 626 | 627 | 628 | if( wm.config.labels.draw ) { 629 | // description 630 | var className = ent._wmClassName.replace(/^Entity/, ''); 631 | var description = className + (ent.name ? ': ' + ent.name : '' ); 632 | 633 | // text-shadow 634 | ig.system.context.fillStyle = 'rgba(0,0,0,0.4)'; 635 | ig.system.context.fillText( 636 | description, 637 | ig.system.getDrawPos(ent.pos.x - ig.game.screen.x), 638 | ig.system.getDrawPos(ent.pos.y - ig.game.screen.y + 0.5) 639 | ); 640 | 641 | // text 642 | ig.system.context.fillStyle = wm.config.colors.primary; 643 | ig.system.context.fillText( 644 | description, 645 | ig.system.getDrawPos(ent.pos.x - ig.game.screen.x), 646 | ig.system.getDrawPos(ent.pos.y - ig.game.screen.y) 647 | ); 648 | } 649 | 650 | 651 | // line to targets 652 | if( typeof(ent.target) == 'object' ) { 653 | for( var t in ent.target ) { 654 | this.drawLineToTarget( ent, ent.target[t] ); 655 | } 656 | } 657 | }, 658 | 659 | 660 | drawLineToTarget: function( ent, target ) { 661 | target = ig.game.getEntityByName( target ); 662 | if( !target ) { 663 | return; 664 | } 665 | 666 | ig.system.context.strokeStyle = '#fff'; 667 | ig.system.context.lineWidth = 1; 668 | 669 | ig.system.context.beginPath(); 670 | ig.system.context.moveTo( 671 | ig.system.getDrawPos(ent.pos.x + ent.size.x/2 - ig.game.screen.x), 672 | ig.system.getDrawPos(ent.pos.y + ent.size.y/2 - ig.game.screen.y) 673 | ); 674 | ig.system.context.lineTo( 675 | ig.system.getDrawPos(target.pos.x + target.size.x/2 - ig.game.screen.x), 676 | ig.system.getDrawPos(target.pos.y + target.size.y/2 - ig.game.screen.y) 677 | ); 678 | ig.system.context.stroke(); 679 | ig.system.context.closePath(); 680 | }, 681 | 682 | 683 | drawCursor: function( x, y ) { 684 | if( this.selectedEntity ) { 685 | ig.system.context.lineWidth = 1; 686 | ig.system.context.strokeStyle = wm.config.colors.highlight; 687 | ig.system.context.strokeRect( 688 | ig.system.getDrawPos(this.selectedEntity.pos.x - ig.editor.screen.x) - 0.5, 689 | ig.system.getDrawPos(this.selectedEntity.pos.y - ig.editor.screen.y) - 0.5, 690 | this.selectedEntity.size.x * ig.system.scale + 1, 691 | this.selectedEntity.size.y * ig.system.scale + 1 692 | ); 693 | } 694 | } 695 | }); 696 | 697 | }); -------------------------------------------------------------------------------- /lib/weltmeister/jquery-ui-1.8.1.custom.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery UI 1.8.1 3 | * 4 | * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about) 5 | * Dual licensed under the MIT (MIT-LICENSE.txt) 6 | * and GPL (GPL-LICENSE.txt) licenses. 7 | * 8 | * http://docs.jquery.com/UI 9 | */ 10 | jQuery.ui||function(c){c.ui={version:"1.8.1",plugin:{add:function(a,b,d){a=c.ui[a].prototype;for(var e in d){a.plugins[e]=a.plugins[e]||[];a.plugins[e].push([b,d[e]])}},call:function(a,b,d){if((b=a.plugins[b])&&a.element[0].parentNode)for(var e=0;e0)return true;a[b]=1;d=a[b]>0;a[b]=0;return d},isOverAxis:function(a,b,d){return a>b&&a=0)&&c(a).is(":focusable")}})}(jQuery); 16 | ;/*! 17 | * jQuery UI Widget 1.8.1 18 | * 19 | * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about) 20 | * Dual licensed under the MIT (MIT-LICENSE.txt) 21 | * and GPL (GPL-LICENSE.txt) licenses. 22 | * 23 | * http://docs.jquery.com/UI/Widget 24 | */ 25 | (function(b){var j=b.fn.remove;b.fn.remove=function(a,c){return this.each(function(){if(!c)if(!a||b.filter(a,[this]).length)b("*",this).add(this).each(function(){b(this).triggerHandler("remove")});return j.call(b(this),a,c)})};b.widget=function(a,c,d){var e=a.split(".")[0],f;a=a.split(".")[1];f=e+"-"+a;if(!d){d=c;c=b.Widget}b.expr[":"][f]=function(h){return!!b.data(h,a)};b[e]=b[e]||{};b[e][a]=function(h,g){arguments.length&&this._createWidget(h,g)};c=new c;c.options=b.extend({},c.options);b[e][a].prototype= 26 | b.extend(true,c,{namespace:e,widgetName:a,widgetEventPrefix:b[e][a].prototype.widgetEventPrefix||a,widgetBaseClass:f},d);b.widget.bridge(a,b[e][a])};b.widget.bridge=function(a,c){b.fn[a]=function(d){var e=typeof d==="string",f=Array.prototype.slice.call(arguments,1),h=this;d=!e&&f.length?b.extend.apply(null,[true,d].concat(f)):d;if(e&&d.substring(0,1)==="_")return h;e?this.each(function(){var g=b.data(this,a),i=g&&b.isFunction(g[d])?g[d].apply(g,f):g;if(i!==g&&i!==undefined){h=i;return false}}):this.each(function(){var g= 27 | b.data(this,a);if(g){d&&g.option(d);g._init()}else b.data(this,a,new c(d,this))});return h}};b.Widget=function(a,c){arguments.length&&this._createWidget(a,c)};b.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",options:{disabled:false},_createWidget:function(a,c){this.element=b(c).data(this.widgetName,this);this.options=b.extend(true,{},this.options,b.metadata&&b.metadata.get(c)[this.widgetName],a);var d=this;this.element.bind("remove."+this.widgetName,function(){d.destroy()});this._create(); 28 | this._init()},_create:function(){},_init:function(){},destroy:function(){this.element.unbind("."+this.widgetName).removeData(this.widgetName);this.widget().unbind("."+this.widgetName).removeAttr("aria-disabled").removeClass(this.widgetBaseClass+"-disabled ui-state-disabled")},widget:function(){return this.element},option:function(a,c){var d=a,e=this;if(arguments.length===0)return b.extend({},e.options);if(typeof a==="string"){if(c===undefined)return this.options[a];d={};d[a]=c}b.each(d,function(f, 29 | h){e._setOption(f,h)});return e},_setOption:function(a,c){this.options[a]=c;if(a==="disabled")this.widget()[c?"addClass":"removeClass"](this.widgetBaseClass+"-disabled ui-state-disabled").attr("aria-disabled",c);return this},enable:function(){return this._setOption("disabled",false)},disable:function(){return this._setOption("disabled",true)},_trigger:function(a,c,d){var e=this.options[a];c=b.Event(c);c.type=(a===this.widgetEventPrefix?a:this.widgetEventPrefix+a).toLowerCase();d=d||{};if(c.originalEvent){a= 30 | b.event.props.length;for(var f;a;){f=b.event.props[--a];c[f]=c.originalEvent[f]}}this.element.trigger(c,d);return!(b.isFunction(e)&&e.call(this.element[0],c,d)===false||c.isDefaultPrevented())}}})(jQuery); 31 | ;/*! 32 | * jQuery UI Mouse 1.8.1 33 | * 34 | * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about) 35 | * Dual licensed under the MIT (MIT-LICENSE.txt) 36 | * and GPL (GPL-LICENSE.txt) licenses. 37 | * 38 | * http://docs.jquery.com/UI/Mouse 39 | * 40 | * Depends: 41 | * jquery.ui.widget.js 42 | */ 43 | (function(c){c.widget("ui.mouse",{options:{cancel:":input,option",distance:1,delay:0},_mouseInit:function(){var a=this;this.element.bind("mousedown."+this.widgetName,function(b){return a._mouseDown(b)}).bind("click."+this.widgetName,function(b){if(a._preventClickEvent){a._preventClickEvent=false;b.stopImmediatePropagation();return false}});this.started=false},_mouseDestroy:function(){this.element.unbind("."+this.widgetName)},_mouseDown:function(a){a.originalEvent=a.originalEvent||{};if(!a.originalEvent.mouseHandled){this._mouseStarted&& 44 | this._mouseUp(a);this._mouseDownEvent=a;var b=this,e=a.which==1,f=typeof this.options.cancel=="string"?c(a.target).parents().add(a.target).filter(this.options.cancel).length:false;if(!e||f||!this._mouseCapture(a))return true;this.mouseDelayMet=!this.options.delay;if(!this.mouseDelayMet)this._mouseDelayTimer=setTimeout(function(){b.mouseDelayMet=true},this.options.delay);if(this._mouseDistanceMet(a)&&this._mouseDelayMet(a)){this._mouseStarted=this._mouseStart(a)!==false;if(!this._mouseStarted){a.preventDefault(); 45 | return true}}this._mouseMoveDelegate=function(d){return b._mouseMove(d)};this._mouseUpDelegate=function(d){return b._mouseUp(d)};c(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate);c.browser.safari||a.preventDefault();return a.originalEvent.mouseHandled=true}},_mouseMove:function(a){if(c.browser.msie&&!a.button)return this._mouseUp(a);if(this._mouseStarted){this._mouseDrag(a);return a.preventDefault()}if(this._mouseDistanceMet(a)&& 46 | this._mouseDelayMet(a))(this._mouseStarted=this._mouseStart(this._mouseDownEvent,a)!==false)?this._mouseDrag(a):this._mouseUp(a);return!this._mouseStarted},_mouseUp:function(a){c(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate);if(this._mouseStarted){this._mouseStarted=false;this._preventClickEvent=a.target==this._mouseDownEvent.target;this._mouseStop(a)}return false},_mouseDistanceMet:function(a){return Math.max(Math.abs(this._mouseDownEvent.pageX- 47 | a.pageX),Math.abs(this._mouseDownEvent.pageY-a.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return true}})})(jQuery); 48 | ;/* 49 | * jQuery UI Sortable 1.8.1 50 | * 51 | * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about) 52 | * Dual licensed under the MIT (MIT-LICENSE.txt) 53 | * and GPL (GPL-LICENSE.txt) licenses. 54 | * 55 | * http://docs.jquery.com/UI/Sortables 56 | * 57 | * Depends: 58 | * jquery.ui.core.js 59 | * jquery.ui.mouse.js 60 | * jquery.ui.widget.js 61 | */ 62 | (function(d){d.widget("ui.sortable",d.ui.mouse,{widgetEventPrefix:"sort",options:{appendTo:"parent",axis:false,connectWith:false,containment:false,cursor:"auto",cursorAt:false,dropOnEmpty:true,forcePlaceholderSize:false,forceHelperSize:false,grid:false,handle:false,helper:"original",items:"> *",opacity:false,placeholder:false,revert:false,scroll:true,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",zIndex:1E3},_create:function(){this.containerCache={};this.element.addClass("ui-sortable"); 63 | this.refresh();this.floating=this.items.length?/left|right/.test(this.items[0].item.css("float")):false;this.offset=this.element.offset();this._mouseInit()},destroy:function(){this.element.removeClass("ui-sortable ui-sortable-disabled").removeData("sortable").unbind(".sortable");this._mouseDestroy();for(var a=this.items.length-1;a>=0;a--)this.items[a].item.removeData("sortable-item");return this},_setOption:function(a,b){if(a==="disabled"){this.options[a]=b;this.widget()[b?"addClass":"removeClass"]("ui-sortable-disabled")}else d.Widget.prototype._setOption.apply(self, 64 | arguments)},_mouseCapture:function(a,b){if(this.reverting)return false;if(this.options.disabled||this.options.type=="static")return false;this._refreshItems(a);var c=null,e=this;d(a.target).parents().each(function(){if(d.data(this,"sortable-item")==e){c=d(this);return false}});if(d.data(a.target,"sortable-item")==e)c=d(a.target);if(!c)return false;if(this.options.handle&&!b){var f=false;d(this.options.handle,c).find("*").andSelf().each(function(){if(this==a.target)f=true});if(!f)return false}this.currentItem= 65 | c;this._removeCurrentsFromItems();return true},_mouseStart:function(a,b,c){b=this.options;var e=this;this.currentContainer=this;this.refreshPositions();this.helper=this._createHelper(a);this._cacheHelperProportions();this._cacheMargins();this.scrollParent=this.helper.scrollParent();this.offset=this.currentItem.offset();this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left};this.helper.css("position","absolute");this.cssPosition=this.helper.css("position");d.extend(this.offset, 66 | {click:{left:a.pageX-this.offset.left,top:a.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()});this.originalPosition=this._generatePosition(a);this.originalPageX=a.pageX;this.originalPageY=a.pageY;b.cursorAt&&this._adjustOffsetFromHelper(b.cursorAt);this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]};this.helper[0]!=this.currentItem[0]&&this.currentItem.hide();this._createPlaceholder();b.containment&&this._setContainment(); 67 | if(b.cursor){if(d("body").css("cursor"))this._storedCursor=d("body").css("cursor");d("body").css("cursor",b.cursor)}if(b.opacity){if(this.helper.css("opacity"))this._storedOpacity=this.helper.css("opacity");this.helper.css("opacity",b.opacity)}if(b.zIndex){if(this.helper.css("zIndex"))this._storedZIndex=this.helper.css("zIndex");this.helper.css("zIndex",b.zIndex)}if(this.scrollParent[0]!=document&&this.scrollParent[0].tagName!="HTML")this.overflowOffset=this.scrollParent.offset();this._trigger("start", 68 | a,this._uiHash());this._preserveHelperProportions||this._cacheHelperProportions();if(!c)for(c=this.containers.length-1;c>=0;c--)this.containers[c]._trigger("activate",a,e._uiHash(this));if(d.ui.ddmanager)d.ui.ddmanager.current=this;d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,a);this.dragging=true;this.helper.addClass("ui-sortable-helper");this._mouseDrag(a);return true},_mouseDrag:function(a){this.position=this._generatePosition(a);this.positionAbs=this._convertPositionTo("absolute"); 69 | if(!this.lastPositionAbs)this.lastPositionAbs=this.positionAbs;if(this.options.scroll){var b=this.options,c=false;if(this.scrollParent[0]!=document&&this.scrollParent[0].tagName!="HTML"){if(this.overflowOffset.top+this.scrollParent[0].offsetHeight-a.pageY=0;b--){c=this.items[b];var e=c.item[0],f=this._intersectsWithPointer(c);if(f)if(e!=this.currentItem[0]&&this.placeholder[f==1?"next":"prev"]()[0]!=e&&!d.ui.contains(this.placeholder[0],e)&&(this.options.type=="semi-dynamic"?!d.ui.contains(this.element[0],e):true)){this.direction=f==1?"down":"up";if(this.options.tolerance=="pointer"||this._intersectsWithSides(c))this._rearrange(a, 73 | c);else break;this._trigger("change",a,this._uiHash());break}}this._contactContainers(a);d.ui.ddmanager&&d.ui.ddmanager.drag(this,a);this._trigger("sort",a,this._uiHash());this.lastPositionAbs=this.positionAbs;return false},_mouseStop:function(a,b){if(a){d.ui.ddmanager&&!this.options.dropBehaviour&&d.ui.ddmanager.drop(this,a);if(this.options.revert){var c=this;b=c.placeholder.offset();c.reverting=true;d(this.helper).animate({left:b.left-this.offset.parent.left-c.margins.left+(this.offsetParent[0]== 74 | document.body?0:this.offsetParent[0].scrollLeft),top:b.top-this.offset.parent.top-c.margins.top+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollTop)},parseInt(this.options.revert,10)||500,function(){c._clear(a)})}else this._clear(a,b);return false}},cancel:function(){var a=this;if(this.dragging){this._mouseUp();this.options.helper=="original"?this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"):this.currentItem.show();for(var b=this.containers.length-1;b>=0;b--){this.containers[b]._trigger("deactivate", 75 | null,a._uiHash(this));if(this.containers[b].containerCache.over){this.containers[b]._trigger("out",null,a._uiHash(this));this.containers[b].containerCache.over=0}}}this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]);this.options.helper!="original"&&this.helper&&this.helper[0].parentNode&&this.helper.remove();d.extend(this,{helper:null,dragging:false,reverting:false,_noFinalSort:null});this.domPosition.prev?d(this.domPosition.prev).after(this.currentItem): 76 | d(this.domPosition.parent).prepend(this.currentItem);return this},serialize:function(a){var b=this._getItemsAsjQuery(a&&a.connected),c=[];a=a||{};d(b).each(function(){var e=(d(a.item||this).attr(a.attribute||"id")||"").match(a.expression||/(.+)[-=_](.+)/);if(e)c.push((a.key||e[1]+"[]")+"="+(a.key&&a.expression?e[1]:e[2]))});return c.join("&")},toArray:function(a){var b=this._getItemsAsjQuery(a&&a.connected),c=[];a=a||{};b.each(function(){c.push(d(a.item||this).attr(a.attribute||"id")||"")});return c}, 77 | _intersectsWith:function(a){var b=this.positionAbs.left,c=b+this.helperProportions.width,e=this.positionAbs.top,f=e+this.helperProportions.height,g=a.left,h=g+a.width,i=a.top,k=i+a.height,j=this.offset.click.top,l=this.offset.click.left;j=e+j>i&&e+jg&&b+la[this.floating?"width":"height"]?j:g0?"down":"up")},_getDragHorizontalDirection:function(){var a= 80 | this.positionAbs.left-this.lastPositionAbs.left;return a!=0&&(a>0?"right":"left")},refresh:function(a){this._refreshItems(a);this.refreshPositions();return this},_connectWith:function(){var a=this.options;return a.connectWith.constructor==String?[a.connectWith]:a.connectWith},_getItemsAsjQuery:function(a){var b=[],c=[],e=this._connectWith();if(e&&a)for(a=e.length-1;a>=0;a--)for(var f=d(e[a]),g=f.length-1;g>=0;g--){var h=d.data(f[g],"sortable");if(h&&h!=this&&!h.options.disabled)c.push([d.isFunction(h.options.items)? 81 | h.options.items.call(h.element):d(h.options.items,h.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),h])}c.push([d.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):d(this.options.items,this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),this]);for(a=c.length-1;a>=0;a--)c[a][0].each(function(){b.push(this)});return d(b)},_removeCurrentsFromItems:function(){for(var a=this.currentItem.find(":data(sortable-item)"), 82 | b=0;b=0;f--)for(var g=d(e[f]),h=g.length-1;h>=0;h--){var i=d.data(g[h],"sortable");if(i&&i!=this&&!i.options.disabled){c.push([d.isFunction(i.options.items)? 83 | i.options.items.call(i.element[0],a,{item:this.currentItem}):d(i.options.items,i.element),i]);this.containers.push(i)}}for(f=c.length-1;f>=0;f--){a=c[f][1];e=c[f][0];h=0;for(g=e.length;h=0;b--){var c=this.items[b],e=this.options.toleranceElement?d(this.options.toleranceElement, 84 | c.item):c.item;if(!a){c.width=e.outerWidth();c.height=e.outerHeight()}e=e.offset();c.left=e.left;c.top=e.top}if(this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);else for(b=this.containers.length-1;b>=0;b--){e=this.containers[b].element.offset();this.containers[b].containerCache.left=e.left;this.containers[b].containerCache.top=e.top;this.containers[b].containerCache.width=this.containers[b].element.outerWidth();this.containers[b].containerCache.height= 85 | this.containers[b].element.outerHeight()}return this},_createPlaceholder:function(a){var b=a||this,c=b.options;if(!c.placeholder||c.placeholder.constructor==String){var e=c.placeholder;c.placeholder={element:function(){var f=d(document.createElement(b.currentItem[0].nodeName)).addClass(e||b.currentItem[0].className+" ui-sortable-placeholder").removeClass("ui-sortable-helper")[0];if(!e)f.style.visibility="hidden";return f},update:function(f,g){if(!(e&&!c.forcePlaceholderSize)){g.height()||g.height(b.currentItem.innerHeight()- 86 | parseInt(b.currentItem.css("paddingTop")||0,10)-parseInt(b.currentItem.css("paddingBottom")||0,10));g.width()||g.width(b.currentItem.innerWidth()-parseInt(b.currentItem.css("paddingLeft")||0,10)-parseInt(b.currentItem.css("paddingRight")||0,10))}}}}b.placeholder=d(c.placeholder.element.call(b.element,b.currentItem));b.currentItem.after(b.placeholder);c.placeholder.update(b,b.placeholder)},_contactContainers:function(a){for(var b=null,c=null,e=this.containers.length-1;e>=0;e--)if(!d.ui.contains(this.currentItem[0], 87 | this.containers[e].element[0]))if(this._intersectsWith(this.containers[e].containerCache)){if(!(b&&d.ui.contains(this.containers[e].element[0],b.element[0]))){b=this.containers[e];c=e}}else if(this.containers[e].containerCache.over){this.containers[e]._trigger("out",a,this._uiHash(this));this.containers[e].containerCache.over=0}if(b)if(this.containers.length===1){this.containers[c]._trigger("over",a,this._uiHash(this));this.containers[c].containerCache.over=1}else if(this.currentContainer!=this.containers[c]){b= 88 | 1E4;e=null;for(var f=this.positionAbs[this.containers[c].floating?"left":"top"],g=this.items.length-1;g>=0;g--)if(d.ui.contains(this.containers[c].element[0],this.items[g].item[0])){var h=this.items[g][this.containers[c].floating?"left":"top"];if(Math.abs(h-f)this.containment[2])f=this.containment[2]+this.offset.click.left;if(a.pageY-this.offset.click.top>this.containment[3])g=this.containment[3]+this.offset.click.top}if(b.grid){g=this.originalPageY+Math.round((g-this.originalPageY)/b.grid[1])*b.grid[1];g=this.containment?!(g-this.offset.click.topthis.containment[3])?g:!(g-this.offset.click.topthis.containment[2])?f:!(f-this.offset.click.left=0;e--)if(d.ui.contains(this.containers[e].element[0],this.currentItem[0])&&!b){c.push(function(f){return function(g){f._trigger("receive",g,this._uiHash(this))}}.call(this,this.containers[e]));c.push(function(f){return function(g){f._trigger("update", 104 | g,this._uiHash(this))}}.call(this,this.containers[e]))}}for(e=this.containers.length-1;e>=0;e--){b||c.push(function(f){return function(g){f._trigger("deactivate",g,this._uiHash(this))}}.call(this,this.containers[e]));if(this.containers[e].containerCache.over){c.push(function(f){return function(g){f._trigger("out",g,this._uiHash(this))}}.call(this,this.containers[e]));this.containers[e].containerCache.over=0}}this._storedCursor&&d("body").css("cursor",this._storedCursor);this._storedOpacity&&this.helper.css("opacity", 105 | this._storedOpacity);if(this._storedZIndex)this.helper.css("zIndex",this._storedZIndex=="auto"?"":this._storedZIndex);this.dragging=false;if(this.cancelHelperRemoval){if(!b){this._trigger("beforeStop",a,this._uiHash());for(e=0;e 0 ? this.collisionSolid : 0; 844 | 845 | var oldCollisionTile = this.collisionLayer.getOldTile(mapx, mapy); 846 | this.collisionLayer.setTile( mapx, mapy, collisionLayerTile ); 847 | this.undo.pushMapDraw( this.collisionLayer, mapx, mapy, oldCollisionTile, collisionLayerTile ); 848 | } 849 | } 850 | } 851 | 852 | this.setModified(); 853 | }, 854 | 855 | 856 | // ------------------------------------------------------------------------- 857 | // Drawing 858 | 859 | draw: function() { 860 | // The actual drawing loop is scheduled via ig.setAnimation() already. 861 | // We just set a flag to indicate that a redraw is needed. 862 | this.needsDraw = true; 863 | }, 864 | 865 | 866 | drawIfNeeded: function() { 867 | // Only draw if flag is set 868 | if( !this.needsDraw ) { return; } 869 | this.needsDraw = false; 870 | 871 | 872 | ig.system.clear( wm.config.colors.clear ); 873 | 874 | var entitiesDrawn = false; 875 | for( var i = 0; i < this.layers.length; i++ ) { 876 | var layer = this.layers[i]; 877 | 878 | // This layer is a foreground layer? -> Draw entities first! 879 | if( !entitiesDrawn && layer.foreground ) { 880 | entitiesDrawn = true; 881 | this.entities.draw(); 882 | } 883 | layer.draw(); 884 | } 885 | 886 | if( !entitiesDrawn ) { 887 | this.entities.draw(); 888 | } 889 | 890 | 891 | if( this.activeLayer ) { 892 | if( this.mode == this.MODE.TILESELECT ) { 893 | this.activeLayer.tileSelect.draw(); 894 | this.activeLayer.tileSelect.drawCursor( ig.input.mouse.x, ig.input.mouse.y ); 895 | } 896 | 897 | if( this.mode == this.MODE.DEFAULT ) { 898 | this.activeLayer.drawCursor( ig.input.mouse.x, ig.input.mouse.y ); 899 | } 900 | } 901 | 902 | if( wm.config.labels.draw ) { 903 | this.drawLabels( wm.config.labels.step ); 904 | } 905 | }, 906 | 907 | 908 | drawLabels: function( step ) { 909 | ig.system.context.fillStyle = wm.config.colors.primary; 910 | var xlabel = this.screen.x - this.screen.x % step - step; 911 | for( var tx = Math.floor(-this.screen.x % step); tx < ig.system.width; tx += step ) { 912 | xlabel += step; 913 | ig.system.context.fillText( xlabel, tx * ig.system.scale, 0 ); 914 | } 915 | 916 | var ylabel = this.screen.y - this.screen.y % step - step; 917 | for( var ty = Math.floor(-this.screen.y % step); ty < ig.system.height; ty += step ) { 918 | ylabel += step; 919 | ig.system.context.fillText( ylabel, 0, ty * ig.system.scale ); 920 | } 921 | }, 922 | 923 | 924 | getEntityByName: function( name ) { 925 | return this.entities.getEntityByName( name ); 926 | } 927 | }); 928 | 929 | 930 | wm.Weltmeister.getMaxWidth = function() { 931 | return $(window).width(); 932 | }; 933 | 934 | wm.Weltmeister.getMaxHeight = function() { 935 | return $(window).height() - $('#headerMenu').height(); 936 | }; 937 | 938 | 939 | // Custom ig.Image class for use in Weltmeister. To make the zoom function 940 | // work, we need some additional scaling behavior: 941 | // Keep the original image, maintain a cache of scaled versions and use the 942 | // default Canvas scaling (~bicubic) instead of nearest neighbor when 943 | // zooming out. 944 | ig.Image.inject({ 945 | resize: function( scale ) { 946 | if( !this.loaded ) { return; } 947 | if( !this.scaleCache ) { this.scaleCache = {}; } 948 | if( this.scaleCache['x'+scale] ) { 949 | this.data = this.scaleCache['x'+scale]; 950 | return; 951 | } 952 | 953 | // Retain the original image when scaling 954 | this.origData = this.data = this.origData || this.data; 955 | 956 | if( scale > 1 ) { 957 | // Nearest neighbor when zooming in 958 | this.parent( scale ); 959 | } 960 | else { 961 | // Otherwise blur 962 | var scaled = ig.$new('canvas'); 963 | scaled.width = Math.ceil(this.width * scale); 964 | scaled.height = Math.ceil(this.height * scale); 965 | var scaledCtx = scaled.getContext('2d'); 966 | scaledCtx.drawImage( this.data, 0, 0, this.width, this.height, 0, 0, scaled.width, scaled.height ); 967 | this.data = scaled; 968 | } 969 | 970 | this.scaleCache['x'+scale] = this.data; 971 | } 972 | }); 973 | 974 | 975 | 976 | // Create a custom loader, to skip sound files and the run loop creation 977 | wm.Loader = ig.Loader.extend({ 978 | end: function() { 979 | if( this.done ) { return; } 980 | 981 | clearInterval( this._intervalId ); 982 | this.done = true; 983 | ig.system.clear( wm.config.colors.clear ); 984 | ig.game = new (this.gameClass)(); 985 | }, 986 | 987 | loadResource: function( res ) { 988 | if( res instanceof ig.Sound ) { 989 | this._unloaded.erase( res.path ); 990 | } 991 | else { 992 | this.parent( res ); 993 | } 994 | } 995 | }); 996 | 997 | 998 | // Define a dummy module to load all plugins 999 | ig.module('weltmeister.loader').requires.apply(ig, wm.config.plugins).defines(function(){ 1000 | // Init! 1001 | ig.system = new ig.System( 1002 | '#canvas', 1, 1003 | Math.floor(wm.Weltmeister.getMaxWidth() / wm.config.view.zoom), 1004 | Math.floor(wm.Weltmeister.getMaxHeight() / wm.config.view.zoom), 1005 | wm.config.view.zoom 1006 | ); 1007 | 1008 | ig.input = new wm.EventedInput(); 1009 | ig.soundManager = new ig.SoundManager(); 1010 | ig.ready = true; 1011 | 1012 | var loader = new wm.Loader( wm.Weltmeister, ig.resources ); 1013 | loader.load(); 1014 | }); 1015 | 1016 | }); 1017 | --------------------------------------------------------------------------------