├── .DS_Store ├── README.md ├── grunt-workflow ├── .bowerrc ├── .gitignore ├── Gruntfile.coffee ├── app │ ├── cjs │ │ ├── app.coffee │ │ ├── bootstrap.coffee │ │ ├── collections │ │ │ ├── unit_stats.coffee │ │ │ └── units.coffee │ │ ├── config │ │ │ ├── backbone_fixins_ext.coffee │ │ │ └── max_stat_points_for_rank.coffee │ │ ├── converters.coffee │ │ ├── data │ │ │ └── unit_data.coffee │ │ ├── models │ │ │ ├── unit.coffee │ │ │ └── unit_stat.coffee │ │ ├── router.coffee │ │ └── views │ │ │ ├── character_selector.coffee │ │ │ ├── loadout.coffee │ │ │ ├── loadout_slot.coffee │ │ │ ├── stat_control.coffee │ │ │ ├── stat_editor.coffee │ │ │ ├── unit_grouping.coffee │ │ │ └── unit_selector.coffee │ ├── coffee │ │ ├── app.coffee │ │ ├── collections │ │ │ ├── unit_stats.coffee │ │ │ └── units.coffee │ │ ├── config │ │ │ ├── backbone_fixins_ext.coffee │ │ │ └── extend_ext.coffee │ │ ├── converters.coffee │ │ ├── data │ │ │ └── unit_data.coffee │ │ ├── models │ │ │ ├── unit.coffee │ │ │ └── unit_stat.coffee │ │ ├── router.coffee │ │ └── views │ │ │ ├── character_selector.coffee │ │ │ ├── loadout.coffee │ │ │ ├── loadout_slot.coffee │ │ │ ├── stat_control.coffee │ │ │ ├── stat_editor.coffee │ │ │ ├── unit_grouping.coffee │ │ │ └── unit_selector.coffee │ ├── css │ │ ├── _mixins.less │ │ └── style.less │ ├── img │ │ ├── banner.png │ │ ├── characters │ │ │ ├── base │ │ │ │ ├── archer.png │ │ │ │ ├── raider.png │ │ │ │ ├── shieldbanger.png │ │ │ │ └── warrior.png │ │ │ ├── portraits │ │ │ │ ├── backbiter.png │ │ │ │ ├── bowmaster.png │ │ │ │ ├── provoker.png │ │ │ │ ├── raidmaster.png │ │ │ │ ├── shieldmaster.png │ │ │ │ ├── siegearcher.png │ │ │ │ ├── skystriker.png │ │ │ │ ├── strongarm.png │ │ │ │ ├── thrasher.png │ │ │ │ ├── warhawk.png │ │ │ │ ├── warleader.png │ │ │ │ └── warmaster.png │ │ │ └── rank1 │ │ │ │ ├── backbiter.png │ │ │ │ ├── bowmaster.png │ │ │ │ ├── provoker.png │ │ │ │ ├── raidmaster.png │ │ │ │ ├── shieldmaster.png │ │ │ │ ├── siegearcher.png │ │ │ │ ├── skystriker.png │ │ │ │ ├── strongarm.png │ │ │ │ ├── thrasher.png │ │ │ │ ├── warhawk.png │ │ │ │ ├── warleader.png │ │ │ │ └── warmaster.png │ │ ├── empty.character.frame.png │ │ └── icon │ │ │ ├── armor.png │ │ │ ├── break.png │ │ │ ├── exertion.png │ │ │ ├── move-left.png │ │ │ ├── move-right.png │ │ │ ├── special.png │ │ │ ├── strength.png │ │ │ └── willpower.png │ ├── index.html │ └── templates │ │ ├── loadout.hb │ │ ├── loadout_slot.hb │ │ ├── stat_control.hb │ │ ├── stat_editor.hb │ │ ├── unit_grouping.hb │ │ └── unit_selector.hb ├── bower.json ├── package.json └── tasks │ └── server.js ├── lineman-workflow ├── .gitignore ├── .npmignore ├── Gruntfile.js ├── README.md ├── app │ ├── css │ │ ├── mixins.less │ │ └── style.less │ ├── img │ │ ├── .gitkeep │ │ ├── banner.png │ │ ├── characters │ │ │ ├── base │ │ │ │ ├── archer.png │ │ │ │ ├── raider.png │ │ │ │ ├── shieldbanger.png │ │ │ │ └── warrior.png │ │ │ ├── portraits │ │ │ │ ├── backbiter.png │ │ │ │ ├── bowmaster.png │ │ │ │ ├── provoker.png │ │ │ │ ├── raidmaster.png │ │ │ │ ├── shieldmaster.png │ │ │ │ ├── siegearcher.png │ │ │ │ ├── skystriker.png │ │ │ │ ├── strongarm.png │ │ │ │ ├── thrasher.png │ │ │ │ ├── warhawk.png │ │ │ │ ├── warleader.png │ │ │ │ └── warmaster.png │ │ │ └── rank1 │ │ │ │ ├── backbiter.png │ │ │ │ ├── bowmaster.png │ │ │ │ ├── provoker.png │ │ │ │ ├── raidmaster.png │ │ │ │ ├── shieldmaster.png │ │ │ │ ├── siegearcher.png │ │ │ │ ├── skystriker.png │ │ │ │ ├── strongarm.png │ │ │ │ ├── thrasher.png │ │ │ │ ├── warhawk.png │ │ │ │ ├── warleader.png │ │ │ │ └── warmaster.png │ │ ├── empty.character.frame.png │ │ └── icon │ │ │ ├── armor.png │ │ │ ├── break.png │ │ │ ├── exertion.png │ │ │ ├── move-left.png │ │ │ ├── move-right.png │ │ │ ├── special.png │ │ │ ├── strength.png │ │ │ └── willpower.png │ ├── js │ │ ├── .gitkeep │ │ ├── app.coffee │ │ ├── collections │ │ │ ├── unit_stats.coffee │ │ │ └── units.coffee │ │ ├── config │ │ │ ├── backbone_fixins_ext.coffee │ │ │ └── extend_ext.coffee │ │ ├── converters.coffee │ │ ├── data │ │ │ └── unit_data.coffee │ │ ├── models │ │ │ ├── unit.coffee │ │ │ └── unit_stat.coffee │ │ ├── router.coffee │ │ └── views │ │ │ ├── character_selector.coffee │ │ │ ├── loadout.coffee │ │ │ ├── loadout_slot.coffee │ │ │ ├── stat_control.coffee │ │ │ ├── stat_editor.coffee │ │ │ ├── unit_grouping.coffee │ │ │ └── unit_selector.coffee │ ├── pages │ │ └── index.hb │ └── templates │ │ ├── loadout.hb │ │ ├── loadout_slot.hb │ │ ├── stat_control.hb │ │ ├── stat_editor.hb │ │ ├── unit_grouping.hb │ │ └── unit_selector.hb ├── config │ ├── application.coffee │ ├── files.coffee │ ├── server.coffee │ └── spec.json ├── package.json ├── spec │ └── js │ │ └── helpers │ │ ├── helper.js │ │ ├── jasmine-fixture.js │ │ ├── jasmine-given.js │ │ └── jasmine-stealth.js ├── tasks │ └── .gitkeep └── vendor │ ├── css │ └── .gitkeep │ ├── img │ └── .gitkeep │ └── js │ ├── backbone-fixins.js │ ├── backbone.js │ ├── backbone.stickit.js │ ├── base64.js │ ├── bootstrap.js │ ├── extend.js │ ├── handlebars.js │ ├── jquery.js │ ├── underscore.js │ └── underscore.string.js ├── steps.md └── yeoman-workflow ├── .bowerrc ├── .editorconfig ├── .gitattributes ├── .gitignore ├── .jshintrc ├── Gruntfile.js ├── app ├── .htaccess ├── 404.html ├── favicon.ico ├── images │ ├── .gitkeep │ ├── banner.png │ ├── characters │ │ ├── base │ │ │ ├── archer.png │ │ │ ├── raider.png │ │ │ ├── shieldbanger.png │ │ │ └── warrior.png │ │ ├── portraits │ │ │ ├── backbiter.png │ │ │ ├── bowmaster.png │ │ │ ├── provoker.png │ │ │ ├── raidmaster.png │ │ │ ├── shieldmaster.png │ │ │ ├── siegearcher.png │ │ │ ├── skystriker.png │ │ │ ├── strongarm.png │ │ │ ├── thrasher.png │ │ │ ├── warhawk.png │ │ │ ├── warleader.png │ │ │ └── warmaster.png │ │ └── rank1 │ │ │ ├── backbiter.png │ │ │ ├── bowmaster.png │ │ │ ├── provoker.png │ │ │ ├── raidmaster.png │ │ │ ├── shieldmaster.png │ │ │ ├── siegearcher.png │ │ │ ├── skystriker.png │ │ │ ├── strongarm.png │ │ │ ├── thrasher.png │ │ │ ├── warhawk.png │ │ │ ├── warleader.png │ │ │ └── warmaster.png │ ├── empty.character.frame.png │ └── icon │ │ ├── armor.png │ │ ├── break.png │ │ ├── exertion.png │ │ ├── move-left.png │ │ ├── move-right.png │ │ ├── special.png │ │ ├── strength.png │ │ └── willpower.png ├── index.html ├── robots.txt ├── scripts │ ├── app.coffee │ ├── collections │ │ ├── unit.coffee │ │ └── unit_stat.coffee │ ├── config │ │ └── backbone_fixins_ext.coffee │ ├── converters.coffee │ ├── data │ │ └── unit_data.coffee │ ├── main.coffee │ ├── models │ │ ├── unit.coffee │ │ └── unit_stat.coffee │ ├── routes │ │ └── router.coffee │ ├── templates │ │ ├── loadout.hbs │ │ ├── loadout_slot.hbs │ │ ├── stat_control.hbs │ │ ├── stat_editor.hbs │ │ ├── unit_grouping.hbs │ │ └── unit_selector.hbs │ └── views │ │ ├── character_selector.coffee │ │ ├── loadout.coffee │ │ ├── loadout_slot.coffee │ │ ├── stat_control.coffee │ │ ├── stat_editor.coffee │ │ ├── unit_grouping.coffee │ │ └── unit_selector.coffee └── styles │ └── main.scss ├── bower.json ├── package.json └── test ├── index.html ├── lib ├── chai.js ├── expect.js └── mocha │ ├── mocha.css │ └── mocha.js └── spec └── test.js /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/.DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | fem-grunt-workflow 2 | ================== 3 | 4 | A custom app workflow to teach people how to use Grunt.js 5 | 6 | ## Getting Started 7 | 8 | Pre-requisites: 9 | 10 | 1. [Node.JS](http://www.nodejs.org) 11 | 2. NPM (which comes with Node) 12 | 3. some kind of text editor, I use [Sublime Text](http://www.sublimetext.com/2), feel free to use what you feel most comfortable with :) 13 | 4. git installed on your machine (if you are on Linux or Mac, this should be fairly straightforward, Windows users you should install http://msysgit.github.io/) 14 | 5. (optional) I use a number of aliases in my shell configuration to make working with git easier, feel free to yoink them into yours if you want to: https://gist.github.com/davemo/5329141#file-gitconfig 15 | 6. (optional) [git-crawl](https://github.com/magnusstahre/git-stuff): I'll be using this to "crawl" through a commit history for the project we are working on, this makes it much easier to move through commits than having to manually stash and checkout 16 | 7. (optional) [rowanj's fork of GitX for OSX](http://rowanj.github.io/gitx/), is a nice Git GUI client, but totally optional; if you have another Git GUI client feel free to use that. (Windows users, https://code.google.com/p/gitextensions/ looks about equivalent). 17 | 18 | Alternatively, if you don't want to work on your own machine and would prefer to use a virtualized environment, you can signup for a free account on Nitrous.io, I tried it out the other day and it should work fine for the things we are going to be working on. This environment will come pre-installed with numbers 1 through 4 in the list above. 19 | 20 | ## Crawling with `next`, `prev` aliases 21 | 22 | Put these aliases in your path for easy crawling :) 23 | 24 | ```shell 25 | $ cat ~/bin/next 26 | #!/usr/bin/env bash 27 | git crawl pdc 28 | ``` 29 | 30 | ```shell 31 | $ cat ~/bin/prev 32 | #!/usr/bin/env bash 33 | git co HEAD^1 34 | ``` 35 | -------------------------------------------------------------------------------- /grunt-workflow/.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "bower_modules" 3 | } 4 | -------------------------------------------------------------------------------- /grunt-workflow/.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | generated 3 | node_modules 4 | bower_modules 5 | .DS_Store 6 | -------------------------------------------------------------------------------- /grunt-workflow/Gruntfile.coffee: -------------------------------------------------------------------------------- 1 | module.exports = (grunt) -> 2 | 3 | # task configurations 4 | # initializing task configuration 5 | grunt.initConfig 6 | 7 | # meta data 8 | pkg: grunt.file.readJSON("package.json") 9 | banner: "/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - " + 10 | "<%= grunt.template.today(\"yyyy-mm-dd\") %>\n" + 11 | "<%= pkg.homepage ? \"* \" + pkg.homepage + \"\\n\" : \"\" %>" + 12 | "* Copyright (c) <%= grunt.template.today(\"yyyy\") %> <%= pkg.author.name %>;" + 13 | " Licensed <%= _.pluck(pkg.licenses, \"type\").join(\", \") %> */\n" 14 | 15 | # files that our tasks will use 16 | files: 17 | html: 18 | src: "app/index.html" 19 | 20 | less: 21 | src: ["app/css/style.less"] 22 | 23 | js: 24 | vendor: [ 25 | "bower_modules/jquery/jquery.js" 26 | "bower_modules/underscore/underscore.js" 27 | "bower_modules/underscore.string/lib/underscore.string.js" 28 | "bower_modules/extend.js/index.js" 29 | "bower_modules/backbone/backbone.js" 30 | "bower_modules/backbone-fixins.js/index.js" 31 | "bower_modules/backbone.stickit/backbone.stickit.js" 32 | "bower_modules/base64js/base64.js" 33 | "bower_modules/handlebars/handlebars.js" 34 | ] 35 | 36 | app: 37 | main: "app/cjs/bootstrap.coffee" 38 | compiled: "generated/js/app.min.js" 39 | 40 | templates: 41 | src: "app/templates/**/*.hb" 42 | compiled: "generated/template-cache.js" 43 | 44 | # task configuration 45 | browserify: 46 | app: 47 | files: 48 | "<%= files.js.app.compiled %>" : "<%= files.js.app.main %>" 49 | options: 50 | debug: true 51 | transform: ["coffeeify"] 52 | 53 | concat_sourcemap: 54 | options: 55 | sourcesContent: true 56 | app: 57 | src: [ 58 | "<%= files.js.vendor %>" 59 | "<%= files.templates.compiled %>" 60 | ] 61 | dest: "generated/js/vendor.min.js" 62 | 63 | watch: 64 | options: 65 | livereload: true 66 | 67 | # targets for watch 68 | html: 69 | files: ["<%= files.html.src %>"] 70 | tasks: ["copy"] 71 | 72 | js: 73 | files: ["<%= files.js.vendor %>"] 74 | tasks: ["concat_sourcemap"] 75 | 76 | coffee: 77 | files: ["app/cjs/**/*.coffee"] 78 | tasks: ["browserify", "concat_sourcemap"] 79 | 80 | less: 81 | files: ["<%= files.less.src %>"] 82 | tasks: ["less:dev"] 83 | 84 | imagemin: 85 | dist: 86 | files: [ 87 | expand: true, 88 | cwd: 'app/img' 89 | src: '**/*.{png,jpg,jpeg}' 90 | dest: 'generated/img-min' 91 | ] 92 | less: 93 | options: 94 | ieCompat: false 95 | 96 | dev: 97 | src: "<%= files.less.src %>" 98 | dest: "generated/css/style.css" 99 | 100 | dist: 101 | options: 102 | cleancss: true 103 | compress: true 104 | src: "<%= files.less.src %>" 105 | dest: "dist/css/style.css" 106 | 107 | handlebars: 108 | options: 109 | namespace: "JST" 110 | wrapped: true 111 | compile: 112 | src: "<%= files.templates.src %>" 113 | dest: "<%= files.templates.compiled %>" 114 | 115 | copy: 116 | html: 117 | files: 118 | "generated/index.html" : "<%= files.html.src %>" 119 | "dist/index.html" : "<%= files.html.src %>" 120 | 121 | server: 122 | base: "#{process.env.SERVER_BASE || 'generated'}" 123 | web: 124 | port: 8000 125 | 126 | open: 127 | dev: 128 | path: "http://localhost:<%= server.web.port %>" 129 | 130 | uglify: 131 | options: 132 | banner: "<%= banner %>" 133 | 134 | dist: 135 | sourceMapIn: "dist/js/app.min.js.map" 136 | sourceMap: "dist/js/app.min.js.map" 137 | src: "<%= concat_sourcemap.app.dest %>" # input from the concat_sourcemap process 138 | dest: "dist/js/app.min.js" 139 | 140 | clean: 141 | workspaces: ["dist", "generated"] 142 | 143 | # loading local tasks 144 | grunt.loadTasks "tasks" 145 | 146 | # loading external tasks (aka: plugins) 147 | # Loads all plugins that match "grunt-", in this case all of our current plugins 148 | require('matchdep').filterAll('grunt-*').forEach(grunt.loadNpmTasks) 149 | 150 | # creating workflows 151 | grunt.registerTask "default", ["handlebars", "imagemin", "less:dev", "browserify", "concat_sourcemap", "copy", "server", "open", "watch"] 152 | grunt.registerTask "build", ["clean", "handlebars", "imagemin", "less:dist", "browserify", "concat_sourcemap", "uglify", "copy"] 153 | grunt.registerTask "prodsim", ["build", "server", "open", "watch"] 154 | -------------------------------------------------------------------------------- /grunt-workflow/app/cjs/app.coffee: -------------------------------------------------------------------------------- 1 | Units = require("./collections/units.coffee") 2 | Router = require("./router.coffee") 3 | Editor = require("./views/stat_editor.coffee") 4 | Loadout = require("./views/loadout.coffee") 5 | Selector = require("./views/character_selector.coffee") 6 | Unit = require("./models/unit.coffee") 7 | UnitData = require("./data/unit_data.coffee") 8 | maxStatPointsForRank = require("./config/max_stat_points_for_rank.coffee") 9 | 10 | module.exports = class BattlePlanner 11 | 12 | constructor: -> 13 | @units = new Units(@defaultUnits()) 14 | @loadout_units = new Units(@defaultLoadoutUnits()) 15 | @router = new Router(loadout: @loadout_units) 16 | 17 | # stat editor 18 | @stat_editor = new Editor(el: "#stat-editor") 19 | 20 | # loadout 21 | @loadout = new Loadout( 22 | el: "#loadout" 23 | collection: @loadout_units 24 | ).render() 25 | 26 | #character_selector 27 | @character_selector = new Selector( 28 | el: "#character-selector" 29 | collection: @unitTypesWithoutBase() 30 | loadout_units: @loadout_units 31 | ).render() 32 | 33 | unitTypesWithoutBase: => 34 | _(@units.groupBy("type")).tap (types) -> delete types.base 35 | 36 | defaultLoadoutUnits: => 37 | _(_.range(0,6)).map (slot) => new Unit 38 | 39 | defaultUnits: => 40 | _(UnitData()).tap (units) => 41 | _(units).each (unit) => 42 | unit.max_stat_points = maxStatPointsForRank(unit.rank) 43 | unit.allocated_stat_points = 0 44 | 45 | start: -> 46 | Backbone.history.start() 47 | -------------------------------------------------------------------------------- /grunt-workflow/app/cjs/bootstrap.coffee: -------------------------------------------------------------------------------- 1 | require("./config/backbone_fixins_ext.coffee")() 2 | 3 | app = require("./app.coffee") 4 | 5 | $ -> 6 | window.app = new app(); 7 | window.app.start() 8 | -------------------------------------------------------------------------------- /grunt-workflow/app/cjs/collections/unit_stats.coffee: -------------------------------------------------------------------------------- 1 | UnitStat = require("../models/unit_stat.coffee") 2 | 3 | module.exports = class UnitStats extends Backbone.Collection 4 | model: UnitStat 5 | -------------------------------------------------------------------------------- /grunt-workflow/app/cjs/collections/units.coffee: -------------------------------------------------------------------------------- 1 | Unit = require("../models/unit.coffee") 2 | UnitStats = require("./unit_stats.coffee") 3 | 4 | module.exports = class Units extends Backbone.Collection 5 | model: Unit 6 | 7 | UNIT_NAME_FULL_TO_ENCODED: 8 | 'raidmaster' : 'rm' 9 | 'thrasher' : 'th' 10 | 'backbiter' : 'bb' 11 | 'bowmaster' : 'bm' 12 | 'skystriker' : 'ss' 13 | 'siegearcher' : 'sg' 14 | 'warmaster' : 'wm' 15 | 'warhawk' : 'wh' 16 | 'warleader' : 'wl' 17 | 'provoker' : 'pk' 18 | 'strongarm' : 'sa' 19 | 'shieldmaster': 'sm' 20 | 21 | UNIT_NAME_ENCODED_TO_FULL: 22 | 'rm' : 'raidmaster' 23 | 'th' : 'thrasher' 24 | 'bb' : 'backbiter' 25 | 'bm' : 'bowmaster' 26 | 'ss' : 'skystriker' 27 | 'sg' : 'siegearcher' 28 | 'wm' : 'warmaster' 29 | 'wh' : 'warhawk' 30 | 'wl' : 'warleader' 31 | 'pk' : 'provoker' 32 | 'sa' : 'strongarm' 33 | 'sm' : 'shieldmaster' 34 | 35 | STAT_NAME_FULL_TO_ENCODED: 36 | 'armor' : 'a' 37 | 'strength' : 's' 38 | 'willpower' : 'w' 39 | 'exertion' : 'e' 40 | 'break' : 'b' 41 | 42 | STAT_NAME_ENCODED_TO_FULL: 43 | 'a' : 'armor' 44 | 's' : 'strength' 45 | 'w' : 'willpower' 46 | 'e' : 'exertion' 47 | 'b' : 'break' 48 | 49 | toJSON: => 50 | @map (unit) => 51 | converted = unit.toJSON() 52 | if converted.stats 53 | converted.stats = _(converted.stats.models).map (stat) => 54 | stat.attributes 55 | converted 56 | 57 | serialize: => 58 | output = [] 59 | _(@toJSON()).each (u) => 60 | stats = _(u.stats).map (s) => [@STAT_NAME_FULL_TO_ENCODED[s.stat],s.current,s.min,s.max] 61 | output.push([@UNIT_NAME_FULL_TO_ENCODED[u.name],u.rank,u.allocated_stat_points,u.max_stat_points,stats]) 62 | base64.encode(JSON.stringify(output)) 63 | 64 | deserialize: (encoded) => 65 | units = JSON.parse(base64.decode(encoded)) 66 | model_data = [] 67 | _(units).each (unit_data) => 68 | if unit_data[4].length is 0 69 | model_data.push(new Unit) 70 | else 71 | model_data.push( 72 | name: @UNIT_NAME_ENCODED_TO_FULL[unit_data[0]] 73 | rank: unit_data[1] 74 | allocated_stat_points: unit_data[2] 75 | max_stat_points: unit_data[3] 76 | stats: new UnitStats( 77 | _(unit_data[4]).map (stat) => 78 | stat: @STAT_NAME_ENCODED_TO_FULL[stat[0]] 79 | current: stat[1] 80 | min: stat[2] 81 | max: stat[3] 82 | ) 83 | ) 84 | model_data 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /grunt-workflow/app/cjs/config/backbone_fixins_ext.coffee: -------------------------------------------------------------------------------- 1 | module.exports = -> 2 | Backbone.Fixins.configure 3 | templateFunction: (name) -> 4 | JST[name] || throw "Could not find a template in JST[#{name}]" 5 | 6 | defaultTemplateLocator: (view) -> 7 | "app/templates/#{subPathFor(view)}.hb" 8 | 9 | subPathFor = (view) -> 10 | Backbone.Fixins.helpers.titleToSnakeCase(view.constructor.name) 11 | -------------------------------------------------------------------------------- /grunt-workflow/app/cjs/config/max_stat_points_for_rank.coffee: -------------------------------------------------------------------------------- 1 | module.exports = (rank) -> 2 | switch rank 3 | when 0 then 10 4 | when 1 then 11 5 | when 2 then 12 6 | when 3 then 13 7 | -------------------------------------------------------------------------------- /grunt-workflow/app/cjs/converters.coffee: -------------------------------------------------------------------------------- 1 | UnitStats = require("./collections/unit_stats.coffee") 2 | 3 | module.exports = (raw_stats_array, rank) -> 4 | 5 | STATS = ["armor", "strength", "willpower", "exertion", "break"] 6 | 7 | # stats come in from unit_data.coffee 8 | # arm str wil exr brk (min,max) 9 | # [[6,9], [6,9], [4,6,] [1,2,] [1,2]] 10 | 11 | model_values = _(raw_stats_array).map (min_max, i) -> 12 | min: min_max[0] 13 | max: min_max[1] 14 | current: min_max[0] 15 | stat: STATS[i] 16 | 17 | new UnitStats(model_values) 18 | 19 | 20 | -------------------------------------------------------------------------------- /grunt-workflow/app/cjs/data/unit_data.coffee: -------------------------------------------------------------------------------- 1 | buildStatsCollection = require("../converters.coffee") 2 | 3 | module.exports = -> 4 | #arm str wil exr brk (min,max) 5 | [ 6 | { 7 | name: "raider" 8 | type: "base" 9 | rank: 0 10 | stats: buildStatsCollection([[6,9], [6,9], [4,6], [1,2], [1,2]]) 11 | } 12 | { 13 | name: "raidmaster" 14 | type: "raider" 15 | rank: 1 16 | stats: buildStatsCollection([[6,12], [6,12], [4,11], [1,3], [1,3]]) 17 | } 18 | { 19 | name: "thrasher" 20 | type: "raider" 21 | rank: 1 22 | stats: buildStatsCollection([[5,11], [8,12], [3,13], [1,3], [1,2]]) 23 | } 24 | { 25 | name: "backbiter" 26 | type: "raider" 27 | rank: 1 28 | stats: buildStatsCollection([[5,12], [8,10], [4,13], [0,3], [1,3]]) 29 | } 30 | { 31 | name: "archer" 32 | type: "base" 33 | rank: 0 34 | stats: buildStatsCollection([[4,7], [4,7], [5,8], [1,2], [1,1]]) 35 | } 36 | { 37 | name: "bowmaster" 38 | type: "archer" 39 | rank: 1 40 | stats: buildStatsCollection([[4,9], [4,8], [5,12], [1,3], [1,2]]) 41 | } 42 | { 43 | name: "skystriker" 44 | type: "archer" 45 | rank: 1 46 | stats: buildStatsCollection([[3,9], [4,8], [7,13], [1,3], [0,1]]) 47 | } 48 | { 49 | name: "siegearcher" 50 | type: "archer" 51 | rank: 1 52 | stats: buildStatsCollection([[4,9], [4,7], [6,13], [1,3], [0,2]]) 53 | } 54 | { 55 | name: "warrior" 56 | type: "base" 57 | rank: 0 58 | stats: buildStatsCollection([[9,9], [12,12], [5,5], [2,2], [2,2]]) 59 | } 60 | { 61 | name: "warmaster" 62 | type: "warrior" 63 | rank: 1 64 | stats: buildStatsCollection([[6,11], [9,17], [3,10], [1,2], [1,3]]) 65 | } 66 | { 67 | name: "warhawk" 68 | type: "warrior" 69 | rank: 1 70 | stats: buildStatsCollection([[7,12], [10,16], [2,11], [0,2], [1,2]]) 71 | } 72 | { 73 | name: "warleader" 74 | type: "warrior" 75 | rank: 1 76 | stats: buildStatsCollection([[5,12], [9,15], [5,9], [1,3], [0,4]]) 77 | } 78 | { 79 | name: "shieldbanger" 80 | type: "base" 81 | rank: 0 82 | stats: buildStatsCollection([[9,14], [8,10], [3,5], [1,1], [1,2]]) 83 | } 84 | { 85 | name: "provoker" 86 | type: "shieldbanger" 87 | rank: 1 88 | stats: buildStatsCollection([[11,18], [7,12], [3,9], [0,2], [1,3]]) 89 | } 90 | { 91 | name: "strongarm" 92 | type: "shieldbanger" 93 | rank: 1 94 | stats: buildStatsCollection([[9,15], [9,15], [3,10], [0,2], [1,2]]) 95 | } 96 | { 97 | name: "shieldmaster" 98 | type: "shieldbanger" 99 | rank: 1 100 | stats: buildStatsCollection([[9,16], [8,13], [3,9], [1,2], [1,4]]) 101 | } 102 | ] 103 | -------------------------------------------------------------------------------- /grunt-workflow/app/cjs/models/unit.coffee: -------------------------------------------------------------------------------- 1 | module.exports = class Unit extends Backbone.Model 2 | defaults: 3 | name: "" 4 | type: "" 5 | stats: undefined 6 | allocated_stat_points: 0 7 | max_stat_points: 0 8 | 9 | isChosen: => 10 | @has("stats") 11 | 12 | isEmpty: => 13 | not @has("stats") 14 | 15 | validate: (attrs) => 16 | if attrs.allocated_stat_points < 0 or attrs.allocated_stat_points > attrs.max_stat_points 17 | "would go over" 18 | -------------------------------------------------------------------------------- /grunt-workflow/app/cjs/models/unit_stat.coffee: -------------------------------------------------------------------------------- 1 | module.exports = class UnitStat extends Backbone.Model 2 | -------------------------------------------------------------------------------- /grunt-workflow/app/cjs/router.coffee: -------------------------------------------------------------------------------- 1 | module.exports = class Router extends Backbone.Router 2 | 3 | initialize: (options) => 4 | @loadout = options.loadout 5 | @loadout.on("change", @storeHash) 6 | 7 | routes: 8 | ":encoded" : "loadLoadout" 9 | 10 | loadLoadout: (encoded) -> 11 | @loadout.reset(@loadout.deserialize(encoded)) 12 | Backbone.trigger("loaded:from:hash") 13 | 14 | storeHash: => 15 | @navigate(@loadout.serialize(), {replace: true}) 16 | -------------------------------------------------------------------------------- /grunt-workflow/app/cjs/views/character_selector.coffee: -------------------------------------------------------------------------------- 1 | UnitGrouping = require("./unit_grouping.coffee") 2 | 3 | module.exports = class CharacterSelector extends Backbone.View 4 | 5 | initialize: (options) => 6 | Backbone.on("edit:unit", @hide) 7 | Backbone.on("choose:unit", @show) 8 | @loadout_units = options.loadout_units 9 | 10 | render: => 11 | _(@collection).each (units, type) => 12 | @$("#unit-groupings").append(new UnitGrouping( 13 | units: units 14 | type: type 15 | loadout_units: @loadout_units 16 | ).render().el) 17 | 18 | hide: => 19 | @$el.hide() 20 | 21 | show: => 22 | @$el.show() 23 | 24 | -------------------------------------------------------------------------------- /grunt-workflow/app/cjs/views/loadout.coffee: -------------------------------------------------------------------------------- 1 | LoadoutSlot = require("./loadout_slot.coffee") 2 | 3 | module.exports = class Loadout extends Backbone.View 4 | 5 | events: 6 | "click .character" : "editUnit" 7 | 8 | initialize: => 9 | Backbone.on("loaded:from:hash", @render) 10 | 11 | editUnit: (e) => 12 | unit = @fetchUnitFromCollectionViaSlotDataAttribute(e) 13 | if unit.isChosen() 14 | Backbone.trigger("edit:unit", unit) 15 | else 16 | Backbone.trigger("choose:unit") 17 | 18 | fetchUnitFromCollectionViaSlotDataAttribute: (element) => 19 | @collection.at($(element.currentTarget).data("slot")) 20 | 21 | render: => 22 | @$("#selected-characters").empty() 23 | @collection.each (unit, i) => 24 | @$("#selected-characters").append(new LoadoutSlot( 25 | model: unit 26 | slot: i 27 | ).render().el) 28 | @ 29 | -------------------------------------------------------------------------------- /grunt-workflow/app/cjs/views/loadout_slot.coffee: -------------------------------------------------------------------------------- 1 | Unit = require("../models/unit.coffee") 2 | maxStatPointsForRank = require("../config/max_stat_points_for_rank.coffee") 3 | 4 | module.exports = class LoadoutSlot extends Backbone.Fixins.SuperView 5 | 6 | tagName: "li" 7 | 8 | events: 9 | "mouseover" : "showOverlay" 10 | "mouseout" : "hideOverlay" 11 | "click .remove" : "removeFromLoadout" 12 | "click .move-left" : "moveLeft" 13 | "click .move-right" : "moveRight" 14 | 15 | removeFromLoadout: (e) -> 16 | if confirm("Remove #{@model.get('name')} from slot #{@slot + 1}?") 17 | @model.clear() 18 | @model.set(new Unit().attributes) 19 | Backbone.trigger("choose:unit") 20 | else 21 | e.preventDefault() 22 | e.stopPropagation() 23 | 24 | attributes: => 25 | "class" : "character #{@model.get('name')}" 26 | "data-slot" : @slot 27 | 28 | showOverlay: => 29 | if @model.isChosen() then @$(".remove").show() 30 | if @model.isChosen() 31 | @$(".move-left, .move-right").show() 32 | 33 | if @position() is 0 then @$(".move-left").hide() 34 | if @position() is 5 then @$(".move-right").hide() 35 | 36 | moveLeft: (e) => 37 | e.stopPropagation() 38 | @move(@position(-1)) 39 | 40 | moveRight: (e) => 41 | e.stopPropagation() 42 | @move(@position(1)) 43 | 44 | move: (new_position_index) => 45 | original_attrs = _(@model.attributes).clone() 46 | new_attrs = _(@model.collection.at(new_position_index).attributes).clone() 47 | other_model = @model.collection.at(new_position_index) 48 | @model.clear() 49 | @model.set(new_attrs) 50 | other_model.clear() 51 | other_model.set(original_attrs) 52 | Backbone.trigger("loadout:reorganized", other_model) 53 | 54 | position: (offset=0)=> 55 | @model.collection.indexOf(@model) + offset 56 | 57 | hideOverlay: => 58 | @$(".remove, .move-left, .move-right").hide() 59 | 60 | initialize: (options) => 61 | @slot = options.slot 62 | @model.on("change", @render, @) 63 | @model.on("change:rank", @updateMaxStatPoints) 64 | 65 | renderAttributes: => 66 | attrs = @attributes() 67 | @$el.attr("class", attrs.class) 68 | @$el.attr("data-slot", attrs['data-slot']) 69 | 70 | renderStatsOverlay: => 71 | if stats = @model.get('stats') 72 | stats.each (stat) => 73 | @$(".stats-overlay .#{stat.get('stat')}").text(stat.get('current')) 74 | 75 | renderAllocatedStatsOverlay: => 76 | @$(".allocated-max-stats .allocated").text(@model.get("allocated_stat_points")) 77 | @$(".allocated-max-stats .max").text(@model.get("max_stat_points")) 78 | 79 | renderHelpText: => 80 | if @model.isChosen() 81 | @$(".help-text").text("") 82 | else 83 | @$(".help-text").text("Click a unit below") 84 | 85 | updateMaxStatPoints: (__, new_rank) => 86 | @model.set("max_stat_points", maxStatPointsForRank(new_rank)) 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /grunt-workflow/app/cjs/views/stat_control.coffee: -------------------------------------------------------------------------------- 1 | module.exports = class StatControl extends Backbone.Fixins.SuperView 2 | 3 | tagName: "li" 4 | 5 | events: 6 | "mousedown .icon" : "increaseOrDecreaseStat" 7 | "contextmenu .icon" : -> false 8 | 9 | initialize: (options) -> 10 | @unit = options.unit 11 | @model.on("change:current", @update) 12 | 13 | attributes: => 14 | class: @model.get("stat") 15 | 16 | increaseOrDecreaseStat: (e) => 17 | e.preventDefault() 18 | e.stopPropagation() 19 | switch e.which 20 | when 1 then @changeBy(1) #left click 21 | when 3 then @changeBy(-1) #right click 22 | 23 | changeBy: (amount) => 24 | # if the amount does not exceed the units max_stat_points 25 | # if the amount does not exceed the stats maximum 26 | if !(@unit.get('allocated_stat_points') + amount > @unit.get('max_stat_points') or @unit.get('allocated_stat_points') + amount < 0) 27 | if !(@model.get('current') + amount < @model.get('min') or @model.get('current') + amount > @model.get('max')) 28 | @model.set(current: @model.get("current") + amount) 29 | @unit.set(allocated_stat_points: @unit.get('allocated_stat_points') + amount) 30 | 31 | update: => 32 | @$(".current").text(@model.get("current")) 33 | 34 | -------------------------------------------------------------------------------- /grunt-workflow/app/cjs/views/stat_editor.coffee: -------------------------------------------------------------------------------- 1 | StatControl = require("./stat_control.coffee") 2 | UnitStats = require("../collections/unit_stats.coffee") 3 | 4 | module.exports = class StatEditor extends Backbone.Fixins.SuperView 5 | 6 | events: 7 | "click .reset" : "resetToMinimums" 8 | "click .done" : -> Backbone.trigger("choose:unit") 9 | "click .change-rank" : "setRank" 10 | 11 | initialize: => 12 | Backbone.on("edit:unit", @show) 13 | Backbone.on("choose:unit", @hide) 14 | Backbone.on("loadout:reorganized", @show) 15 | 16 | renderVisible: => 17 | if @model.isChosen() then @$el.show() else @$el.hide() 18 | 19 | renderStatControls: => 20 | unit_stats = @model.get('stats') 21 | unit_stats.each (stat, i) => 22 | @$(".stats").append(new StatControl(unit: @model, model: stat).render().el) 23 | 24 | resetToMinimums: => 25 | @model.set("allocated_stat_points", 0) 26 | stats = @model.get('stats') 27 | stats.each (stat) => 28 | stat.set('current', stat.get('min')) 29 | @model.trigger('change') 30 | 31 | renderTotals: => 32 | @$(".total .allocated").text(@model.get("allocated_stat_points")) 33 | @$(".total .max").text(@model.get("max_stat_points")) 34 | 35 | renderRankChanger: => 36 | @$("#rank-changer").prop("class", "rank#{@model.get("rank")}") 37 | 38 | renderRankInEditorTitle: => 39 | @$(".title .rank").text(@model.get("rank")) 40 | 41 | setRank: (e) => 42 | rank = parseInt($(e.currentTarget).text(), 10) 43 | oldrank = @model.get("rank") 44 | @model.set({rank}) 45 | if rank < oldrank 46 | @resetToMinimums() 47 | 48 | hide: => 49 | @$el.hide() 50 | 51 | show: (unit) => 52 | @model = unit 53 | # had to clone stats here, as editing the same class was buggy 54 | @model.set("stats", new UnitStats(@model.get('stats').toJSON())) 55 | @model.on("change:allocated_stat_points", @renderTotals) 56 | @model.on("change:rank", @renderRankChanger) 57 | @model.on("change:rank", @renderRankInEditorTitle) 58 | @model.on("change:rank", @renderTotals) 59 | @render() 60 | @$el.show() 61 | -------------------------------------------------------------------------------- /grunt-workflow/app/cjs/views/unit_grouping.coffee: -------------------------------------------------------------------------------- 1 | UnitSelector = require("./unit_selector.coffee") 2 | 3 | module.exports = class UnitGrouping extends Backbone.Fixins.SuperView 4 | 5 | attributes: 6 | class: "unit-type" 7 | 8 | initialize: (options) -> 9 | @type = options.type 10 | @units = options.units 11 | @loadout_units = options.loadout_units 12 | 13 | renderTitle: => 14 | @$(".title").text(@type) 15 | 16 | renderUnits: => 17 | _(@units).each (unit) => 18 | @$(".classes").append(new UnitSelector( 19 | model: unit 20 | loadout_units: @loadout_units 21 | ).render().el) 22 | -------------------------------------------------------------------------------- /grunt-workflow/app/cjs/views/unit_selector.coffee: -------------------------------------------------------------------------------- 1 | module.exports = class UnitSelector extends Backbone.Fixins.SuperView 2 | 3 | tagName: "li" 4 | 5 | initialize: (options) -> 6 | @loadout_units = options.loadout_units 7 | 8 | attributes: => 9 | class: "character #{@model.get('name')}" 10 | 11 | events: 12 | "click" : "assignUnitToNextAvailableLoadoutSlot" 13 | 14 | assignUnitToNextAvailableLoadoutSlot: => 15 | # get the next active loadout slot 16 | loadout_unit = @loadout_units.find (unit) => unit.isEmpty() 17 | if not loadout_unit then loadout_unit = @loadout_units.at(5) 18 | loadout_unit.clear(silent: true) 19 | loadout_unit.set(@model.clone().attributes) 20 | -------------------------------------------------------------------------------- /grunt-workflow/app/coffee/app.coffee: -------------------------------------------------------------------------------- 1 | def 'tbs.BattlePlanner', class BattlePlanner 2 | 3 | constructor: -> 4 | @units = new tbs.collections.Units(@defaultUnits()) 5 | @loadout_units = new tbs.collections.Units(@defaultLoadoutUnits()) 6 | @router = new tbs.Router(loadout: @loadout_units) 7 | 8 | # stat editor 9 | @stat_editor = new tbs.views.StatEditor(el: "#stat-editor") 10 | 11 | # loadout 12 | @loadout = new tbs.views.Loadout( 13 | el: "#loadout" 14 | collection: @loadout_units 15 | ).render() 16 | 17 | #character_selector 18 | @character_selector = new tbs.views.CharacterSelector( 19 | el: "#character-selector" 20 | collection: @unitTypesWithoutBase() 21 | ).render() 22 | 23 | unitTypesWithoutBase: => 24 | _(@units.groupBy("type")).tap (types) -> delete types.base 25 | 26 | defaultLoadoutUnits: => 27 | _(_.range(0,6)).map (slot) => 28 | new tbs.models.Unit 29 | 30 | defaultUnits: => 31 | _(tbs.data.Units()).tap (units) => 32 | _(units).each (unit) => 33 | unit.max_stat_points = @maxStatPointsForRank(unit.rank) 34 | unit.allocated_stat_points = 0 35 | 36 | maxStatPointsForRank: (rank) => 37 | switch rank 38 | when 0 then 10 39 | when 1 then 11 40 | when 2 then 12 41 | when 3 then 13 42 | 43 | start: -> 44 | Backbone.history.start() 45 | 46 | $ -> tbs.BattlePlanner = new tbs.BattlePlanner(); tbs.BattlePlanner.start() 47 | 48 | -------------------------------------------------------------------------------- /grunt-workflow/app/coffee/collections/unit_stats.coffee: -------------------------------------------------------------------------------- 1 | def 'tbs.collections.UnitStats', class UnitStats extends Backbone.Collection 2 | model: tbs.models.UnitStat 3 | -------------------------------------------------------------------------------- /grunt-workflow/app/coffee/collections/units.coffee: -------------------------------------------------------------------------------- 1 | def 'tbs.collections.Units', class Units extends Backbone.Collection 2 | model: tbs.models.Unit 3 | 4 | UNIT_NAME_FULL_TO_ENCODED: 5 | 'raidmaster' : 'rm' 6 | 'thrasher' : 'th' 7 | 'backbiter' : 'bb' 8 | 'bowmaster' : 'bm' 9 | 'skystriker' : 'ss' 10 | 'siegearcher' : 'sg' 11 | 'warmaster' : 'wm' 12 | 'warhawk' : 'wh' 13 | 'warleader' : 'wl' 14 | 'provoker' : 'pk' 15 | 'strongarm' : 'sa' 16 | 'shieldmaster': 'sm' 17 | 18 | UNIT_NAME_ENCODED_TO_FULL: 19 | 'rm' : 'raidmaster' 20 | 'th' : 'thrasher' 21 | 'bb' : 'backbiter' 22 | 'bm' : 'bowmaster' 23 | 'ss' : 'skystriker' 24 | 'sg' : 'siegearcher' 25 | 'wm' : 'warmaster' 26 | 'wh' : 'warhawk' 27 | 'wl' : 'warleader' 28 | 'pk' : 'provoker' 29 | 'sa' : 'strongarm' 30 | 'sm' : 'shieldmaster' 31 | 32 | STAT_NAME_FULL_TO_ENCODED: 33 | 'armor' : 'a' 34 | 'strength' : 's' 35 | 'willpower' : 'w' 36 | 'exertion' : 'e' 37 | 'break' : 'b' 38 | 39 | STAT_NAME_ENCODED_TO_FULL: 40 | 'a' : 'armor' 41 | 's' : 'strength' 42 | 'w' : 'willpower' 43 | 'e' : 'exertion' 44 | 'b' : 'break' 45 | 46 | toJSON: => 47 | @map (unit) => 48 | converted = unit.toJSON() 49 | if converted.stats 50 | converted.stats = _(converted.stats.models).map (stat) => 51 | stat.attributes 52 | converted 53 | 54 | serialize: => 55 | output = [] 56 | _(@toJSON()).each (u) => 57 | stats = _(u.stats).map (s) => [@STAT_NAME_FULL_TO_ENCODED[s.stat],s.current,s.min,s.max] 58 | output.push([@UNIT_NAME_FULL_TO_ENCODED[u.name],u.rank,u.allocated_stat_points,u.max_stat_points,stats]) 59 | base64.encode(JSON.stringify(output)) 60 | 61 | deserialize: (encoded) => 62 | units = JSON.parse(base64.decode(encoded)) 63 | model_data = [] 64 | _(units).each (unit_data) => 65 | if unit_data[4].length is 0 66 | model_data.push(new tbs.models.Unit) 67 | else 68 | model_data.push( 69 | name: @UNIT_NAME_ENCODED_TO_FULL[unit_data[0]] 70 | rank: unit_data[1] 71 | allocated_stat_points: unit_data[2] 72 | max_stat_points: unit_data[3] 73 | stats: new tbs.collections.UnitStats( 74 | _(unit_data[4]).map (stat) => 75 | stat: @STAT_NAME_ENCODED_TO_FULL[stat[0]] 76 | current: stat[1] 77 | min: stat[2] 78 | max: stat[3] 79 | ) 80 | ) 81 | model_data 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /grunt-workflow/app/coffee/config/backbone_fixins_ext.coffee: -------------------------------------------------------------------------------- 1 | Backbone.Fixins.configure 2 | templateFunction: (name) -> 3 | JST[name] || throw "Could not find a template in JST[#{name}]" 4 | 5 | defaultTemplateLocator: (view) -> 6 | "app/templates/#{subPathFor(view)}.hb" 7 | 8 | subPathFor = (view) -> 9 | _(view.namespacePath.split('.')).chain(). 10 | rest(2). #skip "app" and "views" 11 | map(Backbone.Fixins.helpers.titleToSnakeCase). 12 | value().join('/') 13 | -------------------------------------------------------------------------------- /grunt-workflow/app/coffee/config/extend_ext.coffee: -------------------------------------------------------------------------------- 1 | # we want to abstract away "extend", because it's conflated with _.extend and Coffee's "extends" 2 | 3 | root = this 4 | root.def = _(extend).wrap (ex, args...) -> 5 | [namespace, target] = args 6 | unless typeof(target) is "object" 7 | target.prototype.namespacePath = namespace 8 | ex.apply(this, args) 9 | -------------------------------------------------------------------------------- /grunt-workflow/app/coffee/converters.coffee: -------------------------------------------------------------------------------- 1 | def 'tbs.converters.buildStatsCollection', (raw_stats_array, rank) -> 2 | 3 | STATS = ["armor", "strength", "willpower", "exertion", "break"] 4 | 5 | # stats come in from unit_data.coffee 6 | # arm str wil exr brk (min,max) 7 | # [[6,9], [6,9], [4,6,] [1,2,] [1,2]] 8 | 9 | model_values = _(raw_stats_array).map (min_max, i) -> 10 | min: min_max[0] 11 | max: min_max[1] 12 | current: min_max[0] 13 | stat: STATS[i] 14 | 15 | new tbs.collections.UnitStats(model_values) 16 | 17 | 18 | -------------------------------------------------------------------------------- /grunt-workflow/app/coffee/data/unit_data.coffee: -------------------------------------------------------------------------------- 1 | def 'tbs.data.Units', -> 2 | #arm str wil exr brk (min,max) 3 | [ 4 | { 5 | name: "raider" 6 | type: "base" 7 | rank: 0 8 | stats: tbs.converters.buildStatsCollection([[6,9], [6,9], [4,6], [1,2], [1,2]]) 9 | } 10 | { 11 | name: "raidmaster" 12 | type: "raider" 13 | rank: 1 14 | stats: tbs.converters.buildStatsCollection([[6,12], [6,12], [4,11], [1,3], [1,3]]) 15 | } 16 | { 17 | name: "thrasher" 18 | type: "raider" 19 | rank: 1 20 | stats: tbs.converters.buildStatsCollection([[5,11], [8,12], [3,13], [1,3], [1,2]]) 21 | } 22 | { 23 | name: "backbiter" 24 | type: "raider" 25 | rank: 1 26 | stats: tbs.converters.buildStatsCollection([[5,12], [8,10], [4,13], [0,3], [1,3]]) 27 | } 28 | { 29 | name: "archer" 30 | type: "base" 31 | rank: 0 32 | stats: tbs.converters.buildStatsCollection([[4,7], [4,7], [5,8], [1,2], [1,1]]) 33 | } 34 | { 35 | name: "bowmaster" 36 | type: "archer" 37 | rank: 1 38 | stats: tbs.converters.buildStatsCollection([[4,9], [4,8], [5,12], [1,3], [1,2]]) 39 | } 40 | { 41 | name: "skystriker" 42 | type: "archer" 43 | rank: 1 44 | stats: tbs.converters.buildStatsCollection([[3,9], [4,8], [7,13], [1,3], [0,1]]) 45 | } 46 | { 47 | name: "siegearcher" 48 | type: "archer" 49 | rank: 1 50 | stats: tbs.converters.buildStatsCollection([[4,9], [4,7], [6,13], [1,3], [0,2]]) 51 | } 52 | { 53 | name: "warrior" 54 | type: "base" 55 | rank: 0 56 | stats: tbs.converters.buildStatsCollection([[9,9], [12,12], [5,5], [2,2], [2,2]]) 57 | } 58 | { 59 | name: "warmaster" 60 | type: "warrior" 61 | rank: 1 62 | stats: tbs.converters.buildStatsCollection([[6,11], [9,17], [3,10], [1,2], [1,3]]) 63 | } 64 | { 65 | name: "warhawk" 66 | type: "warrior" 67 | rank: 1 68 | stats: tbs.converters.buildStatsCollection([[7,12], [10,16], [2,11], [0,2], [1,2]]) 69 | } 70 | { 71 | name: "warleader" 72 | type: "warrior" 73 | rank: 1 74 | stats: tbs.converters.buildStatsCollection([[5,12], [9,15], [5,9], [1,3], [0,4]]) 75 | } 76 | { 77 | name: "shieldbanger" 78 | type: "base" 79 | rank: 0 80 | stats: tbs.converters.buildStatsCollection([[9,14], [8,10], [3,5], [1,1], [1,2]]) 81 | } 82 | { 83 | name: "provoker" 84 | type: "shieldbanger" 85 | rank: 1 86 | stats: tbs.converters.buildStatsCollection([[11,18], [7,12], [3,9], [0,2], [1,3]]) 87 | } 88 | { 89 | name: "strongarm" 90 | type: "shieldbanger" 91 | rank: 1 92 | stats: tbs.converters.buildStatsCollection([[9,15], [9,15], [3,10], [0,2], [1,2]]) 93 | } 94 | { 95 | name: "shieldmaster" 96 | type: "shieldbanger" 97 | rank: 1 98 | stats: tbs.converters.buildStatsCollection([[9,16], [8,13], [3,9], [1,2], [1,4]]) 99 | } 100 | ] 101 | -------------------------------------------------------------------------------- /grunt-workflow/app/coffee/models/unit.coffee: -------------------------------------------------------------------------------- 1 | def 'tbs.models.Unit', class Unit extends Backbone.Model 2 | 3 | defaults: 4 | name: "" 5 | type: "" 6 | stats: undefined 7 | allocated_stat_points: 0 8 | max_stat_points: 0 9 | 10 | isChosen: => 11 | @has("stats") 12 | 13 | isEmpty: => 14 | not @has("stats") 15 | 16 | validate: (attrs) => 17 | if attrs.allocated_stat_points < 0 or attrs.allocated_stat_points > attrs.max_stat_points 18 | "would go over" 19 | -------------------------------------------------------------------------------- /grunt-workflow/app/coffee/models/unit_stat.coffee: -------------------------------------------------------------------------------- 1 | def('tbs.models.UnitStat', class UnitStat extends Backbone.Model) 2 | -------------------------------------------------------------------------------- /grunt-workflow/app/coffee/router.coffee: -------------------------------------------------------------------------------- 1 | def 'tbs.Router', class Router extends Backbone.Router 2 | 3 | initialize: (options) => 4 | @loadout = options.loadout 5 | @loadout.on("change", @storeHash) 6 | 7 | routes: 8 | ":encoded" : "loadLoadout" 9 | 10 | loadLoadout: (encoded) -> 11 | @loadout.reset(@loadout.deserialize(encoded)) 12 | Backbone.trigger("loaded:from:hash") 13 | 14 | storeHash: => 15 | @navigate(@loadout.serialize(), {replace: true}) 16 | -------------------------------------------------------------------------------- /grunt-workflow/app/coffee/views/character_selector.coffee: -------------------------------------------------------------------------------- 1 | def 'tbs.views.CharacterSelector', class CharacterSelector extends Backbone.View 2 | 3 | initialize: => 4 | Backbone.on("edit:unit", @hide) 5 | Backbone.on("choose:unit", @show) 6 | 7 | render: => 8 | _(@collection).each (units, type) => 9 | @$("#unit-groupings").append(new tbs.views.UnitGrouping( 10 | units: units 11 | type: type 12 | ).render().el) 13 | 14 | hide: => 15 | @$el.hide() 16 | 17 | show: => 18 | @$el.show() 19 | 20 | -------------------------------------------------------------------------------- /grunt-workflow/app/coffee/views/loadout.coffee: -------------------------------------------------------------------------------- 1 | def 'tbs.views.Loadout', class Loadout extends Backbone.View 2 | 3 | events: 4 | "click .character" : "editUnit" 5 | 6 | initialize: => 7 | Backbone.on("loaded:from:hash", @render) 8 | 9 | editUnit: (e) => 10 | unit = @fetchUnitFromCollectionViaSlotDataAttribute(e) 11 | if unit.isChosen() 12 | Backbone.trigger("edit:unit", unit) 13 | else 14 | Backbone.trigger("choose:unit") 15 | 16 | fetchUnitFromCollectionViaSlotDataAttribute: (element) => 17 | @collection.at($(element.currentTarget).data("slot")) 18 | 19 | render: => 20 | @$("#selected-characters").empty() 21 | @collection.each (unit, i) => 22 | @$("#selected-characters").append(new tbs.views.LoadoutSlot( 23 | model: unit 24 | slot: i 25 | ).render().el) 26 | @ 27 | -------------------------------------------------------------------------------- /grunt-workflow/app/coffee/views/loadout_slot.coffee: -------------------------------------------------------------------------------- 1 | def 'tbs.views.LoadoutSlot', class LoadoutSlot extends Backbone.Fixins.SuperView 2 | 3 | tagName: "li" 4 | 5 | events: 6 | "mouseover" : "showOverlay" 7 | "mouseout" : "hideOverlay" 8 | "click .remove" : "removeFromLoadout" 9 | "click .move-left" : "moveLeft" 10 | "click .move-right" : "moveRight" 11 | 12 | removeFromLoadout: (e) -> 13 | if confirm("Remove #{@model.get('name')} from slot #{@slot + 1}?") 14 | @model.clear() 15 | @model.set(new tbs.models.Unit().attributes) 16 | Backbone.trigger("choose:unit") 17 | else 18 | e.preventDefault() 19 | e.stopPropagation() 20 | 21 | attributes: => 22 | "class" : "character #{@model.get('name')}" 23 | "data-slot" : @slot 24 | 25 | showOverlay: => 26 | if @model.isChosen() then @$(".remove").show() 27 | if @model.isChosen() 28 | @$(".move-left, .move-right").show() 29 | 30 | if @position() is 0 then @$(".move-left").hide() 31 | if @position() is 5 then @$(".move-right").hide() 32 | 33 | moveLeft: (e) => 34 | e.stopPropagation() 35 | @move(@position(-1)) 36 | 37 | moveRight: (e) => 38 | e.stopPropagation() 39 | @move(@position(1)) 40 | 41 | move: (new_position_index) => 42 | original_attrs = _(@model.attributes).clone() 43 | new_attrs = _(@model.collection.at(new_position_index).attributes).clone() 44 | other_model = @model.collection.at(new_position_index) 45 | @model.clear() 46 | @model.set(new_attrs) 47 | other_model.clear() 48 | other_model.set(original_attrs) 49 | Backbone.trigger("loadout:reorganized", other_model) 50 | 51 | position: (offset=0)=> 52 | @model.collection.indexOf(@model) + offset 53 | 54 | hideOverlay: => 55 | @$(".remove, .move-left, .move-right").hide() 56 | 57 | initialize: (options) => 58 | @slot = options.slot 59 | @model.on("change", @render, @) 60 | @model.on("change:rank", @updateMaxStatPoints) 61 | 62 | renderAttributes: => 63 | attrs = @attributes() 64 | @$el.attr("class", attrs.class) 65 | @$el.attr("data-slot", attrs['data-slot']) 66 | 67 | renderStatsOverlay: => 68 | if stats = @model.get('stats') 69 | stats.each (stat) => 70 | @$(".stats-overlay .#{stat.get('stat')}").text(stat.get('current')) 71 | 72 | renderAllocatedStatsOverlay: => 73 | @$(".allocated-max-stats .allocated").text(@model.get("allocated_stat_points")) 74 | @$(".allocated-max-stats .max").text(@model.get("max_stat_points")) 75 | 76 | renderHelpText: => 77 | if @model.isChosen() 78 | @$(".help-text").text("") 79 | else 80 | @$(".help-text").text("Click a unit below") 81 | 82 | updateMaxStatPoints: (__, new_rank) => 83 | @model.set("max_stat_points", tbs.BattlePlanner.maxStatPointsForRank(new_rank)) 84 | -------------------------------------------------------------------------------- /grunt-workflow/app/coffee/views/stat_control.coffee: -------------------------------------------------------------------------------- 1 | def 'tbs.views.StatControl', class StatControl extends Backbone.Fixins.SuperView 2 | 3 | tagName: "li" 4 | 5 | events: 6 | "mousedown .icon" : "increaseOrDecreaseStat" 7 | "contextmenu .icon" : -> false 8 | 9 | initialize: (options) -> 10 | @unit = options.unit 11 | @model.on("change:current", @update) 12 | 13 | attributes: => 14 | class: @model.get("stat") 15 | 16 | increaseOrDecreaseStat: (e) => 17 | e.preventDefault() 18 | e.stopPropagation() 19 | switch e.which 20 | when 1 then @changeBy(1) #left click 21 | when 3 then @changeBy(-1) #right click 22 | 23 | changeBy: (amount) => 24 | # if the amount does not exceed the units max_stat_points 25 | # if the amount does not exceed the stats maximum 26 | if !(@unit.get('allocated_stat_points') + amount > @unit.get('max_stat_points') or @unit.get('allocated_stat_points') + amount < 0) 27 | if !(@model.get('current') + amount < @model.get('min') or @model.get('current') + amount > @model.get('max')) 28 | @model.set(current: @model.get("current") + amount) 29 | @unit.set(allocated_stat_points: @unit.get('allocated_stat_points') + amount) 30 | 31 | update: => 32 | @$(".current").text(@model.get("current")) 33 | 34 | -------------------------------------------------------------------------------- /grunt-workflow/app/coffee/views/stat_editor.coffee: -------------------------------------------------------------------------------- 1 | def 'tbs.views.StatEditor', class StatEditor extends Backbone.Fixins.SuperView 2 | 3 | events: 4 | "click .reset" : "resetToMinimums" 5 | "click .done" : -> Backbone.trigger("choose:unit") 6 | "click .change-rank" : "setRank" 7 | 8 | initialize: => 9 | Backbone.on("edit:unit", @show) 10 | Backbone.on("choose:unit", @hide) 11 | Backbone.on("loadout:reorganized", @show) 12 | 13 | renderVisible: => 14 | if @model.isChosen() then @$el.show() else @$el.hide() 15 | 16 | renderStatControls: => 17 | unit_stats = @model.get('stats') 18 | unit_stats.each (stat, i) => 19 | @$(".stats").append(new tbs.views.StatControl(unit: @model, model: stat).render().el) 20 | 21 | resetToMinimums: => 22 | @model.set("allocated_stat_points", 0) 23 | stats = @model.get('stats') 24 | stats.each (stat) => 25 | stat.set('current', stat.get('min')) 26 | @model.trigger('change') 27 | 28 | renderTotals: => 29 | @$(".total .allocated").text(@model.get("allocated_stat_points")) 30 | @$(".total .max").text(@model.get("max_stat_points")) 31 | 32 | renderRankChanger: => 33 | @$("#rank-changer").prop("class", "rank#{@model.get("rank")}") 34 | 35 | renderRankInEditorTitle: => 36 | @$(".title .rank").text(@model.get("rank")) 37 | 38 | setRank: (e) => 39 | rank = parseInt($(e.currentTarget).text(), 10) 40 | oldrank = @model.get("rank") 41 | @model.set({rank}) 42 | if rank < oldrank 43 | @resetToMinimums() 44 | 45 | hide: => 46 | @$el.hide() 47 | 48 | show: (unit) => 49 | @model = unit 50 | # had to clone stats here, as editing the same class was buggy 51 | @model.set("stats", new tbs.collections.UnitStats(@model.get('stats').toJSON())) 52 | @model.on("change:allocated_stat_points", @renderTotals) 53 | @model.on("change:rank", @renderRankChanger) 54 | @model.on("change:rank", @renderRankInEditorTitle) 55 | @model.on("change:rank", @renderTotals) 56 | @render() 57 | @$el.show() 58 | -------------------------------------------------------------------------------- /grunt-workflow/app/coffee/views/unit_grouping.coffee: -------------------------------------------------------------------------------- 1 | def 'tbs.views.UnitGrouping', class UnitGrouping extends Backbone.Fixins.SuperView 2 | 3 | attributes: 4 | class: "unit-type" 5 | 6 | initialize: (options) -> 7 | @type = options.type 8 | @units = options.units 9 | 10 | renderTitle: => 11 | @$(".title").text(@type) 12 | 13 | renderUnits: => 14 | _(@units).each (unit) => 15 | @$(".classes").append(new tbs.views.UnitSelector( 16 | model: unit 17 | ).render().el) 18 | -------------------------------------------------------------------------------- /grunt-workflow/app/coffee/views/unit_selector.coffee: -------------------------------------------------------------------------------- 1 | def 'tbs.views.UnitSelector', class UnitSelector extends Backbone.Fixins.SuperView 2 | 3 | tagName: "li" 4 | 5 | attributes: => 6 | class: "character #{@model.get('name')}" 7 | 8 | events: 9 | "click" : "assignUnitToNextAvailableLoadoutSlot" 10 | 11 | assignUnitToNextAvailableLoadoutSlot: => 12 | # get the next active loadout slot 13 | loadout_unit = tbs.BattlePlanner.loadout_units.find (unit) => unit.isEmpty() 14 | if not loadout_unit then loadout_unit = tbs.BattlePlanner.loadout_units.at(5) 15 | loadout_unit.clear(silent: true) 16 | loadout_unit.set(@model.clone().attributes) 17 | -------------------------------------------------------------------------------- /grunt-workflow/app/css/style.less: -------------------------------------------------------------------------------- 1 | @import "_mixins.less"; 2 | 3 | .black-text-outline () { 4 | text-shadow: -1px 0 black, 0 1px black, 1px 0 black, 0 -1px black; 5 | } 6 | 7 | html { 8 | font-family: 'Skranji', cursive; 9 | color: #9caab2; 10 | } 11 | 12 | html, body, h1, h2, h3, ul, li { 13 | margin: 0; 14 | padding: 0; 15 | } 16 | 17 | ul, li { 18 | list-style-type: none; 19 | } 20 | 21 | body { 22 | background: black; 23 | } 24 | 25 | // Initial State 26 | 27 | .wrapper { 28 | width: 960px; 29 | margin: 0 auto; 30 | } 31 | 32 | #header { 33 | height: 100px; 34 | background-color: #1d1f32; 35 | #gradient > .vertical(#1d1f32, #494e6a); 36 | .box-shadow(0px 5px 10px 0px rgba(73, 78, 106, 0.75)); 37 | position: relative; 38 | h1 { 39 | padding-top: 25px; 40 | font-size: 2.5em; 41 | text-align: center; 42 | color: #9abaf0; 43 | .black-text-outline(); 44 | } 45 | } 46 | 47 | .allocated-max-stats { 48 | padding: 6px; 49 | border-top-left-radius: 6px; 50 | border: 2px solid black; 51 | background-color: rgba(0,0,0, 0.75); 52 | color: orange; 53 | line-height: 1em; 54 | border-right: none; 55 | border-bottom: none; 56 | bottom: 38px; 57 | right: 8px; 58 | position: absolute; 59 | z-index: 4; 60 | } 61 | 62 | .stats-overlay { 63 | padding: 3px; 64 | border-radius: 6px; 65 | overflow: hidden; 66 | background: black; 67 | margin: 0 auto; 68 | margin-top: -5px; 69 | width: 129px; 70 | position: relative; 71 | z-index: 0; 72 | li { 73 | float: left; 74 | display: block; 75 | width: 25px; 76 | height: 22px; 77 | color: white; 78 | text-align: center; 79 | margin-right: 1px; 80 | &:last-child { 81 | margin-right: 0px; 82 | }; 83 | border-radius: 2px; 84 | } 85 | 86 | .armor { 87 | background-color: #4873b2; 88 | } 89 | .strength { 90 | background-color: #c4453b; 91 | } 92 | .willpower { 93 | background-color: #c5a500; 94 | } 95 | .exertion { 96 | background-color: #458e48; 97 | } 98 | .break { 99 | background-color: #313131; 100 | } 101 | } 102 | 103 | #loadout { 104 | padding: 20px 0; 105 | #gradient > .vertical(#121a1d, #25404c); 106 | .box-shadow(0px 10px 20px 0px rgba(0,0,0,0.75)); 107 | .character { 108 | padding-bottom: 30px; 109 | position: relative; 110 | .move-left, .move-right { 111 | position: absolute; 112 | display: none; 113 | width: 34px; 114 | height: 34px; 115 | top: 88px; 116 | background-size: 100%; 117 | z-index: 5; 118 | } 119 | .move-left { 120 | background-image: data-uri("../../generated/img-min/icon/move-left.png"); 121 | left: -8px; 122 | } 123 | .move-right { 124 | background-image: data-uri("../../generated/img-min/icon/move-right.png"); 125 | right: -8px; 126 | } 127 | .arrow { 128 | display: none; 129 | position: absolute; 130 | bottom: -55px; 131 | left: 51px; 132 | color: #5f4d4d; 133 | font-size: 3em; 134 | } 135 | .help-text { 136 | text-align: center; 137 | position: absolute; 138 | top: 37%; 139 | left: 11%; 140 | width: 80%; 141 | } 142 | .remove { 143 | display: none; 144 | width: 25px; 145 | height: 25px; 146 | border-radius: 13px; 147 | #gradient > .radial(#b62f12, #8f250d); 148 | .box-shadow(0px 10px 20px 0px rgba(0,0,0,0.75)); 149 | border: 2px solid #7c8990; 150 | color: white; 151 | position: absolute; 152 | z-index: 2; 153 | top: 26px; 154 | right: 0; 155 | text-align: center; 156 | line-height: 0.75em; 157 | font-size: 2em; 158 | } 159 | &.choosing .arrow, &.edit-stats .arrow { 160 | display: block; 161 | } 162 | } 163 | 164 | .character.choosing { 165 | .arrow { 166 | display: block; 167 | } 168 | } 169 | } 170 | 171 | #character-selector, #loadout, .classes { 172 | overflow: hidden; 173 | } 174 | 175 | #character-selector { 176 | padding: 20px 0px; 177 | #gradient > .vertical(#5f4d4d, #8b6049); 178 | .character { 179 | width: 125px; 180 | margin-right: 35px; 181 | &:last-child { 182 | margin-right: 0px; 183 | }; 184 | .title { 185 | width: 125px; 186 | } 187 | .portrait { 188 | width: 125px; 189 | height: 125px; 190 | background-size: 125px; 191 | } 192 | } 193 | .unit-type { 194 | float: left; 195 | margin-right: 25px; 196 | &.archers, &.shieldbangers { 197 | margin-right: 0px; 198 | } 199 | > .title { 200 | border-bottom: 1px solid #999; 201 | line-height: 60px; 202 | margin-bottom: 10px; 203 | font-size: 3em; 204 | } 205 | } 206 | } 207 | 208 | .build { 209 | float: right; 210 | .version { 211 | color: orange; 212 | } 213 | } 214 | 215 | #stat-editor { 216 | #gradient > .vertical(#5f4d4d, #8b6049); 217 | overflow: hidden; 218 | padding: 20px 0; 219 | .title-and-controls { 220 | position: relative; 221 | } 222 | .controls { 223 | position: absolute; 224 | top: 0; 225 | right: 0; 226 | .reset, .total, .done, #rank-changer { 227 | border: 1px solid orange; 228 | display: block; 229 | float: right; 230 | margin-left: 10px; 231 | padding: 10px; 232 | color: white; 233 | } 234 | #rank-changer { 235 | padding: 0 10px; 236 | padding-right: 0px; 237 | .ui-title { 238 | padding-right: 5px; 239 | } 240 | &.rank1 .set-rank-1, 241 | &.rank2 .set-rank-2, 242 | &.rank3 .set-rank-3 { 243 | background-color: rgba(222, 157, 82, 0.35); 244 | } 245 | } 246 | .change-rank { 247 | display: inline-block; 248 | border-left: 1px solid orange; 249 | padding: 10px; 250 | color: white; 251 | } 252 | .reset:hover, .done:hover, .change-rank:hover { 253 | cursor: pointer; 254 | background-color: rgba(222, 157, 82, 0.35); 255 | } 256 | 257 | 258 | } 259 | .title { 260 | border-bottom: 1px solid #999; 261 | line-height: 60px; 262 | margin-bottom: 10px; 263 | font-size: 2.5em; 264 | } 265 | .portrait, .stats { 266 | float: left; 267 | } 268 | .stats { 269 | margin-top: 30px; 270 | width: 775px; 271 | li { 272 | text-align: right; 273 | padding-right: 175px; 274 | margin-bottom: 25px; 275 | font-size: 2.5em; 276 | border-width: 1px; 277 | border-style: solid; 278 | color: #ea9d2e; 279 | position: relative; 280 | .black-text-outline(); 281 | .current { 282 | color: #ffdba6; 283 | } 284 | .max { 285 | color: #ea9d2e; 286 | } 287 | .icon { 288 | display: block; 289 | position: absolute; 290 | right: 60px; 291 | top: -8px; 292 | width: 70px; 293 | height: 70px; 294 | background-size: 100%; 295 | &:hover { 296 | cursor: pointer; 297 | } 298 | } 299 | } 300 | .armor { 301 | background-color: #4873b2; 302 | border-color: #37005a; 303 | } 304 | .strength { 305 | background-color: #c4453b; 306 | border-color: #5d0300; 307 | } 308 | .willpower { 309 | background-color: #c5a500; 310 | border-color: #602a01; 311 | } 312 | .exertion { 313 | background-color: #458e48; 314 | border-color: #004514; 315 | } 316 | .break { 317 | background-color: #313131; 318 | border-color: #070708; 319 | } 320 | .total { 321 | margin-left: 425px; 322 | width: 175px; 323 | border: none; 324 | } 325 | } 326 | .portrait { 327 | width: 160px; 328 | height: 460px; 329 | margin-right: 20px; 330 | background-size: 100%; 331 | } 332 | } 333 | 334 | .character { 335 | width: 150px; 336 | height: 180px; 337 | float: left; 338 | margin-right: 10px; 339 | &:hover { 340 | cursor: pointer; 341 | } 342 | .title { 343 | font-size: 1.4em; 344 | height: 30px; 345 | .black-text-outline(); 346 | } 347 | .portrait { 348 | height: 150px; 349 | } 350 | &:first-child { 351 | margin-left: 5px; 352 | } 353 | &:last-child { 354 | margin-right: 0px; 355 | } 356 | } 357 | 358 | #credits { 359 | padding: 20px; 360 | #gradient > .vertical(#1d1f32, #494e6a); 361 | a { 362 | color: orange; 363 | text-decoration: none; 364 | } 365 | } 366 | 367 | // Characters 368 | 369 | // Mixins 370 | .base-character-portrait (@class) { 371 | .character.@{class} { 372 | .portrait { 373 | background-image: data-uri("../../generated/img-min/characters/base/@{class}.png"); 374 | } 375 | } 376 | } 377 | 378 | .rank-1-character-portrait (@class) { 379 | .character.@{class} { 380 | .portrait { 381 | background-image: data-uri("../../generated/img-min/characters/rank1/@{class}.png"); 382 | } 383 | } 384 | } 385 | 386 | // Large Portraits for Stat Editor 387 | .rank-1-stat-editor-portrait (@class) { 388 | #stat-editor .portrait.@{class} { 389 | background-image: data-uri("../../generated/img-min/characters/portraits/@{class}.png"); 390 | } 391 | } 392 | 393 | .stat-icon (@stat) { 394 | #stat-editor .@{stat} .icon { 395 | background-image: data-uri("../../generated/img-min/icon/@{stat}.png"); 396 | } 397 | } 398 | 399 | // Basic Character Structure 400 | 401 | .character { 402 | .title { 403 | text-align: center; 404 | } 405 | .portrait { 406 | background-image: data-uri("../../generated/img-min/empty.character.frame.png"); 407 | background-size: 150px; 408 | z-index: 1; 409 | position: relative; 410 | } 411 | } 412 | 413 | // Icons 414 | 415 | .stat-icon(armor); 416 | .stat-icon(strength); 417 | .stat-icon(willpower); 418 | .stat-icon(exertion); 419 | .stat-icon(break); 420 | .stat-icon(special); 421 | 422 | // Base Character Portraits 423 | 424 | .base-character-portrait(raider); 425 | .base-character-portrait(archer); 426 | .base-character-portrait(warrior); 427 | .base-character-portrait(shieldbanger); 428 | 429 | // Rank 1 Stat Editor Large Portraits 430 | 431 | // Warriors 432 | .rank-1-stat-editor-portrait(warhawk); 433 | .rank-1-stat-editor-portrait(warleader); 434 | .rank-1-stat-editor-portrait(warmaster); 435 | 436 | // Archers 437 | .rank-1-stat-editor-portrait(bowmaster); 438 | .rank-1-stat-editor-portrait(skystriker); 439 | .rank-1-stat-editor-portrait(siegearcher); 440 | 441 | // Raiders 442 | .rank-1-stat-editor-portrait(raidmaster); 443 | .rank-1-stat-editor-portrait(thrasher); 444 | .rank-1-stat-editor-portrait(backbiter); 445 | 446 | // Shieldbangers 447 | .rank-1-stat-editor-portrait(strongarm); 448 | .rank-1-stat-editor-portrait(provoker); 449 | .rank-1-stat-editor-portrait(shieldmaster); 450 | 451 | // Rank 1 Character Portraits 452 | 453 | // Warriors 454 | .rank-1-character-portrait(warhawk); 455 | .rank-1-character-portrait(warleader); 456 | .rank-1-character-portrait(warmaster); 457 | 458 | // Archers 459 | .rank-1-character-portrait(bowmaster); 460 | .rank-1-character-portrait(skystriker); 461 | .rank-1-character-portrait(siegearcher); 462 | 463 | // Raiders 464 | .rank-1-character-portrait(raidmaster); 465 | .rank-1-character-portrait(thrasher); 466 | .rank-1-character-portrait(backbiter); 467 | 468 | // Shieldbangers 469 | .rank-1-character-portrait(strongarm); 470 | .rank-1-character-portrait(provoker); 471 | .rank-1-character-portrait(shieldmaster); 472 | -------------------------------------------------------------------------------- /grunt-workflow/app/img/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/grunt-workflow/app/img/banner.png -------------------------------------------------------------------------------- /grunt-workflow/app/img/characters/base/archer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/grunt-workflow/app/img/characters/base/archer.png -------------------------------------------------------------------------------- /grunt-workflow/app/img/characters/base/raider.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/grunt-workflow/app/img/characters/base/raider.png -------------------------------------------------------------------------------- /grunt-workflow/app/img/characters/base/shieldbanger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/grunt-workflow/app/img/characters/base/shieldbanger.png -------------------------------------------------------------------------------- /grunt-workflow/app/img/characters/base/warrior.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/grunt-workflow/app/img/characters/base/warrior.png -------------------------------------------------------------------------------- /grunt-workflow/app/img/characters/portraits/backbiter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/grunt-workflow/app/img/characters/portraits/backbiter.png -------------------------------------------------------------------------------- /grunt-workflow/app/img/characters/portraits/bowmaster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/grunt-workflow/app/img/characters/portraits/bowmaster.png -------------------------------------------------------------------------------- /grunt-workflow/app/img/characters/portraits/provoker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/grunt-workflow/app/img/characters/portraits/provoker.png -------------------------------------------------------------------------------- /grunt-workflow/app/img/characters/portraits/raidmaster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/grunt-workflow/app/img/characters/portraits/raidmaster.png -------------------------------------------------------------------------------- /grunt-workflow/app/img/characters/portraits/shieldmaster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/grunt-workflow/app/img/characters/portraits/shieldmaster.png -------------------------------------------------------------------------------- /grunt-workflow/app/img/characters/portraits/siegearcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/grunt-workflow/app/img/characters/portraits/siegearcher.png -------------------------------------------------------------------------------- /grunt-workflow/app/img/characters/portraits/skystriker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/grunt-workflow/app/img/characters/portraits/skystriker.png -------------------------------------------------------------------------------- /grunt-workflow/app/img/characters/portraits/strongarm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/grunt-workflow/app/img/characters/portraits/strongarm.png -------------------------------------------------------------------------------- /grunt-workflow/app/img/characters/portraits/thrasher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/grunt-workflow/app/img/characters/portraits/thrasher.png -------------------------------------------------------------------------------- /grunt-workflow/app/img/characters/portraits/warhawk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/grunt-workflow/app/img/characters/portraits/warhawk.png -------------------------------------------------------------------------------- /grunt-workflow/app/img/characters/portraits/warleader.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/grunt-workflow/app/img/characters/portraits/warleader.png -------------------------------------------------------------------------------- /grunt-workflow/app/img/characters/portraits/warmaster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/grunt-workflow/app/img/characters/portraits/warmaster.png -------------------------------------------------------------------------------- /grunt-workflow/app/img/characters/rank1/backbiter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/grunt-workflow/app/img/characters/rank1/backbiter.png -------------------------------------------------------------------------------- /grunt-workflow/app/img/characters/rank1/bowmaster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/grunt-workflow/app/img/characters/rank1/bowmaster.png -------------------------------------------------------------------------------- /grunt-workflow/app/img/characters/rank1/provoker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/grunt-workflow/app/img/characters/rank1/provoker.png -------------------------------------------------------------------------------- /grunt-workflow/app/img/characters/rank1/raidmaster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/grunt-workflow/app/img/characters/rank1/raidmaster.png -------------------------------------------------------------------------------- /grunt-workflow/app/img/characters/rank1/shieldmaster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/grunt-workflow/app/img/characters/rank1/shieldmaster.png -------------------------------------------------------------------------------- /grunt-workflow/app/img/characters/rank1/siegearcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/grunt-workflow/app/img/characters/rank1/siegearcher.png -------------------------------------------------------------------------------- /grunt-workflow/app/img/characters/rank1/skystriker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/grunt-workflow/app/img/characters/rank1/skystriker.png -------------------------------------------------------------------------------- /grunt-workflow/app/img/characters/rank1/strongarm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/grunt-workflow/app/img/characters/rank1/strongarm.png -------------------------------------------------------------------------------- /grunt-workflow/app/img/characters/rank1/thrasher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/grunt-workflow/app/img/characters/rank1/thrasher.png -------------------------------------------------------------------------------- /grunt-workflow/app/img/characters/rank1/warhawk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/grunt-workflow/app/img/characters/rank1/warhawk.png -------------------------------------------------------------------------------- /grunt-workflow/app/img/characters/rank1/warleader.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/grunt-workflow/app/img/characters/rank1/warleader.png -------------------------------------------------------------------------------- /grunt-workflow/app/img/characters/rank1/warmaster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/grunt-workflow/app/img/characters/rank1/warmaster.png -------------------------------------------------------------------------------- /grunt-workflow/app/img/empty.character.frame.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/grunt-workflow/app/img/empty.character.frame.png -------------------------------------------------------------------------------- /grunt-workflow/app/img/icon/armor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/grunt-workflow/app/img/icon/armor.png -------------------------------------------------------------------------------- /grunt-workflow/app/img/icon/break.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/grunt-workflow/app/img/icon/break.png -------------------------------------------------------------------------------- /grunt-workflow/app/img/icon/exertion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/grunt-workflow/app/img/icon/exertion.png -------------------------------------------------------------------------------- /grunt-workflow/app/img/icon/move-left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/grunt-workflow/app/img/icon/move-left.png -------------------------------------------------------------------------------- /grunt-workflow/app/img/icon/move-right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/grunt-workflow/app/img/icon/move-right.png -------------------------------------------------------------------------------- /grunt-workflow/app/img/icon/special.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/grunt-workflow/app/img/icon/special.png -------------------------------------------------------------------------------- /grunt-workflow/app/img/icon/strength.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/grunt-workflow/app/img/icon/strength.png -------------------------------------------------------------------------------- /grunt-workflow/app/img/icon/willpower.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/grunt-workflow/app/img/icon/willpower.png -------------------------------------------------------------------------------- /grunt-workflow/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The Banner Saga: Factions | Battle Planner 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 20 | 21 |
22 |
23 | 24 |
25 |
26 | 27 |
28 |
29 |
30 |
31 |
32 | 33 |
34 | 35 |
36 |
37 | built by @dmosher 38 | 39 | Calculator Release {{ pkg.version }} based on The Banner Saga: Factions build {{ pkg.tbs_version }} 40 | 41 |
42 |
43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /grunt-workflow/app/templates/loadout.hb: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /grunt-workflow/app/templates/loadout_slot.hb: -------------------------------------------------------------------------------- 1 |
{{ name }}
2 |
3 | Click a unit below 4 |
5 | × 6 | 13 | 14 | 15 |
16 | 0/11 17 |
18 | -------------------------------------------------------------------------------- /grunt-workflow/app/templates/stat_control.hb: -------------------------------------------------------------------------------- 1 | {{ current }}/{{ max }} 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /grunt-workflow/app/templates/stat_editor.hb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
{{ name }} - (rank {{ rank }})
4 |
5 | Done Editing 6 | Reset to Minimums 7 | 8 | / 9 | 10 | 11 | Rank 12 | 123 13 | 14 |
15 |
16 | 17 |
18 | 19 |
20 | -------------------------------------------------------------------------------- /grunt-workflow/app/templates/unit_grouping.hb: -------------------------------------------------------------------------------- 1 |
{{ type }}
2 | 3 | -------------------------------------------------------------------------------- /grunt-workflow/app/templates/unit_selector.hb: -------------------------------------------------------------------------------- 1 |
{{ name }}
2 |
3 | -------------------------------------------------------------------------------- /grunt-workflow/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-app", 3 | "version": "0.0.0", 4 | "homepage": "https://github.com/davemo/fem-grunt-workflow", 5 | "authors": [ 6 | "David Mosher " 7 | ], 8 | "license": "MIT", 9 | "private": true, 10 | "ignore": [ 11 | "**/.*", 12 | "node_modules", 13 | "bower_components", 14 | "bower_modules", 15 | "test", 16 | "tests" 17 | ], 18 | "dependencies": { 19 | "base64js": "~1.0.0", 20 | "jquery": "~1.8.2", 21 | "underscore": "~1.4.3", 22 | "extend.js": "https://github.com/searls/extend.js/releases/download/0.1.0/extend.js", 23 | "backbone-fixins.js": "https://raw.github.com/testdouble/backbone-fixins/master/dist/backbone-fixins.js", 24 | "backbone.stickit": "~0.6.2", 25 | "backbone": "~0.9.10", 26 | "underscore.string": "~2.3.0", 27 | "handlebars": "~1.0.0" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /grunt-workflow/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-app", 3 | "version": "0.0.0", 4 | "description": "", 5 | "main": "Gruntfile.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "david mosher", 10 | "license": "MIT", 11 | "dependencies": { 12 | "grunt-contrib-concat": "~0.3.0", 13 | "grunt": "~0.4.1", 14 | "grunt-contrib-watch": "~0.5.3", 15 | "grunt-contrib-copy": "~0.4.1", 16 | "grunt-contrib-clean": "~0.5.0", 17 | "grunt-contrib-less": "~0.8.0", 18 | "grunt-contrib-uglify": "~0.2.4", 19 | "grunt-contrib-coffee": "~0.7.0", 20 | "grunt-angular-templates": "~0.4.7", 21 | "grunt-concat-sourcemap": "~0.3.1", 22 | "grunt-contrib-handlebars": "~0.5.11", 23 | "browserify": "~2.34.3", 24 | "grunt-browserify": "~1.2.9", 25 | "coffeeify": "~0.5.2", 26 | "grunt-contrib-imagemin": "~0.3.0" 27 | }, 28 | "devDependencies": { 29 | "grunt-open": "~0.2.2", 30 | "matchdep": "~0.3.0" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /grunt-workflow/tasks/server.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | var express = require("express"); 3 | grunt.registerTask("server", "static file development server", function() { 4 | var app, webPort, webRoot; 5 | webPort = grunt.config.get("server.web.port") || 8000; 6 | webRoot = grunt.config.get("server.base") || "dist"; 7 | 8 | app = express(); 9 | app.use(express.compress()); 10 | app.use(express.static("" + (process.cwd()) + "/" + webRoot)); 11 | app.use(express.errorHandler()); 12 | app.listen(webPort); 13 | 14 | grunt.log.writeln("Starting express web server in \"" + webRoot + "\" on port " + webPort); 15 | 16 | return app; 17 | }); 18 | }; 19 | -------------------------------------------------------------------------------- /lineman-workflow/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | #ignore node_modules, as the node project is not "deployed" per se: http://www.mikealrogers.com/posts/nodemodules-in-git.html 4 | /node_modules 5 | /dist 6 | /generated 7 | -------------------------------------------------------------------------------- /lineman-workflow/.npmignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | #ignore node_modules, as the node project is not "deployed" per se: http://www.mikealrogers.com/posts/nodemodules-in-git.html 4 | /node_modules 5 | 6 | /dist 7 | /generated 8 | -------------------------------------------------------------------------------- /lineman-workflow/Gruntfile.js: -------------------------------------------------------------------------------- 1 | /*global module:false*/ 2 | module.exports = function(grunt) { 3 | require(process.env['LINEMAN_MAIN']).config.grunt.run(grunt); 4 | }; 5 | -------------------------------------------------------------------------------- /lineman-workflow/README.md: -------------------------------------------------------------------------------- 1 | # My Lineman Application 2 | 3 | - clone me 4 | - npm install -g lineman 5 | - npm install 6 | - lineman run 7 | -------------------------------------------------------------------------------- /lineman-workflow/app/css/style.less: -------------------------------------------------------------------------------- 1 | @import "mixins.less"; 2 | 3 | .black-text-outline () { 4 | text-shadow: -1px 0 black, 0 1px black, 1px 0 black, 0 -1px black; 5 | } 6 | 7 | html { 8 | font-family: 'Skranji', cursive; 9 | color: #9caab2; 10 | } 11 | 12 | html, body, h1, h2, h3, ul, li { 13 | margin: 0; 14 | padding: 0; 15 | } 16 | 17 | ul, li { 18 | list-style-type: none; 19 | } 20 | 21 | body { 22 | background: black; 23 | } 24 | 25 | // Initial State 26 | 27 | #stat-editor { 28 | display: none; 29 | } 30 | 31 | .wrapper { 32 | width: 960px; 33 | margin: 0 auto; 34 | } 35 | 36 | #header { 37 | height: 100px; 38 | background-color: #1d1f32; 39 | #gradient > .vertical(#1d1f32, #494e6a); 40 | .box-shadow(0px 5px 10px 0px rgba(73, 78, 106, 0.75)); 41 | position: relative; 42 | h1 { 43 | padding-top: 25px; 44 | font-size: 2.5em; 45 | text-align: center; 46 | color: #9abaf0; 47 | .black-text-outline(); 48 | } 49 | } 50 | 51 | .allocated-max-stats { 52 | padding: 6px; 53 | border-top-left-radius: 6px; 54 | border: 2px solid black; 55 | background-color: rgba(0,0,0, 0.75); 56 | color: orange; 57 | line-height: 1em; 58 | border-right: none; 59 | border-bottom: none; 60 | bottom: 38px; 61 | right: 8px; 62 | position: absolute; 63 | z-index: 4; 64 | } 65 | 66 | .stats-overlay { 67 | padding: 3px; 68 | border-radius: 6px; 69 | overflow: hidden; 70 | background: black; 71 | margin: 0 auto; 72 | margin-top: -5px; 73 | width: 129px; 74 | position: relative; 75 | z-index: 0; 76 | li { 77 | float: left; 78 | display: block; 79 | width: 25px; 80 | color: white; 81 | text-align: center; 82 | margin-right: 1px; 83 | &:last-child { 84 | margin-right: 0px; 85 | }; 86 | border-radius: 2px; 87 | } 88 | 89 | .armor { 90 | background-color: #4873b2; 91 | } 92 | .strength { 93 | background-color: #c4453b; 94 | } 95 | .willpower { 96 | background-color: #c5a500; 97 | } 98 | .exertion { 99 | background-color: #458e48; 100 | } 101 | .break { 102 | background-color: #313131; 103 | } 104 | } 105 | 106 | #loadout { 107 | padding: 20px 0; 108 | #gradient > .vertical(#121a1d, #25404c); 109 | .box-shadow(0px 10px 20px 0px rgba(0,0,0,0.75)); 110 | .character { 111 | padding-bottom: 30px; 112 | position: relative; 113 | .move-left, .move-right { 114 | position: absolute; 115 | display: none; 116 | width: 34px; 117 | height: 34px; 118 | top: 88px; 119 | background-size: 100%; 120 | z-index: 5; 121 | } 122 | .move-left { 123 | background-image: url(../img/icon/move-left.png); 124 | left: -8px; 125 | } 126 | .move-right { 127 | background-image: url(../img/icon/move-right.png); 128 | right: -8px; 129 | } 130 | .arrow { 131 | display: none; 132 | position: absolute; 133 | bottom: -55px; 134 | left: 51px; 135 | color: #5f4d4d; 136 | font-size: 3em; 137 | } 138 | .help-text { 139 | text-align: center; 140 | position: absolute; 141 | top: 37%; 142 | left: 11%; 143 | width: 80%; 144 | } 145 | .remove { 146 | display: none; 147 | width: 25px; 148 | height: 25px; 149 | border-radius: 13px; 150 | #gradient > .radial(#b62f12, #8f250d); 151 | .box-shadow(0px 10px 20px 0px rgba(0,0,0,0.75)); 152 | border: 2px solid #7c8990; 153 | color: white; 154 | position: absolute; 155 | z-index: 2; 156 | top: 26px; 157 | right: 0; 158 | text-align: center; 159 | line-height: 0.75em; 160 | font-size: 2em; 161 | } 162 | &.choosing .arrow, &.edit-stats .arrow { 163 | display: block; 164 | } 165 | } 166 | 167 | .character.choosing { 168 | .arrow { 169 | display: block; 170 | } 171 | } 172 | } 173 | 174 | #character-selector, #loadout, .classes { 175 | overflow: hidden; 176 | } 177 | 178 | #character-selector { 179 | padding: 20px 0px; 180 | #gradient > .vertical(#5f4d4d, #8b6049); 181 | .character { 182 | width: 125px; 183 | margin-right: 35px; 184 | &:last-child { 185 | margin-right: 0px; 186 | }; 187 | .title { 188 | width: 125px; 189 | } 190 | .portrait { 191 | width: 125px; 192 | height: 125px; 193 | background-size: 125px; 194 | } 195 | } 196 | .unit-type { 197 | float: left; 198 | margin-right: 25px; 199 | &.archers, &.shieldbangers { 200 | margin-right: 0px; 201 | } 202 | > .title { 203 | border-bottom: 1px solid #999; 204 | line-height: 60px; 205 | margin-bottom: 10px; 206 | font-size: 3em; 207 | } 208 | } 209 | } 210 | 211 | .build { 212 | float: right; 213 | .version { 214 | color: orange; 215 | } 216 | } 217 | 218 | #stat-editor { 219 | #gradient > .vertical(#5f4d4d, #8b6049); 220 | overflow: hidden; 221 | padding: 20px 0; 222 | .title-and-controls { 223 | position: relative; 224 | } 225 | .controls { 226 | position: absolute; 227 | top: 0; 228 | right: 0; 229 | .reset, .total, .done, #rank-changer { 230 | border: 1px solid orange; 231 | display: block; 232 | float: right; 233 | margin-left: 10px; 234 | padding: 10px; 235 | color: white; 236 | } 237 | #rank-changer { 238 | padding: 0 10px; 239 | padding-right: 0px; 240 | .ui-title { 241 | padding-right: 5px; 242 | } 243 | &.rank1 .set-rank-1, 244 | &.rank2 .set-rank-2, 245 | &.rank3 .set-rank-3 { 246 | background-color: rgba(222, 157, 82, 0.35); 247 | } 248 | } 249 | .change-rank { 250 | display: inline-block; 251 | border-left: 1px solid orange; 252 | padding: 10px; 253 | color: white; 254 | } 255 | .reset:hover, .done:hover, .change-rank:hover { 256 | cursor: pointer; 257 | background-color: rgba(222, 157, 82, 0.35); 258 | } 259 | 260 | 261 | } 262 | .title { 263 | border-bottom: 1px solid #999; 264 | line-height: 60px; 265 | margin-bottom: 10px; 266 | font-size: 2.5em; 267 | } 268 | .portrait, .stats { 269 | float: left; 270 | } 271 | .stats { 272 | margin-top: 30px; 273 | width: 775px; 274 | li { 275 | text-align: right; 276 | padding-right: 175px; 277 | margin-bottom: 25px; 278 | font-size: 2.5em; 279 | border-width: 1px; 280 | border-style: solid; 281 | color: #ea9d2e; 282 | position: relative; 283 | .black-text-outline(); 284 | .current { 285 | color: #ffdba6; 286 | } 287 | .max { 288 | color: #ea9d2e; 289 | } 290 | .icon { 291 | display: block; 292 | position: absolute; 293 | right: 60px; 294 | top: -8px; 295 | width: 70px; 296 | height: 70px; 297 | background-size: 100%; 298 | &:hover { 299 | cursor: pointer; 300 | } 301 | } 302 | } 303 | .armor { 304 | background-color: #4873b2; 305 | border-color: #37005a; 306 | } 307 | .strength { 308 | background-color: #c4453b; 309 | border-color: #5d0300; 310 | } 311 | .willpower { 312 | background-color: #c5a500; 313 | border-color: #602a01; 314 | } 315 | .exertion { 316 | background-color: #458e48; 317 | border-color: #004514; 318 | } 319 | .break { 320 | background-color: #313131; 321 | border-color: #070708; 322 | } 323 | .total { 324 | margin-left: 425px; 325 | width: 175px; 326 | border: none; 327 | } 328 | } 329 | .portrait { 330 | width: 160px; 331 | height: 460px; 332 | margin-right: 20px; 333 | background-size: 100%; 334 | } 335 | } 336 | 337 | .character { 338 | width: 150px; 339 | height: 180px; 340 | float: left; 341 | margin-right: 10px; 342 | &:hover { 343 | cursor: pointer; 344 | } 345 | .title { 346 | font-size: 1.4em; 347 | height: 30px; 348 | .black-text-outline(); 349 | } 350 | .portrait { 351 | height: 150px; 352 | } 353 | &:first-child { 354 | margin-left: 5px; 355 | } 356 | &:last-child { 357 | margin-right: 0px; 358 | } 359 | } 360 | 361 | #credits { 362 | padding: 20px; 363 | #gradient > .vertical(#1d1f32, #494e6a); 364 | a { 365 | color: orange; 366 | text-decoration: none; 367 | } 368 | } 369 | 370 | // Characters 371 | 372 | // Mixins 373 | .base-character-portrait (@class) { 374 | .character.@{class} { 375 | .portrait { 376 | background-image: url("../img/characters/base/@{class}.png"); 377 | } 378 | } 379 | } 380 | 381 | .rank-1-character-portrait (@class) { 382 | .character.@{class} { 383 | .portrait { 384 | background-image: url("../img/characters/rank1/@{class}.png"); 385 | } 386 | } 387 | } 388 | 389 | // Large Portraits for Stat Editor 390 | .rank-1-stat-editor-portrait (@class) { 391 | #stat-editor .portrait.@{class} { 392 | background-image: url("../img/characters/portraits/@{class}.png"); 393 | } 394 | } 395 | 396 | .stat-icon (@stat) { 397 | #stat-editor .@{stat} .icon { 398 | background-image: url("../img/icon/@{stat}.png"); 399 | } 400 | } 401 | 402 | // Basic Character Structure 403 | 404 | .character { 405 | .title { 406 | text-align: center; 407 | } 408 | .portrait { 409 | background-image: url(../img/empty.character.frame.png); 410 | background-size: 150px; 411 | z-index: 1; 412 | position: relative; 413 | } 414 | } 415 | 416 | // Icons 417 | 418 | .stat-icon(armor); 419 | .stat-icon(strength); 420 | .stat-icon(willpower); 421 | .stat-icon(exertion); 422 | .stat-icon(break); 423 | .stat-icon(special); 424 | 425 | // Base Character Portraits 426 | 427 | .base-character-portrait(raider); 428 | .base-character-portrait(archer); 429 | .base-character-portrait(warrior); 430 | .base-character-portrait(shieldbanger); 431 | 432 | // Rank 1 Stat Editor Large Portraits 433 | 434 | // Warriors 435 | .rank-1-stat-editor-portrait(warhawk); 436 | .rank-1-stat-editor-portrait(warleader); 437 | .rank-1-stat-editor-portrait(warmaster); 438 | 439 | // Archers 440 | .rank-1-stat-editor-portrait(bowmaster); 441 | .rank-1-stat-editor-portrait(skystriker); 442 | .rank-1-stat-editor-portrait(siegearcher); 443 | 444 | // Raiders 445 | .rank-1-stat-editor-portrait(raidmaster); 446 | .rank-1-stat-editor-portrait(thrasher); 447 | .rank-1-stat-editor-portrait(backbiter); 448 | 449 | // Shieldbangers 450 | .rank-1-stat-editor-portrait(strongarm); 451 | .rank-1-stat-editor-portrait(provoker); 452 | .rank-1-stat-editor-portrait(shieldmaster); 453 | 454 | // Rank 1 Character Portraits 455 | 456 | // Warriors 457 | .rank-1-character-portrait(warhawk); 458 | .rank-1-character-portrait(warleader); 459 | .rank-1-character-portrait(warmaster); 460 | 461 | // Archers 462 | .rank-1-character-portrait(bowmaster); 463 | .rank-1-character-portrait(skystriker); 464 | .rank-1-character-portrait(siegearcher); 465 | 466 | // Raiders 467 | .rank-1-character-portrait(raidmaster); 468 | .rank-1-character-portrait(thrasher); 469 | .rank-1-character-portrait(backbiter); 470 | 471 | // Shieldbangers 472 | .rank-1-character-portrait(strongarm); 473 | .rank-1-character-portrait(provoker); 474 | .rank-1-character-portrait(shieldmaster); 475 | -------------------------------------------------------------------------------- /lineman-workflow/app/img/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/lineman-workflow/app/img/.gitkeep -------------------------------------------------------------------------------- /lineman-workflow/app/img/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/lineman-workflow/app/img/banner.png -------------------------------------------------------------------------------- /lineman-workflow/app/img/characters/base/archer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/lineman-workflow/app/img/characters/base/archer.png -------------------------------------------------------------------------------- /lineman-workflow/app/img/characters/base/raider.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/lineman-workflow/app/img/characters/base/raider.png -------------------------------------------------------------------------------- /lineman-workflow/app/img/characters/base/shieldbanger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/lineman-workflow/app/img/characters/base/shieldbanger.png -------------------------------------------------------------------------------- /lineman-workflow/app/img/characters/base/warrior.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/lineman-workflow/app/img/characters/base/warrior.png -------------------------------------------------------------------------------- /lineman-workflow/app/img/characters/portraits/backbiter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/lineman-workflow/app/img/characters/portraits/backbiter.png -------------------------------------------------------------------------------- /lineman-workflow/app/img/characters/portraits/bowmaster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/lineman-workflow/app/img/characters/portraits/bowmaster.png -------------------------------------------------------------------------------- /lineman-workflow/app/img/characters/portraits/provoker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/lineman-workflow/app/img/characters/portraits/provoker.png -------------------------------------------------------------------------------- /lineman-workflow/app/img/characters/portraits/raidmaster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/lineman-workflow/app/img/characters/portraits/raidmaster.png -------------------------------------------------------------------------------- /lineman-workflow/app/img/characters/portraits/shieldmaster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/lineman-workflow/app/img/characters/portraits/shieldmaster.png -------------------------------------------------------------------------------- /lineman-workflow/app/img/characters/portraits/siegearcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/lineman-workflow/app/img/characters/portraits/siegearcher.png -------------------------------------------------------------------------------- /lineman-workflow/app/img/characters/portraits/skystriker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/lineman-workflow/app/img/characters/portraits/skystriker.png -------------------------------------------------------------------------------- /lineman-workflow/app/img/characters/portraits/strongarm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/lineman-workflow/app/img/characters/portraits/strongarm.png -------------------------------------------------------------------------------- /lineman-workflow/app/img/characters/portraits/thrasher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/lineman-workflow/app/img/characters/portraits/thrasher.png -------------------------------------------------------------------------------- /lineman-workflow/app/img/characters/portraits/warhawk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/lineman-workflow/app/img/characters/portraits/warhawk.png -------------------------------------------------------------------------------- /lineman-workflow/app/img/characters/portraits/warleader.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/lineman-workflow/app/img/characters/portraits/warleader.png -------------------------------------------------------------------------------- /lineman-workflow/app/img/characters/portraits/warmaster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/lineman-workflow/app/img/characters/portraits/warmaster.png -------------------------------------------------------------------------------- /lineman-workflow/app/img/characters/rank1/backbiter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/lineman-workflow/app/img/characters/rank1/backbiter.png -------------------------------------------------------------------------------- /lineman-workflow/app/img/characters/rank1/bowmaster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/lineman-workflow/app/img/characters/rank1/bowmaster.png -------------------------------------------------------------------------------- /lineman-workflow/app/img/characters/rank1/provoker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/lineman-workflow/app/img/characters/rank1/provoker.png -------------------------------------------------------------------------------- /lineman-workflow/app/img/characters/rank1/raidmaster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/lineman-workflow/app/img/characters/rank1/raidmaster.png -------------------------------------------------------------------------------- /lineman-workflow/app/img/characters/rank1/shieldmaster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/lineman-workflow/app/img/characters/rank1/shieldmaster.png -------------------------------------------------------------------------------- /lineman-workflow/app/img/characters/rank1/siegearcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/lineman-workflow/app/img/characters/rank1/siegearcher.png -------------------------------------------------------------------------------- /lineman-workflow/app/img/characters/rank1/skystriker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/lineman-workflow/app/img/characters/rank1/skystriker.png -------------------------------------------------------------------------------- /lineman-workflow/app/img/characters/rank1/strongarm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/lineman-workflow/app/img/characters/rank1/strongarm.png -------------------------------------------------------------------------------- /lineman-workflow/app/img/characters/rank1/thrasher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/lineman-workflow/app/img/characters/rank1/thrasher.png -------------------------------------------------------------------------------- /lineman-workflow/app/img/characters/rank1/warhawk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/lineman-workflow/app/img/characters/rank1/warhawk.png -------------------------------------------------------------------------------- /lineman-workflow/app/img/characters/rank1/warleader.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/lineman-workflow/app/img/characters/rank1/warleader.png -------------------------------------------------------------------------------- /lineman-workflow/app/img/characters/rank1/warmaster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/lineman-workflow/app/img/characters/rank1/warmaster.png -------------------------------------------------------------------------------- /lineman-workflow/app/img/empty.character.frame.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/lineman-workflow/app/img/empty.character.frame.png -------------------------------------------------------------------------------- /lineman-workflow/app/img/icon/armor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/lineman-workflow/app/img/icon/armor.png -------------------------------------------------------------------------------- /lineman-workflow/app/img/icon/break.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/lineman-workflow/app/img/icon/break.png -------------------------------------------------------------------------------- /lineman-workflow/app/img/icon/exertion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/lineman-workflow/app/img/icon/exertion.png -------------------------------------------------------------------------------- /lineman-workflow/app/img/icon/move-left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/lineman-workflow/app/img/icon/move-left.png -------------------------------------------------------------------------------- /lineman-workflow/app/img/icon/move-right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/lineman-workflow/app/img/icon/move-right.png -------------------------------------------------------------------------------- /lineman-workflow/app/img/icon/special.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/lineman-workflow/app/img/icon/special.png -------------------------------------------------------------------------------- /lineman-workflow/app/img/icon/strength.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/lineman-workflow/app/img/icon/strength.png -------------------------------------------------------------------------------- /lineman-workflow/app/img/icon/willpower.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/lineman-workflow/app/img/icon/willpower.png -------------------------------------------------------------------------------- /lineman-workflow/app/js/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/lineman-workflow/app/js/.gitkeep -------------------------------------------------------------------------------- /lineman-workflow/app/js/app.coffee: -------------------------------------------------------------------------------- 1 | def 'tbs.BattlePlanner', class BattlePlanner 2 | 3 | constructor: -> 4 | @units = new tbs.collections.Units(@defaultUnits()) 5 | @loadout_units = new tbs.collections.Units(@defaultLoadoutUnits()) 6 | @router = new tbs.Router(loadout: @loadout_units) 7 | 8 | # stat editor 9 | @stat_editor = new tbs.views.StatEditor(el: "#stat-editor") 10 | 11 | # loadout 12 | @loadout = new tbs.views.Loadout( 13 | el: "#loadout" 14 | collection: @loadout_units 15 | ).render() 16 | 17 | #character_selector 18 | @character_selector = new tbs.views.CharacterSelector( 19 | el: "#character-selector" 20 | collection: @unitTypesWithoutBase() 21 | ).render() 22 | 23 | unitTypesWithoutBase: => 24 | _(@units.groupBy("type")).tap (types) -> delete types.base 25 | 26 | defaultLoadoutUnits: => 27 | _(_.range(0,6)).map (slot) => 28 | new tbs.models.Unit 29 | 30 | defaultUnits: => 31 | _(tbs.data.Units()).tap (units) => 32 | _(units).each (unit) => 33 | unit.max_stat_points = @maxStatPointsForRank(unit.rank) 34 | unit.allocated_stat_points = 0 35 | 36 | maxStatPointsForRank: (rank) => 37 | switch rank 38 | when 0 then 10 39 | when 1 then 11 40 | when 2 then 12 41 | when 3 then 13 42 | 43 | start: -> 44 | Backbone.history.start() 45 | 46 | $ -> tbs.BattlePlanner = new tbs.BattlePlanner(); tbs.BattlePlanner.start() 47 | 48 | -------------------------------------------------------------------------------- /lineman-workflow/app/js/collections/unit_stats.coffee: -------------------------------------------------------------------------------- 1 | def 'tbs.collections.UnitStats', class UnitStats extends Backbone.Collection 2 | model: tbs.models.UnitStat 3 | -------------------------------------------------------------------------------- /lineman-workflow/app/js/collections/units.coffee: -------------------------------------------------------------------------------- 1 | def 'tbs.collections.Units', class Units extends Backbone.Collection 2 | model: tbs.models.Unit 3 | 4 | UNIT_NAME_FULL_TO_ENCODED: 5 | 'raidmaster' : 'rm' 6 | 'thrasher' : 'th' 7 | 'backbiter' : 'bb' 8 | 'bowmaster' : 'bm' 9 | 'skystriker' : 'ss' 10 | 'siegearcher' : 'sg' 11 | 'warmaster' : 'wm' 12 | 'warhawk' : 'wh' 13 | 'warleader' : 'wl' 14 | 'provoker' : 'pk' 15 | 'strongarm' : 'sa' 16 | 'shieldmaster': 'sm' 17 | 18 | UNIT_NAME_ENCODED_TO_FULL: 19 | 'rm' : 'raidmaster' 20 | 'th' : 'thrasher' 21 | 'bb' : 'backbiter' 22 | 'bm' : 'bowmaster' 23 | 'ss' : 'skystriker' 24 | 'sg' : 'siegearcher' 25 | 'wm' : 'warmaster' 26 | 'wh' : 'warhawk' 27 | 'wl' : 'warleader' 28 | 'pk' : 'provoker' 29 | 'sa' : 'strongarm' 30 | 'sm' : 'shieldmaster' 31 | 32 | STAT_NAME_FULL_TO_ENCODED: 33 | 'armor' : 'a' 34 | 'strength' : 's' 35 | 'willpower' : 'w' 36 | 'exertion' : 'e' 37 | 'break' : 'b' 38 | 39 | STAT_NAME_ENCODED_TO_FULL: 40 | 'a' : 'armor' 41 | 's' : 'strength' 42 | 'w' : 'willpower' 43 | 'e' : 'exertion' 44 | 'b' : 'break' 45 | 46 | toJSON: => 47 | @map (unit) => 48 | converted = unit.toJSON() 49 | if converted.stats 50 | converted.stats = _(converted.stats.models).map (stat) => 51 | stat.attributes 52 | converted 53 | 54 | serialize: => 55 | output = [] 56 | _(@toJSON()).each (u) => 57 | stats = _(u.stats).map (s) => [@STAT_NAME_FULL_TO_ENCODED[s.stat],s.current,s.min,s.max] 58 | output.push([@UNIT_NAME_FULL_TO_ENCODED[u.name],u.rank,u.allocated_stat_points,u.max_stat_points,stats]) 59 | Base64.encode(JSON.stringify(output)) 60 | 61 | deserialize: (encoded) => 62 | units = JSON.parse(Base64.decode(encoded)) 63 | model_data = [] 64 | _(units).each (unit_data) => 65 | if unit_data[4].length is 0 66 | model_data.push(new tbs.models.Unit) 67 | else 68 | model_data.push( 69 | name: @UNIT_NAME_ENCODED_TO_FULL[unit_data[0]] 70 | rank: unit_data[1] 71 | allocated_stat_points: unit_data[2] 72 | max_stat_points: unit_data[3] 73 | stats: new tbs.collections.UnitStats( 74 | _(unit_data[4]).map (stat) => 75 | stat: @STAT_NAME_ENCODED_TO_FULL[stat[0]] 76 | current: stat[1] 77 | min: stat[2] 78 | max: stat[3] 79 | ) 80 | ) 81 | model_data 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /lineman-workflow/app/js/config/backbone_fixins_ext.coffee: -------------------------------------------------------------------------------- 1 | Backbone.Fixins.configure 2 | templateFunction: (name) -> 3 | JST[name] || throw "Could not find a template in JST[#{name}]" 4 | 5 | defaultTemplateLocator: (view) -> 6 | "app/templates/#{subPathFor(view)}.hb" 7 | 8 | subPathFor = (view) -> 9 | _(view.namespacePath.split('.')).chain(). 10 | rest(2). #skip "app" and "views" 11 | map(Backbone.Fixins.helpers.titleToSnakeCase). 12 | value().join('/') 13 | -------------------------------------------------------------------------------- /lineman-workflow/app/js/config/extend_ext.coffee: -------------------------------------------------------------------------------- 1 | # we want to abstract away "extend", because it's conflated with _.extend and Coffee's "extends" 2 | 3 | root = this 4 | root.def = _(extend).wrap (ex, args...) -> 5 | [namespace, target] = args 6 | unless typeof(target) is "object" 7 | target.prototype.namespacePath = namespace 8 | ex.apply(this, args) 9 | -------------------------------------------------------------------------------- /lineman-workflow/app/js/converters.coffee: -------------------------------------------------------------------------------- 1 | def 'tbs.converters.buildStatsCollection', (raw_stats_array, rank) -> 2 | 3 | STATS = ["armor", "strength", "willpower", "exertion", "break"] 4 | 5 | # stats come in from unit_data.coffee 6 | # arm str wil exr brk (min,max) 7 | # [[6,9], [6,9], [4,6,] [1,2,] [1,2]] 8 | 9 | model_values = _(raw_stats_array).map (min_max, i) -> 10 | min: min_max[0] 11 | max: min_max[1] 12 | current: min_max[0] 13 | stat: STATS[i] 14 | 15 | new tbs.collections.UnitStats(model_values) 16 | 17 | 18 | -------------------------------------------------------------------------------- /lineman-workflow/app/js/data/unit_data.coffee: -------------------------------------------------------------------------------- 1 | def 'tbs.data.Units', -> 2 | #arm str wil exr brk (min,max) 3 | [ 4 | { 5 | name: "raider" 6 | type: "base" 7 | rank: 0 8 | stats: tbs.converters.buildStatsCollection([[6,9], [6,9], [4,6], [1,2], [1,2]]) 9 | } 10 | { 11 | name: "raidmaster" 12 | type: "raider" 13 | rank: 1 14 | stats: tbs.converters.buildStatsCollection([[6,12], [6,12], [4,11], [1,3], [1,3]]) 15 | } 16 | { 17 | name: "thrasher" 18 | type: "raider" 19 | rank: 1 20 | stats: tbs.converters.buildStatsCollection([[5,11], [8,12], [3,13], [1,3], [1,2]]) 21 | } 22 | { 23 | name: "backbiter" 24 | type: "raider" 25 | rank: 1 26 | stats: tbs.converters.buildStatsCollection([[5,12], [8,10], [4,13], [0,3], [1,3]]) 27 | } 28 | { 29 | name: "archer" 30 | type: "base" 31 | rank: 0 32 | stats: tbs.converters.buildStatsCollection([[4,7], [4,7], [5,8], [1,2], [1,1]]) 33 | } 34 | { 35 | name: "bowmaster" 36 | type: "archer" 37 | rank: 1 38 | stats: tbs.converters.buildStatsCollection([[4,9], [4,8], [5,12], [1,3], [1,2]]) 39 | } 40 | { 41 | name: "skystriker" 42 | type: "archer" 43 | rank: 1 44 | stats: tbs.converters.buildStatsCollection([[3,9], [4,8], [7,13], [1,3], [0,1]]) 45 | } 46 | { 47 | name: "siegearcher" 48 | type: "archer" 49 | rank: 1 50 | stats: tbs.converters.buildStatsCollection([[4,9], [4,7], [6,13], [1,3], [0,2]]) 51 | } 52 | { 53 | name: "warrior" 54 | type: "base" 55 | rank: 0 56 | stats: tbs.converters.buildStatsCollection([[9,9], [12,12], [5,5], [2,2], [2,2]]) 57 | } 58 | { 59 | name: "warmaster" 60 | type: "warrior" 61 | rank: 1 62 | stats: tbs.converters.buildStatsCollection([[6,11], [9,17], [3,10], [1,2], [1,3]]) 63 | } 64 | { 65 | name: "warhawk" 66 | type: "warrior" 67 | rank: 1 68 | stats: tbs.converters.buildStatsCollection([[7,12], [10,16], [2,11], [0,2], [1,2]]) 69 | } 70 | { 71 | name: "warleader" 72 | type: "warrior" 73 | rank: 1 74 | stats: tbs.converters.buildStatsCollection([[5,12], [9,15], [5,9], [1,3], [0,4]]) 75 | } 76 | { 77 | name: "shieldbanger" 78 | type: "base" 79 | rank: 0 80 | stats: tbs.converters.buildStatsCollection([[9,14], [8,10], [3,5], [1,1], [1,2]]) 81 | } 82 | { 83 | name: "provoker" 84 | type: "shieldbanger" 85 | rank: 1 86 | stats: tbs.converters.buildStatsCollection([[11,18], [7,12], [3,9], [0,2], [1,3]]) 87 | } 88 | { 89 | name: "strongarm" 90 | type: "shieldbanger" 91 | rank: 1 92 | stats: tbs.converters.buildStatsCollection([[9,15], [9,15], [3,10], [0,2], [1,2]]) 93 | } 94 | { 95 | name: "shieldmaster" 96 | type: "shieldbanger" 97 | rank: 1 98 | stats: tbs.converters.buildStatsCollection([[9,16], [8,13], [3,9], [1,2], [1,4]]) 99 | } 100 | ] 101 | -------------------------------------------------------------------------------- /lineman-workflow/app/js/models/unit.coffee: -------------------------------------------------------------------------------- 1 | def 'tbs.models.Unit', class Unit extends Backbone.Model 2 | 3 | defaults: 4 | name: "" 5 | type: "" 6 | stats: undefined 7 | allocated_stat_points: 0 8 | max_stat_points: 0 9 | 10 | isChosen: => 11 | @has("stats") 12 | 13 | isEmpty: => 14 | not @has("stats") 15 | 16 | validate: (attrs) => 17 | if attrs.allocated_stat_points < 0 or attrs.allocated_stat_points > attrs.max_stat_points 18 | "would go over" 19 | -------------------------------------------------------------------------------- /lineman-workflow/app/js/models/unit_stat.coffee: -------------------------------------------------------------------------------- 1 | def('tbs.models.UnitStat', class UnitStat extends Backbone.Model) 2 | -------------------------------------------------------------------------------- /lineman-workflow/app/js/router.coffee: -------------------------------------------------------------------------------- 1 | def 'tbs.Router', class Router extends Backbone.Router 2 | 3 | initialize: (options) => 4 | @loadout = options.loadout 5 | @loadout.on("change", @storeHash) 6 | 7 | routes: 8 | ":encoded" : "loadLoadout" 9 | 10 | loadLoadout: (encoded) -> 11 | @loadout.reset(@loadout.deserialize(encoded)) 12 | Backbone.trigger("loaded:from:hash") 13 | 14 | storeHash: => 15 | @navigate(@loadout.serialize(), {replace: true}) 16 | -------------------------------------------------------------------------------- /lineman-workflow/app/js/views/character_selector.coffee: -------------------------------------------------------------------------------- 1 | def 'tbs.views.CharacterSelector', class CharacterSelector extends Backbone.View 2 | 3 | initialize: => 4 | Backbone.on("edit:unit", @hide) 5 | Backbone.on("choose:unit", @show) 6 | 7 | render: => 8 | _(@collection).each (units, type) => 9 | @$("#unit-groupings").append(new tbs.views.UnitGrouping( 10 | units: units 11 | type: type 12 | ).render().el) 13 | 14 | hide: => 15 | @$el.hide() 16 | 17 | show: => 18 | @$el.show() 19 | 20 | -------------------------------------------------------------------------------- /lineman-workflow/app/js/views/loadout.coffee: -------------------------------------------------------------------------------- 1 | def 'tbs.views.Loadout', class Loadout extends Backbone.View 2 | 3 | events: 4 | "click .character" : "editUnit" 5 | 6 | initialize: => 7 | Backbone.on("loaded:from:hash", @render) 8 | 9 | editUnit: (e) => 10 | unit = @fetchUnitFromCollectionViaSlotDataAttribute(e) 11 | if unit.isChosen() 12 | Backbone.trigger("edit:unit", unit) 13 | else 14 | Backbone.trigger("choose:unit") 15 | 16 | fetchUnitFromCollectionViaSlotDataAttribute: (element) => 17 | @collection.at($(element.currentTarget).data("slot")) 18 | 19 | render: => 20 | @$("#selected-characters").empty() 21 | @collection.each (unit, i) => 22 | @$("#selected-characters").append(new tbs.views.LoadoutSlot( 23 | model: unit 24 | slot: i 25 | ).render().el) 26 | @ 27 | -------------------------------------------------------------------------------- /lineman-workflow/app/js/views/loadout_slot.coffee: -------------------------------------------------------------------------------- 1 | def 'tbs.views.LoadoutSlot', class LoadoutSlot extends Backbone.Fixins.SuperView 2 | 3 | tagName: "li" 4 | 5 | events: 6 | "mouseover" : "showOverlay" 7 | "mouseout" : "hideOverlay" 8 | "click .remove" : "removeFromLoadout" 9 | "click .move-left" : "moveLeft" 10 | "click .move-right" : "moveRight" 11 | 12 | removeFromLoadout: (e) -> 13 | if confirm("Remove #{@model.get('name')} from slot #{@slot + 1}?") 14 | @model.clear() 15 | @model.set(new tbs.models.Unit().attributes) 16 | Backbone.trigger("choose:unit") 17 | else 18 | e.preventDefault() 19 | e.stopPropagation() 20 | 21 | attributes: => 22 | "class" : "character #{@model.get('name')}" 23 | "data-slot" : @slot 24 | 25 | showOverlay: => 26 | if @model.isChosen() then @$(".remove").show() 27 | if @model.isChosen() 28 | @$(".move-left, .move-right").show() 29 | 30 | if @position() is 0 then @$(".move-left").hide() 31 | if @position() is 5 then @$(".move-right").hide() 32 | 33 | moveLeft: (e) => 34 | e.stopPropagation() 35 | @move(@position(-1)) 36 | 37 | moveRight: (e) => 38 | e.stopPropagation() 39 | @move(@position(1)) 40 | 41 | move: (new_position_index) => 42 | original_attrs = _(@model.attributes).clone() 43 | new_attrs = _(@model.collection.at(new_position_index).attributes).clone() 44 | other_model = @model.collection.at(new_position_index) 45 | @model.clear() 46 | @model.set(new_attrs) 47 | other_model.clear() 48 | other_model.set(original_attrs) 49 | Backbone.trigger("loadout:reorganized", other_model) 50 | 51 | position: (offset=0)=> 52 | @model.collection.indexOf(@model) + offset 53 | 54 | hideOverlay: => 55 | @$(".remove, .move-left, .move-right").hide() 56 | 57 | initialize: (options) -> 58 | @slot = options.slot 59 | @model.on("change", @render) 60 | @model.on("change:rank", @updateMaxStatPoints) 61 | 62 | renderAttributes: => 63 | attrs = @attributes() 64 | @$el.attr("class", attrs.class) 65 | @$el.attr("data-slot", attrs['data-slot']) 66 | 67 | renderStatsOverlay: => 68 | if stats = @model.get('stats') 69 | stats.each (stat) => 70 | @$(".stats-overlay .#{stat.get('stat')}").text(stat.get('current')) 71 | 72 | renderAllocatedStatsOverlay: => 73 | @$(".allocated-max-stats .allocated").text(@model.get("allocated_stat_points")) 74 | @$(".allocated-max-stats .max").text(@model.get("max_stat_points")) 75 | 76 | renderHelpText: => 77 | if @model.isChosen() 78 | @$(".help-text").text("") 79 | else 80 | @$(".help-text").text("Click a unit below") 81 | 82 | updateMaxStatPoints: (__, new_rank) => 83 | @model.set("max_stat_points", tbs.BattlePlanner.maxStatPointsForRank(new_rank)) 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /lineman-workflow/app/js/views/stat_control.coffee: -------------------------------------------------------------------------------- 1 | def 'tbs.views.StatControl', class StatControl extends Backbone.Fixins.SuperView 2 | 3 | tagName: "li" 4 | 5 | events: 6 | "mousedown .icon" : "increaseOrDecreaseStat" 7 | "contextmenu .icon" : -> false 8 | 9 | initialize: (options) -> 10 | @unit = options.unit 11 | @model.on("change:current", @update) 12 | 13 | attributes: => 14 | class: @model.get("stat") 15 | 16 | increaseOrDecreaseStat: (e) => 17 | e.preventDefault() 18 | e.stopPropagation() 19 | switch e.which 20 | when 1 then @changeBy(1) #left click 21 | when 3 then @changeBy(-1) #right click 22 | 23 | changeBy: (amount) => 24 | # if the amount does not exceed the units max_stat_points 25 | # if the amount does not exceed the stats maximum 26 | if !(@unit.get('allocated_stat_points') + amount > @unit.get('max_stat_points') or @unit.get('allocated_stat_points') + amount < 0) 27 | if !(@model.get('current') + amount < @model.get('min') or @model.get('current') + amount > @model.get('max')) 28 | @model.set(current: @model.get("current") + amount) 29 | @unit.set(allocated_stat_points: @unit.get('allocated_stat_points') + amount) 30 | 31 | update: => 32 | @$(".current").text(@model.get("current")) 33 | 34 | -------------------------------------------------------------------------------- /lineman-workflow/app/js/views/stat_editor.coffee: -------------------------------------------------------------------------------- 1 | def 'tbs.views.StatEditor', class StatEditor extends Backbone.Fixins.SuperView 2 | 3 | events: 4 | "click .reset" : "resetToMinimums" 5 | "click .done" : -> Backbone.trigger("choose:unit") 6 | "click .change-rank" : "setRank" 7 | 8 | initialize: => 9 | Backbone.on("edit:unit", @show) 10 | Backbone.on("choose:unit", @hide) 11 | Backbone.on("loadout:reorganized", @show) 12 | 13 | renderVisible: => 14 | if @model.isChosen() then @$el.show() else @$el.hide() 15 | 16 | renderStatControls: => 17 | unit_stats = @model.get('stats') 18 | unit_stats.each (stat, i) => 19 | @$(".stats").append(new tbs.views.StatControl(unit: @model, model: stat).render().el) 20 | 21 | resetToMinimums: => 22 | @model.set("allocated_stat_points", 0) 23 | stats = @model.get('stats') 24 | stats.each (stat) => 25 | stat.set('current', stat.get('min')) 26 | @model.trigger('change') 27 | 28 | renderTotals: => 29 | @$(".total .allocated").text(@model.get("allocated_stat_points")) 30 | @$(".total .max").text(@model.get("max_stat_points")) 31 | 32 | renderRankChanger: => 33 | @$("#rank-changer").prop("class", "rank#{@model.get("rank")}") 34 | 35 | renderRankInEditorTitle: => 36 | @$(".title .rank").text(@model.get("rank")) 37 | 38 | setRank: (e) => 39 | rank = parseInt($(e.currentTarget).text(), 10) 40 | oldrank = @model.get("rank") 41 | @model.set({rank}) 42 | if rank < oldrank 43 | @resetToMinimums() 44 | 45 | hide: => 46 | @$el.hide() 47 | 48 | show: (unit) => 49 | @model = unit 50 | # had to clone stats here, as editing the same class was buggy 51 | @model.set("stats", new tbs.collections.UnitStats(@model.get('stats').toJSON())) 52 | @model.on("change:allocated_stat_points", @renderTotals) 53 | @model.on("change:rank", @renderRankChanger) 54 | @model.on("change:rank", @renderRankInEditorTitle) 55 | @model.on("change:rank", @renderTotals) 56 | @render() 57 | @$el.show() 58 | -------------------------------------------------------------------------------- /lineman-workflow/app/js/views/unit_grouping.coffee: -------------------------------------------------------------------------------- 1 | def 'tbs.views.UnitGrouping', class UnitGrouping extends Backbone.Fixins.SuperView 2 | 3 | attributes: 4 | class: "unit-type" 5 | 6 | initialize: (options) -> 7 | @type = options.type 8 | @units = options.units 9 | 10 | renderTitle: => 11 | @$(".title").text(@type) 12 | 13 | renderUnits: => 14 | _(@units).each (unit) => 15 | @$(".classes").append(new tbs.views.UnitSelector( 16 | model: unit 17 | ).render().el) 18 | -------------------------------------------------------------------------------- /lineman-workflow/app/js/views/unit_selector.coffee: -------------------------------------------------------------------------------- 1 | def 'tbs.views.UnitSelector', class UnitSelector extends Backbone.Fixins.SuperView 2 | 3 | tagName: "li" 4 | 5 | attributes: => 6 | class: "character #{@model.get('name')}" 7 | 8 | events: 9 | "click" : "assignUnitToNextAvailableLoadoutSlot" 10 | 11 | assignUnitToNextAvailableLoadoutSlot: => 12 | # get the next active loadout slot 13 | loadout_unit = tbs.BattlePlanner.loadout_units.find (unit) => unit.isEmpty() 14 | if not loadout_unit then loadout_unit = tbs.BattlePlanner.loadout_units.at(5) 15 | loadout_unit.clear() 16 | loadout_unit.set(@model.clone().attributes) 17 | -------------------------------------------------------------------------------- /lineman-workflow/app/pages/index.hb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{ pkg.name }} 5 | 6 | 7 | 8 | 9 | 10 | 11 | 16 | 17 |
18 |
19 |
    20 |
    21 |
    22 | 23 |
    24 |
    25 |
    26 |
    27 |
    28 | 29 |
    30 | 31 |
    32 |
    33 | built by @dmosher 34 | 35 | Calculator Release {{ pkg.version }} based on The Banner Saga: Factions build {{ pkg.tbs_version }} 36 | 37 |
    38 |
    39 | 40 | 41 | -------------------------------------------------------------------------------- /lineman-workflow/app/templates/loadout.hb: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /lineman-workflow/app/templates/loadout_slot.hb: -------------------------------------------------------------------------------- 1 |
    {{ name }}
    2 |
    3 | Click a unit below 4 |
    5 | × 6 | 13 | 14 | 15 |
    16 | 0/11 17 |
    18 | -------------------------------------------------------------------------------- /lineman-workflow/app/templates/stat_control.hb: -------------------------------------------------------------------------------- 1 | {{ current }}/{{ max }} 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /lineman-workflow/app/templates/stat_editor.hb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    {{ name }} - (rank {{ rank }})
    4 |
    5 | Done Editing 6 | Reset to Minimums 7 | 8 | / 9 | 10 | 11 | Rank 12 | 123 13 | 14 |
    15 |
    16 | 17 |
    18 | 19 |
    20 | -------------------------------------------------------------------------------- /lineman-workflow/app/templates/unit_grouping.hb: -------------------------------------------------------------------------------- 1 |
    {{ type }}
    2 | 3 | -------------------------------------------------------------------------------- /lineman-workflow/app/templates/unit_selector.hb: -------------------------------------------------------------------------------- 1 |
    {{ name }}
    2 |
    3 | -------------------------------------------------------------------------------- /lineman-workflow/config/application.coffee: -------------------------------------------------------------------------------- 1 | # Exports an object that defines all of the configuration needed by the 2 | # projects' depended-on grunt tasks. 3 | # 4 | # You can find the parent object in: node_modules/lineman/config/application.js 5 | # 6 | module.exports = require(process.env['LINEMAN_MAIN']).config.extend("application", 7 | 8 | ) 9 | -------------------------------------------------------------------------------- /lineman-workflow/config/files.coffee: -------------------------------------------------------------------------------- 1 | # Exports an object that defines all of the paths & globs that the project is 2 | # concerned with. 3 | # 4 | # The "configure" task will require this file and then re-initialize the grunt 5 | # config such that directives like will work regardless 6 | # of the point you're at in the build lifecycle. 7 | # 8 | # You can find the parent object in: node_modules/lineman/config/files.js 9 | # 10 | module.exports = require(process.env['LINEMAN_MAIN']).config.extend("files", 11 | 12 | #Override file patterns here 13 | js: 14 | vendor: [ 15 | "vendor/js/underscore.js", 16 | "vendor/js/jquery.js", 17 | "vendor/js/backbone.js", 18 | "vendor/js/**/*.js" 19 | ] 20 | 21 | coffee: 22 | app: [ 23 | "app/js/config/**/*.coffee", 24 | "app/js/converters.coffee", 25 | "app/js/data/**/*.coffee", 26 | "app/js/models/**/*.coffee", 27 | "app/js/**/*.coffee" 28 | ] 29 | 30 | less: 31 | compile: 32 | options: 33 | paths: ["vendor/css/**/*.css", "app/css/**/*.less"] 34 | ) 35 | -------------------------------------------------------------------------------- /lineman-workflow/config/server.coffee: -------------------------------------------------------------------------------- 1 | # Define custom server-side HTTP routes for lineman's development server 2 | # These might be as simple as stubbing a little JSON to 3 | # facilitate development of code that interacts with an HTTP service 4 | # (presumably, mirroring one that will be reachable in a live environment). 5 | # 6 | # It's important to remember that any custom endpoints defined here 7 | # will only be available in development, as lineman only builds 8 | # static assets, it can't run server-side code. 9 | # 10 | # This file can be very useful for rapid prototyping or even organically 11 | # defining a spec based on the needs of the client code that emerge. 12 | # 13 | # 14 | 15 | module.exports = drawRoutes: (app) -> 16 | 17 | #app.get "/devices/116/program", (req, res) -> 18 | #res.json(generateSmartPriceTiersInServerFormat()) 19 | -------------------------------------------------------------------------------- /lineman-workflow/config/spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "framework" : "jasmine", 3 | "launch_in_dev" : ["Chrome"], 4 | "launch_in_ci" : ["PhantomJS"], 5 | "src_files" : [ 6 | "generated/js/app.js", 7 | "generated/js/spec.js" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /lineman-workflow/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tbs-factions-battle-planner", 3 | "title": "A battle planner for The Banner Saga : Factions", 4 | "version": "0.2.58", 5 | "tbs_version": "1.7.43", 6 | "private": true, 7 | "author": { 8 | "name": "David A. Mosher", 9 | "company": "DAVEMO Consulting" 10 | }, 11 | "dependencies": { 12 | "handlebars": "~1.0.12", 13 | "lineman": "~0.14.5" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lineman-workflow/spec/js/helpers/helper.js: -------------------------------------------------------------------------------- 1 | var root = this; 2 | 3 | root.context = root.describe; 4 | root.xcontext = root.xdescribe; -------------------------------------------------------------------------------- /lineman-workflow/spec/js/helpers/jasmine-given.js: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | jasmine-given 0.0.7 4 | Adds a Given-When-Then DSL to jasmine as an alternative style for specs 5 | site: https://github.com/searls/jasmine-given 6 | */ 7 | 8 | 9 | (function() { 10 | 11 | (function(jasmine) { 12 | var mostRecentlyUsed, o, root, stringifyExpectation; 13 | stringifyExpectation = function(expectation) { 14 | var matches; 15 | matches = expectation.toString().replace(/\n/g, '').match(/function\s?\(\)\s?{\s*(return\s+)?(.*?)(;)?\s*}/i); 16 | if (matches && matches.length >= 3) { 17 | return matches[2]; 18 | } else { 19 | return ""; 20 | } 21 | }; 22 | beforeEach(function() { 23 | return this.addMatchers({ 24 | toHaveReturnedFalseFromThen: function(context, n) { 25 | var exception, result; 26 | result = false; 27 | exception = void 0; 28 | try { 29 | result = this.actual.call(context); 30 | } catch (e) { 31 | exception = e; 32 | } 33 | this.message = function() { 34 | var msg; 35 | msg = "Then clause " + (n > 1 ? " #" + n : "") + " `" + (stringifyExpectation(this.actual)) + "` failed by "; 36 | if (exception) { 37 | msg += "throwing: " + exception.toString(); 38 | } else { 39 | msg += "returning false"; 40 | } 41 | return msg; 42 | }; 43 | return result === false; 44 | } 45 | }); 46 | }); 47 | root = this; 48 | root.When = root.Given = function() { 49 | var assignResultTo, mostRecentlyUsed, setupFunction; 50 | setupFunction = o(arguments).firstThat(function(arg) { 51 | return o(arg).isFunction(); 52 | }); 53 | assignResultTo = o(arguments).firstThat(function(arg) { 54 | return o(arg).isString(); 55 | }); 56 | mostRecentlyUsed = root.Given; 57 | return beforeEach(function() { 58 | var context, result; 59 | context = jasmine.getEnv().currentSpec; 60 | result = setupFunction.call(context); 61 | if (assignResultTo) { 62 | if (!context[assignResultTo]) { 63 | return context[assignResultTo] = result; 64 | } else { 65 | throw new Error("Unfortunately, the variable '" + assignResultTo + "' is already assigned to: " + context[assignResultTo]); 66 | } 67 | } 68 | }); 69 | }; 70 | root.Then = function(expectationFunction) { 71 | var expectations, mostRecentlyUsed, subsequentThen; 72 | mostRecentlyUsed = root.Then; 73 | expectations = [expectationFunction]; 74 | subsequentThen = function(additionalExpectation) { 75 | expectations.push(additionalExpectation); 76 | return this; 77 | }; 78 | it("then " + (stringifyExpectation(expectations)), function() { 79 | var i, _results; 80 | i = 0; 81 | _results = []; 82 | while (i < expectations.length) { 83 | expect(expectations[i]).not.toHaveReturnedFalseFromThen(jasmine.getEnv().currentSpec, i + 1); 84 | _results.push(i++); 85 | } 86 | return _results; 87 | }); 88 | return { 89 | Then: subsequentThen, 90 | And: subsequentThen 91 | }; 92 | }; 93 | mostRecentlyUsed = root.Given; 94 | root.And = function() { 95 | return mostRecentlyUsed.apply(this, jasmine.util.argsToArray(arguments)); 96 | }; 97 | return o = function(thing) { 98 | return { 99 | isFunction: function() { 100 | return Object.prototype.toString.call(thing) === "[object Function]"; 101 | }, 102 | isString: function() { 103 | return Object.prototype.toString.call(thing) === "[object String]"; 104 | }, 105 | firstThat: function(test) { 106 | var i; 107 | i = 0; 108 | while (i < thing.length) { 109 | if (test(thing[i]) === true) { 110 | return thing[i]; 111 | } 112 | i++; 113 | } 114 | return void 0; 115 | } 116 | }; 117 | }; 118 | })(jasmine); 119 | 120 | }).call(this); 121 | -------------------------------------------------------------------------------- /lineman-workflow/spec/js/helpers/jasmine-stealth.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | 3 | /* 4 | jasmine-stealth 0.0.12 5 | Makes Jasmine spies a bit more robust 6 | site: https://github.com/searls/jasmine-stealth 7 | */ 8 | 9 | 10 | (function() { 11 | var Captor, fake, root, unfakes, whatToDoWhenTheSpyGetsCalled, _, 12 | __hasProp = {}.hasOwnProperty, 13 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; 14 | 15 | root = this; 16 | 17 | _ = function(obj) { 18 | return { 19 | each: function(iterator) { 20 | var item, _i, _len, _results; 21 | _results = []; 22 | for (_i = 0, _len = obj.length; _i < _len; _i++) { 23 | item = obj[_i]; 24 | _results.push(iterator(item)); 25 | } 26 | return _results; 27 | }, 28 | isFunction: function() { 29 | return Object.prototype.toString.call(obj) === "[object Function]"; 30 | }, 31 | isString: function() { 32 | return Object.prototype.toString.call(obj) === "[object String]"; 33 | } 34 | }; 35 | }; 36 | 37 | root.spyOnConstructor = function(owner, classToFake, methodsToSpy) { 38 | var fakeClass, spies; 39 | if (methodsToSpy == null) { 40 | methodsToSpy = []; 41 | } 42 | if (_(methodsToSpy).isString()) { 43 | methodsToSpy = [methodsToSpy]; 44 | } 45 | spies = { 46 | constructor: jasmine.createSpy("" + classToFake + "'s constructor") 47 | }; 48 | fakeClass = (function() { 49 | 50 | function _Class() { 51 | spies.constructor.apply(this, arguments); 52 | } 53 | 54 | return _Class; 55 | 56 | })(); 57 | _(methodsToSpy).each(function(methodName) { 58 | spies[methodName] = jasmine.createSpy("" + classToFake + "#" + methodName); 59 | return fakeClass.prototype[methodName] = function() { 60 | return spies[methodName].apply(this, arguments); 61 | }; 62 | }); 63 | fake(owner, classToFake, fakeClass); 64 | return spies; 65 | }; 66 | 67 | unfakes = []; 68 | 69 | afterEach(function() { 70 | _(unfakes).each(function(u) { 71 | return u(); 72 | }); 73 | return unfakes = []; 74 | }); 75 | 76 | fake = function(owner, thingToFake, newThing) { 77 | var originalThing; 78 | originalThing = owner[thingToFake]; 79 | owner[thingToFake] = newThing; 80 | return unfakes.push(function() { 81 | return owner[thingToFake] = originalThing; 82 | }); 83 | }; 84 | 85 | root.stubFor = root.spyOn; 86 | 87 | jasmine.createStub = jasmine.createSpy; 88 | 89 | jasmine.createStubObj = function(baseName, stubbings) { 90 | var name, obj, stubbing; 91 | if (stubbings.constructor === Array) { 92 | return jasmine.createSpyObj(baseName, stubbings); 93 | } else { 94 | obj = {}; 95 | for (name in stubbings) { 96 | stubbing = stubbings[name]; 97 | obj[name] = jasmine.createSpy(baseName + "." + name); 98 | if (_(stubbing).isFunction()) { 99 | obj[name].andCallFake(stubbing); 100 | } else { 101 | obj[name].andReturn(stubbing); 102 | } 103 | } 104 | return obj; 105 | } 106 | }; 107 | 108 | whatToDoWhenTheSpyGetsCalled = function(spy) { 109 | var matchesStub, priorStubbing; 110 | matchesStub = function(stubbing, args, context) { 111 | switch (stubbing.type) { 112 | case "args": 113 | return jasmine.getEnv().equals_(stubbing.ifThis, jasmine.util.argsToArray(args)); 114 | case "context": 115 | return jasmine.getEnv().equals_(stubbing.ifThis, context); 116 | } 117 | }; 118 | priorStubbing = spy.plan(); 119 | return spy.andCallFake(function() { 120 | var i, stubbing; 121 | i = 0; 122 | while (i < spy._stealth_stubbings.length) { 123 | stubbing = spy._stealth_stubbings[i]; 124 | if (matchesStub(stubbing, arguments, this)) { 125 | if (Object.prototype.toString.call(stubbing.thenThat) === "[object Function]") { 126 | return stubbing.thenThat(); 127 | } else { 128 | return stubbing.thenThat; 129 | } 130 | } 131 | i++; 132 | } 133 | return priorStubbing; 134 | }); 135 | }; 136 | 137 | jasmine.Spy.prototype.whenContext = function(context) { 138 | var addStubbing, spy; 139 | spy = this; 140 | spy._stealth_stubbings || (spy._stealth_stubbings = []); 141 | whatToDoWhenTheSpyGetsCalled(spy); 142 | addStubbing = function(thenThat) { 143 | spy._stealth_stubbings.push({ 144 | type: 'context', 145 | ifThis: context, 146 | thenThat: thenThat 147 | }); 148 | return spy; 149 | }; 150 | return { 151 | thenReturn: addStubbing, 152 | thenCallFake: addStubbing 153 | }; 154 | }; 155 | 156 | jasmine.Spy.prototype.when = function() { 157 | var addStubbing, ifThis, spy; 158 | spy = this; 159 | ifThis = jasmine.util.argsToArray(arguments); 160 | spy._stealth_stubbings || (spy._stealth_stubbings = []); 161 | whatToDoWhenTheSpyGetsCalled(spy); 162 | addStubbing = function(thenThat) { 163 | spy._stealth_stubbings.push({ 164 | type: 'args', 165 | ifThis: ifThis, 166 | thenThat: thenThat 167 | }); 168 | return spy; 169 | }; 170 | return { 171 | thenReturn: addStubbing, 172 | thenCallFake: addStubbing 173 | }; 174 | }; 175 | 176 | jasmine.Spy.prototype.mostRecentCallThat = function(callThat, context) { 177 | var i; 178 | i = this.calls.length - 1; 179 | while (i >= 0) { 180 | if (callThat.call(context || this, this.calls[i]) === true) { 181 | return this.calls[i]; 182 | } 183 | i--; 184 | } 185 | }; 186 | 187 | jasmine.Matchers.ArgThat = (function(_super) { 188 | 189 | __extends(ArgThat, _super); 190 | 191 | function ArgThat(matcher) { 192 | this.matcher = matcher; 193 | } 194 | 195 | ArgThat.prototype.jasmineMatches = function(actual) { 196 | return this.matcher(actual); 197 | }; 198 | 199 | return ArgThat; 200 | 201 | })(jasmine.Matchers.Any); 202 | 203 | jasmine.Matchers.ArgThat.prototype.matches = jasmine.Matchers.ArgThat.prototype.jasmineMatches; 204 | 205 | jasmine.argThat = function(expected) { 206 | return new jasmine.Matchers.ArgThat(expected); 207 | }; 208 | 209 | jasmine.Matchers.Capture = (function(_super) { 210 | 211 | __extends(Capture, _super); 212 | 213 | function Capture(captor) { 214 | this.captor = captor; 215 | } 216 | 217 | Capture.prototype.jasmineMatches = function(actual) { 218 | this.captor.value = actual; 219 | return true; 220 | }; 221 | 222 | return Capture; 223 | 224 | })(jasmine.Matchers.Any); 225 | 226 | jasmine.Matchers.Capture.prototype.matches = jasmine.Matchers.Capture.prototype.jasmineMatches; 227 | 228 | Captor = (function() { 229 | 230 | function Captor() {} 231 | 232 | Captor.prototype.capture = function() { 233 | return new jasmine.Matchers.Capture(this); 234 | }; 235 | 236 | return Captor; 237 | 238 | })(); 239 | 240 | jasmine.captor = function() { 241 | return new Captor(); 242 | }; 243 | 244 | }).call(this); 245 | -------------------------------------------------------------------------------- /lineman-workflow/tasks/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/lineman-workflow/tasks/.gitkeep -------------------------------------------------------------------------------- /lineman-workflow/vendor/css/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/lineman-workflow/vendor/css/.gitkeep -------------------------------------------------------------------------------- /lineman-workflow/vendor/img/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/lineman-workflow/vendor/img/.gitkeep -------------------------------------------------------------------------------- /lineman-workflow/vendor/js/backbone-fixins.js: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | backbone-fixins 0.0.1 4 | 5 | Boilerplate that strengthens your backbone 6 | 7 | site: https://github.com/testdouble/backbone-fixins 8 | */ 9 | 10 | 11 | (function() { 12 | var fixins, ogConfig, root, superView, _base, 13 | __hasProp = {}.hasOwnProperty, 14 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; 15 | 16 | root = this; 17 | 18 | if (root.Backbone == null) { 19 | throw "backbone-fixins requires Backbone. Make sure you load Backbone first"; 20 | } 21 | 22 | (_base = root.Backbone).Fixins || (_base.Fixins = {}); 23 | 24 | /* 25 | Extendable Configuration. 26 | 27 | Feel free to override the configuration to suit your project's needs. 28 | */ 29 | 30 | 31 | root = this; 32 | 33 | fixins = root.Backbone.Fixins; 34 | 35 | fixins.configuration = ogConfig = { 36 | templateFunction: function(name) { 37 | return root.JST[name]; 38 | }, 39 | defaultTemplateContext: function(view) { 40 | var _ref; 41 | return ((_ref = view.model || view.collection) != null ? _ref.toJSON() : void 0) || {}; 42 | }, 43 | defaultTemplateLocator: function(view) { 44 | var name; 45 | name = fixins.helpers.constructorNameOf(view); 46 | return "templates/" + (fixins.helpers.titleToSnakeCase(name)); 47 | } 48 | }; 49 | 50 | fixins.configure = function(customConfigurationObject) { 51 | return fixins.configuration = fixins.helpers.merge(fixins.configuration, customConfigurationObject); 52 | }; 53 | 54 | fixins.resetConfiguration = function() { 55 | return fixins.configuration = ogConfig; 56 | }; 57 | 58 | /* 59 | # Random helpers. Pretend these are private, but I won't hide them 60 | # in the event you have a really good reason to override them. 61 | */ 62 | 63 | 64 | Backbone.Fixins.helpers = { 65 | constructorNameOf: function(obj) { 66 | var results; 67 | results = /function (.{1,})\(/.exec(obj.constructor.toString()); 68 | if (results && results.length > 1) { 69 | return results[1]; 70 | } else { 71 | return ""; 72 | } 73 | }, 74 | titleToSnakeCase: function(titleCasedString) { 75 | return titleCasedString.replace(/([a-z\d])([A-Z]+)/g, '$1_$2').replace(/[-\s]+/g, '_').toLowerCase(); 76 | }, 77 | startsWith: function(str, options) { 78 | var starts; 79 | starts = options.butIsntExactly; 80 | return str.length > starts.length && str.substr(0, starts.length) === starts; 81 | }, 82 | merge: function(orig, newStuff) { 83 | return _(orig).chain().clone().extend(newStuff).value(); 84 | } 85 | }; 86 | 87 | /* 88 | Backbone.Fixins.SuperView 89 | 90 | Intended to be a view from which each application view will extend in order to DRY 91 | up the typical housekeeping normally carried out by Backbone.View subclasses (e.g. 92 | find template, plug in serialized context, render markup into element) 93 | */ 94 | 95 | var fixins, root, superView, 96 | __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, 97 | __hasProp = {}.hasOwnProperty, 98 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; 99 | 100 | root = this; 101 | 102 | fixins = root.Backbone.Fixins; 103 | 104 | Backbone.Fixins.SuperView = (function(_super) { 105 | 106 | __extends(SuperView, _super); 107 | 108 | function SuperView() { 109 | this.render = __bind(this.render, this); 110 | return SuperView.__super__.constructor.apply(this, arguments); 111 | } 112 | 113 | SuperView.prototype.render = function() { 114 | var context, f, template, _i, _len, _ref; 115 | template = fixins.configuration.templateFunction(superView.locateTemplate(this)); 116 | context = superView.templateContext(this); 117 | this.$el.html(template(context)); 118 | _ref = _(this).functions(); 119 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 120 | f = _ref[_i]; 121 | if (fixins.helpers.startsWith(f, { 122 | butIsntExactly: "render" 123 | })) { 124 | this[f](); 125 | } 126 | } 127 | return this.trigger('rendered'); 128 | }; 129 | 130 | return SuperView; 131 | 132 | })(root.Backbone.View); 133 | 134 | superView = { 135 | templateContext: function(view) { 136 | if (view.templateContext != null) { 137 | return (typeof view.templateContext === "function" ? view.templateContext() : void 0) || view.templateContext; 138 | } else { 139 | return fixins.configuration.defaultTemplateContext(view); 140 | } 141 | }, 142 | locateTemplate: function(view) { 143 | if (view.template != null) { 144 | return (typeof view.template === "function" ? view.template() : void 0) || view.template; 145 | } else { 146 | return fixins.configuration.defaultTemplateLocator(view); 147 | } 148 | } 149 | }; 150 | 151 | }).call(this); 152 | -------------------------------------------------------------------------------- /lineman-workflow/vendor/js/base64.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Base64 encode / decode 4 | * http://www.webtoolkit.info/ 5 | * 6 | **/ 7 | 8 | var Base64 = { 9 | 10 | // private property 11 | _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", 12 | 13 | // public method for encoding 14 | encode : function (input) { 15 | var output = ""; 16 | var chr1, chr2, chr3, enc1, enc2, enc3, enc4; 17 | var i = 0; 18 | 19 | input = Base64._utf8_encode(input); 20 | 21 | while (i < input.length) { 22 | 23 | chr1 = input.charCodeAt(i++); 24 | chr2 = input.charCodeAt(i++); 25 | chr3 = input.charCodeAt(i++); 26 | 27 | enc1 = chr1 >> 2; 28 | enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); 29 | enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); 30 | enc4 = chr3 & 63; 31 | 32 | if (isNaN(chr2)) { 33 | enc3 = enc4 = 64; 34 | } else if (isNaN(chr3)) { 35 | enc4 = 64; 36 | } 37 | 38 | output = output + 39 | this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) + 40 | this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4); 41 | 42 | } 43 | 44 | return output; 45 | }, 46 | 47 | // public method for decoding 48 | decode : function (input) { 49 | var output = ""; 50 | var chr1, chr2, chr3; 51 | var enc1, enc2, enc3, enc4; 52 | var i = 0; 53 | 54 | input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); 55 | 56 | while (i < input.length) { 57 | 58 | enc1 = this._keyStr.indexOf(input.charAt(i++)); 59 | enc2 = this._keyStr.indexOf(input.charAt(i++)); 60 | enc3 = this._keyStr.indexOf(input.charAt(i++)); 61 | enc4 = this._keyStr.indexOf(input.charAt(i++)); 62 | 63 | chr1 = (enc1 << 2) | (enc2 >> 4); 64 | chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); 65 | chr3 = ((enc3 & 3) << 6) | enc4; 66 | 67 | output = output + String.fromCharCode(chr1); 68 | 69 | if (enc3 != 64) { 70 | output = output + String.fromCharCode(chr2); 71 | } 72 | if (enc4 != 64) { 73 | output = output + String.fromCharCode(chr3); 74 | } 75 | 76 | } 77 | 78 | output = Base64._utf8_decode(output); 79 | 80 | return output; 81 | 82 | }, 83 | 84 | // private method for UTF-8 encoding 85 | _utf8_encode : function (string) { 86 | string = string.replace(/\r\n/g,"\n"); 87 | var utftext = ""; 88 | 89 | for (var n = 0; n < string.length; n++) { 90 | 91 | var c = string.charCodeAt(n); 92 | 93 | if (c < 128) { 94 | utftext += String.fromCharCode(c); 95 | } 96 | else if((c > 127) && (c < 2048)) { 97 | utftext += String.fromCharCode((c >> 6) | 192); 98 | utftext += String.fromCharCode((c & 63) | 128); 99 | } 100 | else { 101 | utftext += String.fromCharCode((c >> 12) | 224); 102 | utftext += String.fromCharCode(((c >> 6) & 63) | 128); 103 | utftext += String.fromCharCode((c & 63) | 128); 104 | } 105 | 106 | } 107 | 108 | return utftext; 109 | }, 110 | 111 | // private method for UTF-8 decoding 112 | _utf8_decode : function (utftext) { 113 | var string = ""; 114 | var i = 0; 115 | var c = c1 = c2 = 0; 116 | 117 | while ( i < utftext.length ) { 118 | 119 | c = utftext.charCodeAt(i); 120 | 121 | if (c < 128) { 122 | string += String.fromCharCode(c); 123 | i++; 124 | } 125 | else if((c > 191) && (c < 224)) { 126 | c2 = utftext.charCodeAt(i+1); 127 | string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); 128 | i += 2; 129 | } 130 | else { 131 | c2 = utftext.charCodeAt(i+1); 132 | c3 = utftext.charCodeAt(i+2); 133 | string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); 134 | i += 3; 135 | } 136 | 137 | } 138 | 139 | return string; 140 | } 141 | 142 | } 143 | -------------------------------------------------------------------------------- /lineman-workflow/vendor/js/extend.js: -------------------------------------------------------------------------------- 1 | // https://github.com/searls/extend.js 2 | (function(_) { 3 | var makeExtender = function(top) { 4 | return function(name,value) { 5 | var ancestors = name.split('.'), 6 | leaf = ancestors.pop(), 7 | parent = resolveAncestors(ancestors,top); 8 | 9 | verifyDistinctness(name,value,parent[leaf]); 10 | 11 | if(isExtensible(parent[leaf],value)) { 12 | _(parent[leaf]).extend(value); 13 | } else if(arguments.length > 1) { 14 | parent[leaf] = value; 15 | } 16 | return parent[leaf]; 17 | }; 18 | }; 19 | 20 | var isExtensible = function(existing,value) { 21 | return existing && !_(value).isFunction() && !_(existing).isFunction(); 22 | }; 23 | 24 | var resolveAncestors = function(ancestors,top) { 25 | return _(ancestors).reduce(function(ancestor,child) { 26 | ancestor[child] = ancestor[child] || {}; 27 | return ancestor[child]; 28 | },top); 29 | }; 30 | 31 | var verifyDistinctness = function(name,value,existing) { 32 | if(_(existing).isFunction() && value && existing !== value) { 33 | throw 'Cannot define a new function "'+name+'", because one is already defined.'; 34 | } 35 | }; 36 | 37 | var originalExtend = window.extend; 38 | 39 | window.extend = makeExtender(window); 40 | 41 | window.extend.myNamespace = function(namespace) { 42 | namespace.extend = makeExtender(namespace); 43 | return namespace.extend; 44 | }; 45 | 46 | window.extend.noConflict = function() { 47 | var ourExtend = window.extend; 48 | window.extend = originalExtend; 49 | return ourExtend; 50 | }; 51 | })(_); -------------------------------------------------------------------------------- /steps.md: -------------------------------------------------------------------------------- 1 | # Grunt the JavaScript Task Runner 2 | 3 | - pre-existing static assets 4 | - npm init 5 | - touch Gruntfile.js 6 | 7 | - npm install grunt-cli -g 8 | - npm install grunt 9 | - npm install grunt-contrib-concat --save 10 | 11 | # Stylesheet Preprocessors 12 | 13 | - npm install grunt-contrib-watch --save 14 | - npm install grunt-contrib-less 15 | - npm install grunt-contrib-copy --save 16 | - npm install express --save-dev 17 | - mkdir tasks 18 | - touch tasks/server.js 19 | - install the chrome live-reload extension 20 | -- https://chrome.google.com/webstore/detail/livereload/jnihajbhpnppcggbcgedagnkighmdlei?hl=en 21 | 22 | # Thinking about Dependency Management 23 | 24 | ## CoffeeScript 25 | 26 | - npm install grunt-contrib-clean --save 27 | - npm install grunt-contrib-uglify --save 28 | - npm install grunt-open --save-dev 29 | - npm install grunt-contrib-coffee --save 30 | 31 | ## Bower 32 | 33 | - npm install -g bower 34 | - touch .bowerrc 35 | - bower init 36 | - bower install angular#1.2.0-rc.3 --save 37 | - bower install angular-route#1.2.0-rc.3 --save 38 | - bower install base64js --save 39 | - bower install jquery --save 40 | - bower install underscore --save 41 | - bower install https://github.com/searls/extend.js/releases/download/0.1.0/extend.js --save 42 | 43 | # Precompiling Templates for MV* 44 | 45 | ## grunt-angular-templates 46 | 47 | - npm install grunt-angular-templates --save 48 | 49 | # Optimizing for Developer Happiness 50 | 51 | ## grunt-concat-sourcemap 52 | 53 | - npm install grunt-concat-sourcemap --save 54 | 55 | # Alternative Frameworks & Dependency Management Strategies 56 | 57 | ## Backbone, precompiling handlebars 58 | 59 | - npm install grunt-contrib-handlebars --save 60 | 61 | - rm -rf bower_modules 62 | - bower install https://raw.github.com/testdouble/backbone-fixins/master/dist/backbone-fixins.js --save 63 | - bower install backbone.stickit#0.6.2 --save 64 | - bower install backbone#0.9.10 --save 65 | - bower install underscore.string#2.3.0 --save 66 | - bower install handlebars#1.0.0 --save 67 | - bower install underscore#1.4.3 --save 68 | - bower install jquery#1.8.2 --save 69 | - bower install https://github.com/searls/extend.js/releases/download/0.1.0/extend.js --save 70 | - bower install base64js --save 71 | 72 | ## CommonJS, A simple module format 73 | 74 | - npm install grunt-browserify --save 75 | - npm install coffeeify --save 76 | 77 | ## Yeoman 78 | 79 | - cd yeoman-workflow 80 | - npm install 81 | - bower install 82 | - grunt server 83 | 84 | ## Lineman 85 | 86 | - cd lineman-workflow 87 | - npm installl 88 | - lineman 89 | 90 | ## Bonus: Grunt Contrib Imagemin 91 | 92 | - cd grunt-workflow 93 | - npm install grunt-contrib-imagemin --save 94 | - grunt imagemin 95 | -------------------------------------------------------------------------------- /yeoman-workflow/.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "app/bower_components" 3 | } 4 | -------------------------------------------------------------------------------- /yeoman-workflow/.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | 10 | # Change these settings to your own preference 11 | indent_style = space 12 | indent_size = 4 13 | 14 | # We recommend you to keep these unchanged 15 | end_of_line = lf 16 | charset = utf-8 17 | trim_trailing_whitespace = true 18 | insert_final_newline = true 19 | 20 | [*.md] 21 | trim_trailing_whitespace = false 22 | -------------------------------------------------------------------------------- /yeoman-workflow/.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto -------------------------------------------------------------------------------- /yeoman-workflow/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | test/temp 4 | .sass-cache 5 | app/bower_components 6 | .tmp 7 | test/bower_components/ 8 | -------------------------------------------------------------------------------- /yeoman-workflow/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "browser": true, 4 | "esnext": true, 5 | "bitwise": true, 6 | "camelcase": true, 7 | "curly": true, 8 | "eqeqeq": true, 9 | "immed": true, 10 | "indent": 4, 11 | "latedef": true, 12 | "newcap": true, 13 | "noarg": true, 14 | "quotmark": "single", 15 | "regexp": true, 16 | "undef": true, 17 | "unused": true, 18 | "strict": true, 19 | "trailing": true, 20 | "smarttabs": true, 21 | "jquery": true 22 | } 23 | -------------------------------------------------------------------------------- /yeoman-workflow/app/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Page Not Found :( 6 | 141 | 142 | 143 |
    144 |

    Not found :(

    145 |

    Sorry, but the page you were trying to view does not exist.

    146 |

    It looks like this was the result of either:

    147 |
      148 |
    • a mistyped address
    • 149 |
    • an out-of-date link
    • 150 |
    151 | 154 | 155 |
    156 | 157 | 158 | -------------------------------------------------------------------------------- /yeoman-workflow/app/favicon.ico: -------------------------------------------------------------------------------- 1 |   �( @   -2Op"=p�Jt��Jt��b���������������������������������������������������b���Jt��Jt��"=p�Op-2O`O�O�O�O�O�O�O� $\�Jt��������������v���v���������������Jt�� $\�O�O�O�O�O�O�O�O`O�O�O�O�O�O�O�O�O�O� ;n�s���>���>���>���>���s��� ;n�O�O�O�O�O�O�O�O�O�O�O`O�O�O�O�O�O�O�O�O�O� $\�]���^n��^n��]��� $\�O�O�O�O�O�O�O�O�O�O�O`O�O�O�O�O�O�O�O�O�O�O�n�*��*��n�O�O�O�O�O�O�O�O�O�O�O�  O�O�O�O�O�O�O�O�O�O�O�5>Y�5>Y�O�O�O�O�O�O�O�O�O�O�O�  -2O�O�O�O�O�O�O�O�O�O�&6e�&6e�O�O�O�O�O�O�O�O�O�O�-25r�4���E��� $\�O�O�O�O�O�O�O�O�O�O�O�O�O�O�O�O�O�O� $\�E���4���5r�5r�E���M���M���v���0\��O�O�O�O�O�O�O� $\� $\�O�O�O�O�O�O�O�0\��v���M���M���E���5r�)��p&��p��&��������������b���Jt��Jt��Jt��0\��#i��.r��.r��#i��0\��Jt��Jt��Jt��b���������������&��p��&��)��p4���&��-���_������������������]���]�������7���p�����������p���7�������]���]�������������������_��-���-���4���qֈp��p��p����������������������p���7���#i��p�����������p���#i��7���p�����������������������p��&��-���qֈ8��(p��p��I���v���v���]���7���n���v���p���#i��]���v���v���]���#i��p���v���n���7���]���v���v���I���-���-���8��(;��`-���M���7���7���7���.r��R��E��R��E��7���7���7���7���E��R��E��R��.r��7���7���7���M���M���;��`���������������������������z��������������������������� 2 | �  ��� 3 | � 9� 9� 9� 9� 9� 9� 9� 9� 4 |  �n�n� 5 |  � 9� 9� 9� 9� 9� 9� 9� 9� 6 | ����*�x*��*��*��*��*��*��*��n�&��#��&��&��n�*��*��*��*��*��*��*��*�x*ݟ*��*��*��*��*��*��!��#��&��#��&��*��!��!��*��*��*��*��*��*��*ݟ*ݿ*��*��*��*��*��*��n�*��*�� 9� 9�*��*���*��*��*��*��*��*��*ݿ*��*��*��*��*��*��*��!��#��&��&��&��*��#��!��*��*��*��*��*��*��*��  ��������I�&��&��&��&��I���������  U��������� 7 |  �n�n� 8 |  ����������-2z����������������������z������������������������ 9 | ����������������������� 10 | ������������������������� 11 | ������������������������-2����������������������U�������������������z5r������������������-25r�U�����������z  ������������������������������?��� -------------------------------------------------------------------------------- /yeoman-workflow/app/images/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/yeoman-workflow/app/images/.gitkeep -------------------------------------------------------------------------------- /yeoman-workflow/app/images/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/yeoman-workflow/app/images/banner.png -------------------------------------------------------------------------------- /yeoman-workflow/app/images/characters/base/archer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/yeoman-workflow/app/images/characters/base/archer.png -------------------------------------------------------------------------------- /yeoman-workflow/app/images/characters/base/raider.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/yeoman-workflow/app/images/characters/base/raider.png -------------------------------------------------------------------------------- /yeoman-workflow/app/images/characters/base/shieldbanger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/yeoman-workflow/app/images/characters/base/shieldbanger.png -------------------------------------------------------------------------------- /yeoman-workflow/app/images/characters/base/warrior.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/yeoman-workflow/app/images/characters/base/warrior.png -------------------------------------------------------------------------------- /yeoman-workflow/app/images/characters/portraits/backbiter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/yeoman-workflow/app/images/characters/portraits/backbiter.png -------------------------------------------------------------------------------- /yeoman-workflow/app/images/characters/portraits/bowmaster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/yeoman-workflow/app/images/characters/portraits/bowmaster.png -------------------------------------------------------------------------------- /yeoman-workflow/app/images/characters/portraits/provoker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/yeoman-workflow/app/images/characters/portraits/provoker.png -------------------------------------------------------------------------------- /yeoman-workflow/app/images/characters/portraits/raidmaster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/yeoman-workflow/app/images/characters/portraits/raidmaster.png -------------------------------------------------------------------------------- /yeoman-workflow/app/images/characters/portraits/shieldmaster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/yeoman-workflow/app/images/characters/portraits/shieldmaster.png -------------------------------------------------------------------------------- /yeoman-workflow/app/images/characters/portraits/siegearcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/yeoman-workflow/app/images/characters/portraits/siegearcher.png -------------------------------------------------------------------------------- /yeoman-workflow/app/images/characters/portraits/skystriker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/yeoman-workflow/app/images/characters/portraits/skystriker.png -------------------------------------------------------------------------------- /yeoman-workflow/app/images/characters/portraits/strongarm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/yeoman-workflow/app/images/characters/portraits/strongarm.png -------------------------------------------------------------------------------- /yeoman-workflow/app/images/characters/portraits/thrasher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/yeoman-workflow/app/images/characters/portraits/thrasher.png -------------------------------------------------------------------------------- /yeoman-workflow/app/images/characters/portraits/warhawk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/yeoman-workflow/app/images/characters/portraits/warhawk.png -------------------------------------------------------------------------------- /yeoman-workflow/app/images/characters/portraits/warleader.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/yeoman-workflow/app/images/characters/portraits/warleader.png -------------------------------------------------------------------------------- /yeoman-workflow/app/images/characters/portraits/warmaster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/yeoman-workflow/app/images/characters/portraits/warmaster.png -------------------------------------------------------------------------------- /yeoman-workflow/app/images/characters/rank1/backbiter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/yeoman-workflow/app/images/characters/rank1/backbiter.png -------------------------------------------------------------------------------- /yeoman-workflow/app/images/characters/rank1/bowmaster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/yeoman-workflow/app/images/characters/rank1/bowmaster.png -------------------------------------------------------------------------------- /yeoman-workflow/app/images/characters/rank1/provoker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/yeoman-workflow/app/images/characters/rank1/provoker.png -------------------------------------------------------------------------------- /yeoman-workflow/app/images/characters/rank1/raidmaster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/yeoman-workflow/app/images/characters/rank1/raidmaster.png -------------------------------------------------------------------------------- /yeoman-workflow/app/images/characters/rank1/shieldmaster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/yeoman-workflow/app/images/characters/rank1/shieldmaster.png -------------------------------------------------------------------------------- /yeoman-workflow/app/images/characters/rank1/siegearcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/yeoman-workflow/app/images/characters/rank1/siegearcher.png -------------------------------------------------------------------------------- /yeoman-workflow/app/images/characters/rank1/skystriker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/yeoman-workflow/app/images/characters/rank1/skystriker.png -------------------------------------------------------------------------------- /yeoman-workflow/app/images/characters/rank1/strongarm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/yeoman-workflow/app/images/characters/rank1/strongarm.png -------------------------------------------------------------------------------- /yeoman-workflow/app/images/characters/rank1/thrasher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/yeoman-workflow/app/images/characters/rank1/thrasher.png -------------------------------------------------------------------------------- /yeoman-workflow/app/images/characters/rank1/warhawk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/yeoman-workflow/app/images/characters/rank1/warhawk.png -------------------------------------------------------------------------------- /yeoman-workflow/app/images/characters/rank1/warleader.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/yeoman-workflow/app/images/characters/rank1/warleader.png -------------------------------------------------------------------------------- /yeoman-workflow/app/images/characters/rank1/warmaster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/yeoman-workflow/app/images/characters/rank1/warmaster.png -------------------------------------------------------------------------------- /yeoman-workflow/app/images/empty.character.frame.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/yeoman-workflow/app/images/empty.character.frame.png -------------------------------------------------------------------------------- /yeoman-workflow/app/images/icon/armor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/yeoman-workflow/app/images/icon/armor.png -------------------------------------------------------------------------------- /yeoman-workflow/app/images/icon/break.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/yeoman-workflow/app/images/icon/break.png -------------------------------------------------------------------------------- /yeoman-workflow/app/images/icon/exertion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/yeoman-workflow/app/images/icon/exertion.png -------------------------------------------------------------------------------- /yeoman-workflow/app/images/icon/move-left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/yeoman-workflow/app/images/icon/move-left.png -------------------------------------------------------------------------------- /yeoman-workflow/app/images/icon/move-right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/yeoman-workflow/app/images/icon/move-right.png -------------------------------------------------------------------------------- /yeoman-workflow/app/images/icon/special.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/yeoman-workflow/app/images/icon/special.png -------------------------------------------------------------------------------- /yeoman-workflow/app/images/icon/strength.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/yeoman-workflow/app/images/icon/strength.png -------------------------------------------------------------------------------- /yeoman-workflow/app/images/icon/willpower.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemo/fem-grunt-workflow/973e2945185ae74102ee73582dd3700bb59f03a6/yeoman-workflow/app/images/icon/willpower.png -------------------------------------------------------------------------------- /yeoman-workflow/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | yeoman workflow 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 27 | 28 | 29 | 34 | 35 |
    36 |
    37 |
      38 |
      39 |
      40 | 41 |
      42 |
      43 |
      44 |
      45 |
      46 | 47 |
      48 | 49 |
      50 |
      51 | built by @dmosher 52 | 53 | Calculator Release {{ pkg.version }} based on The Banner Saga: Factions build {{ pkg.tbs_version }} 54 | 55 |
      56 |
      57 | 58 | 59 | 60 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /yeoman-workflow/app/robots.txt: -------------------------------------------------------------------------------- 1 | # robotstxt.org 2 | 3 | User-agent: * 4 | -------------------------------------------------------------------------------- /yeoman-workflow/app/scripts/app.coffee: -------------------------------------------------------------------------------- 1 | class window.yeomanWorkflow.BattlePlanner 2 | 3 | constructor: -> 4 | @units = new window.yeomanWorkflow.Collections.UnitCollection(@defaultUnits()) 5 | @loadout_units = new window.yeomanWorkflow.Collections.UnitCollection(@defaultLoadoutUnits()) 6 | @router = new window.yeomanWorkflow.Routers.Router(loadout: @loadout_units) 7 | 8 | # stat editor 9 | @stat_editor = new window.yeomanWorkflow.Views.StatEditorView(el: "#stat-editor") 10 | 11 | # loadout 12 | @loadout = new window.yeomanWorkflow.Views.LoadoutView( 13 | el: "#loadout" 14 | collection: @loadout_units 15 | ).render() 16 | 17 | #character_selector 18 | @character_selector = new window.yeomanWorkflow.Views.CharacterSelectorView( 19 | el: "#character-selector" 20 | collection: @unitTypesWithoutBase() 21 | ).render() 22 | 23 | unitTypesWithoutBase: => 24 | _(@units.groupBy("type")).tap (types) -> delete types.base 25 | 26 | defaultLoadoutUnits: => 27 | _(_.range(0,6)).map (slot) => 28 | new window.yeomanWorkflow.Models.UnitModel 29 | 30 | defaultUnits: => 31 | _(window.yeomanWorkflow.Data.Units()).tap (units) => 32 | _(units).each (unit) => 33 | unit.max_stat_points = @maxStatPointsForRank(unit.rank) 34 | unit.allocated_stat_points = 0 35 | 36 | maxStatPointsForRank: (rank) => 37 | switch rank 38 | when 0 then 10 39 | when 1 then 11 40 | when 2 then 12 41 | when 3 then 13 42 | 43 | start: -> 44 | Backbone.history.start() 45 | 46 | $ -> 47 | window.BattlePlanner = new window.yeomanWorkflow.BattlePlanner() 48 | window.BattlePlanner.start() 49 | -------------------------------------------------------------------------------- /yeoman-workflow/app/scripts/collections/unit.coffee: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | class yeomanWorkflow.Collections.UnitCollection extends Backbone.Collection 4 | model: yeomanWorkflow.Models.UnitModel 5 | 6 | UNIT_NAME_FULL_TO_ENCODED: 7 | 'raidmaster' : 'rm' 8 | 'thrasher' : 'th' 9 | 'backbiter' : 'bb' 10 | 'bowmaster' : 'bm' 11 | 'skystriker' : 'ss' 12 | 'siegearcher' : 'sg' 13 | 'warmaster' : 'wm' 14 | 'warhawk' : 'wh' 15 | 'warleader' : 'wl' 16 | 'provoker' : 'pk' 17 | 'strongarm' : 'sa' 18 | 'shieldmaster': 'sm' 19 | 20 | UNIT_NAME_ENCODED_TO_FULL: 21 | 'rm' : 'raidmaster' 22 | 'th' : 'thrasher' 23 | 'bb' : 'backbiter' 24 | 'bm' : 'bowmaster' 25 | 'ss' : 'skystriker' 26 | 'sg' : 'siegearcher' 27 | 'wm' : 'warmaster' 28 | 'wh' : 'warhawk' 29 | 'wl' : 'warleader' 30 | 'pk' : 'provoker' 31 | 'sa' : 'strongarm' 32 | 'sm' : 'shieldmaster' 33 | 34 | STAT_NAME_FULL_TO_ENCODED: 35 | 'armor' : 'a' 36 | 'strength' : 's' 37 | 'willpower' : 'w' 38 | 'exertion' : 'e' 39 | 'break' : 'b' 40 | 41 | STAT_NAME_ENCODED_TO_FULL: 42 | 'a' : 'armor' 43 | 's' : 'strength' 44 | 'w' : 'willpower' 45 | 'e' : 'exertion' 46 | 'b' : 'break' 47 | 48 | toJSON: => 49 | @map (unit) => 50 | converted = unit.toJSON() 51 | if converted.stats 52 | converted.stats = _(converted.stats.models).map (stat) => 53 | stat.attributes 54 | converted 55 | 56 | serialize: => 57 | output = [] 58 | _(@toJSON()).each (u) => 59 | stats = _(u.stats).map (s) => [@STAT_NAME_FULL_TO_ENCODED[s.stat],s.current,s.min,s.max] 60 | output.push([@UNIT_NAME_FULL_TO_ENCODED[u.name],u.rank,u.allocated_stat_points,u.max_stat_points,stats]) 61 | base64.encode(JSON.stringify(output)) 62 | 63 | deserialize: (encoded) => 64 | units = JSON.parse(base64.decode(encoded)) 65 | model_data = [] 66 | _(units).each (unit_data) => 67 | if unit_data[4].length is 0 68 | model_data.push(new window.yeomanWorkflow.Models.UnitModel) 69 | else 70 | model_data.push( 71 | name: @UNIT_NAME_ENCODED_TO_FULL[unit_data[0]] 72 | rank: unit_data[1] 73 | allocated_stat_points: unit_data[2] 74 | max_stat_points: unit_data[3] 75 | stats: new window.yeomanWorkflow.Collections.UnitStatCollection( 76 | _(unit_data[4]).map (stat) => 77 | stat: @STAT_NAME_ENCODED_TO_FULL[stat[0]] 78 | current: stat[1] 79 | min: stat[2] 80 | max: stat[3] 81 | ) 82 | ) 83 | model_data 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /yeoman-workflow/app/scripts/collections/unit_stat.coffee: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | class yeomanWorkflow.Collections.UnitStatCollection extends Backbone.Collection 4 | model: yeomanWorkflow.Models.UnitStatModel 5 | -------------------------------------------------------------------------------- /yeoman-workflow/app/scripts/config/backbone_fixins_ext.coffee: -------------------------------------------------------------------------------- 1 | Backbone.Fixins.configure 2 | templateFunction: (name) -> 3 | JST[name] || throw "Could not find a template in JST[#{name}]" 4 | 5 | defaultTemplateLocator: (view) -> 6 | "app/scripts/templates/#{viewPathFor(view)}.hbs" 7 | 8 | viewPathFor = (view) -> 9 | Backbone.Fixins.helpers.titleToSnakeCase(view.constructor.name.split("View")[0]) 10 | -------------------------------------------------------------------------------- /yeoman-workflow/app/scripts/converters.coffee: -------------------------------------------------------------------------------- 1 | window.yeomanWorkflow.Converters.buildStatsCollection = (raw_stats_array, rank) -> 2 | 3 | STATS = ["armor", "strength", "willpower", "exertion", "break"] 4 | 5 | # stats come in from unit_data.coffee 6 | # arm str wil exr brk (min,max) 7 | # [[6,9], [6,9], [4,6,] [1,2,] [1,2]] 8 | 9 | model_values = _(raw_stats_array).map (min_max, i) -> 10 | min: min_max[0] 11 | max: min_max[1] 12 | current: min_max[0] 13 | stat: STATS[i] 14 | 15 | new window.yeomanWorkflow.Collections.UnitStatCollection(model_values) 16 | -------------------------------------------------------------------------------- /yeoman-workflow/app/scripts/data/unit_data.coffee: -------------------------------------------------------------------------------- 1 | 2 | window.yeomanWorkflow.Data.Units = -> 3 | #arm str wil exr brk (min,max) 4 | [ 5 | { 6 | name: "raider" 7 | type: "base" 8 | rank: 0 9 | stats: window.yeomanWorkflow.Converters.buildStatsCollection([[6,9], [6,9], [4,6], [1,2], [1,2]]) 10 | } 11 | { 12 | name: "raidmaster" 13 | type: "raider" 14 | rank: 1 15 | stats: window.yeomanWorkflow.Converters.buildStatsCollection([[6,12], [6,12], [4,11], [1,3], [1,3]]) 16 | } 17 | { 18 | name: "thrasher" 19 | type: "raider" 20 | rank: 1 21 | stats: window.yeomanWorkflow.Converters.buildStatsCollection([[5,11], [8,12], [3,13], [1,3], [1,2]]) 22 | } 23 | { 24 | name: "backbiter" 25 | type: "raider" 26 | rank: 1 27 | stats: window.yeomanWorkflow.Converters.buildStatsCollection([[5,12], [8,10], [4,13], [0,3], [1,3]]) 28 | } 29 | { 30 | name: "archer" 31 | type: "base" 32 | rank: 0 33 | stats: window.yeomanWorkflow.Converters.buildStatsCollection([[4,7], [4,7], [5,8], [1,2], [1,1]]) 34 | } 35 | { 36 | name: "bowmaster" 37 | type: "archer" 38 | rank: 1 39 | stats: window.yeomanWorkflow.Converters.buildStatsCollection([[4,9], [4,8], [5,12], [1,3], [1,2]]) 40 | } 41 | { 42 | name: "skystriker" 43 | type: "archer" 44 | rank: 1 45 | stats: window.yeomanWorkflow.Converters.buildStatsCollection([[3,9], [4,8], [7,13], [1,3], [0,1]]) 46 | } 47 | { 48 | name: "siegearcher" 49 | type: "archer" 50 | rank: 1 51 | stats: window.yeomanWorkflow.Converters.buildStatsCollection([[4,9], [4,7], [6,13], [1,3], [0,2]]) 52 | } 53 | { 54 | name: "warrior" 55 | type: "base" 56 | rank: 0 57 | stats: window.yeomanWorkflow.Converters.buildStatsCollection([[9,9], [12,12], [5,5], [2,2], [2,2]]) 58 | } 59 | { 60 | name: "warmaster" 61 | type: "warrior" 62 | rank: 1 63 | stats: window.yeomanWorkflow.Converters.buildStatsCollection([[6,11], [9,17], [3,10], [1,2], [1,3]]) 64 | } 65 | { 66 | name: "warhawk" 67 | type: "warrior" 68 | rank: 1 69 | stats: window.yeomanWorkflow.Converters.buildStatsCollection([[7,12], [10,16], [2,11], [0,2], [1,2]]) 70 | } 71 | { 72 | name: "warleader" 73 | type: "warrior" 74 | rank: 1 75 | stats: window.yeomanWorkflow.Converters.buildStatsCollection([[5,12], [9,15], [5,9], [1,3], [0,4]]) 76 | } 77 | { 78 | name: "shieldbanger" 79 | type: "base" 80 | rank: 0 81 | stats: window.yeomanWorkflow.Converters.buildStatsCollection([[9,14], [8,10], [3,5], [1,1], [1,2]]) 82 | } 83 | { 84 | name: "provoker" 85 | type: "shieldbanger" 86 | rank: 1 87 | stats: window.yeomanWorkflow.Converters.buildStatsCollection([[11,18], [7,12], [3,9], [0,2], [1,3]]) 88 | } 89 | { 90 | name: "strongarm" 91 | type: "shieldbanger" 92 | rank: 1 93 | stats: window.yeomanWorkflow.Converters.buildStatsCollection([[9,15], [9,15], [3,10], [0,2], [1,2]]) 94 | } 95 | { 96 | name: "shieldmaster" 97 | type: "shieldbanger" 98 | rank: 1 99 | stats: window.yeomanWorkflow.Converters.buildStatsCollection([[9,16], [8,13], [3,9], [1,2], [1,4]]) 100 | } 101 | ] 102 | -------------------------------------------------------------------------------- /yeoman-workflow/app/scripts/main.coffee: -------------------------------------------------------------------------------- 1 | window.yeomanWorkflow = 2 | Models: {} 3 | Collections: {} 4 | Views: {} 5 | Routers: {} 6 | Data: {} 7 | Converters: {} 8 | -------------------------------------------------------------------------------- /yeoman-workflow/app/scripts/models/unit.coffee: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | class yeomanWorkflow.Models.UnitModel extends Backbone.Model 4 | 5 | defaults: 6 | name: "" 7 | type: "" 8 | stats: undefined 9 | allocated_stat_points: 0 10 | max_stat_points: 0 11 | 12 | isChosen: => 13 | @has("stats") 14 | 15 | isEmpty: => 16 | not @has("stats") 17 | 18 | validate: (attrs) => 19 | if attrs.allocated_stat_points < 0 or attrs.allocated_stat_points > attrs.max_stat_points 20 | "would go over" 21 | -------------------------------------------------------------------------------- /yeoman-workflow/app/scripts/models/unit_stat.coffee: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | class yeomanWorkflow.Models.UnitStatModel extends Backbone.Model 4 | -------------------------------------------------------------------------------- /yeoman-workflow/app/scripts/routes/router.coffee: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | class yeomanWorkflow.Routers.Router extends Backbone.Router 4 | initialize: (options) => 5 | @loadout = options.loadout 6 | @loadout.on("change", @storeHash) 7 | 8 | routes: 9 | ":encoded" : "loadLoadout" 10 | 11 | loadLoadout: (encoded) -> 12 | @loadout.reset(@loadout.deserialize(encoded)) 13 | Backbone.trigger("loaded:from:hash") 14 | 15 | storeHash: => 16 | @navigate(@loadout.serialize(), {replace: true}) 17 | -------------------------------------------------------------------------------- /yeoman-workflow/app/scripts/templates/loadout.hbs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /yeoman-workflow/app/scripts/templates/loadout_slot.hbs: -------------------------------------------------------------------------------- 1 |
      {{ name }}
      2 |
      3 | Click a unit below 4 |
      5 | × 6 | 13 | 14 | 15 |
      16 | 0/11 17 |
      18 | -------------------------------------------------------------------------------- /yeoman-workflow/app/scripts/templates/stat_control.hbs: -------------------------------------------------------------------------------- 1 | {{ current }}/{{ max }} 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /yeoman-workflow/app/scripts/templates/stat_editor.hbs: -------------------------------------------------------------------------------- 1 |
      2 |
      3 |
      {{ name }} - (rank {{ rank }})
      4 |
      5 | Done Editing 6 | Reset to Minimums 7 | 8 | / 9 | 10 | 11 | Rank 12 | 123 13 | 14 |
      15 |
      16 | 17 |
      18 |
        19 |
        20 | -------------------------------------------------------------------------------- /yeoman-workflow/app/scripts/templates/unit_grouping.hbs: -------------------------------------------------------------------------------- 1 |
        {{ type }}
        2 | 3 | -------------------------------------------------------------------------------- /yeoman-workflow/app/scripts/templates/unit_selector.hbs: -------------------------------------------------------------------------------- 1 |
        {{ name }}
        2 |
        3 | -------------------------------------------------------------------------------- /yeoman-workflow/app/scripts/views/character_selector.coffee: -------------------------------------------------------------------------------- 1 | class window.yeomanWorkflow.Views.CharacterSelectorView extends Backbone.View 2 | 3 | initialize: => 4 | Backbone.on("edit:unit", @hide) 5 | Backbone.on("choose:unit", @show) 6 | 7 | render: => 8 | _(@collection).each (units, type) => 9 | @$("#unit-groupings").append(new window.yeomanWorkflow.Views.UnitGroupingView( 10 | units: units 11 | type: type 12 | ).render().el) 13 | 14 | hide: => 15 | @$el.hide() 16 | 17 | show: => 18 | @$el.show() 19 | 20 | -------------------------------------------------------------------------------- /yeoman-workflow/app/scripts/views/loadout.coffee: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | class yeomanWorkflow.Views.LoadoutView extends Backbone.View 4 | events: 5 | "click .character" : "editUnit" 6 | 7 | initialize: => 8 | Backbone.on("loaded:from:hash", @render) 9 | 10 | editUnit: (e) => 11 | unit = @fetchUnitFromCollectionViaSlotDataAttribute(e) 12 | if unit.isChosen() 13 | Backbone.trigger("edit:unit", unit) 14 | else 15 | Backbone.trigger("choose:unit") 16 | 17 | fetchUnitFromCollectionViaSlotDataAttribute: (element) => 18 | @collection.at($(element.currentTarget).data("slot")) 19 | 20 | render: => 21 | @$("#selected-characters").empty() 22 | @collection.each (unit, i) => 23 | @$("#selected-characters").append(new window.yeomanWorkflow.Views.LoadoutSlotView( 24 | model: unit 25 | slot: i 26 | ).render().el) 27 | @ 28 | 29 | -------------------------------------------------------------------------------- /yeoman-workflow/app/scripts/views/loadout_slot.coffee: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | class yeomanWorkflow.Views.LoadoutSlotView extends Backbone.Fixins.SuperView 4 | tagName: "li" 5 | 6 | events: 7 | "mouseover" : "showOverlay" 8 | "mouseout" : "hideOverlay" 9 | "click .remove" : "removeFromLoadout" 10 | "click .move-left" : "moveLeft" 11 | "click .move-right" : "moveRight" 12 | 13 | removeFromLoadout: (e) -> 14 | if confirm("Remove #{@model.get('name')} from slot #{@slot + 1}?") 15 | @model.clear() 16 | @model.set(new window.yeomanWorkflow.Models.UnitModel().attributes) 17 | Backbone.trigger("choose:unit") 18 | else 19 | e.preventDefault() 20 | e.stopPropagation() 21 | 22 | attributes: => 23 | "class" : "character #{@model.get('name')}" 24 | "data-slot" : @slot 25 | 26 | showOverlay: => 27 | if @model.isChosen() then @$(".remove").show() 28 | if @model.isChosen() 29 | @$(".move-left, .move-right").show() 30 | 31 | if @position() is 0 then @$(".move-left").hide() 32 | if @position() is 5 then @$(".move-right").hide() 33 | 34 | moveLeft: (e) => 35 | e.stopPropagation() 36 | @move(@position(-1)) 37 | 38 | moveRight: (e) => 39 | e.stopPropagation() 40 | @move(@position(1)) 41 | 42 | move: (new_position_index) => 43 | original_attrs = _(@model.attributes).clone() 44 | new_attrs = _(@model.collection.at(new_position_index).attributes).clone() 45 | other_model = @model.collection.at(new_position_index) 46 | @model.clear() 47 | @model.set(new_attrs) 48 | other_model.clear() 49 | other_model.set(original_attrs) 50 | Backbone.trigger("loadout:reorganized", other_model) 51 | 52 | position: (offset=0)=> 53 | @model.collection.indexOf(@model) + offset 54 | 55 | hideOverlay: => 56 | @$(".remove, .move-left, .move-right").hide() 57 | 58 | initialize: (options) -> 59 | @slot = options.slot 60 | @model.on("change", @render, @) 61 | @model.on("change:rank", @updateMaxStatPoints) 62 | 63 | renderAttributes: => 64 | attrs = @attributes() 65 | @$el.attr("class", attrs.class) 66 | @$el.attr("data-slot", attrs['data-slot']) 67 | 68 | renderStatsOverlay: => 69 | if stats = @model.get('stats') 70 | stats.each (stat) => 71 | @$(".stats-overlay .#{stat.get('stat')}").text(stat.get('current')) 72 | 73 | renderAllocatedStatsOverlay: => 74 | @$(".allocated-max-stats .allocated").text(@model.get("allocated_stat_points")) 75 | @$(".allocated-max-stats .max").text(@model.get("max_stat_points")) 76 | 77 | renderHelpText: => 78 | if @model.isChosen() 79 | @$(".help-text").text("") 80 | else 81 | @$(".help-text").text("Click a unit below") 82 | 83 | updateMaxStatPoints: (__, new_rank) => 84 | @model.set("max_stat_points", BattlePlanner.maxStatPointsForRank(new_rank)) 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /yeoman-workflow/app/scripts/views/stat_control.coffee: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | class yeomanWorkflow.Views.StatControlView extends Backbone.Fixins.SuperView 4 | tagName: "li" 5 | 6 | events: 7 | "mousedown .icon" : "increaseOrDecreaseStat" 8 | "contextmenu .icon" : -> false 9 | 10 | initialize: (options) -> 11 | @unit = options.unit 12 | @model.on("change:current", @update) 13 | 14 | attributes: => 15 | class: @model.get("stat") 16 | 17 | increaseOrDecreaseStat: (e) => 18 | e.preventDefault() 19 | e.stopPropagation() 20 | switch e.which 21 | when 1 then @changeBy(1) #left click 22 | when 3 then @changeBy(-1) #right click 23 | 24 | changeBy: (amount) => 25 | # if the amount does not exceed the units max_stat_points 26 | # if the amount does not exceed the stats maximum 27 | if !(@unit.get('allocated_stat_points') + amount > @unit.get('max_stat_points') or @unit.get('allocated_stat_points') + amount < 0) 28 | if !(@model.get('current') + amount < @model.get('min') or @model.get('current') + amount > @model.get('max')) 29 | @model.set(current: @model.get("current") + amount) 30 | @unit.set(allocated_stat_points: @unit.get('allocated_stat_points') + amount) 31 | 32 | update: => 33 | @$(".current").text(@model.get("current")) 34 | 35 | -------------------------------------------------------------------------------- /yeoman-workflow/app/scripts/views/stat_editor.coffee: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | class yeomanWorkflow.Views.StatEditorView extends Backbone.Fixins.SuperView 4 | events: 5 | "click .reset" : "resetToMinimums" 6 | "click .done" : -> Backbone.trigger("choose:unit") 7 | "click .change-rank" : "setRank" 8 | 9 | initialize: => 10 | Backbone.on("edit:unit", @show) 11 | Backbone.on("choose:unit", @hide) 12 | Backbone.on("loadout:reorganized", @show) 13 | 14 | renderVisible: => 15 | if @model.isChosen() then @$el.show() else @$el.hide() 16 | 17 | renderStatControls: => 18 | unit_stats = @model.get('stats') 19 | unit_stats.each (stat, i) => 20 | @$(".stats").append(new window.yeomanWorkflow.Views.StatControlView(unit: @model, model: stat).render().el) 21 | 22 | resetToMinimums: => 23 | @model.set("allocated_stat_points", 0) 24 | stats = @model.get('stats') 25 | stats.each (stat) => 26 | stat.set('current', stat.get('min')) 27 | @model.trigger('change') 28 | 29 | renderTotals: => 30 | @$(".total .allocated").text(@model.get("allocated_stat_points")) 31 | @$(".total .max").text(@model.get("max_stat_points")) 32 | 33 | renderRankChanger: => 34 | @$("#rank-changer").prop("class", "rank#{@model.get("rank")}") 35 | 36 | renderRankInEditorTitle: => 37 | @$(".title .rank").text(@model.get("rank")) 38 | 39 | setRank: (e) => 40 | rank = parseInt($(e.currentTarget).text(), 10) 41 | oldrank = @model.get("rank") 42 | @model.set({rank}) 43 | if rank < oldrank 44 | @resetToMinimums() 45 | 46 | hide: => 47 | @$el.hide() 48 | 49 | show: (unit) => 50 | @model = unit 51 | # had to clone stats here, as editing the same class was buggy 52 | @model.set("stats", new window.yeomanWorkflow.Models.UnitStatModel(@model.get('stats').toJSON())) 53 | @model.on("change:allocated_stat_points", @renderTotals) 54 | @model.on("change:rank", @renderRankChanger) 55 | @model.on("change:rank", @renderRankInEditorTitle) 56 | @model.on("change:rank", @renderTotals) 57 | @render() 58 | @$el.show() 59 | -------------------------------------------------------------------------------- /yeoman-workflow/app/scripts/views/unit_grouping.coffee: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | class yeomanWorkflow.Views.UnitGroupingView extends Backbone.Fixins.SuperView 4 | attributes: 5 | class: "unit-type" 6 | 7 | initialize: (options) -> 8 | @type = options.type 9 | @units = options.units 10 | 11 | renderTitle: => 12 | @$(".title").text(@type) 13 | 14 | renderUnits: => 15 | _(@units).each (unit) => 16 | @$(".classes").append(new window.yeomanWorkflow.Views.UnitSelectorView( 17 | model: unit 18 | ).render().el) 19 | -------------------------------------------------------------------------------- /yeoman-workflow/app/scripts/views/unit_selector.coffee: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | class yeomanWorkflow.Views.UnitSelectorView extends Backbone.Fixins.SuperView 4 | tagName: "li" 5 | 6 | attributes: => 7 | class: "character #{@model.get('name')}" 8 | 9 | events: 10 | "click" : "assignUnitToNextAvailableLoadoutSlot" 11 | 12 | assignUnitToNextAvailableLoadoutSlot: => 13 | # get the next active loadout slot 14 | loadout_unit = BattlePlanner.loadout_units.find (unit) => unit.isEmpty() 15 | if not loadout_unit then loadout_unit = BattlePlanner.loadout_units.at(5) 16 | loadout_unit.clear() 17 | loadout_unit.set(@model.clone().attributes) 18 | -------------------------------------------------------------------------------- /yeoman-workflow/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yeoman-workflow", 3 | "version": "0.0.0", 4 | "dependencies": { 5 | "jquery": "~1.9.0", 6 | "underscore": "~1.4.3", 7 | "backbone": "~1.0.0", 8 | "modernizr": "~2.6.2", 9 | "handlebars": "~1.0.0", 10 | "backbone-fixins.js": "https://raw.github.com/testdouble/backbone-fixins/master/dist/backbone-fixins.js", 11 | "base64js": "~1.0.0" 12 | }, 13 | "devDependencies": {} 14 | } 15 | -------------------------------------------------------------------------------- /yeoman-workflow/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yeoman-workflow", 3 | "version": "0.0.0", 4 | "dependencies": {}, 5 | "devDependencies": { 6 | "grunt": "~0.4.1", 7 | "grunt-contrib-copy": "~0.4.0", 8 | "grunt-contrib-concat": "~0.3.0", 9 | "grunt-contrib-coffee": "~0.7.0", 10 | "grunt-contrib-handlebars": "~0.5.8", 11 | "grunt-contrib-uglify": "~0.2.0", 12 | "grunt-contrib-compass": "~0.5.0", 13 | "grunt-contrib-jshint": "~0.6.3", 14 | "grunt-contrib-cssmin": "~0.6.0", 15 | "grunt-contrib-connect": "~0.3.0", 16 | "grunt-contrib-clean": "~0.5.0", 17 | "grunt-contrib-htmlmin": "~0.1.3", 18 | "grunt-contrib-imagemin": "~0.2.0", 19 | "grunt-contrib-watch": "~0.5.2", 20 | "grunt-mocha": "~0.4.1", 21 | "grunt-bower-requirejs": "~0.7.0", 22 | "grunt-usemin": "~0.1.10", 23 | "grunt-rev": "~0.1.0", 24 | "grunt-open": "~0.2.0", 25 | "load-grunt-tasks": "~0.1.0", 26 | "connect-livereload": "~0.2.0", 27 | "time-grunt": "~0.1.1" 28 | }, 29 | "engines": { 30 | "node": ">=0.8.0" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /yeoman-workflow/test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Mocha Spec Runner 6 | 7 | 8 | 9 |
        10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /yeoman-workflow/test/lib/expect.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * chai 3 | * Copyright(c) 2011-2012 Jake Luer 4 | * MIT Licensed 5 | */ 6 | 7 | module.exports = function (chai, util) { 8 | chai.expect = function (val, message) { 9 | return new chai.Assertion(val, message); 10 | }; 11 | }; 12 | 13 | -------------------------------------------------------------------------------- /yeoman-workflow/test/lib/mocha/mocha.css: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | 3 | body { 4 | font: 20px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif; 5 | padding: 60px 50px; 6 | } 7 | 8 | #mocha ul, #mocha li { 9 | margin: 0; 10 | padding: 0; 11 | } 12 | 13 | #mocha ul { 14 | list-style: none; 15 | } 16 | 17 | #mocha h1, #mocha h2 { 18 | margin: 0; 19 | } 20 | 21 | #mocha h1 { 22 | margin-top: 15px; 23 | font-size: 1em; 24 | font-weight: 200; 25 | } 26 | 27 | #mocha h1 a { 28 | text-decoration: none; 29 | color: inherit; 30 | } 31 | 32 | #mocha h1 a:hover { 33 | text-decoration: underline; 34 | } 35 | 36 | #mocha .suite .suite h1 { 37 | margin-top: 0; 38 | font-size: .8em; 39 | } 40 | 41 | .hidden { 42 | display: none; 43 | } 44 | 45 | #mocha h2 { 46 | font-size: 12px; 47 | font-weight: normal; 48 | cursor: pointer; 49 | } 50 | 51 | #mocha .suite { 52 | margin-left: 15px; 53 | } 54 | 55 | #mocha .test { 56 | margin-left: 15px; 57 | overflow: hidden; 58 | } 59 | 60 | #mocha .test.pending:hover h2::after { 61 | content: '(pending)'; 62 | font-family: arial; 63 | } 64 | 65 | #mocha .test.pass.medium .duration { 66 | background: #C09853; 67 | } 68 | 69 | #mocha .test.pass.slow .duration { 70 | background: #B94A48; 71 | } 72 | 73 | #mocha .test.pass::before { 74 | content: '✓'; 75 | font-size: 12px; 76 | display: block; 77 | float: left; 78 | margin-right: 5px; 79 | color: #00d6b2; 80 | } 81 | 82 | #mocha .test.pass .duration { 83 | font-size: 9px; 84 | margin-left: 5px; 85 | padding: 2px 5px; 86 | color: white; 87 | -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 88 | -moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 89 | box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 90 | -webkit-border-radius: 5px; 91 | -moz-border-radius: 5px; 92 | -ms-border-radius: 5px; 93 | -o-border-radius: 5px; 94 | border-radius: 5px; 95 | } 96 | 97 | #mocha .test.pass.fast .duration { 98 | display: none; 99 | } 100 | 101 | #mocha .test.pending { 102 | color: #0b97c4; 103 | } 104 | 105 | #mocha .test.pending::before { 106 | content: '◦'; 107 | color: #0b97c4; 108 | } 109 | 110 | #mocha .test.fail { 111 | color: #c00; 112 | } 113 | 114 | #mocha .test.fail pre { 115 | color: black; 116 | } 117 | 118 | #mocha .test.fail::before { 119 | content: '✖'; 120 | font-size: 12px; 121 | display: block; 122 | float: left; 123 | margin-right: 5px; 124 | color: #c00; 125 | } 126 | 127 | #mocha .test pre.error { 128 | color: #c00; 129 | max-height: 300px; 130 | overflow: auto; 131 | } 132 | 133 | #mocha .test pre { 134 | display: block; 135 | float: left; 136 | clear: left; 137 | font: 12px/1.5 monaco, monospace; 138 | margin: 5px; 139 | padding: 15px; 140 | border: 1px solid #eee; 141 | border-bottom-color: #ddd; 142 | -webkit-border-radius: 3px; 143 | -webkit-box-shadow: 0 1px 3px #eee; 144 | -moz-border-radius: 3px; 145 | -moz-box-shadow: 0 1px 3px #eee; 146 | } 147 | 148 | #mocha .test h2 { 149 | position: relative; 150 | } 151 | 152 | #mocha .test a.replay { 153 | position: absolute; 154 | top: 3px; 155 | right: 0; 156 | text-decoration: none; 157 | vertical-align: middle; 158 | display: block; 159 | width: 15px; 160 | height: 15px; 161 | line-height: 15px; 162 | text-align: center; 163 | background: #eee; 164 | font-size: 15px; 165 | -moz-border-radius: 15px; 166 | border-radius: 15px; 167 | -webkit-transition: opacity 200ms; 168 | -moz-transition: opacity 200ms; 169 | transition: opacity 200ms; 170 | opacity: 0.3; 171 | color: #888; 172 | } 173 | 174 | #mocha .test:hover a.replay { 175 | opacity: 1; 176 | } 177 | 178 | #mocha-report.pass .test.fail { 179 | display: none; 180 | } 181 | 182 | #mocha-report.fail .test.pass { 183 | display: none; 184 | } 185 | 186 | #mocha-error { 187 | color: #c00; 188 | font-size: 1.5 em; 189 | font-weight: 100; 190 | letter-spacing: 1px; 191 | } 192 | 193 | #mocha-stats { 194 | position: fixed; 195 | top: 15px; 196 | right: 10px; 197 | font-size: 12px; 198 | margin: 0; 199 | color: #888; 200 | } 201 | 202 | #mocha-stats .progress { 203 | float: right; 204 | padding-top: 0; 205 | } 206 | 207 | #mocha-stats em { 208 | color: black; 209 | } 210 | 211 | #mocha-stats a { 212 | text-decoration: none; 213 | color: inherit; 214 | } 215 | 216 | #mocha-stats a:hover { 217 | border-bottom: 1px solid #eee; 218 | } 219 | 220 | #mocha-stats li { 221 | display: inline-block; 222 | margin: 0 5px; 223 | list-style: none; 224 | padding-top: 11px; 225 | } 226 | 227 | code .comment { color: #ddd } 228 | code .init { color: #2F6FAD } 229 | code .string { color: #5890AD } 230 | code .keyword { color: #8A6343 } 231 | code .number { color: #2F6FAD } 232 | -------------------------------------------------------------------------------- /yeoman-workflow/test/spec/test.js: -------------------------------------------------------------------------------- 1 | /*global describe, it */ 2 | 'use strict'; 3 | (function () { 4 | describe('Give it some context', function () { 5 | describe('maybe a bit more context here', function () { 6 | it('should run here few assertions', function () { 7 | 8 | }); 9 | }); 10 | }); 11 | })(); 12 | --------------------------------------------------------------------------------