├── .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 |
26 |
27 |
32 |
33 |
34 |
35 |
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 |
7 | -
8 | -
9 | -
10 | -
11 | -
12 |
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 |
22 |
23 |
28 |
29 |
30 |
31 |
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 |
7 | -
8 | -
9 | -
10 | -
11 | -
12 |
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-2 O`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�-2 5r�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��&��)��p 4���&��-���_������������������]���]�������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�&