├── css
├── Shadowrun Bold.ttf
├── Shadowrun Italic.ttf
├── Shadowrun Regular.ttf
├── fonts
│ ├── shadowrun_bold.ttf
│ ├── shadowrun_italic.ttf
│ └── shadowrun_regular.ttf
├── images
│ ├── ui-icons_000000_256x240.png
│ ├── ui-icons_00498f_256x240.png
│ ├── ui-icons_1f1f1f_256x240.png
│ ├── ui-icons_75abff_256x240.png
│ ├── ui-icons_9ccdfc_256x240.png
│ ├── ui-icons_ffffff_256x240.png
│ ├── ui-bg_hexagon_30_0b58a2_12x10.png
│ ├── ui-bg_hexagon_30_a32d00_12x10.png
│ ├── ui-bg_inset-soft_40_00498f_1x100.png
│ ├── ui-bg_diagonals-small_40_0a0a0a_40x40.png
│ ├── ui-bg_diagonals-small_50_262626_40x40.png
│ └── ui-bg_diagonals-small_60_000000_40x40.png
├── sr_tools.css
└── jquery-ui.css
├── LICENSE
├── js
├── sr_tools_roll.js
├── sr_tools_run.js
├── sr_tools_storage.js
├── sr_tools_gen.js
└── sr_tools.js
├── README.md
└── index.html
/css/Shadowrun Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toktic/sr_gmt/HEAD/css/Shadowrun Bold.ttf
--------------------------------------------------------------------------------
/css/Shadowrun Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toktic/sr_gmt/HEAD/css/Shadowrun Italic.ttf
--------------------------------------------------------------------------------
/css/Shadowrun Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toktic/sr_gmt/HEAD/css/Shadowrun Regular.ttf
--------------------------------------------------------------------------------
/css/fonts/shadowrun_bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toktic/sr_gmt/HEAD/css/fonts/shadowrun_bold.ttf
--------------------------------------------------------------------------------
/css/fonts/shadowrun_italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toktic/sr_gmt/HEAD/css/fonts/shadowrun_italic.ttf
--------------------------------------------------------------------------------
/css/fonts/shadowrun_regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toktic/sr_gmt/HEAD/css/fonts/shadowrun_regular.ttf
--------------------------------------------------------------------------------
/css/images/ui-icons_000000_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toktic/sr_gmt/HEAD/css/images/ui-icons_000000_256x240.png
--------------------------------------------------------------------------------
/css/images/ui-icons_00498f_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toktic/sr_gmt/HEAD/css/images/ui-icons_00498f_256x240.png
--------------------------------------------------------------------------------
/css/images/ui-icons_1f1f1f_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toktic/sr_gmt/HEAD/css/images/ui-icons_1f1f1f_256x240.png
--------------------------------------------------------------------------------
/css/images/ui-icons_75abff_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toktic/sr_gmt/HEAD/css/images/ui-icons_75abff_256x240.png
--------------------------------------------------------------------------------
/css/images/ui-icons_9ccdfc_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toktic/sr_gmt/HEAD/css/images/ui-icons_9ccdfc_256x240.png
--------------------------------------------------------------------------------
/css/images/ui-icons_ffffff_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toktic/sr_gmt/HEAD/css/images/ui-icons_ffffff_256x240.png
--------------------------------------------------------------------------------
/css/images/ui-bg_hexagon_30_0b58a2_12x10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toktic/sr_gmt/HEAD/css/images/ui-bg_hexagon_30_0b58a2_12x10.png
--------------------------------------------------------------------------------
/css/images/ui-bg_hexagon_30_a32d00_12x10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toktic/sr_gmt/HEAD/css/images/ui-bg_hexagon_30_a32d00_12x10.png
--------------------------------------------------------------------------------
/css/images/ui-bg_inset-soft_40_00498f_1x100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toktic/sr_gmt/HEAD/css/images/ui-bg_inset-soft_40_00498f_1x100.png
--------------------------------------------------------------------------------
/css/images/ui-bg_diagonals-small_40_0a0a0a_40x40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toktic/sr_gmt/HEAD/css/images/ui-bg_diagonals-small_40_0a0a0a_40x40.png
--------------------------------------------------------------------------------
/css/images/ui-bg_diagonals-small_50_262626_40x40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toktic/sr_gmt/HEAD/css/images/ui-bg_diagonals-small_50_262626_40x40.png
--------------------------------------------------------------------------------
/css/images/ui-bg_diagonals-small_60_000000_40x40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toktic/sr_gmt/HEAD/css/images/ui-bg_diagonals-small_60_000000_40x40.png
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 toktic
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/js/sr_tools_roll.js:
--------------------------------------------------------------------------------
1 | var roll = {
2 | dval: function(dice)
3 | {
4 | return Math.floor(Math.random() * dice + 1);
5 | },
6 |
7 | d: function(count, options)
8 | {
9 | if (options === undefined)
10 | {
11 | options = {};
12 | }
13 |
14 | options = $.extend({}, {
15 | pre_edge: false
16 | }, options);
17 |
18 | var hits = 0, i, exploding_count = count, v, e, res = {
19 | glitch: false,
20 | crit_glitch: false
21 | };
22 |
23 | var rolls = [];
24 |
25 | for (i = count; i > 0; i--)
26 | {
27 | v = this.dval(6);
28 | rolls.push(v);
29 | hits += (v >= 5) ? 1 : 0;
30 | if (v === 6 && options.pre_edge)
31 | {
32 | e = this.d(1, {pre_edge: true});
33 | exploding_count++;
34 | hits += e.hits;
35 | rolls = rolls.concat(e.rolls);
36 | }
37 | }
38 |
39 | res.hits = hits;
40 |
41 | res.rolls = rolls.sort(function (a, b)
42 | {
43 | return a - b;
44 | });
45 |
46 | res.misses = rolls.filter(function (i) {return i === 1}).length;
47 |
48 | if (res.misses > (exploding_count / 2))
49 | {
50 | if (hits === 0)
51 | {
52 | res.crit_glitch = true;
53 | }
54 | else
55 | {
56 | res.glitch = true;
57 | }
58 | }
59 |
60 | return res;
61 | },
62 |
63 | half: function(pool, down)
64 | {
65 | if (down)
66 | {
67 | return Math.floor(pool / 2);
68 | }
69 | return Math.ceil(pool / 2);
70 | },
71 |
72 | random_attribute: function ()
73 | {
74 | switch(this.dval(2))
75 | {
76 | case 1:
77 | return this.random_mental_attribute();
78 | default:
79 | return this.random_physical_attribute();
80 | }
81 | },
82 |
83 | random_mental_attribute: function ()
84 | {
85 | switch(this.dval(4))
86 | {
87 | case 1:
88 | return 'will';
89 | case 2:
90 | return 'logic';
91 | case 3:
92 | return 'intuition';
93 | default:
94 | return 'charisma';
95 | }
96 | },
97 |
98 | random_physical_attribute: function ()
99 | {
100 | switch(this.dval(4))
101 | {
102 | case 1:
103 | return 'body';
104 | case 2:
105 | return 'agility';
106 | case 3:
107 | return 'reaction';
108 | default:
109 | return 'strength';
110 | }
111 | }
112 | };
113 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Welcome to my Shadowrun GM Mob Master!
2 |
3 | The GM Mob Master was created as a set of tools for Shadowrun game masters. It started as a way of generating extra NPCs for combat-heavy runs, and as a thought exercise in how to expand the list of grunts in the Core Rulebook to more situations. The intent was to include generators and lists that would make it easier for game masters to get mechanics out of the way and spend more time telling stories. My hope was that this will eventually make it easy for game masters to generate an entire session of entertainment with the click of a few buttons.
4 |
5 | It is divided into several sections, all of which can be accessed by the buttons on the left. There is also a dice-simulator at the top to let you quickly simulate a fist-full of dice. Remember, as a GM you don't have to be honest with how many successes or failures you "rolled"...
6 |
7 | - The first section is **Cast of Shadows**. This holds the full assortment of npcs that you create and save for use later. The "Full Cast" lists all saved cast members. You can create plenty of custom tabs and list only the cast members you need for a scene on that tab.
8 | - Next up is the **NPC Generator**. You can use the tabs within to create a single NPC or a horde of them. Just be aware that if you create one and don't save them to the **Cast of Shadows**, they will disappear and be lost.
9 | - **Settings** has a selection of, you guessed it, Settings that you can change. This also has a Nuke button if you want to delete all of your saved information and start over from scratch.
10 |
11 | This has also served as a creative outlet for programming for my own enjoyment. I have spent quite a few hours on this utility in the name of fun. There was never any intention of turning this into a money-generating enterprise. I don't want to deal with the legal or infrastructure headaches. Though if you happen to find me at a convention and want to say thanks, you can buy me a beer at GenCon. If you want to complain or sue me, I don't exist and don't attend GenCon. :D
12 |
13 | On data-retention: I use a piece of web programming functionality called Local Storage that act a little bit like cookies. The data is stored in your browser's cache for you for that browser. I have to specify both of those because it's not shared to other users on your computer, nor even other browsers. It certainly isn't shared to other computers, and I don't have access to it at all. So if you spend the time to create and save a cast of shadow denizens, make sure you create it on the computer you're bringing to the game session. Or print them out, that works too!
14 |
15 | As with many side projects, there is a whole list of things I want to add to this utility. There are plenty of features that I haven't had the time to add yet. Since I'm not getting paid to work on this, it only happens when I do have spare time that isn't being spent on other things. And there will eventually come a time that I wash my hands of this and let it sit on a shelf to die from neglect. It may not be a nice thing to say, but it's the truth. This utility isn't meant to be a replacement for the Shadowrun books, either. I make an assumption that users are familiar with the basics of Shadowrun, from what an Adept is to what constitutes a Mr. Johnson, and are able to look things up as needed, like specific equipment information. It is meant as a supplement and assistant, much like how HeroLab and Chummer make PC creation easier for runners. I will not try to make this fully comprehensive and cover every possible option. There are too many source books to keep up with and this isn't meant to be a replacement for sourcebooks.
16 |
17 | Have fun with this tool! It is meant to be used and intended to make your life easier as a GM. Speaking of which, always try to offer your players the chance to make a deal with a dragon.
18 |
19 | _The Topps Company, Inc. has sole ownership of the names, logo, artwork, marks, photographs, sounds, audio, video and/or any proprietary matrial used in connection with the game Shadowrun. The Topps Company, Inc. has granted permission to GM Mob Master to use such names, logos, artwork, marks and/or any proprietary materials for promotional and informational purposes on its website but does not endorse, and is not affiliated with this utility in any official capacity whatsoever. Original content within GM Mob Master is licensed under the Creative Commons Attribution-NonCommercial 4.0 International. So if you use anything here, please give credit where it is due. GM Mob Master is provided "as is" and no warranties or guarantees are provided or inferred. In no event shall the copyright holder or contributors be liable for damage however they are caused. Don't be a jerk!_
20 |
--------------------------------------------------------------------------------
/js/sr_tools_run.js:
--------------------------------------------------------------------------------
1 | var run = {
2 | _employer_type: [
3 | 'Secret Society',
4 | 'Political or Activist Group',
5 | 'Government',
6 | 'Minor Corporation',
7 | 'A Corporation',
8 | 'AA Corporation',
9 | 'AAA Corporation',
10 | 'Criminal Syndicate',
11 | 'Magical Group',
12 | 'Private Individual',
13 | 'Exotic'
14 | ],
15 |
16 | _employer_sub_type: {
17 | 'Secret Society': [
18 | 'Black Lodge',
19 | 'Human Nation'
20 | ],
21 | 'Political or Activist Group': [
22 | 'Humanis Policlub',
23 | 'Mothers of Metahumans',
24 | 'Sons of Sauron'
25 | ],
26 | 'Government': [
27 | 'Govenor',
28 | 'Senator',
29 | 'Local Department'
30 | ],
31 | 'Minor Corporation': [],
32 | 'A Corporation': [],
33 | 'AA Corporation': [],
34 | 'AAA Corporation': [
35 | 'Ares Macrotechnology',
36 | 'Aztechnology',
37 | 'EVO Corporation',
38 | 'Horizon Group',
39 | 'Mitsuhama Computer Technologies',
40 | 'NeoNET',
41 | 'Renraku Computer Systems',
42 | 'Saeder-Krupp Heavy Industries',
43 | 'Shiawase Corporation',
44 | 'Wuxing Incorporated'
45 | ],
46 | 'Criminal Syndicate': [
47 | 'Koshari',
48 | 'Mafia',
49 | 'Triad',
50 | 'Yakuza',
51 | 'Vory'
52 | ],
53 | 'Magical Group': [
54 | 'Illuminates of the New Dawn',
55 | 'Draco Foundation'
56 | ],
57 | 'Private Individual': [],
58 | 'Exotic': [
59 | 'AI',
60 | 'Dragon',
61 | 'Free Spirit'
62 | ]
63 | },
64 |
65 | _run_type:['Datasteal', 'Assassination', 'Destruction', 'Extraction', 'Insertion', 'Misdirection', 'Protection', 'Delivery'],
66 |
67 | _run_sub_type: {
68 | Datasteal: [
69 | 'Prototype Object',
70 | 'Research',
71 | 'Hidden Records'
72 | ],
73 |
74 | Assassination: [
75 | 'Politician',
76 | 'Corporate Management',
77 | 'Whistleblower'
78 | ],
79 |
80 | Destruction: [
81 | 'Vehicle',
82 | 'Public Infrastructure',
83 | 'Private Residence',
84 | 'Corporate Property'
85 | ],
86 | Extraction: [
87 | 'Scientist',
88 | 'Mole',
89 | 'Hostage',
90 | 'Artist',
91 | 'Test Subject'
92 | ],
93 | Insertion: [
94 | 'Mole'
95 | ],
96 | Misdirection: [
97 | 'Draw Security',
98 | 'Plant Evidence',
99 | 'Create Illusion'
100 | ],
101 | Protection: [
102 | 'Rescue Hostage',
103 | 'Prevent Extraction',
104 | 'Prevent Destruction'
105 | ],
106 | Delivery: [
107 | 'Creature', // Lab creature, pet of someone important, pet that ate data
108 | 'Equipment' // Something magical, something toxic
109 | ]
110 | },
111 |
112 | _objectives: [
113 | {
114 | run_type: 'Extraction',
115 | run_sub_type: 'Mole',
116 | title: 'Rescue mole before their discovery', // Print-ready title of some type
117 | motivation: '', // Why is the employer doing this? Print-ready
118 | target: {
119 |
120 | // Maybe flag for needing to generate a company?
121 | // Or generate special NPCs for it?
122 | },
123 | pay: {
124 | base: 1000,
125 | increment: 500
126 | },
127 | karma: 2,
128 | street_cred: 0,
129 | noteriety: 0,
130 | timeline: '2 days'
131 | },
132 | {
133 | run_type: 'Datasteal',
134 | title: 'Steal prototype commlink model', // Print-ready title of some type
135 | motivation: "Employer wants early access to the commlink's physical dimensions in order to start making accessories before any competition",
136 | target: {
137 |
138 | // Maybe flag for needing to generate a company?
139 | // Or generate special NPCs for it?
140 | },
141 | pay: {
142 | base: 2000,
143 | increment: 100
144 | },
145 | karma: 2,
146 | street_cred: 0,
147 | noteriety: 0,
148 | timeline: '72 hours'
149 | }
150 | ],
151 |
152 | _twists: [
153 | {
154 | run_type: 'Delivery', // If present or set, limits where the twist can be
155 | run_sub_type: 'Creature', // If present or set, limits where the twist can be
156 | is_main: false, // If true, this means it can be a required twist
157 | is_push: false, // If true, this means it can be included when Pushing the Envelope
158 | title: '', // Print-ready, what is going on
159 | notes: '', // Notes to the GM on what is going on
160 | is_separate_scene: false, // If true, will be included as it's own scene in the write-up
161 | pay: { // Only really included in the mission pay when this is a required twist, it is ignored when an optional twist
162 | base: 500, // Add this to the base pay
163 | increment: 25
164 | }
165 | },
166 | {
167 | is_push: true, // If true, this means it can be included when Pushing the Envelope
168 | title: 'Troll Partakes Kamikaze', // Print-ready, what is going on
169 | notes: 'As the party is travelling, they encounter a Troll ganger who has gotten their first taste of the drug Kamikaze. In a drug-fueled haze, the Troll decides to take on the PCs in combat.',
170 | is_separate_scene: true,
171 | npcs: [
172 | {
173 | name: 'TPK Troll',
174 | race: 'Troll',
175 | professional_type: 'ganger',
176 | notes: "This troll has just taken a dose of Kamikaze and has decided to forcefully eject the PCs from the gang's turf, or maybe just kill them outright. Negotiations are not likely to happen."
177 | }
178 | ]
179 | }
180 | ],
181 |
182 | get_run_type: function ()
183 | {
184 | var i = roll.dval(this._run_type.length) - 1;
185 |
186 | return this._run_type[i];
187 | }
188 | };
189 |
--------------------------------------------------------------------------------
/js/sr_tools_storage.js:
--------------------------------------------------------------------------------
1 | var storage = {
2 | initialize_storage: function()
3 | {
4 | // Set up any expected things in localStorage
5 | localStorage.build_id = build_id;
6 |
7 | // // Cast of Shadows
8 | // What tab ID did we last create?
9 | localStorage.cast_tab_id = 1;
10 |
11 | // What character ID did we last create?
12 | localStorage.cast_character_id = 0;
13 |
14 | // What tab did we show last?
15 | // Start with the management tab, so new users see it at least once
16 | localStorage.cast_current_tab = 0;
17 |
18 | // What tabs are there?
19 | localStorage.cast_tabs = JSON.stringify([
20 | {
21 | tab_id: 1,
22 | name: 'Full Cast',
23 | order: 1,
24 | characters: [] // This is an array of {character_id, order} objects
25 | }
26 | ]);
27 |
28 | // Save the characters
29 | localStorage.cast_characters = JSON.stringify([]);
30 |
31 | // Save a character template
32 | localStorage.cast_character_template = JSON.stringify({
33 | character_id: null,
34 | type: '',
35 | data: null
36 | });
37 |
38 | // Settings
39 | localStorage.setting_condition_monitor = 'combined';
40 | localStorage.setting_wound_penalty = '3';
41 | },
42 |
43 | // Return an array of tabs with their name, tab ID, and display ordering
44 | get_cast_tabs: function()
45 | {
46 | var stored_tabs = $.parseJSON(localStorage.cast_tabs);
47 |
48 | stored_tabs.forEach(function(tab)
49 | {
50 | tab.href = tab.name.replace(/( )/g, '_').replace(/\W/g, '');
51 | });
52 |
53 | stored_tabs.sort(function (a, b)
54 | {
55 | return a.order - b.order;
56 | });
57 |
58 | return stored_tabs;
59 | },
60 |
61 | // Return the currently displayed tab ID
62 | get_current_cast_tab: function()
63 | {
64 | return parseInt(localStorage.cast_current_tab);
65 | },
66 |
67 | // Set which tab we are viewing now
68 | set_current_cast_tab: function(id)
69 | {
70 | localStorage.cast_current_tab = id;
71 | },
72 |
73 | // Get information about a tab
74 | get_cast_tab: function(tab_id)
75 | {
76 | var stored_tabs = $.parseJSON(localStorage.cast_tabs), ret = null;
77 |
78 | stored_tabs.forEach(function(tab)
79 | {
80 | tab.href = tab.name.replace(/( )/g, '_').replace(/\W/g, '');
81 | if (tab.tab_id === tab_id)
82 | ret = tab;
83 | });
84 |
85 | if (ret === null)
86 | console.log('ERROR: get_cast_tab() unable to find specified tab', tab_id);
87 |
88 | return ret;
89 | },
90 |
91 | // Update a given tab, also for adding a new tab
92 | set_cast_tab: function(tab_id, tab_data)
93 | {
94 | // If the order isn't the same as the existing tabs, update other tabs to match?
95 | var stored_tabs = $.parseJSON(localStorage.cast_tabs);
96 |
97 | var lower_order, upper_order, change_direction = false;
98 |
99 | stored_tabs.forEach(function(tab)
100 | {
101 | if (tab_id === tab.tab_id)
102 | {
103 | if (tab_data.name != null && tab_data.name !== tab.name)
104 | {
105 | tab.name = tab_data.name;
106 | }
107 |
108 | if (tab_data.hasOwnProperty('characters') && tab_data.characters.length !== tab.characters.length)
109 | tab.characters = tab_data.characters;
110 |
111 | if (Number.isInteger(tab_data.order) && tab_data.order !== tab.order)
112 | {
113 | upper_order = Math.max(tab.order, tab_data.order);
114 | lower_order = Math.min(tab.order, tab_data.order);
115 | change_direction = (tab.order > tab_data.order) ? 1 : -1;
116 | tab.order = tab_data.order;
117 | }
118 | }
119 | });
120 |
121 | if (change_direction !== false)
122 | {
123 | stored_tabs.forEach(function(tab)
124 | {
125 | if (tab_id !== tab.tab_id && tab.order >= lower_order && tab.order <= upper_order)
126 | {
127 | tab.order += change_direction;
128 | }
129 | });
130 | }
131 |
132 | localStorage.cast_tabs = JSON.stringify(stored_tabs);
133 | },
134 |
135 | // Delete a given tab from storage
136 | delete_cast_tab: function(tab_id)
137 | {
138 | if (tab_id === 1)
139 | return;
140 |
141 | var stored_tabs = $.parseJSON(localStorage.cast_tabs), new_tabs = [];
142 |
143 | stored_tabs.forEach(function(tab)
144 | {
145 | if (tab.tab_id !== tab_id)
146 | new_tabs.push(tab);
147 | });
148 |
149 | localStorage.cast_tabs = JSON.stringify(new_tabs);
150 | },
151 |
152 | generate_character_id: function()
153 | {
154 | var id = parseInt(localStorage.cast_character_id) + 1;
155 | localStorage.cast_character_id = id;
156 | return id;
157 | },
158 |
159 | generate_cast_tab_id: function()
160 | {
161 | var id = parseInt(localStorage.cast_tab_id) + 1;
162 | localStorage.cast_tab_id = id;
163 | return id;
164 | },
165 |
166 | get_characters: function()
167 | {
168 | return $.parseJSON(localStorage.cast_characters);
169 | },
170 |
171 | get_character: function(id)
172 | {
173 | var character = null, all = this.get_characters();
174 |
175 | all.forEach(function(char)
176 | {
177 | if (id === char.character_id)
178 | character = char;
179 | });
180 |
181 | return character;
182 | },
183 |
184 | set_character: function(data)
185 | {
186 | var cast_characters = $.parseJSON(localStorage.cast_characters), new_char = false;
187 | var updated_cast = [];
188 |
189 | if (!data.hasOwnProperty('character_id'))
190 | {
191 | new_char = true;
192 | data.character_id = this.generate_character_id();
193 | }
194 |
195 | if (new_char)
196 | {
197 | cast_characters.push(data);
198 | updated_cast = cast_characters;
199 | }
200 | else
201 | {
202 | cast_characters.forEach(function(char)
203 | {
204 | if (char.character_id === data.character_id)
205 | updated_cast.push(data);
206 | else
207 | updated_cast.push(char);
208 | });
209 | }
210 |
211 | localStorage.cast_characters = JSON.stringify(updated_cast);
212 |
213 | return data;
214 | },
215 |
216 | delete_character_from_tab: function(tab_id, character_id)
217 | {
218 | var tab_data = this.get_cast_tab(tab_id);
219 |
220 | tab_data.characters = tab_data.characters.filter(function(id)
221 | {
222 | return id !== character_id;
223 | });
224 |
225 | this.set_cast_tab(tab_id, tab_data);
226 | },
227 |
228 | delete_character: function(id)
229 | {
230 | var old_cast = $.parseJSON(localStorage.cast_characters), new_cast = [], i = 0;
231 |
232 | for (i; i < old_cast.length; i++)
233 | {
234 | if (id !== old_cast[i].character_id)
235 | new_cast.push(old_cast[i]);
236 | }
237 |
238 | localStorage.cast_characters = JSON.stringify(new_cast);
239 |
240 | var tabs = this.get_cast_tabs();
241 |
242 | tabs.forEach(function(tab)
243 | {
244 | storage.delete_character_from_tab(tab.tab_id, id);
245 | });
246 | },
247 |
248 | // Clone the character, optionally adding them to the same tabs as the original
249 | clone_character: function(id, clone_tabs)
250 | {
251 | var old_character = this.get_character(id), new_character, new_id;
252 |
253 | new_character = $.extend({}, old_character);
254 | delete new_character.character_id;
255 |
256 | // Change the name, either adding "Copy", or updating the copy #
257 | var copiedCharacter = new RegExp('.* Copy ([0-9])([0-9])');
258 | var copyTest = copiedCharacter.exec(new_character.name);
259 |
260 | if (copiedCharacter.test(new_character.name))
261 | {
262 | // Increment the copy number
263 | new_character.name = new_character.name.slice(0, -2);
264 |
265 | var newName = parseInt(copyTest[1]) * 10 + parseInt(copyTest[2]) + 1;
266 |
267 | if (newName < 10)
268 | {
269 | newName = '0' + newName;
270 | }
271 |
272 | new_character.name += newName;
273 | }
274 | else if (new_character.name.slice(-5) == ' Copy')
275 | {
276 | new_character.name += ' 01';
277 | }
278 | else
279 | {
280 | new_character.name += ' Copy';
281 | }
282 |
283 | new_character = this.set_character(new_character);
284 | new_id = new_character.character_id;
285 |
286 | if (clone_tabs === true) {
287 | var tabs = this.get_cast_tabs();
288 |
289 | tabs.forEach(function(tab)
290 | {
291 | var old_char_index = tab.characters.indexOf(id);
292 |
293 | if (tab.characters.includes(id))
294 | {
295 | tab.characters.splice(old_char_index + 1, 0, new_id);
296 | storage.set_cast_tab(tab.tab_id, tab);
297 | }
298 | });
299 | }
300 |
301 | return new_character;
302 | },
303 |
304 | // Return the ID of the newly created tab
305 | create_cast_tab: function(tab_name)
306 | {
307 | // Find the highest tab ID now
308 | var tab_id = this.generate_cast_tab_id(), sort_order, stored_tabs = $.parseJSON(localStorage.cast_tabs);
309 |
310 | sort_order = stored_tabs.length + 1;
311 |
312 | stored_tabs.push({
313 | tab_id: tab_id,
314 | name: tab_name,
315 | order: sort_order,
316 | characters: []
317 | });
318 |
319 | localStorage.cast_tabs = JSON.stringify(stored_tabs);
320 |
321 | return tab_id;
322 | },
323 |
324 | // Get a specific setting
325 | // Note that actually changing settings is just left to the Settings tab
326 | setting: function(name)
327 | {
328 | if (localStorage.hasOwnProperty('setting_' + name))
329 | {
330 | return localStorage['setting_' + name];
331 | }
332 | else
333 | {
334 | return null;
335 | }
336 | }
337 | };
338 |
--------------------------------------------------------------------------------
/css/sr_tools.css:
--------------------------------------------------------------------------------
1 | body
2 | {
3 | background-color: #383838;
4 | font-family: "Trebuchet MS", sans-serif;
5 | margin: 50px;
6 | }
7 |
8 | @font-face
9 | {
10 | font-family: ShadowrunNormal;
11 | src: url("fonts/shadowrun_regular.ttf");
12 | }
13 |
14 | @font-face
15 | {
16 | font-family: ShadowrunItalics;
17 | src: url("fonts/shadowrun_italic.ttf");
18 | }
19 |
20 | @font-face
21 | {
22 | font-family: ShadowrunBold;
23 | src: url("fonts/shadowrun_bold.ttf");
24 | }
25 |
26 |
27 |
28 | .big_button
29 | {
30 | padding: 5px;
31 | }
32 |
33 | .big_button:hover
34 | {
35 | }
36 |
37 | .smaller_button
38 | {
39 | font-weight: normal;
40 | padding: 1px 3px;
41 | font-size: 0.7em;
42 | }
43 |
44 | .smaller_button:hover
45 | {
46 | font-weight: normal;
47 | }
48 |
49 | .tiny_button
50 | {
51 | font-weight: normal;
52 | padding: 0;
53 | font-size: 0.5em;
54 | }
55 |
56 | .tiny_button:hover
57 | {
58 | font-weight: normal;
59 | }
60 |
61 | .top_bar
62 | {
63 | position: absolute;
64 | top: 0;
65 | left: 0;
66 | right: 0;
67 | background-color: #383838;
68 | height: 90px;
69 | }
70 |
71 | .top_bar .title
72 | {
73 | cursor: pointer;
74 | font-size: 28px;
75 | font-weight: bold;
76 | font-style: normal;
77 | color: #fff;
78 | margin-top: 24px;
79 | margin-left: 24px;
80 | font-family: "ShadowrunNormal", Arial, sans-serif;
81 | }
82 |
83 | .top_bar .version
84 | {
85 | color: #fff;
86 | margin-left: 24px;
87 | }
88 |
89 | .top_bar .top_bar_roller
90 | {
91 | position: absolute;
92 | top: 5px;
93 | right: 5px;
94 | }
95 |
96 | .top_bar .top_bar_roller > div
97 | {
98 | display: inline-block;
99 | vertical-align: top;
100 | }
101 |
102 | .top_bar .top_bar_roller .edging
103 | {
104 | color: #fff;
105 | margin-top: 24px;
106 | }
107 |
108 | .top_bar .top_bar_roller #roll_results
109 | {
110 | background-color: #383838;
111 | color: #fff;
112 | height: 77px;
113 | width: 450px;
114 | }
115 |
116 | .menu
117 | {
118 | position: absolute;
119 | top: 90px;
120 | left: 0;
121 | bottom: 0;
122 | width: 250px;
123 | background-color: #383838;
124 | }
125 |
126 | .menu button
127 | {
128 | width: 200px;
129 | margin-left: 24px;
130 | margin-top: 10px;
131 | }
132 |
133 | .main_content
134 | {
135 | position: absolute;
136 | top: 90px;
137 | bottom: 0;
138 | left: 250px;
139 | right: 0;
140 | background-color: #383838;
141 | color: #fff;
142 | }
143 |
144 | .main_content .intro_screen
145 | {
146 | padding-right: 25%;
147 | padding-bottom: 20px;
148 | font-family: "ShadowrunNormal", Arial, sans-serif;
149 | }
150 |
151 | .main_content .intro_screen .legalese
152 | {
153 | color: #ddd;
154 | font-style: italic;
155 | margin-top: 40px;
156 | }
157 |
158 | div[template_holder]
159 | {
160 | display: none;
161 | }
162 |
163 | .entry_form
164 | {
165 |
166 | }
167 |
168 | .entry_form > .input_row
169 | {
170 | padding-bottom: 2px;
171 | }
172 |
173 | .entry_form > .input_row label
174 | {
175 | display: inline-block;
176 | }
177 | .entry_form > .input_row select
178 | {
179 | width: 200px;
180 | }
181 |
182 | .spacer_5
183 | {
184 | height: 5px;
185 | }
186 |
187 | .spacer_10
188 | {
189 | height: 10px;
190 | }
191 |
192 | .spacer_15
193 | {
194 | height: 15px;
195 | }
196 |
197 | .spacer_20
198 | {
199 | height: 20px;
200 | }
201 |
202 | .spacer_25
203 | {
204 | height: 25px;
205 | }
206 |
207 | .spacer_30
208 | {
209 | height: 30px;
210 | }
211 |
212 | .spacer_35
213 | {
214 | height: 35px;
215 | }
216 |
217 | .spacer_40
218 | {
219 | height: 40px;
220 | }
221 |
222 | .spacer_45
223 | {
224 | height: 45px;
225 | }
226 |
227 | .minion_generator_section
228 | {
229 | padding-right: 25px;
230 | }
231 |
232 | .minion_generator_section #overview .intro:not(:first-child)
233 | {
234 | margin-top: 10px;
235 | }
236 |
237 | #minion_generator
238 | {
239 |
240 | }
241 |
242 | #minion_generator .entry_form
243 | {
244 |
245 | }
246 |
247 | #minion_generator .entry_form #generated_results
248 | {
249 | width: 650px;
250 | margin-top: 20px;
251 | margin-bottom: 20px;
252 | }
253 |
254 | #mob_generator .entry_form .input_row > label[equalize]
255 | {
256 | vertical-align: top;
257 | }
258 |
259 | #mob_generator .entry_form #generated_results
260 | {
261 | width: 650px;
262 | margin-top: 20px;
263 | margin-bottom: 20px;
264 | }
265 |
266 | #mob_generator .entry_form #generated_results .mob_entry:not(:first-child)
267 | {
268 | margin-top: 10px;
269 | }
270 |
271 | #mob_generator .entry_form .add_special_types
272 | {
273 | display: inline-block;
274 | }
275 |
276 | #mob_generator .entry_form .add_special_types label
277 | {
278 | font-size: 0.75em;
279 | display: block;
280 | text-align: left;
281 | }
282 |
283 | .display_npc_wrapper
284 | {
285 | border: 1px solid #ccee00;
286 | font-size: 0.9em;
287 | }
288 |
289 | .display_npc_wrapper .controls
290 | {
291 | float: right;
292 | font-size: 0;
293 | margin-top: 2px;
294 | margin-right: 2px;
295 | }
296 |
297 | .display_npc_wrapper .controls button
298 | {
299 | margin-left: 1px;
300 | margin-right: 1px;
301 | }
302 |
303 | .display_npc_wrapper .npc_name,
304 | .display_npc_wrapper .npc_description,
305 | .display_npc_wrapper .npc_notes
306 | {
307 | border-bottom: 1px solid #ccee00;
308 | padding: 2px 2px 2px 4px;
309 | }
310 | .display_npc_wrapper .attribute_names
311 | {
312 | border-bottom: 1px solid #ccee00;
313 | display: flex;
314 | font-weight: bold;
315 | font-size: 1.1em;
316 | }
317 | .display_npc_wrapper .attribute_values
318 | {
319 | border-bottom: 1px solid #ccee00;
320 | display: flex;
321 | }
322 |
323 | .display_npc_wrapper .attribute_names .attribute_name,
324 | .display_npc_wrapper .attribute_values .attribute_value
325 | {
326 | text-align: center;
327 | flex-grow: 1;
328 | flex-basis: 10%;
329 | }
330 |
331 | .display_npc_wrapper .information > div
332 | {
333 | background-color: #383838;
334 | display: table;
335 | width: 100%
336 | }
337 |
338 | .display_npc_wrapper .information > div > label
339 | {
340 | padding: 2px;
341 | width: 160px;
342 | display: table-cell;
343 | vertical-align: top;
344 | color: #ccee00;
345 | background-color: #000;
346 | font-weight: bold;
347 | }
348 |
349 | .display_npc_wrapper .information > div > div,
350 | .display_npc_wrapper .information > div.skills > div > div
351 | {
352 | padding: 2px;
353 | display: flex;
354 | vertical-align: top;
355 | }
356 |
357 | .display_npc_wrapper .information > div.gear > div,
358 | .display_npc_wrapper .information > div.skills > div
359 | {
360 | display: table-cell;
361 | }
362 |
363 | .display_npc_wrapper .information > div.skills .skill
364 | {
365 | flex: 3 0 120px;
366 | border-bottom: 1px dotted #666;
367 | }
368 |
369 | .display_npc_wrapper .information > div.skills button
370 | {
371 | margin-left: 4px;
372 | margin-right: 4px;
373 | }
374 |
375 | .display_npc_wrapper .information > div.skills .result
376 | {
377 | flex: 2 0 40px;
378 | }
379 |
380 | .action_npc_wrapper .information .rollable
381 | {
382 | display: flex;
383 | }
384 |
385 | .action_npc_wrapper .information .rollable > div:not(.result)
386 | {
387 | flex: 3 0 120px;
388 | }
389 |
390 | .action_npc_wrapper .information .rollable > button
391 | {
392 | margin-left: 4px;
393 | margin-right: 4px;
394 | }
395 |
396 | .action_npc_wrapper .information .rollable > .result
397 | {
398 | flex: 2 0 40px;
399 | }
400 |
401 | .action_npc_wrapper .information > div > div > button
402 | {
403 | margin-left: 5px;
404 | margin-right: 5px;
405 | }
406 |
407 | .action_npc_wrapper .information > div > div .result
408 | {
409 | color: #000;
410 | background-color: #ccc;
411 | min-width: 40px;
412 | padding-left: 4px;
413 | padding-right: 4px;
414 | text-align: center;
415 | }
416 |
417 | .action_npc_wrapper .information > .gear > .value > div
418 | {
419 | display: flex;
420 | padding: 2px;
421 | }
422 |
423 | .action_npc_wrapper .information > .gear > .value > div .stats
424 | {
425 | flex: 3 0 120px;
426 | border-bottom: 1px dotted #666;
427 | }
428 |
429 | .action_npc_wrapper .information > .gear > .value > div button
430 | {
431 | margin-left: 5px;
432 | margin-right: 5px;
433 | }
434 |
435 | .action_npc_wrapper .information > .gear > .value > div .result
436 | {
437 | flex: 0 0 40px;
438 | }
439 |
440 | .action_npc_wrapper .information .condition_monitor_combined,
441 | .action_npc_wrapper .information .condition_monitor_separate
442 | {
443 | width: 100%;
444 | display: flex;
445 | }
446 |
447 | .action_npc_wrapper .information .condition_monitor_combined .monitor,
448 | .action_npc_wrapper .information .condition_monitor_separate .monitor
449 | {
450 | border: 1px solid #999;
451 | flex: 3 0 1px;
452 | margin-left: 4px;
453 | }
454 |
455 | .action_npc_wrapper .information .condition_monitor_combined .monitor .boxes,
456 | .action_npc_wrapper .information .condition_monitor_separate .monitor .boxes
457 | {
458 | display: flex;
459 | }
460 |
461 | .action_npc_wrapper .information .condition_monitor_combined .monitor .boxes > div,
462 | .action_npc_wrapper .information .condition_monitor_separate .monitor .boxes > div
463 | {
464 | background-color: #222;
465 | border: 1px solid #333;
466 | color: #666;
467 | flex: 1 1 1px;
468 | text-align: center;
469 | }
470 |
471 | .action_npc_wrapper .information .condition_monitor_combined .monitor .markers,
472 | .action_npc_wrapper .information .condition_monitor_separate .monitor .markers
473 | {
474 | display: flex;
475 | }
476 |
477 | .action_npc_wrapper .information .condition_monitor_combined .monitor .markers > div,
478 | .action_npc_wrapper .information .condition_monitor_separate .monitor .markers > div
479 | {
480 | flex: 1 1 1px;
481 | }
482 |
483 | .action_npc_wrapper .information .condition_monitor_combined .penalty,
484 | .action_npc_wrapper .information .condition_monitor_separate .penalty
485 | {
486 | flex: 2 0 1px;
487 | padding-left: 4px;
488 | padding-right: 4px;
489 | }
490 |
491 | .edit_npc_wrapper .npc_notes textarea
492 | {
493 | margin: 2px 2px 2px 4px;
494 | width: calc(100% - 16px);
495 | }
496 |
497 | .edit_npc_wrapper .npc_notes label,
498 | .edit_npc_wrapper .other_information > div > label
499 | {
500 | display: block;
501 | padding-left: 2px;
502 | color: #ccee00;
503 | font-weight: bold;
504 | }
505 |
506 | .edit_npc_wrapper .other_information .condition_monitor > div,
507 | .edit_npc_wrapper .other_information .wound_penalty > div
508 | {
509 | padding-left: 5px;
510 | }
511 |
512 | .edit_npc_wrapper .other_information .skills
513 | {
514 |
515 | }
516 |
517 | .edit_npc_wrapper .other_information .skills .value
518 | {
519 | padding-left: 5px;
520 | }
521 |
522 | .edit_npc_wrapper .other_information .skills .value .skill
523 | {
524 | width: 40%;
525 | display: inline-block;
526 | }
527 |
528 | .edit_npc_wrapper .other_information .skills .value div.skill_rating
529 | {
530 | width: 7%;
531 | display: inline-block;
532 | }
533 |
534 | .edit_npc_wrapper .other_information .skills .value button
535 | {
536 | vertical-align: text-bottom;
537 | }
538 |
539 | .edit_npc_wrapper .other_information .qualities div.quality
540 | {
541 | padding-left: 5px;
542 | }
543 |
544 | .edit_npc_wrapper .other_information .qualities div.quality span.quality
545 | {
546 | width: 40%;
547 | display: inline-block;
548 | }
549 |
550 | .edit_npc_wrapper .other_information .augments .value
551 | {
552 | padding-left: 5px;
553 | }
554 |
555 | .edit_npc_wrapper .other_information .augments .value .augmentation
556 | {
557 | width: 40%;
558 | display: inline-block;
559 | }
560 |
561 | .edit_npc_wrapper .other_information .augments .value div.augmentation_rating
562 | {
563 | width: 7%;
564 | display: inline-block;
565 | }
566 |
567 | .edit_npc_wrapper .other_information .augments .value button
568 | {
569 | vertical-align: text-bottom;
570 | }
571 |
572 | .cast_wrapper
573 | {
574 | padding-right: 25px;
575 | }
576 |
577 | .cast_of_shadows
578 | {
579 |
580 | }
581 |
582 | .cast_of_shadows .add_tab_wrapper
583 | {
584 |
585 | }
586 |
587 | .cast_of_shadows .add_tab_wrapper button
588 | {
589 | vertical-align: bottom;
590 | }
591 |
592 | .cast_of_shadows .edit_tab_wrapper button
593 | {
594 | vertical-align: bottom;
595 | }
596 |
597 | .cast_of_shadows .cast_tabs
598 | {
599 |
600 | }
601 |
602 | .cast_of_shadows .cast__full_list
603 | {
604 |
605 | }
606 |
607 | .cast_of_shadows .cast__full_list > div[list]
608 | {
609 |
610 | }
611 |
612 | .cast_of_shadows .cast__full_list > div[list] > div
613 | {
614 | display: flex;
615 | margin-bottom: 10px;
616 | }
617 |
618 | .cast_of_shadows .cast__full_list > div[list] > div > .entry
619 | {
620 | flex: 1 0 500px;
621 | }
622 |
623 | .cast_of_shadows .cast__full_list > div[list] > div > .tools
624 | {
625 | flex: 2 0 200px;
626 | padding-left: 10px;
627 | }
628 |
629 | .cast_of_shadows .cast__full_list > div[list] > div > .tools .add_to_tab
630 | {
631 | padding-left: 4px;
632 | }
633 |
634 | .cast_of_shadows .cast__full_list > div[list] > div > .tools .add_to_tab > div
635 | {
636 | padding: 6px 0;
637 | }
638 |
639 | .cast_of_shadows .cast__tab_list > div[list] > div
640 | {
641 | display: flex;
642 | margin-bottom: 10px;
643 | }
644 |
645 | .cast_of_shadows .cast__tab_list > div[list] > div > .entry
646 | {
647 | flex: 1 0 500px;
648 | }
649 |
650 | .cast_of_shadows .cast__tab_list > div[list] > div > .tools
651 | {
652 | flex: 2 0 200px;
653 | padding-left: 10px;
654 | }
655 |
--------------------------------------------------------------------------------
/js/sr_tools_gen.js:
--------------------------------------------------------------------------------
1 | var gen = {
2 | type_options: ['civilian', 'thug', 'ganger', 'corpsec', 'police', 'cultist', 'htr', 'specops', 'mob'],
3 |
4 | random_type: function()
5 | {
6 | return this.type_options[roll.dval(this.type_options.length - 1)];
7 | },
8 |
9 | _merge_adjustments: function(base, adjust)
10 | {
11 | var i, attributes = ['body', 'agility', 'reaction', 'strength', 'will', 'logic', 'intuition', 'charisma'];
12 |
13 | if (!base.hasOwnProperty('professional_description') && adjust.hasOwnProperty('professional_description'))
14 | {
15 | base.professional_description = adjust.professional_description;
16 | }
17 |
18 | if (adjust.hasOwnProperty('attributes'))
19 | {
20 | var racial = false;
21 |
22 | if (base.hasOwnProperty('race'))
23 | racial = db.get_metatype_adjustment(base.race);
24 |
25 | attributes.forEach(function(att)
26 | {
27 | if (adjust.attributes.hasOwnProperty(att))
28 | {
29 | base.attributes[att] += adjust.attributes[att];
30 |
31 | if (racial)
32 | {
33 | // Racial Minimum
34 | base.attributes[att] = Math.max(racial.min_attributes[att], base.attributes[att]);
35 |
36 | // Racial Maximum
37 | base.attributes[att] = Math.min(racial.max_attributes[att], base.attributes[att]);
38 | }
39 | }
40 | });
41 | }
42 |
43 | if (adjust.hasOwnProperty('skills'))
44 | {
45 | for (i in adjust.skills)
46 | {
47 | if (i && base.skills.hasOwnProperty(i))
48 | {
49 | base.skills[i] = Math.max(base.skills[i], adjust.skills[i]);
50 | }
51 | else
52 | {
53 | base.skills[i] = adjust.skills[i];
54 | }
55 | }
56 | }
57 |
58 | if (adjust.hasOwnProperty('qualities'))
59 | {
60 | adjust.qualities.positive.forEach(function (item) {
61 | if (typeof item === 'object' || !base.qualities.positive.includes(item))
62 | {
63 | base.qualities.positive.push(item);
64 | }
65 | });
66 |
67 | adjust.qualities.negative.forEach(function (item) {
68 | if (typeof item === 'object' || !base.qualities.negative.includes(item))
69 | {
70 | base.qualities.negative.push(item);
71 | }
72 | });
73 | }
74 |
75 | if (adjust.hasOwnProperty('armor'))
76 | {
77 | base.armor = adjust.armor;
78 | }
79 |
80 | if (adjust.hasOwnProperty('weapons'))
81 | {
82 | adjust.weapons.forEach(function (item) {
83 | if (typeof item === 'object' || !base.weapons.includes(item))
84 | {
85 | base.weapons.push(item);
86 | }
87 | });
88 | }
89 |
90 | if (adjust.hasOwnProperty('augmentations'))
91 | {
92 | adjust.augmentations.forEach(function (item) {
93 | if (typeof item === 'object' || !base.augmentations.includes(item))
94 | {
95 | base.augmentations.push(item);
96 | }
97 | });
98 | }
99 |
100 | if (adjust.hasOwnProperty('gear'))
101 | {
102 | adjust.gear.forEach(function (item) {
103 | if (typeof item === 'object' || !base.gear.includes(item))
104 | {
105 | base.gear.push(item);
106 | }
107 | });
108 | }
109 |
110 | if (adjust.hasOwnProperty('special'))
111 | {
112 | base.special = $.extend({}, base.special, adjust.special);
113 | // If we have a magic rating, remove any augmentations
114 | if (adjust.special.hasOwnProperty('Magic'))
115 | {
116 | base.augmentations = [];
117 | }
118 | }
119 |
120 | if (adjust.hasOwnProperty('commlink'))
121 | {
122 | if (adjust.commlink > base.commlink)
123 | {
124 | base.commlink = adjust.commlink;
125 | }
126 | }
127 |
128 | return base;
129 | },
130 |
131 | mob: function(options)
132 | {
133 | if (options === undefined)
134 | {
135 | options = {};
136 | }
137 |
138 | options = $.extend({}, {
139 | size: roll.dval(10),
140 | professional_rating: roll.dval(5) - 1,
141 | professional_type: this.type_options[roll.dval(this.type_options.length - 1)],
142 | all_race: false,
143 | include_special: (roll.dval(10) > 6),
144 | include_lt: false,
145 | include_adept: false,
146 | include_mage: false,
147 | include_decker: false
148 | }, options);
149 |
150 | var mob = [], mob_options = {
151 | professional_rating: options.professional_rating,
152 | professional_type: options.professional_type
153 | };
154 |
155 | if (options.all_race !== false)
156 | {
157 | mob_options.race = options.all_race;
158 | }
159 |
160 | // If we want to include one of the specials, but haven't set which one, choose one at random
161 | var mook_count = options.size;
162 |
163 | if (options.include_special)
164 | {
165 | mook_count = options.size = 1;
166 |
167 | if (!options.include_lt && !options.include_adept && !options.include_mage && !options.include_decker)
168 | {
169 | var i = roll.dval(10);
170 |
171 | switch (true)
172 | {
173 | case (i < 6):
174 | options.include_lt = true;
175 | break;
176 | case (i < 8):
177 | options.include_decker = true;
178 | break;
179 | case (i < 10):
180 | options.include_adept = true;
181 | break;
182 | default:
183 | options.include_mage = true;
184 | break;
185 | }
186 | }
187 |
188 | var this_special = $.clone(mob_options);
189 |
190 | if (options.include_lt)
191 | {
192 | this_special.is_lt = true;
193 | }
194 | else if (options.include_decker)
195 | {
196 | this_special.is_decker = true;
197 | }
198 | else if (options.include_adept)
199 | {
200 | this_special.is_adept = true;
201 | }
202 | else if (options.include_mage)
203 | {
204 | this_special.is_mage = true;
205 | }
206 |
207 | mob.push(this.mook(this_special));
208 | }
209 |
210 | for (mook_count; mook_count > 0; mook_count--)
211 | {
212 | mob.push(this.mook(mob_options));
213 | }
214 |
215 | return mob;
216 | },
217 |
218 | mook: function(options)
219 | {
220 | if (options === undefined)
221 | {
222 | options = {};
223 | }
224 |
225 | options = $.extend({}, {
226 | name: 'Mook #' + roll.dval(10) + roll.dval(10) + roll.dval(10),
227 | gender: false, // false for random
228 | race: false,
229 | professional_rating: -1,
230 | professional_type: false,
231 | is_lt: false,
232 | is_adept: false,
233 | is_mage: false,
234 | is_decker: false,
235 | is_johnson: false,
236 | is_gunbunny: false,
237 | is_samurai: false,
238 | is_tank: false,
239 | is_shaman: false,
240 | is_contact: false,
241 | contact: false, // {connection rating, loyalty rating, type} || false
242 | notes: null
243 | }, options);
244 |
245 | var mook = {
246 | name: options.name,
247 | attributes: {body: 0, agility: 0, reaction: 0, strength: 0, will: 0, logic: 0, intuition: 0, charisma: 0},
248 | skills: {},
249 | knowledge_skills: {},
250 | qualities: {
251 | positive: [],
252 | negative: []
253 | },
254 | weapons: [],
255 | armor: [],
256 | gear: [],
257 | augmentations: [],
258 | special: {},
259 | commlink: 1,
260 | created: new Date().toJSON(),
261 | professional_type: options.professional_type
262 | };
263 |
264 | // Pull in copies from global settings
265 | mook.condition_monitor = storage.setting('condition_monitor');
266 | mook.wound_penalty = storage.setting('wound_penalty');
267 |
268 | // If we don't have a gender, assign a binary gender.
269 | // Will limiting gender to a binary decision piss off some people? Probably yes.
270 | // However, the author is not spending time developing a fully politically correct gender-determination system at this time.
271 | // If you really want to hear how the author feels about the situation, buy him a beer
272 | if (options.gender !== 'Male' && options.gender !== 'Female')
273 | {
274 | if (options.is_contact)
275 | {
276 | // Even split
277 | if (roll.dval(2) === 2)
278 | {
279 | mook.gender = 'Female';
280 | }
281 | else
282 | {
283 | mook.gender = 'Male';
284 | }
285 | }
286 | else
287 | {
288 | // Probably not so even
289 | if (roll.dval(10) >= 9)
290 | {
291 | mook.gender = 'Female';
292 | }
293 | else
294 | {
295 | mook.gender = 'Male';
296 | }
297 | }
298 | }
299 | else
300 | {
301 | mook.gender = options.gender;
302 | }
303 |
304 | // If we don't have a professional rating, then generate a random one from 0-4
305 | if (options.professional_rating === -1)
306 | {
307 | options.professional_rating = roll.dval(5) - 1;
308 | }
309 |
310 | mook.professional_rating = options.professional_rating;
311 |
312 | var rating_baseline = db.get_base_attributes(options.professional_rating);
313 |
314 | this._merge_adjustments(mook, rating_baseline);
315 |
316 | // If we don't have a race, generate one
317 | if (options.race === false)
318 | {
319 | options.race = db.gen_race();
320 | }
321 |
322 | mook.race = options.race;
323 | // Get the attribute adjustments from race and apply them
324 | var racial_baseline = db.get_metatype_adjustment(options.race);
325 |
326 | this._merge_adjustments(mook, racial_baseline);
327 |
328 | // If we don't have a professional type and we aren't a contact, then generate one
329 | if (options.professional_type === false)
330 | {
331 | if (options.is_contact === false)
332 | {
333 | options.professional_type = this.type_options[roll.dval(this.type_options.length - 1)];
334 | }
335 | }
336 |
337 | if (options.is_contact === false)
338 | {
339 | mook.professional_type = options.professional_type;
340 |
341 | switch (mook.professional_type)
342 | {
343 | case 'civilian':
344 | mook.professional_description = 'Civilian';
345 | break;
346 | case 'thug':
347 | mook.professional_description = 'Thug';
348 | break;
349 | case 'ganger':
350 | mook.professional_description = 'Gang Member';
351 | break;
352 | case 'corpsec':
353 | mook.professional_description = 'Corporate Security';
354 | break;
355 | case 'police':
356 | mook.professional_description = 'Law Enforcement';
357 | break;
358 | case 'cultist':
359 | mook.professional_description = 'Cultist';
360 | break;
361 | case 'htr':
362 | mook.professional_description = 'High Threat Response';
363 | break;
364 | case 'specops':
365 | mook.professional_description = 'Special Operations';
366 | break;
367 | case 'mob':
368 | mook.professional_description = 'Organized Crime';
369 | break;
370 | }
371 | }
372 |
373 | // Contacts do not have type adjustments, but everyone else does
374 | if (options.is_contact)
375 | {
376 | // TODO I need to deal with generating contacts!
377 | // This needs to include some stat adjustments for their rating, plus their type of contact-ness and other helpful things.
378 | }
379 | else
380 | {
381 | var type_adjustments = db.get_type_adjustments(options.professional_type, options.professional_rating);
382 | this._merge_adjustments(mook, type_adjustments);
383 | }
384 |
385 | // Is this a special type? [LT, adept, mage, decker]
386 | var adjustments;
387 |
388 | if (options.is_lt)
389 | {
390 | adjustments = db.get_special_adjustments('LT', options);
391 | this._merge_adjustments(mook, adjustments);
392 | mook.special.is_lt = true;
393 | }
394 |
395 | if (options.is_decker)
396 | {
397 | adjustments = db.get_special_adjustments('Decker', options);
398 | this._merge_adjustments(mook, adjustments);
399 | mook.special.is_decker = true;
400 | }
401 |
402 | if (options.is_adept)
403 | {
404 | adjustments = db.get_special_adjustments('Adept', options);
405 | this._merge_adjustments(mook, adjustments);
406 | mook.special.is_adept = true;
407 | }
408 |
409 | if (options.is_mage)
410 | {
411 | adjustments = db.get_special_adjustments('Mage', options);
412 | this._merge_adjustments(mook, adjustments);
413 | mook.special.is_mage = true;
414 | }
415 |
416 | if (options.is_shaman)
417 | {
418 | adjustments = db.get_special_adjustments('Shaman', options);
419 | this._merge_adjustments(mook, adjustments);
420 | mook.special.is_tank = true;
421 | }
422 |
423 | if (options.is_tank)
424 | {
425 | adjustments = db.get_special_adjustments('Tank', options);
426 | this._merge_adjustments(mook, adjustments);
427 | mook.special.is_tank = true;
428 | }
429 |
430 | if (options.is_samurai)
431 | {
432 | adjustments = db.get_special_adjustments('Samurai', options);
433 | this._merge_adjustments(mook, adjustments);
434 | mook.special.is_tank = true;
435 | }
436 |
437 | if (options.is_gunbunny)
438 | {
439 | adjustments = db.get_special_adjustments('Gunbunny', options);
440 | this._merge_adjustments(mook, adjustments);
441 | mook.special.is_tank = true;
442 | }
443 |
444 | if (options.is_johnson)
445 | {
446 | adjustments = db.get_special_adjustments('Johnson', options);
447 | this._merge_adjustments(mook, adjustments);
448 | mook.special.is_tank = true;
449 | }
450 |
451 | // If this is a Troll who has certain augmentations, they need to lose the Troll Dermal Deposits
452 | if (mook.race === 'Troll')
453 | {
454 | var skin_augments = ['Dermal Plating', 'Orthoskin'];
455 |
456 | var augment = mook.augmentations.filter(function (aug)
457 | {
458 | return skin_augments.includes(aug.name);
459 | });
460 |
461 | if (augment.length > 0)
462 | {
463 | augment = mook.augmentations.filter(function (aug)
464 | {
465 | return aug.name !== 'Troll Dermal Deposits';
466 | });
467 | mook.augmentations = augment;
468 | }
469 | }
470 |
471 | return mook;
472 | },
473 |
474 | matrix_host: function(options)
475 | {
476 | if (options === undefined)
477 | {
478 | options = {};
479 | }
480 |
481 | options = $.extend({}, {
482 | // matrix_host: false, // False, or a host rating
483 | // matrix_host_mode: 'Secure', // Ratings order; Secure: FADS, Data: DFAS, Hidden: SFAD
484 | notes: null
485 | }, options);
486 |
487 | // TODO make the rest of this useful later
488 | },
489 |
490 | _corp_name_combine_word: [
491 | 'aim',
492 | 'arm',
493 | 'ash',
494 | 'auto',
495 | 'block',
496 | 'bright',
497 | 'caption',
498 | 'com',
499 | 'down',
500 | 'dream',
501 | 'fund',
502 | 'gate',
503 | 'green',
504 | 'hydro',
505 | 'lion',
506 | 'mark',
507 | 'max',
508 | 'motor',
509 | 'plastic',
510 | 'point',
511 | 'scope',
512 | 'strong',
513 | 'sun',
514 | 'thermo',
515 | 'wood',
516 | 'works'
517 | ],
518 |
519 | _corp_name_modifier_word: [
520 | 'Ace',
521 | 'Action',
522 | 'Advanced',
523 | 'Anchor',
524 | 'Apparel',
525 | 'Aquatic',
526 | 'Atlas',
527 | 'Boulder',
528 | 'Broadcasting',
529 | 'Collective',
530 | 'Construction',
531 | 'Eagle',
532 | 'Electric',
533 | 'Entertainment',
534 | 'Financial',
535 | 'Financial',
536 | 'Global',
537 | 'Gold',
538 | 'Golden',
539 | 'Green',
540 | 'International',
541 | 'Investigative',
542 | 'Lender',
543 | 'Machine',
544 | 'Master',
545 | 'Media',
546 | 'Network',
547 | 'New World',
548 | 'Old World',
549 | 'Research',
550 | 'Robotic',
551 | 'Stone',
552 | 'Visual',
553 | 'Wireless'
554 | ],
555 |
556 | _corp_name_final_word: [
557 | 'Agriculture',
558 | 'Analysis',
559 | 'Analytics',
560 | 'Collective',
561 | 'Construction',
562 | 'Consumables',
563 | 'Corporation',
564 | 'Entertainment',
565 | 'Financial',
566 | 'Global',
567 | 'Group',
568 | 'Holdings',
569 | 'Incorporated',
570 | 'Industries',
571 | 'International',
572 | 'Investigations',
573 | 'Network',
574 | 'Press',
575 | 'Processing',
576 | 'Productions',
577 | 'Reporting',
578 | 'Research',
579 | 'Robotics',
580 | 'Security',
581 | 'Systems',
582 | 'Technologies',
583 | 'Works'
584 | ],
585 |
586 | initials: function(count)
587 | {
588 | if (count === undefined)
589 | {
590 | count = roll.dval(3);
591 | }
592 |
593 | var l = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
594 | var a = '.-/+';
595 | var ret = [];
596 |
597 | for (var i = 1; i <= count; i++)
598 | {
599 | ret.push(l[roll.dval(l.length) - 1])
600 | }
601 |
602 | if (roll.dval(5) === 5)
603 | {
604 | i = a[roll.dval(a.length) - 1];
605 | return ret.join(i);
606 | }
607 | else
608 | {
609 | return ret.join('');
610 | }
611 | },
612 |
613 | combine_words: function()
614 | {
615 | var i, j, k;
616 |
617 | do {
618 | i = roll.dval(this._corp_name_combine_word.length);
619 | j = roll.dval(this._corp_name_combine_word.length);
620 | } while (i === j);
621 |
622 | k = this._corp_name_combine_word[i - 1] + this._corp_name_combine_word[j - 1];
623 | return k[0].toUpperCase() + k.slice(1);
624 | },
625 |
626 | corp_name: function(options)
627 | {
628 | if (options === undefined)
629 | {
630 | options = {};
631 | }
632 |
633 | options = $.extend({}, {
634 | format: null,
635 | notes: null,
636 | subsidiary: null // Subsidiary of '___'
637 | }, options);
638 |
639 | var corp = {name: ''}, name_parts = [];
640 | var formats = ['double', 'initials', 'triple', 'initials double'];
641 |
642 | if (options.format === null)
643 | {
644 | options.format = formats[roll.dval(formats.length) - 1];
645 | }
646 |
647 | switch(options.format)
648 | {
649 | case 'double':
650 | if (roll.dval(2) === 2)
651 | {
652 | name_parts.push(this._corp_name_modifier_word[roll.dval(this._corp_name_modifier_word.length) - 1]);
653 | }
654 | else
655 | {
656 | name_parts.push(this.combine_words());
657 | }
658 | break;
659 |
660 | case 'initials':
661 | name_parts.push(this.initials());
662 | break;
663 |
664 | case 'triple':
665 | if (roll.dval(2) === 2)
666 | {
667 | name_parts.push(this.combine_words());
668 | name_parts.push(this._corp_name_modifier_word[roll.dval(this._corp_name_modifier_word.length) - 1]);
669 | }
670 | else
671 | {
672 | name_parts.push(this._corp_name_modifier_word[roll.dval(this._corp_name_modifier_word.length) - 1]);
673 | name_parts.push(this.combine_words());
674 | }
675 | break;
676 |
677 | case 'initials double':
678 | name_parts.push(this.initials());
679 | name_parts.push(this._corp_name_modifier_word[roll.dval(this._corp_name_modifier_word.length) - 1]);
680 | break;
681 |
682 | default:
683 | console.log('ERROR: corp_name() with unknown format', options.format);
684 | return;
685 | }
686 |
687 | // Generate the final word
688 | name_parts.push(this._corp_name_final_word[roll.dval(this._corp_name_final_word.length) - 1]);
689 |
690 | corp.name = name_parts.join(' ');
691 |
692 | return corp;
693 | }
694 | };
695 |
--------------------------------------------------------------------------------
/js/sr_tools.js:
--------------------------------------------------------------------------------
1 | function view_cast(show_intro)
2 | {
3 | var $container = $('.main_content').empty();
4 | var $template = render.get_template('cast_of_shadows');
5 | var tabs_added = 0, tab_index_to_show;
6 |
7 | $('
').addClass('cast_wrapper').append($template).appendTo($container);
8 |
9 | var tabs = storage.get_cast_tabs();
10 |
11 | tabs.forEach(function(tab)
12 | {
13 | tabs_added++;
14 |
15 | var $href = $('' + tab.name + ' ').attr('href', '#' + tab.href).attr('tab_id', tab.tab_id);
16 |
17 | var $li = $(' ').attr('tab_id', tab.tab_id).append($href);
18 |
19 | $template.find('ul.cast_tabs').append($li);
20 |
21 | $('
', {id: tab.href}).appendTo($template.find('.cast_of_shadows')).attr('tab_id', tab.tab_id);
22 |
23 | // Add a row for editing this tab to the introduction edit area
24 | var $row_template = render.get_template('edit_tab_row');
25 |
26 | $row_template.appendTo($template.find('.edit_tab_wrapper'));
27 |
28 | $row_template.find('#tab_name').val(tab.name);
29 |
30 | // Check will save the name
31 | var save_tab_data = function ()
32 | {
33 | var tab_name = $row_template.find('#tab_name').val().replace(/\W/g, ' ').trim();
34 |
35 | if (tab.name !== tab_name && tab_name !== '')
36 | {
37 | storage.set_cast_tab(tab.tab_id, {name: tab_name});
38 | storage.set_current_cast_tab(tab.tab_id);
39 | view_cast();
40 | }
41 | };
42 |
43 | $row_template.find('button.tab_edit').button().click(save_tab_data);
44 | $row_template.find('#tab_name').on('keyup', function (e)
45 | {
46 | if (e.keyCode === 13)
47 | save_tab_data();
48 | });
49 |
50 | // Up arrow moves tab up
51 | $row_template.find('button.tab_up').button();
52 |
53 | if (tabs_added > 1)
54 | {
55 | $row_template.find('button.tab_up').click(function ()
56 | {
57 | storage.set_cast_tab(tab.tab_id, {order: (tab.order - 1)});
58 | view_cast();
59 | });
60 | }
61 | else
62 | {
63 | $row_template.find('button.tab_up').button('disable');
64 | }
65 |
66 | // Down moves down
67 | $row_template.find('button.tab_down').button();
68 |
69 | if (tabs_added < tabs.length)
70 | {
71 | $row_template.find('button.tab_down').click(function ()
72 | {
73 | storage.set_cast_tab(tab.tab_id, {order: (tab.order + 1)});
74 | view_cast();
75 | });
76 | }
77 | else
78 | {
79 | $row_template.find('button.tab_down').button('disable');
80 | }
81 |
82 | // Don't allow the main tab to be deleted
83 | $row_template.find('button.delete_tab').button();
84 |
85 | if (tab.tab_id !== 1)
86 | {
87 | $row_template.find('button.delete_tab').click(function ()
88 | {
89 | storage.delete_cast_tab(tab.tab_id);
90 | storage.set_current_cast_tab(1);
91 | view_cast(true);
92 | });
93 | }
94 | else
95 | {
96 | $row_template.find('button.delete_tab').button('disable');
97 | }
98 | });
99 |
100 | var redraw_full_cast = function($tab)
101 | {
102 | $tab.empty().append(render.get_template('cast__full_list'));
103 |
104 | var full_cast = storage.get_characters();
105 |
106 | if (full_cast.length > 0)
107 | {
108 | $tab.find('.empty_message').detach();
109 | }
110 | else
111 | {
112 | $tab.find('.cast_message').detach();
113 | }
114 |
115 | full_cast.forEach(function(cast)
116 | {
117 | var $char_template = render.get_template('cast__full_list_entry').appendTo($tab.find('[list]'));
118 |
119 | render.mook_for_action($char_template.find('.entry'), cast);
120 |
121 | $char_template.find('.tab_delete_dialog').detach();
122 | var $deletion_dialog = $char_template.find('.delete_dialog').dialog({
123 | autoOpen: false,
124 | modal: true,
125 | title: 'Remove Cast Member',
126 | width: 450,
127 | buttons: [
128 | {
129 | text: "Ok",
130 | click: function() {
131 | storage.delete_character(cast.character_id);
132 | $(this).dialog("close");
133 | view_cast();
134 | }
135 | },
136 | {
137 | text: "Cancel",
138 | click: function() {
139 | $(this).dialog("close");
140 | }
141 | }
142 | ]
143 | });
144 |
145 | $char_template.find('.delete_cast_member').button().click(function ()
146 | {
147 | $deletion_dialog.dialog('open');
148 | });
149 |
150 | $char_template.find('.tab_clone_dialog').detach();
151 | var $clone_dialog = $char_template.find('.clone_dialog').dialog({
152 | autoOpen: false,
153 | modal: true,
154 | title: 'Clone Cast Member',
155 | width: 450,
156 | buttons: [
157 | {
158 | text: "Ok",
159 | click: function() {
160 | storage.clone_character(cast.character_id);
161 | $(this).dialog("close");
162 | view_cast();
163 | }
164 | },
165 | {
166 | text: "Cancel",
167 | click: function() {
168 | $(this).dialog("close");
169 | }
170 | }
171 | ]
172 | });
173 |
174 | $char_template.find('.clone_cast_member').button().click(function ()
175 | {
176 | $clone_dialog.dialog('open');
177 | });
178 |
179 | $char_template.find('.created_date').html('Created: ' + render.format_string_date(cast.created));
180 |
181 | if (cast.edited)
182 | $char_template.find('.edited_date').html('Last Edited: ' + render.format_string_date(cast.edited));
183 | else
184 | $char_template.find('.edited_date').hide();
185 |
186 | // Add the npc to a given tab it isn't already in
187 | var tabs = storage.get_cast_tabs();
188 |
189 | if (tabs.length > 1)
190 | {
191 | $char_template.find('button.add_npc').button().off('click').click(function()
192 | {
193 | var tab_id = parseInt($char_template.find('select[name="tab_name"]').val());
194 | var this_tab = storage.get_cast_tab(tab_id);
195 |
196 | if (!this_tab.characters.includes(cast.character_id))
197 | {
198 | this_tab.characters.push(cast.character_id);
199 | storage.set_cast_tab(tab_id, this_tab);
200 | }
201 | });
202 |
203 | $char_template.find('button.add_npc_and_switch').button().off('click').click(function()
204 | {
205 | var tab_id = parseInt($char_template.find('select[name="tab_name"]').val());
206 | var this_tab = storage.get_cast_tab(tab_id);
207 |
208 | if (!this_tab.characters.includes(cast.character_id))
209 | {
210 | this_tab.characters.push(cast.character_id);
211 | storage.set_cast_tab(tab_id, this_tab);
212 | }
213 |
214 | $template.find('.cast_tabs li[tab_id] a[tab_id="' + tab_id + '"]').click();
215 | });
216 |
217 | var tabs_available = false;
218 | tabs.forEach(function(tab)
219 | {
220 | if (!tab.characters.includes(cast.character_id) && tab.tab_id !== 1)
221 | {
222 | $char_template.find('.add_to_tab select[name="tab_name"]').append($(' ').html(tab.name).attr('value', tab.tab_id));
223 | tabs_available = true;
224 | }
225 | });
226 |
227 | if (!tabs_available)
228 | $char_template.find('.add_to_tab').detach();
229 | }
230 | else
231 | {
232 | $char_template.find('.add_to_tab').detach();
233 | }
234 | });
235 |
236 | // Set the currently active cast tab
237 | storage.set_current_cast_tab(1);
238 | };
239 |
240 | var redraw_tab = function(tab_id, $tab)
241 | {
242 | var tab_info = storage.get_cast_tab(tab_id);
243 |
244 | $tab.empty().append(render.get_template('cast__tab_list'));
245 |
246 | // Add each NPC who appears on this tab
247 | if (tab_info.characters.length > 0)
248 | {
249 | tab_info.characters.forEach(function(character_id)
250 | {
251 | var character = storage.get_character(character_id);
252 |
253 | var $char_template = render.get_template('cast__full_list_entry').appendTo($tab.find('[list]'));
254 |
255 | render.mook_for_action($char_template.find('.entry'), character);
256 |
257 | $char_template.find('.delete_dialog').detach();
258 | var $deletion_dialog = $char_template.find('.tab_delete_dialog').dialog({
259 | autoOpen: false,
260 | modal: true,
261 | title: 'Remove Cast Member',
262 | width: 450,
263 | buttons: [
264 | {
265 | text: "Ok",
266 | click: function() {
267 | storage.delete_character_from_tab(tab_id, character_id);
268 | $(this).dialog("close");
269 | $char_template.detach();
270 | }
271 | },
272 | {
273 | text: "Cancel",
274 | click: function() {
275 | $(this).dialog("close");
276 | }
277 | }
278 | ]
279 | });
280 |
281 | $char_template.find('.delete_cast_member').button().click(function ()
282 | {
283 | $deletion_dialog.dialog('open');
284 | });
285 |
286 | $char_template.find('.clone_dialog').detach();
287 | var $clone_dialog = $char_template.find('.tab_clone_dialog').dialog({
288 | autoOpen: false,
289 | modal: true,
290 | title: 'Clone Cast Member',
291 | width: 450,
292 | buttons: [
293 | {
294 | text: "Ok",
295 | click: function() {
296 | storage.clone_character(character_id, true);
297 | $(this).dialog("close");
298 | view_cast();
299 | }
300 | },
301 | {
302 | text: "Cancel",
303 | click: function() {
304 | $(this).dialog("close");
305 | }
306 | }
307 | ]
308 | });
309 |
310 | $char_template.find('.clone_cast_member').button().click(function ()
311 | {
312 | $clone_dialog.dialog('open');
313 | });
314 |
315 | $char_template.find('.created_date').html('Created: ' + render.format_string_date(character.created));
316 |
317 | if (character.edited)
318 | $char_template.find('.edited_date').html('Last Edited: ' + render.format_string_date(character.edited));
319 | else
320 | $char_template.find('.edited_date').hide();
321 |
322 | $char_template.find('.add_to_tab').detach();
323 | });
324 | }
325 | else
326 | {
327 | $tab.append($("
").html(tab_info.name)).append("There are currently no NPCs on this tab.");
328 | }
329 |
330 | // Set the currently active cast tab
331 | storage.set_current_cast_tab(tab_id);
332 | };
333 |
334 | $template.tabs({
335 | beforeActivate: function(e, ui)
336 | {
337 | var show_tab_id = parseInt($(ui.newTab).attr('tab_id'));
338 |
339 | if (show_tab_id === 0)
340 | return;
341 | else if (show_tab_id === 1)
342 | redraw_full_cast(ui.newPanel);
343 | else
344 | redraw_tab(show_tab_id, ui.newPanel);
345 | }
346 | });
347 |
348 | var add_new_tab = function()
349 | {
350 | var tab_name = $('.cast_of_shadows #add_tab_name').val(), tab_id;
351 |
352 | if (tab_name === '')
353 | {
354 | return;
355 | }
356 |
357 | tab_id = storage.create_cast_tab(tab_name);
358 | storage.set_current_cast_tab(tab_id);
359 |
360 | view_cast();
361 | };
362 |
363 | // Be able to add a tab
364 | $template.find('button.add_tab').button().click(add_new_tab);
365 | $template.find('.add_tab_wrapper #add_tab_name').on('keyup', function (e)
366 | {
367 | if (e.keyCode === 13)
368 | add_new_tab();
369 | });
370 |
371 | // Activate the last-viewed tab, or the Full Cast if we've just created something
372 | tab_index_to_show = storage.get_current_cast_tab();
373 | tab_index_to_show = $template.find('a[tab_id="' + tab_index_to_show + '"]').parent().index();
374 | if (show_intro === true)
375 | tab_index_to_show = 0;
376 | $template.tabs('option', 'active', tab_index_to_show);
377 | }
378 |
379 | function view_generator()
380 | {
381 | var $container = $('.main_content').empty();
382 |
383 | var $template = render.get_template('minion_generator_section');
384 |
385 | var current_npc;
386 |
387 | $container.append($template);
388 |
389 | $template.find('button').button();
390 |
391 | $template.find('#overview button.generate_mook').on('click', function()
392 | {
393 | $template.find('.section_tabs').tabs('option', 'active', 1);
394 | });
395 |
396 | $template.find('#overview button.generate_mob').on('click', function()
397 | {
398 | $template.find('.section_tabs').tabs('option', 'active', 2);
399 | });
400 |
401 | $template.find('#generate_minion').on('click', function()
402 | {
403 | var options = {};
404 |
405 | if ($('.main_content #minion_generator .entry_form input[name="name"]').val())
406 | {
407 | options['name'] = $('.main_content #minion_generator .entry_form input[name="name"]').val();
408 | }
409 |
410 | $('.main_content #minion_generator .entry_form select').each(function ()
411 | {
412 | var option = $(this).attr('name');
413 |
414 | var value = $(this).find(':selected').val();
415 |
416 | if (option === 'is_special')
417 | {
418 | switch (value)
419 | {
420 | case 'is_lt':
421 | case 'is_decker':
422 | case 'is_adept':
423 | case 'is_mage':
424 | options[value] = true;
425 | break;
426 | }
427 | }
428 | else if (option === 'professional_rating')
429 | {
430 | if (value !== '')
431 | {
432 | options[option] = parseInt(value);
433 | }
434 | }
435 | else
436 | {
437 | if (value != '')
438 | {
439 | options[option] = value;
440 | }
441 | }
442 | });
443 |
444 | current_npc = gen.mook(options);
445 |
446 | render.mook($template.find('#minion_generator #generated_results'), current_npc);
447 |
448 | $template.find('#minion_generator #discard_minion').button('enable');
449 |
450 | $template.find('#minion_generator #add_to_cast').button('enable').off('click').click(function()
451 | {
452 | storage.set_character(current_npc);
453 |
454 | // When we create a new character, go to the full cast
455 | storage.set_current_cast_tab(1);
456 |
457 | view_cast();
458 | });
459 |
460 | var $popup = render.get_template('add_dialog_single');
461 | $popup.find('button').button();
462 |
463 | var current_tabs = storage.get_cast_tabs();
464 |
465 | // If there aren't additional tabs, remove the first part
466 | if (current_tabs.length <= 1)
467 | {
468 | $popup.find('.existing_tab_wrapper').detach()
469 | }
470 | else
471 | {
472 | // Add the other tabs as options
473 | for (var i = 0; i < current_tabs.length; i++)
474 | {
475 | if (current_tabs[i].tab_id !== 1)
476 | {
477 | $(' ').attr('value', current_tabs[i].tab_id).html(current_tabs[i].name).appendTo($popup.find('select[name="existing"]'));
478 | }
479 | }
480 |
481 | // Set the save button to work
482 | $popup.find('button[existing]').off('click').click(function()
483 | {
484 | var tab_id = parseInt($popup.find('select[name="existing"]').val());
485 | var npc_data = storage.set_character(current_npc);
486 | var tab_data = storage.get_cast_tab(tab_id);
487 | tab_data.characters.push(npc_data.character_id);
488 | storage.set_cast_tab(tab_id, tab_data);
489 | storage.set_current_cast_tab(tab_id);
490 | $popup.dialog('close');
491 | view_cast();
492 | });
493 | }
494 |
495 | // Set the save new button to work
496 | $popup.find('button[new_tab]').off('click').click(function()
497 | {
498 | var tab_name = $popup.find('input[new_tab]').val(), tab_id;
499 |
500 | if (tab_name === '')
501 | {
502 | return;
503 | }
504 |
505 | tab_id = storage.create_cast_tab(tab_name);
506 | var tab_data = storage.get_cast_tab(tab_id);
507 | var npc_data = storage.set_character(current_npc);
508 | tab_data.characters.push(npc_data.character_id);
509 | storage.set_cast_tab(tab_id, tab_data);
510 | storage.set_current_cast_tab(tab_id);
511 | $popup.dialog('close');
512 | view_cast();
513 | });
514 |
515 | $popup.dialog({
516 | autoOpen: false,
517 | modal: true,
518 | title: 'Add NPC to specific tab',
519 | width: 500,
520 | buttons: [
521 | {
522 | text: "Cancel",
523 | click: function() {
524 | $(this).dialog( "close" );
525 | }
526 | }
527 | ]
528 | });
529 |
530 | $template.find('#minion_generator #add_dialog').button('enable').off('click').click(function()
531 | {
532 | $popup.dialog('open');
533 | $popup.find('select').selectmenu();
534 | });
535 | });
536 |
537 | $template.find('#minion_generator #discard_minion').button('disable').click(function ()
538 | {
539 | $template.find('#minion_generator #generated_results').empty();
540 | $template.find('#minion_generator #discard_minion').button('disable');
541 | $template.find('#minion_generator #add_to_cast').button('disable');
542 | $template.find('#minion_generator #add_dialog').button('disable');
543 | });
544 |
545 | $template.find('#minion_generator #add_to_cast').button('disable');
546 | $template.find('#minion_generator #add_dialog').button('disable');
547 |
548 | $template.find('#generate_mob').on('click', function()
549 | {
550 | var options = {}, i, mob_name = 'Mob #' + roll.dval(10) + roll.dval(10) + ':', mob = [], special, mook;
551 |
552 | if ($('.main_content #mob_generator .entry_form input[name="name"]').val().trim())
553 | {
554 | mob_name = $('.main_content #mob_generator .entry_form input[name="name"]').val().trim();
555 | }
556 |
557 | var mob_count = $('.main_content #mob_generator .entry_form select[name="number"]');
558 | mob_count = mob_count.find(':selected').val();
559 |
560 | switch (mob_count)
561 | {
562 | case 'two_four':
563 | mob_count = 1 + roll.dval(3);
564 | break;
565 |
566 | case 'three_six':
567 | mob_count = 2 + roll.dval(4);
568 | break;
569 |
570 | case 'four_ten':
571 | mob_count = 3 + roll.dval(7);
572 | break;
573 |
574 | default:
575 | mob_count = 1;
576 | break;
577 | }
578 |
579 | if ($('#mob_generator .entry_form select[name="race"] option:selected').val())
580 | options.race = $('#mob_generator .entry_form select[name="race"] option:selected').val();
581 |
582 | if ($('#mob_generator .entry_form select[name="gender"] option:selected').val())
583 | options.gender = $('#mob_generator .entry_form select[name="gender"] option:selected').val();
584 |
585 | if ($('#mob_generator .entry_form select[name="professional_rating"] option:selected').val())
586 | options.professional_rating = $('#mob_generator .entry_form select[name="professional_rating"] option:selected').val();
587 |
588 | if ($('#mob_generator .entry_form select[name="professional_type"] option:selected').val())
589 | options.professional_type = $('#mob_generator .entry_form select[name="professional_type"] option:selected').val();
590 |
591 | // If we need a group rating, generate that
592 | if (options.professional_rating === 'group')
593 | {
594 | options.professional_rating = roll.dval(5) - 1;
595 | }
596 | else if (options.professional_rating == parseInt(options.professional_rating))
597 | {
598 | options.professional_rating = parseInt(options.professional_rating);
599 | }
600 |
601 | // If we need a group type, generate that
602 | if (options.professional_type === 'group')
603 | {
604 | options.professional_type = gen.random_type();
605 | }
606 |
607 | for (i = 0; i < mob_count; i++)
608 | {
609 | mook = gen.mook($.extend({}, options));
610 | mook.name = mob_name + ' ' + mook.name;
611 | mob.push(mook);
612 | }
613 |
614 | if ($('#mob_generator .entry_form input[name="special_magician"]').prop('checked'))
615 | {
616 | special = $.extend({}, options);
617 | special.is_mage = true;
618 | mook = gen.mook(special);
619 | mook.name = mob_name + ' ' + mook.name;
620 | mob.push(mook);
621 | }
622 | if ($('#mob_generator .entry_form input[name="special_adept"]').prop('checked'))
623 | {
624 | special = $.extend({}, options);
625 | special.is_adept = true;
626 | mook = gen.mook(special);
627 | mook.name = mob_name + ' ' + mook.name;
628 | mob.push(mook);
629 | }
630 | if ($('#mob_generator .entry_form input[name="special_decker"]').prop('checked'))
631 | {
632 | special = $.extend({}, options);
633 | special.is_decker = true;
634 | mook = gen.mook(special);
635 | mook.name = mob_name + ' ' + mook.name;
636 | mob.push(mook);
637 | }
638 | if ($('#mob_generator .entry_form input[name="special_lieutenant"]').prop('checked'))
639 | {
640 | special = $.extend({}, options);
641 | special.is_lt = true;
642 | mook = gen.mook(special);
643 | mook.name = mob_name + ' ' + mook.name;
644 | mob.push(mook);
645 | }
646 |
647 | $template.find('#mob_generator #generated_results').empty();
648 |
649 | for (i = mob.length - 1; i >= 0; i--)
650 | {
651 | var $mob_entry = $('
').addClass('mob_entry').appendTo($template.find('#mob_generator #generated_results'));
652 |
653 | render.mook($mob_entry, mob[i]);
654 | }
655 |
656 | $template.find('#mob_generator #discard_minion').button('enable');
657 |
658 | $template.find('#mob_generator #add_to_cast').button('enable').off('click').click(function()
659 | {
660 | for (i = mob.length - 1; i >= 0; i--)
661 | {
662 | storage.set_character(mob[i]);
663 | }
664 |
665 | // When we create a new character, go to the full cast
666 | storage.set_current_cast_tab(1);
667 |
668 | view_cast();
669 | });
670 |
671 | var $popup = render.get_template('add_dialog_mob');
672 | $popup.find('button').button();
673 |
674 | var current_tabs = storage.get_cast_tabs();
675 |
676 | // If there aren't additional tabs, remove the first part
677 | if (current_tabs.length <= 1)
678 | {
679 | $popup.find('.existing_tab_wrapper').detach()
680 | }
681 | else
682 | {
683 | // Add the other tabs as options
684 | for (i = 0; i < current_tabs.length; i++)
685 | {
686 | if (current_tabs[i].tab_id !== 1)
687 | {
688 | $(' ').attr('value', current_tabs[i].tab_id).html(current_tabs[i].name).appendTo($popup.find('select[name="existing"]'));
689 | }
690 | }
691 |
692 | // Set the save button to work
693 | $popup.find('button[existing]').off('click').click(function()
694 | {
695 | var tab_id = parseInt($popup.find('select[name="existing"]').val());
696 | var npc_data, tab_data;
697 | tab_data = storage.get_cast_tab(tab_id);
698 |
699 | for (i = mob.length - 1; i >= 0; i--)
700 | {
701 | npc_data = storage.set_character(mob[i]);
702 | tab_data.characters.push(npc_data.character_id);
703 | }
704 |
705 | storage.set_cast_tab(tab_id, tab_data);
706 |
707 | storage.set_current_cast_tab(tab_id);
708 | $popup.dialog('close');
709 | view_cast();
710 | });
711 | }
712 |
713 | // Set the save new button to work
714 | $popup.find('button[new_tab]').off('click').click(function()
715 | {
716 | var tab_name = $popup.find('input[new_tab]').val(), tab_id;
717 | var npc_data, tab_data;
718 |
719 | if (tab_name === '')
720 | {
721 | return;
722 | }
723 |
724 | tab_id = storage.create_cast_tab(tab_name);
725 | tab_data = storage.get_cast_tab(tab_id);
726 |
727 | for (i = mob.length - 1; i >= 0; i--)
728 | {
729 | npc_data = storage.set_character(mob[i]);
730 | tab_data.characters.push(npc_data.character_id);
731 | }
732 |
733 | storage.set_cast_tab(tab_id, tab_data);
734 | storage.set_current_cast_tab(tab_id);
735 | $popup.dialog('close');
736 | view_cast();
737 | });
738 |
739 | $popup.dialog({
740 | autoOpen: false,
741 | modal: true,
742 | title: 'Add NPCs to specific tab',
743 | width: 500,
744 | buttons: [
745 | {
746 | text: "Cancel",
747 | click: function() {
748 | $(this).dialog( "close" );
749 | }
750 | }
751 | ]
752 | });
753 |
754 | $template.find('#mob_generator #add_dialog').button('enable').off('click').click(function()
755 | {
756 | $popup.dialog('open');
757 | $popup.find('select').selectmenu();
758 | });
759 | });
760 |
761 | $template.find('#mob_generator #discard_minion').button('disable').click(function ()
762 | {
763 | $template.find('#mob_generator #generated_results').empty();
764 | $template.find('#mob_generator #discard_minion').button('disable');
765 | $template.find('#mob_generator #add_to_cast').button('disable');
766 | $template.find('#mob_generator #add_dialog').button('disable');
767 | });
768 |
769 | $template.find('#mob_generator #add_to_cast').button('disable');
770 | $template.find('#mob_generator #add_dialog').button('disable');
771 |
772 | // TODO add a reset button to the form to reset all the values to default. Could just call view_generator()
773 |
774 | // Make things pretty!
775 | $template.find('select').selectmenu();
776 | $template.find('#mob_generator .add_special_types input').checkboxradio();
777 |
778 | render.equalize_widths($template.find('.section_tabs #minion_generator .input_row label[equalize]'));
779 | render.equalize_widths($template.find('.section_tabs #mob_generator .input_row label[equalize]'));
780 |
781 | $template.find('.section_tabs').tabs();
782 | }
783 |
784 | function view_contact()
785 | {
786 | var $container = $('.main_content').empty();
787 |
788 | $container.html('view the contact generator');
789 | }
790 |
791 | function view_run()
792 | {
793 | var $container = $('.main_content').empty();
794 |
795 | $container.html('view the run generator');
796 | }
797 |
798 | function view_faq()
799 | {
800 | var $container = $('.main_content').empty();
801 |
802 | var $template = render.get_template('faq');
803 |
804 | $container.append($template);
805 | }
806 |
807 | function view_settings()
808 | {
809 | var $container = $('.main_content').empty();
810 |
811 | var $template = render.get_template('settings');
812 |
813 | $container.append($template);
814 |
815 | $container.find('button').button();
816 |
817 | // Need to give it a hard-coded ID only after the template is cloned
818 | $container.find('.delete_localstorage_dialog').attr('id', 'delete_localstorage_dialog');
819 |
820 | $container.find('#delete_localstorage_dialog').dialog({
821 | autoOpen: false,
822 | modal: true,
823 | title: 'Frag the World!',
824 | width: 500,
825 | buttons: [
826 | {
827 | text: "Ok",
828 | click: function() {
829 | localStorage.clear();
830 | storage.initialize_storage();
831 | $( this ).dialog( "close" );
832 | view_settings();
833 | }
834 | },
835 | {
836 | text: "Cancel",
837 | click: function() {
838 | $( this ).dialog( "close" );
839 | }
840 | }
841 | ]
842 | });
843 |
844 | $container.find('#delete_localstorage').on('click', function ()
845 | {
846 | $('#delete_localstorage_dialog').dialog("open");
847 | });
848 |
849 | // Condition Monitor global setting
850 | $container.find('[setting="condition_monitor"]').buttonset();
851 | $container.find('[setting="condition_monitor"] input#' + storage.setting('condition_monitor')).click();
852 |
853 | $container.find('[setting="condition_monitor"] input').click(function()
854 | {
855 | localStorage.setting_condition_monitor = $(this).attr('id');
856 | });
857 |
858 | // Wound Penalties global setting
859 | $container.find('[setting="wound_penalty"]').buttonset();
860 | $container.find('[setting="wound_penalty"] input#' + storage.setting('wound_penalty')).click();
861 |
862 | $container.find('[setting="wound_penalty"] input').click(function()
863 | {
864 | localStorage.setting_wound_penalty = $(this).attr('id');
865 | });
866 | }
867 |
868 | function view_intro()
869 | {
870 | var $container = $('.main_content').empty();
871 |
872 | var template = render.get_template('main_screen');
873 |
874 | $container.append(template);
875 |
876 | var $build_mismatch_dialog = $('.build_mismatch_nuke_storage').dialog({
877 | autoOpen: false,
878 | modal: true,
879 | title: 'Fatal Version Mismatch',
880 | width: 500,
881 | buttons: [
882 | {
883 | text: "Ok",
884 | click: function() {
885 | localStorage.clear();
886 | storage.initialize_storage();
887 | $( this ).dialog( "close" );
888 | }
889 | },
890 | {
891 | text: "Cancel",
892 | click: function() {
893 | $( this ).dialog( "close" );
894 | }
895 | }
896 | ]
897 | });
898 |
899 | if (localStorage.build_id && localStorage.build_id !== build_id)
900 | {
901 | $build_mismatch_dialog.find('[software_version]').html('Version ' + build_id);
902 |
903 | $build_mismatch_dialog.find('[stored_version]').html('Version ' + localStorage.build_id);
904 |
905 | // TODO make this link actually work and point to the 'right' place.
906 | $build_mismatch_dialog.find('[link]').html($(' ').attr('href', download_url).text(download_url));
907 |
908 | // TODO remove this line when the above is done
909 | $build_mismatch_dialog.find('[link]').detach();
910 |
911 | $('.build_mismatch_nuke_storage').dialog('open');
912 | }
913 | }
914 |
915 | function setup_controls()
916 | {
917 | render.get_templates();
918 |
919 | // Initialization checks
920 | if (!localStorage)
921 | {
922 | // TODO prettyify
923 | alert('Your browser is too old to run this utility. Please upgrade your browser.');
924 | }
925 |
926 | // Initial setup of localStorage if it hasn't been
927 | if (!localStorage.hasOwnProperty(['build_id']))
928 | {
929 | storage.initialize_storage();
930 | }
931 |
932 | // Set up the roll controls in the top bar
933 | $('.top_bar button').button();
934 |
935 | $('.top_bar .numbers button').addClass('smaller_button');
936 |
937 | $('.top_bar .numbers button').on('click', function ()
938 | {
939 | var dice = $(this).attr('roll'), options = {}, addition = '\n';
940 |
941 | var timestamp = new Date();
942 |
943 | addition += timestamp.toLocaleTimeString() + '; pool of ' + dice;
944 |
945 | if ($('.top_bar_roller #explode').is(':checked'))
946 | {
947 | options.pre_edge = true;
948 | addition += ' exploding';
949 | }
950 |
951 | addition += '\n';
952 |
953 | var results = roll.d(dice, options);
954 |
955 | addition += results.rolls.join(', ') + '\n' + results.hits;
956 |
957 | addition += (results.hits === 1) ? ' hit' : ' hits';
958 |
959 | // Our roller marks it as either a glitch or a critical glitch, not both
960 | if (results.crit_glitch)
961 | {
962 | addition += ', CRITICAL GLITCH!';
963 | }
964 | else if (results.glitch)
965 | {
966 | addition += ', glitch roll.';
967 | }
968 |
969 | update_results_text(addition);
970 | });
971 |
972 | $('textarea#roll_results').html('');
973 |
974 | function update_results_text(text)
975 | {
976 | var $results = $('.top_bar_roller #roll_results');
977 | var results_text = $results.val() + "\n" + text;
978 |
979 | $results.val(results_text);
980 |
981 | if ($results.val().length)
982 | {
983 | $results.scrollTop($results[0].scrollHeight - $results.height());
984 | }
985 |
986 | $('.top_bar .numbers button').blur();
987 | }
988 |
989 | // Set up the buttons in the menu
990 | $('.menu button').button();
991 |
992 | $('.menu .cast').on('click', view_cast);
993 |
994 | $('.menu .minion').on('click', view_generator);
995 |
996 | $('.menu .contact').on('click', view_contact).hide(); // TODO Hidden until I have it working
997 |
998 | $('.menu .run').on('click', view_run).hide(); // TODO Hidden until I have it working
999 |
1000 | $('.menu .faq').on('click', view_faq);
1001 |
1002 | $('.menu .settings').on('click', view_settings);
1003 |
1004 | $('.top_bar .title').on('click', view_intro);
1005 |
1006 | $('.top_bar .version').text('Version - ' + build_id);
1007 |
1008 | view_intro();
1009 | }
1010 |
1011 | var build_id = '0.12';
1012 | var total_count = 98;
1013 | var download_url = 'https://github.com/toktic/sr_gmt';
1014 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Shadowrun Mob Master
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
24 |
25 |
26 |
27 |
Shadowrun Mob Master
28 |
29 |
30 |
31 |
32 | 01
33 | 02
34 | 03
35 | 04
36 | 05
37 | 06
38 | 07
39 | 08
40 | 09
41 | 10
42 |
43 |
44 | 11
45 | 12
46 | 13
47 | 14
48 | 15
49 | 16
50 | 17
51 | 18
52 | 19
53 | 20
54 |
55 |
56 | 21
57 | 22
58 | 23
59 | 24
60 | 25
61 | 26
62 | 27
63 | 28
64 | 29
65 | 30
66 |
67 |
68 | 31
69 | 32
70 | 33
71 | 34
72 | 35
73 | 36
74 | 37
75 | 38
76 | 39
77 | 40
78 |
79 |
80 |
90 |
91 |
92 |
93 |
94 |
102 |
103 |
104 |
105 |
106 |
107 |
Welcome to my Shadowrun GM Mob Master!
108 |
109 |
The GM Mob Master was created as a set of tools for Shadowrun game masters. It started as a way of generating extra NPCs for combat-heavy runs, and as a thought exercise in how to expand the list of grunts in the Core Rulebook to more situations. The intent was to include generators and lists that would make it easier for game masters to get mechanics out of the way and spend more time telling stories. My hope was that this will eventually make it easy for game masters to generate an entire session of entertainment with the click of a few buttons.
110 |
111 |
It is divided into several sections, all of which can be accessed by the buttons on the left. There is also a dice-simulator at the top to let you quickly simulate a fist-full of dice. Remember, as a GM you don't have to be honest with how many successes or failures you "rolled"...
112 |
113 |
114 | The first section is Cast of Shadows . This holds the full assortment of npcs that you create and save for use later. The "Full Cast" lists all saved cast members. You can create plenty of custom tabs and list only the cast members you need for a scene on that tab.
115 | Next up is the NPC Generator . You can use the tabs within to create a single NPC or a horde of them. Just be aware that if you create one and don't save them to the Cast of Shadows , they will disappear and be lost.
116 | Settings has a selection of, you guessed it, Settings that you can change. This also has a Nuke button if you want to delete all of your saved information and start over from scratch.
117 |
118 |
119 |
This has also served as a creative outlet for programming for my own enjoyment. I have spent quite a few hours on this utility in the name of fun. There was never any intention of turning this into a money-generating enterprise. I don't want to deal with the legal or infrastructure headaches. Though if you happen to find me at a convention and want to say thanks, you can buy me a beer at GenCon. If you want to complain or sue me, I don't exist and don't attend GenCon. :D
120 |
121 |
On data-retention: I use a piece of web programming functionality called Local Storage that act a little bit like cookies. The data is stored in your browser's cache for you for that browser. I have to specify both of those because it's not shared to other users on your computer, nor even other browsers. It certainly isn't shared to other computers, and I don't have access to it at all. So if you spend the time to create and save a cast of shadow denizens, make sure you create it on the computer you're bringing to the game session. Or print them out, that works too!
122 |
123 |
As with many side projects, there is a whole list of things I want to add to this utility. There are plenty of features that I haven't had the time to add yet. Since I'm not getting paid to work on this, it only happens when I do have spare time that isn't being spent on other things. And there will eventually come a time that I wash my hands of this and let it sit on a shelf to die from neglect. It may not be a nice thing to say, but it's the truth. This utility isn't meant to be a replacement for the Shadowrun books, either. I make an assumption that users are familiar with the basics of Shadowrun, from what an Adept is to what constitutes a Mr. Johnson, and are able to look things up as needed, like specific equipment information. It is meant as a supplement and assistant, much like how HeroLab and Chummer make PC creation easier for runners. I will not try to make this fully comprehensive and cover every possible option. There are too many source books to keep up with and this isn't meant to be a replacement for sourcebooks.
124 |
125 |
Have fun with this tool! It is meant to be used and intended to make your life easier as a GM. Speaking of which, always try to offer your players the chance to make a deal with a dragon.
126 |
127 |
The Topps Company, Inc. has sole ownership of the names, logo, artwork, marks, photographs, sounds, audio, video and/or any proprietary matrial used in connection with the game Shadowrun. The Topps Company, Inc. has granted permission to GM Mob Master to use such names, logos, artwork, marks and/or any proprietary materials for promotional and informational purposes on its website but does not endorse, and is not affiliated with this utility in any official capacity whatsoever. Original content within GM Mob Master is licensed under the Creative Commons Attribution-NonCommercial 4.0 International. So if you use anything here, please give credit where it is due. GM Mob Master is provided "as is" and no warranties or guarantees are provided or inferred. In no event shall the copyright holder or contributors be liable for damage however they are caused. Don't be a jerk!
128 |
129 |
130 |
The software version you are running does not match the stored data's version which will cause issues. Do you want to delete the local stored data and start from scratch?
131 |
Alternately, look at the stored data version and grab that version of code instead.
132 |
133 | a
134 | b
135 | c
136 |
137 |
138 |
139 |
140 |
141 |
142 |
145 |
146 |
Welcome to your cast of shadows! You can edit tab names and rearrange them here.
147 |
148 |
149 |
150 |
151 |
152 | Add tab:
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 | Edit tabs:
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
FAQ
178 |
179 |
Frequently Asked Questions & Functionally Annoying Quirks
180 |
181 |
182 | This is a listing of questions that have been asked or other points of knowledge that are likely relevant to new users.
183 | This is also a collection of quirks and annoyances that have cropped up as part of turning a rulebook into an application.
184 |
185 |
186 |
187 | NPC stats: The attributes for all NPCs follow a curve the higher up the professional rating goes.
188 | Cyberlimbs: All cyberlimbs (arms & legs) are assumed to have the same Strength and Agility scores as the NPC. So there is no additional averaging of attributes or other nonsense.
189 |
190 |
191 |
192 |
193 |
Settings
194 |
195 |
This will remove all settings, NPCs, everything. It's the frag grenade in the china shop. Everything will be lost, nothing will be saved.
196 |
Nuke it from orbit
197 |
198 |
199 |
200 |
Minion Condition Monitor
201 |
Minions, by default, have a combined Condition Monitor based on their BODY and WILL scores. If you want your minions to have separate health tracks for Stun and Physical damage, make that change here.
202 |
Changes here will reflect on all minions created after this point. Any minions already existing will need to be edited individually.
203 |
204 | Combined
205 | Separate
206 |
207 |
208 |
209 |
220 |
221 |
222 |
This option cannot be undone. Once you delete everything here, you'll have to start all over again. On the plus side, this is great if you want to make sure no hacker finds traces of your activities here.
223 |
224 |
225 |
226 |
227 |
228 |
233 |
234 |
The NPC Generator is the concept that kicked off the creation of these tools. Here, you can easily create a bunch of thugs to harass the party or a high-powered HTR team to flush them out of a corp building.
235 |
When generating a single NPC, there are several categories and overall options you can choose from or leave it all up to chance. After the NPC is created, you can save them in the Cast of Shadows section. They can also be edited both immediately or later. However, they are not saved until they have been added to the Cast of Shadows .
236 |
Generating a group of NPCs works similarly, defining only broad strokes here and letting you individually refine the NPCs later.
237 |
Generating one of the Special Types only happens if you choose the option for it.
238 |
These NPCs use the sample grunts in the Core Rulebook as guidelines. They aren't meant to be perfect nor even optimal. Attributes and skill ranks are an approximation of what I considered reasonably good for a given profession and power level. Feel free to take all of these with a grain of salt.
239 |
Generate one NPC
240 |
Generate a group
241 |
242 |
243 |
244 |
245 | Name
246 |
247 |
248 |
249 |
250 | Race
251 |
252 | - Random
253 | Human
254 | Elf
255 | Dwarf
256 | Ork
257 | Troll
258 |
259 |
260 |
261 |
262 | Gender
263 |
264 | - Random
265 | Male
266 | Female
267 |
268 |
269 |
270 |
271 | Professional Rating
272 |
273 | - Random
274 | 0
275 | 1
276 | 2
277 | 3
278 | 4
279 | 5
280 | 6
281 |
282 |
283 |
284 |
285 | Professional Type
286 |
287 | - Random
288 | Civilian
289 | Thugs & Protesters
290 | Gang Member
291 | Corporate Security
292 | Law Enforcement
293 | Organized Crime
294 | Cultists & Politicos
295 | High Threat Response
296 | Special Operations
297 |
298 |
299 |
300 |
301 | Special Type?
302 |
303 | - No
304 | Lieutenant
305 | Decker
306 | Physical Adept
307 | Full Magician
308 |
309 |
310 |
311 |
312 |
313 |
Generate NPC
314 |
315 |
316 |
317 |
Discard
318 |
319 |
Add to Cast
320 |
321 |
Add to Specific Tab
322 |
323 |
324 |
325 |
326 |
327 | Mob Name
328 |
329 |
330 |
331 |
332 | Number
333 |
334 | 2 - 4 npcs
335 | 3 - 6 npcs
336 | 4 - 10 npcs
337 |
338 |
339 |
340 |
341 | Race
342 |
343 | - Individually Random
344 | All Human
345 | All Elf
346 | All Dwarf
347 | All Ork
348 | All Troll
349 |
350 |
351 |
352 |
353 | Gender
354 |
355 | - Individually Random
356 | All Male
357 | All Female
358 |
359 |
360 |
361 |
362 | Professional Rating
363 |
364 | - Group Random
365 | - Individually Random
366 | 0
367 | 1
368 | 2
369 | 3
370 | 4
371 | 5
372 | 6
373 |
374 |
375 |
376 |
377 | Professional Type
378 |
379 | - Group Random
380 | - Individually Random
381 | Civilian
382 | Thugs & Protesters
383 | Gang Member
384 | Corporate Security
385 | Law Enforcement
386 | Organized Crime
387 | Cultists & Politicos
388 | High Threat Response
389 | Special Operations
390 |
391 |
392 |
393 |
406 |
407 |
408 |
409 |
Generate Group
410 |
411 |
412 |
413 |
Discard All
414 |
415 |
Add all to Cast
416 |
417 |
Add to Specific Tab
418 |
419 |
420 |
421 |
422 |
423 |
424 |
425 |
426 |
427 |
428 |
429 |
430 |
431 |
432 |
BOD
433 |
AGI
434 |
REA
435 |
STR
436 |
WIL
437 |
LOG
438 |
INT
439 |
CHA
440 |
ESS
441 |
MAG
442 |
443 |
444 |
445 |
446 |
447 |
448 |
449 |
450 |
451 |
452 |
453 |
454 |
455 |
456 |
457 |
533 |
534 |
535 |
536 |
537 |
538 |
539 |
540 |
541 |
542 |
543 |
544 |
BOD
545 |
AGI
546 |
REA
547 |
STR
548 |
WIL
549 |
LOG
550 |
INT
551 |
CHA
552 |
ESS
553 |
MAG
554 |
555 |
556 |
557 |
558 |
559 |
560 |
561 |
562 |
563 |
564 |
565 |
566 |
567 |
667 |
668 |
669 |
670 |
671 |
672 |
673 |
674 |
675 |
676 |
677 |
678 |
679 |
680 | Male
681 | Female
682 |
683 |
684 | Human
685 | Elf
686 | Dwarf
687 | Ork
688 | Troll
689 |
690 |
691 |
692 |
BOD
693 |
AGI
694 |
REA
695 |
STR
696 |
WIL
697 |
LOG
698 |
INT
699 |
CHA
700 |
MAG
701 |
INIT
702 |
703 |
704 | Notes
705 |
706 |
707 |
708 |
709 |
710 |
711 |
712 |
713 |
714 |
715 |
716 |
717 |
718 |
719 |
787 |
788 |
789 |
798 |
799 |
800 |
801 |
802 |
803 |
804 |
805 |
806 |
807 |
808 |
809 |
810 |
811 |
816 |
817 |
822 |
823 |
824 |
825 |
826 |
827 |
828 |
833 |
834 |
835 |
836 |
837 |
838 |
839 |
840 |
841 |
842 |
843 |
844 |
849 |
850 |
851 |
You do not have any denizens saved to your Cast of Shadows. Nothing to see here, move along.
852 |
This is a full list of all denizens that you have added to your Cast of Shadows.
853 |
854 |
855 |
856 |
878 |
879 |
882 |
883 |
893 |
894 |
895 |
896 |
Add this NPC to one of the existing tabs.
897 |
898 |
899 |
Add
900 |
901 |
902 |
903 |
904 |
Add this NPC to a new tab.
905 |
906 |
907 |
Create & Add
908 |
909 |
910 |
911 |
912 |
913 |
Add these NPCs to one of the existing tabs.
914 |
915 |
916 |
Add
917 |
918 |
919 |
920 |
921 |
Add these NPCs to a new tab.
922 |
923 |
924 |
Create & Add
925 |
926 |
927 |
928 |
929 |
--------------------------------------------------------------------------------
/css/jquery-ui.css:
--------------------------------------------------------------------------------
1 | /*! jQuery UI - v1.12.1 - 2017-07-23
2 | * http://jqueryui.com
3 | * Includes: draggable.css, core.css, resizable.css, selectable.css, sortable.css, accordion.css, autocomplete.css, menu.css, button.css, controlgroup.css, checkboxradio.css, datepicker.css, dialog.css, progressbar.css, selectmenu.css, slider.css, spinner.css, tabs.css, tooltip.css, theme.css
4 | * To view and modify this theme, visit http://jqueryui.com/themeroller/?scope=&folderName=custom-theme&bgImgOpacityHeader=40&bgImgOpacityContent=55&bgImgOpacityDefault=40&bgImgOpacityHover=60&bgImgOpacityActive=0&bgImgOpacityHighlight=30&bgImgOpacityError=30&cornerRadiusShadow=12px&offsetLeftShadow=-6px&offsetTopShadow=-6px&thicknessShadow=6px&opacityShadow=50&bgImgOpacityShadow=0&bgTextureShadow=flat&bgColorShadow=%23303030&opacityOverlay=30&bgImgOpacityOverlay=50&bgTextureOverlay=diagonals_small&bgColorOverlay=%23262626&iconColorError=%23ffffff&fcError=%23ffffff&borderColorError=%23cd0a0a&bgTextureError=hexagon&bgColorError=%23a32d00&iconColorHighlight=%23000000&fcHighlight=%23030303&borderColorHighlight=%23052f57&bgTextureHighlight=hexagon&bgColorHighlight=%230b58a2&iconColorActive=%23ffffff&fcActive=%23ffffff&borderColorActive=%23696969&bgTextureActive=flat&bgColorActive=%234c4c4c&iconColorHover=%239ccdfc&fcHover=%23ffffff&borderColorHover=%23000000&bgTextureHover=diagonals_small&bgColorHover=%23000000&iconColorDefault=%2300498f&fcDefault=%2375abff&borderColorDefault=%23333333&bgTextureDefault=diagonals_small&bgColorDefault=%230a0a0a&iconColorContent=%2375abff&fcContent=%23ffffff&borderColorContent=%234a4a4a&bgTextureContent=flat&bgColorContent=%23000000&iconColorHeader=%231f1f1f&fcHeader=%23222222&borderColorHeader=%23000000&bgTextureHeader=inset_soft&bgColorHeader=%2300498f&cornerRadius=6px&fwDefault=bold&fsDefault=1.1em&ffDefault=Segoe%20UI%2CHelvetica%2CArial%2Csans-serif
5 | * Copyright jQuery Foundation and other contributors; Licensed MIT */
6 |
7 | .ui-draggable-handle {
8 | -ms-touch-action: none;
9 | touch-action: none;
10 | }
11 | /* Layout helpers
12 | ----------------------------------*/
13 | .ui-helper-hidden {
14 | display: none;
15 | }
16 | .ui-helper-hidden-accessible {
17 | border: 0;
18 | clip: rect(0 0 0 0);
19 | height: 1px;
20 | margin: -1px;
21 | overflow: hidden;
22 | padding: 0;
23 | position: absolute;
24 | width: 1px;
25 | }
26 | .ui-helper-reset {
27 | margin: 0;
28 | padding: 0;
29 | border: 0;
30 | outline: 0;
31 | line-height: 1.3;
32 | text-decoration: none;
33 | font-size: 100%;
34 | list-style: none;
35 | }
36 | .ui-helper-clearfix:before,
37 | .ui-helper-clearfix:after {
38 | content: "";
39 | display: table;
40 | border-collapse: collapse;
41 | }
42 | .ui-helper-clearfix:after {
43 | clear: both;
44 | }
45 | .ui-helper-zfix {
46 | width: 100%;
47 | height: 100%;
48 | top: 0;
49 | left: 0;
50 | position: absolute;
51 | opacity: 0;
52 | filter:Alpha(Opacity=0); /* support: IE8 */
53 | }
54 |
55 | .ui-front {
56 | z-index: 100;
57 | }
58 |
59 |
60 | /* Interaction Cues
61 | ----------------------------------*/
62 | .ui-state-disabled {
63 | cursor: default !important;
64 | pointer-events: none;
65 | }
66 |
67 |
68 | /* Icons
69 | ----------------------------------*/
70 | .ui-icon {
71 | display: inline-block;
72 | vertical-align: middle;
73 | margin-top: -.25em;
74 | position: relative;
75 | text-indent: -99999px;
76 | overflow: hidden;
77 | background-repeat: no-repeat;
78 | }
79 |
80 | .ui-widget-icon-block {
81 | left: 50%;
82 | margin-left: -8px;
83 | display: block;
84 | }
85 |
86 | /* Misc visuals
87 | ----------------------------------*/
88 |
89 | /* Overlays */
90 | .ui-widget-overlay {
91 | position: fixed;
92 | top: 0;
93 | left: 0;
94 | width: 100%;
95 | height: 100%;
96 | }
97 | .ui-resizable {
98 | position: relative;
99 | }
100 | .ui-resizable-handle {
101 | position: absolute;
102 | font-size: 0.1px;
103 | display: block;
104 | -ms-touch-action: none;
105 | touch-action: none;
106 | }
107 | .ui-resizable-disabled .ui-resizable-handle,
108 | .ui-resizable-autohide .ui-resizable-handle {
109 | display: none;
110 | }
111 | .ui-resizable-n {
112 | cursor: n-resize;
113 | height: 7px;
114 | width: 100%;
115 | top: -5px;
116 | left: 0;
117 | }
118 | .ui-resizable-s {
119 | cursor: s-resize;
120 | height: 7px;
121 | width: 100%;
122 | bottom: -5px;
123 | left: 0;
124 | }
125 | .ui-resizable-e {
126 | cursor: e-resize;
127 | width: 7px;
128 | right: -5px;
129 | top: 0;
130 | height: 100%;
131 | }
132 | .ui-resizable-w {
133 | cursor: w-resize;
134 | width: 7px;
135 | left: -5px;
136 | top: 0;
137 | height: 100%;
138 | }
139 | .ui-resizable-se {
140 | cursor: se-resize;
141 | width: 12px;
142 | height: 12px;
143 | right: 1px;
144 | bottom: 1px;
145 | }
146 | .ui-resizable-sw {
147 | cursor: sw-resize;
148 | width: 9px;
149 | height: 9px;
150 | left: -5px;
151 | bottom: -5px;
152 | }
153 | .ui-resizable-nw {
154 | cursor: nw-resize;
155 | width: 9px;
156 | height: 9px;
157 | left: -5px;
158 | top: -5px;
159 | }
160 | .ui-resizable-ne {
161 | cursor: ne-resize;
162 | width: 9px;
163 | height: 9px;
164 | right: -5px;
165 | top: -5px;
166 | }
167 | .ui-selectable {
168 | -ms-touch-action: none;
169 | touch-action: none;
170 | }
171 | .ui-selectable-helper {
172 | position: absolute;
173 | z-index: 100;
174 | border: 1px dotted black;
175 | }
176 | .ui-sortable-handle {
177 | -ms-touch-action: none;
178 | touch-action: none;
179 | }
180 | .ui-accordion .ui-accordion-header {
181 | display: block;
182 | cursor: pointer;
183 | position: relative;
184 | margin: 2px 0 0 0;
185 | padding: .5em .5em .5em .7em;
186 | font-size: 100%;
187 | }
188 | .ui-accordion .ui-accordion-content {
189 | padding: 1em 2.2em;
190 | border-top: 0;
191 | overflow: auto;
192 | }
193 | .ui-autocomplete {
194 | position: absolute;
195 | top: 0;
196 | left: 0;
197 | cursor: default;
198 | }
199 | .ui-menu {
200 | list-style: none;
201 | padding: 0;
202 | margin: 0;
203 | display: block;
204 | outline: 0;
205 | }
206 | .ui-menu .ui-menu {
207 | position: absolute;
208 | }
209 | .ui-menu .ui-menu-item {
210 | margin: 0;
211 | cursor: pointer;
212 | /* support: IE10, see #8844 */
213 | list-style-image: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");
214 | }
215 | .ui-menu .ui-menu-item-wrapper {
216 | position: relative;
217 | padding: 3px 1em 3px .4em;
218 | }
219 | .ui-menu .ui-menu-divider {
220 | margin: 5px 0;
221 | height: 0;
222 | font-size: 0;
223 | line-height: 0;
224 | border-width: 1px 0 0 0;
225 | }
226 | .ui-menu .ui-state-focus,
227 | .ui-menu .ui-state-active {
228 | margin: -1px;
229 | }
230 |
231 | /* icon support */
232 | .ui-menu-icons {
233 | position: relative;
234 | }
235 | .ui-menu-icons .ui-menu-item-wrapper {
236 | padding-left: 2em;
237 | }
238 |
239 | /* left-aligned */
240 | .ui-menu .ui-icon {
241 | position: absolute;
242 | top: 0;
243 | bottom: 0;
244 | left: .2em;
245 | margin: auto 0;
246 | }
247 |
248 | /* right-aligned */
249 | .ui-menu .ui-menu-icon {
250 | left: auto;
251 | right: 0;
252 | }
253 | .ui-button {
254 | padding: .4em 1em;
255 | display: inline-block;
256 | position: relative;
257 | line-height: normal;
258 | margin-right: .1em;
259 | cursor: pointer;
260 | vertical-align: middle;
261 | text-align: center;
262 | -webkit-user-select: none;
263 | -moz-user-select: none;
264 | -ms-user-select: none;
265 | user-select: none;
266 |
267 | /* Support: IE <= 11 */
268 | overflow: visible;
269 | }
270 |
271 | .ui-button,
272 | .ui-button:link,
273 | .ui-button:visited,
274 | .ui-button:hover,
275 | .ui-button:active {
276 | text-decoration: none;
277 | }
278 |
279 | /* to make room for the icon, a width needs to be set here */
280 | .ui-button-icon-only {
281 | width: 2em;
282 | box-sizing: border-box;
283 | text-indent: -9999px;
284 | white-space: nowrap;
285 | }
286 |
287 | /* no icon support for input elements */
288 | input.ui-button.ui-button-icon-only {
289 | text-indent: 0;
290 | }
291 |
292 | /* button icon element(s) */
293 | .ui-button-icon-only .ui-icon {
294 | position: absolute;
295 | top: 50%;
296 | left: 50%;
297 | margin-top: -8px;
298 | margin-left: -8px;
299 | }
300 |
301 | .ui-button.ui-icon-notext .ui-icon {
302 | padding: 0;
303 | width: 2.1em;
304 | height: 2.1em;
305 | text-indent: -9999px;
306 | white-space: nowrap;
307 |
308 | }
309 |
310 | input.ui-button.ui-icon-notext .ui-icon {
311 | width: auto;
312 | height: auto;
313 | text-indent: 0;
314 | white-space: normal;
315 | padding: .4em 1em;
316 | }
317 |
318 | /* workarounds */
319 | /* Support: Firefox 5 - 40 */
320 | input.ui-button::-moz-focus-inner,
321 | button.ui-button::-moz-focus-inner {
322 | border: 0;
323 | padding: 0;
324 | }
325 | .ui-controlgroup {
326 | vertical-align: middle;
327 | display: inline-block;
328 | }
329 | .ui-controlgroup > .ui-controlgroup-item {
330 | float: left;
331 | margin-left: 0;
332 | margin-right: 0;
333 | }
334 | .ui-controlgroup > .ui-controlgroup-item:focus,
335 | .ui-controlgroup > .ui-controlgroup-item.ui-visual-focus {
336 | z-index: 9999;
337 | }
338 | .ui-controlgroup-vertical > .ui-controlgroup-item {
339 | display: block;
340 | float: none;
341 | width: 100%;
342 | margin-top: 0;
343 | margin-bottom: 0;
344 | text-align: left;
345 | }
346 | .ui-controlgroup-vertical .ui-controlgroup-item {
347 | box-sizing: border-box;
348 | }
349 | .ui-controlgroup .ui-controlgroup-label {
350 | padding: .4em 1em;
351 | }
352 | .ui-controlgroup .ui-controlgroup-label span {
353 | font-size: 80%;
354 | }
355 | .ui-controlgroup-horizontal .ui-controlgroup-label + .ui-controlgroup-item {
356 | border-left: none;
357 | }
358 | .ui-controlgroup-vertical .ui-controlgroup-label + .ui-controlgroup-item {
359 | border-top: none;
360 | }
361 | .ui-controlgroup-horizontal .ui-controlgroup-label.ui-widget-content {
362 | border-right: none;
363 | }
364 | .ui-controlgroup-vertical .ui-controlgroup-label.ui-widget-content {
365 | border-bottom: none;
366 | }
367 |
368 | /* Spinner specific style fixes */
369 | .ui-controlgroup-vertical .ui-spinner-input {
370 |
371 | /* Support: IE8 only, Android < 4.4 only */
372 | width: 75%;
373 | width: calc( 100% - 2.4em );
374 | }
375 | .ui-controlgroup-vertical .ui-spinner .ui-spinner-up {
376 | border-top-style: solid;
377 | }
378 |
379 | .ui-checkboxradio-label .ui-icon-background {
380 | box-shadow: inset 1px 1px 1px #ccc;
381 | border-radius: .12em;
382 | border: none;
383 | }
384 | .ui-checkboxradio-radio-label .ui-icon-background {
385 | width: 16px;
386 | height: 16px;
387 | border-radius: 1em;
388 | overflow: visible;
389 | border: none;
390 | }
391 | .ui-checkboxradio-radio-label.ui-checkboxradio-checked .ui-icon,
392 | .ui-checkboxradio-radio-label.ui-checkboxradio-checked:hover .ui-icon {
393 | background-image: none;
394 | width: 8px;
395 | height: 8px;
396 | border-width: 4px;
397 | border-style: solid;
398 | }
399 | .ui-checkboxradio-disabled {
400 | pointer-events: none;
401 | }
402 | .ui-datepicker {
403 | width: 17em;
404 | padding: .2em .2em 0;
405 | display: none;
406 | }
407 | .ui-datepicker .ui-datepicker-header {
408 | position: relative;
409 | padding: .2em 0;
410 | }
411 | .ui-datepicker .ui-datepicker-prev,
412 | .ui-datepicker .ui-datepicker-next {
413 | position: absolute;
414 | top: 2px;
415 | width: 1.8em;
416 | height: 1.8em;
417 | }
418 | .ui-datepicker .ui-datepicker-prev-hover,
419 | .ui-datepicker .ui-datepicker-next-hover {
420 | top: 1px;
421 | }
422 | .ui-datepicker .ui-datepicker-prev {
423 | left: 2px;
424 | }
425 | .ui-datepicker .ui-datepicker-next {
426 | right: 2px;
427 | }
428 | .ui-datepicker .ui-datepicker-prev-hover {
429 | left: 1px;
430 | }
431 | .ui-datepicker .ui-datepicker-next-hover {
432 | right: 1px;
433 | }
434 | .ui-datepicker .ui-datepicker-prev span,
435 | .ui-datepicker .ui-datepicker-next span {
436 | display: block;
437 | position: absolute;
438 | left: 50%;
439 | margin-left: -8px;
440 | top: 50%;
441 | margin-top: -8px;
442 | }
443 | .ui-datepicker .ui-datepicker-title {
444 | margin: 0 2.3em;
445 | line-height: 1.8em;
446 | text-align: center;
447 | }
448 | .ui-datepicker .ui-datepicker-title select {
449 | font-size: 1em;
450 | margin: 1px 0;
451 | }
452 | .ui-datepicker select.ui-datepicker-month,
453 | .ui-datepicker select.ui-datepicker-year {
454 | width: 45%;
455 | }
456 | .ui-datepicker table {
457 | width: 100%;
458 | font-size: .9em;
459 | border-collapse: collapse;
460 | margin: 0 0 .4em;
461 | }
462 | .ui-datepicker th {
463 | padding: .7em .3em;
464 | text-align: center;
465 | font-weight: bold;
466 | border: 0;
467 | }
468 | .ui-datepicker td {
469 | border: 0;
470 | padding: 1px;
471 | }
472 | .ui-datepicker td span,
473 | .ui-datepicker td a {
474 | display: block;
475 | padding: .2em;
476 | text-align: right;
477 | text-decoration: none;
478 | }
479 | .ui-datepicker .ui-datepicker-buttonpane {
480 | background-image: none;
481 | margin: .7em 0 0 0;
482 | padding: 0 .2em;
483 | border-left: 0;
484 | border-right: 0;
485 | border-bottom: 0;
486 | }
487 | .ui-datepicker .ui-datepicker-buttonpane button {
488 | float: right;
489 | margin: .5em .2em .4em;
490 | cursor: pointer;
491 | padding: .2em .6em .3em .6em;
492 | width: auto;
493 | overflow: visible;
494 | }
495 | .ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current {
496 | float: left;
497 | }
498 |
499 | /* with multiple calendars */
500 | .ui-datepicker.ui-datepicker-multi {
501 | width: auto;
502 | }
503 | .ui-datepicker-multi .ui-datepicker-group {
504 | float: left;
505 | }
506 | .ui-datepicker-multi .ui-datepicker-group table {
507 | width: 95%;
508 | margin: 0 auto .4em;
509 | }
510 | .ui-datepicker-multi-2 .ui-datepicker-group {
511 | width: 50%;
512 | }
513 | .ui-datepicker-multi-3 .ui-datepicker-group {
514 | width: 33.3%;
515 | }
516 | .ui-datepicker-multi-4 .ui-datepicker-group {
517 | width: 25%;
518 | }
519 | .ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,
520 | .ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header {
521 | border-left-width: 0;
522 | }
523 | .ui-datepicker-multi .ui-datepicker-buttonpane {
524 | clear: left;
525 | }
526 | .ui-datepicker-row-break {
527 | clear: both;
528 | width: 100%;
529 | font-size: 0;
530 | }
531 |
532 | /* RTL support */
533 | .ui-datepicker-rtl {
534 | direction: rtl;
535 | }
536 | .ui-datepicker-rtl .ui-datepicker-prev {
537 | right: 2px;
538 | left: auto;
539 | }
540 | .ui-datepicker-rtl .ui-datepicker-next {
541 | left: 2px;
542 | right: auto;
543 | }
544 | .ui-datepicker-rtl .ui-datepicker-prev:hover {
545 | right: 1px;
546 | left: auto;
547 | }
548 | .ui-datepicker-rtl .ui-datepicker-next:hover {
549 | left: 1px;
550 | right: auto;
551 | }
552 | .ui-datepicker-rtl .ui-datepicker-buttonpane {
553 | clear: right;
554 | }
555 | .ui-datepicker-rtl .ui-datepicker-buttonpane button {
556 | float: left;
557 | }
558 | .ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,
559 | .ui-datepicker-rtl .ui-datepicker-group {
560 | float: right;
561 | }
562 | .ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,
563 | .ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header {
564 | border-right-width: 0;
565 | border-left-width: 1px;
566 | }
567 |
568 | /* Icons */
569 | .ui-datepicker .ui-icon {
570 | display: block;
571 | text-indent: -99999px;
572 | overflow: hidden;
573 | background-repeat: no-repeat;
574 | left: .5em;
575 | top: .3em;
576 | }
577 | .ui-dialog {
578 | position: absolute;
579 | top: 0;
580 | left: 0;
581 | padding: .2em;
582 | outline: 0;
583 | }
584 | .ui-dialog .ui-dialog-titlebar {
585 | padding: .4em 1em;
586 | position: relative;
587 | }
588 | .ui-dialog .ui-dialog-title {
589 | float: left;
590 | margin: .1em 0;
591 | white-space: nowrap;
592 | width: 90%;
593 | overflow: hidden;
594 | text-overflow: ellipsis;
595 | }
596 | .ui-dialog .ui-dialog-titlebar-close {
597 | position: absolute;
598 | right: .3em;
599 | top: 50%;
600 | width: 20px;
601 | margin: -10px 0 0 0;
602 | padding: 1px;
603 | height: 20px;
604 | }
605 | .ui-dialog .ui-dialog-content {
606 | position: relative;
607 | border: 0;
608 | padding: .5em 1em;
609 | background: none;
610 | overflow: auto;
611 | }
612 | .ui-dialog .ui-dialog-buttonpane {
613 | text-align: left;
614 | border-width: 1px 0 0 0;
615 | background-image: none;
616 | margin-top: .5em;
617 | padding: .3em 1em .5em .4em;
618 | }
619 | .ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset {
620 | float: right;
621 | }
622 | .ui-dialog .ui-dialog-buttonpane button {
623 | margin: .5em .4em .5em 0;
624 | cursor: pointer;
625 | }
626 | .ui-dialog .ui-resizable-n {
627 | height: 2px;
628 | top: 0;
629 | }
630 | .ui-dialog .ui-resizable-e {
631 | width: 2px;
632 | right: 0;
633 | }
634 | .ui-dialog .ui-resizable-s {
635 | height: 2px;
636 | bottom: 0;
637 | }
638 | .ui-dialog .ui-resizable-w {
639 | width: 2px;
640 | left: 0;
641 | }
642 | .ui-dialog .ui-resizable-se,
643 | .ui-dialog .ui-resizable-sw,
644 | .ui-dialog .ui-resizable-ne,
645 | .ui-dialog .ui-resizable-nw {
646 | width: 7px;
647 | height: 7px;
648 | }
649 | .ui-dialog .ui-resizable-se {
650 | right: 0;
651 | bottom: 0;
652 | }
653 | .ui-dialog .ui-resizable-sw {
654 | left: 0;
655 | bottom: 0;
656 | }
657 | .ui-dialog .ui-resizable-ne {
658 | right: 0;
659 | top: 0;
660 | }
661 | .ui-dialog .ui-resizable-nw {
662 | left: 0;
663 | top: 0;
664 | }
665 | .ui-draggable .ui-dialog-titlebar {
666 | cursor: move;
667 | }
668 | .ui-progressbar {
669 | height: 2em;
670 | text-align: left;
671 | overflow: hidden;
672 | }
673 | .ui-progressbar .ui-progressbar-value {
674 | margin: -1px;
675 | height: 100%;
676 | }
677 | .ui-progressbar .ui-progressbar-overlay {
678 | background: url("data:image/gif;base64,R0lGODlhKAAoAIABAAAAAP///yH/C05FVFNDQVBFMi4wAwEAAAAh+QQJAQABACwAAAAAKAAoAAACkYwNqXrdC52DS06a7MFZI+4FHBCKoDeWKXqymPqGqxvJrXZbMx7Ttc+w9XgU2FB3lOyQRWET2IFGiU9m1frDVpxZZc6bfHwv4c1YXP6k1Vdy292Fb6UkuvFtXpvWSzA+HycXJHUXiGYIiMg2R6W459gnWGfHNdjIqDWVqemH2ekpObkpOlppWUqZiqr6edqqWQAAIfkECQEAAQAsAAAAACgAKAAAApSMgZnGfaqcg1E2uuzDmmHUBR8Qil95hiPKqWn3aqtLsS18y7G1SzNeowWBENtQd+T1JktP05nzPTdJZlR6vUxNWWjV+vUWhWNkWFwxl9VpZRedYcflIOLafaa28XdsH/ynlcc1uPVDZxQIR0K25+cICCmoqCe5mGhZOfeYSUh5yJcJyrkZWWpaR8doJ2o4NYq62lAAACH5BAkBAAEALAAAAAAoACgAAAKVDI4Yy22ZnINRNqosw0Bv7i1gyHUkFj7oSaWlu3ovC8GxNso5fluz3qLVhBVeT/Lz7ZTHyxL5dDalQWPVOsQWtRnuwXaFTj9jVVh8pma9JjZ4zYSj5ZOyma7uuolffh+IR5aW97cHuBUXKGKXlKjn+DiHWMcYJah4N0lYCMlJOXipGRr5qdgoSTrqWSq6WFl2ypoaUAAAIfkECQEAAQAsAAAAACgAKAAAApaEb6HLgd/iO7FNWtcFWe+ufODGjRfoiJ2akShbueb0wtI50zm02pbvwfWEMWBQ1zKGlLIhskiEPm9R6vRXxV4ZzWT2yHOGpWMyorblKlNp8HmHEb/lCXjcW7bmtXP8Xt229OVWR1fod2eWqNfHuMjXCPkIGNileOiImVmCOEmoSfn3yXlJWmoHGhqp6ilYuWYpmTqKUgAAIfkECQEAAQAsAAAAACgAKAAAApiEH6kb58biQ3FNWtMFWW3eNVcojuFGfqnZqSebuS06w5V80/X02pKe8zFwP6EFWOT1lDFk8rGERh1TTNOocQ61Hm4Xm2VexUHpzjymViHrFbiELsefVrn6XKfnt2Q9G/+Xdie499XHd2g4h7ioOGhXGJboGAnXSBnoBwKYyfioubZJ2Hn0RuRZaflZOil56Zp6iioKSXpUAAAh+QQJAQABACwAAAAAKAAoAAACkoQRqRvnxuI7kU1a1UU5bd5tnSeOZXhmn5lWK3qNTWvRdQxP8qvaC+/yaYQzXO7BMvaUEmJRd3TsiMAgswmNYrSgZdYrTX6tSHGZO73ezuAw2uxuQ+BbeZfMxsexY35+/Qe4J1inV0g4x3WHuMhIl2jXOKT2Q+VU5fgoSUI52VfZyfkJGkha6jmY+aaYdirq+lQAACH5BAkBAAEALAAAAAAoACgAAAKWBIKpYe0L3YNKToqswUlvznigd4wiR4KhZrKt9Upqip61i9E3vMvxRdHlbEFiEXfk9YARYxOZZD6VQ2pUunBmtRXo1Lf8hMVVcNl8JafV38aM2/Fu5V16Bn63r6xt97j09+MXSFi4BniGFae3hzbH9+hYBzkpuUh5aZmHuanZOZgIuvbGiNeomCnaxxap2upaCZsq+1kAACH5BAkBAAEALAAAAAAoACgAAAKXjI8By5zf4kOxTVrXNVlv1X0d8IGZGKLnNpYtm8Lr9cqVeuOSvfOW79D9aDHizNhDJidFZhNydEahOaDH6nomtJjp1tutKoNWkvA6JqfRVLHU/QUfau9l2x7G54d1fl995xcIGAdXqMfBNadoYrhH+Mg2KBlpVpbluCiXmMnZ2Sh4GBqJ+ckIOqqJ6LmKSllZmsoq6wpQAAAh+QQJAQABACwAAAAAKAAoAAAClYx/oLvoxuJDkU1a1YUZbJ59nSd2ZXhWqbRa2/gF8Gu2DY3iqs7yrq+xBYEkYvFSM8aSSObE+ZgRl1BHFZNr7pRCavZ5BW2142hY3AN/zWtsmf12p9XxxFl2lpLn1rseztfXZjdIWIf2s5dItwjYKBgo9yg5pHgzJXTEeGlZuenpyPmpGQoKOWkYmSpaSnqKileI2FAAACH5BAkBAAEALAAAAAAoACgAAAKVjB+gu+jG4kORTVrVhRlsnn2dJ3ZleFaptFrb+CXmO9OozeL5VfP99HvAWhpiUdcwkpBH3825AwYdU8xTqlLGhtCosArKMpvfa1mMRae9VvWZfeB2XfPkeLmm18lUcBj+p5dnN8jXZ3YIGEhYuOUn45aoCDkp16hl5IjYJvjWKcnoGQpqyPlpOhr3aElaqrq56Bq7VAAAOw==");
679 | height: 100%;
680 | filter: alpha(opacity=25); /* support: IE8 */
681 | opacity: 0.25;
682 | }
683 | .ui-progressbar-indeterminate .ui-progressbar-value {
684 | background-image: none;
685 | }
686 | .ui-selectmenu-menu {
687 | padding: 0;
688 | margin: 0;
689 | position: absolute;
690 | top: 0;
691 | left: 0;
692 | display: none;
693 | }
694 | .ui-selectmenu-menu .ui-menu {
695 | overflow: auto;
696 | overflow-x: hidden;
697 | padding-bottom: 1px;
698 | }
699 | .ui-selectmenu-menu .ui-menu .ui-selectmenu-optgroup {
700 | font-size: 1em;
701 | font-weight: bold;
702 | line-height: 1.5;
703 | padding: 2px 0.4em;
704 | margin: 0.5em 0 0 0;
705 | height: auto;
706 | border: 0;
707 | }
708 | .ui-selectmenu-open {
709 | display: block;
710 | }
711 | .ui-selectmenu-text {
712 | display: block;
713 | margin-right: 20px;
714 | overflow: hidden;
715 | text-overflow: ellipsis;
716 | }
717 | .ui-selectmenu-button.ui-button {
718 | text-align: left;
719 | white-space: nowrap;
720 | width: 14em;
721 | }
722 | .ui-selectmenu-icon.ui-icon {
723 | float: right;
724 | margin-top: 0;
725 | }
726 | .ui-slider {
727 | position: relative;
728 | text-align: left;
729 | }
730 | .ui-slider .ui-slider-handle {
731 | position: absolute;
732 | z-index: 2;
733 | width: 1.2em;
734 | height: 1.2em;
735 | cursor: default;
736 | -ms-touch-action: none;
737 | touch-action: none;
738 | }
739 | .ui-slider .ui-slider-range {
740 | position: absolute;
741 | z-index: 1;
742 | font-size: .7em;
743 | display: block;
744 | border: 0;
745 | background-position: 0 0;
746 | }
747 |
748 | /* support: IE8 - See #6727 */
749 | .ui-slider.ui-state-disabled .ui-slider-handle,
750 | .ui-slider.ui-state-disabled .ui-slider-range {
751 | filter: inherit;
752 | }
753 |
754 | .ui-slider-horizontal {
755 | height: .8em;
756 | }
757 | .ui-slider-horizontal .ui-slider-handle {
758 | top: -.3em;
759 | margin-left: -.6em;
760 | }
761 | .ui-slider-horizontal .ui-slider-range {
762 | top: 0;
763 | height: 100%;
764 | }
765 | .ui-slider-horizontal .ui-slider-range-min {
766 | left: 0;
767 | }
768 | .ui-slider-horizontal .ui-slider-range-max {
769 | right: 0;
770 | }
771 |
772 | .ui-slider-vertical {
773 | width: .8em;
774 | height: 100px;
775 | }
776 | .ui-slider-vertical .ui-slider-handle {
777 | left: -.3em;
778 | margin-left: 0;
779 | margin-bottom: -.6em;
780 | }
781 | .ui-slider-vertical .ui-slider-range {
782 | left: 0;
783 | width: 100%;
784 | }
785 | .ui-slider-vertical .ui-slider-range-min {
786 | bottom: 0;
787 | }
788 | .ui-slider-vertical .ui-slider-range-max {
789 | top: 0;
790 | }
791 | .ui-spinner {
792 | position: relative;
793 | display: inline-block;
794 | overflow: hidden;
795 | padding: 0;
796 | vertical-align: middle;
797 | }
798 | .ui-spinner-input {
799 | border: none;
800 | background: none;
801 | color: inherit;
802 | padding: .222em 0;
803 | margin: .2em 0;
804 | vertical-align: middle;
805 | margin-left: .4em;
806 | margin-right: 2em;
807 | }
808 | .ui-spinner-button {
809 | width: 1.6em;
810 | height: 50%;
811 | font-size: .5em;
812 | padding: 0;
813 | margin: 0;
814 | text-align: center;
815 | position: absolute;
816 | cursor: default;
817 | display: block;
818 | overflow: hidden;
819 | right: 0;
820 | }
821 | /* more specificity required here to override default borders */
822 | .ui-spinner a.ui-spinner-button {
823 | border-top-style: none;
824 | border-bottom-style: none;
825 | border-right-style: none;
826 | }
827 | .ui-spinner-up {
828 | top: 0;
829 | }
830 | .ui-spinner-down {
831 | bottom: 0;
832 | }
833 | .ui-tabs {
834 | position: relative;/* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */
835 | padding: .2em;
836 | }
837 | .ui-tabs .ui-tabs-nav {
838 | margin: 0;
839 | padding: .2em .2em 0;
840 | }
841 | .ui-tabs .ui-tabs-nav li {
842 | list-style: none;
843 | float: left;
844 | position: relative;
845 | top: 0;
846 | margin: 1px .2em 0 0;
847 | border-bottom-width: 0;
848 | padding: 0;
849 | white-space: nowrap;
850 | }
851 | .ui-tabs .ui-tabs-nav .ui-tabs-anchor {
852 | float: left;
853 | padding: .5em 1em;
854 | text-decoration: none;
855 | }
856 | .ui-tabs .ui-tabs-nav li.ui-tabs-active {
857 | margin-bottom: -1px;
858 | padding-bottom: 1px;
859 | }
860 | .ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor,
861 | .ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor,
862 | .ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor {
863 | cursor: text;
864 | }
865 | .ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor {
866 | cursor: pointer;
867 | }
868 | .ui-tabs .ui-tabs-panel {
869 | display: block;
870 | border-width: 0;
871 | padding: 1em 1.4em;
872 | background: none;
873 | }
874 | .ui-tooltip {
875 | padding: 8px;
876 | position: absolute;
877 | z-index: 9999;
878 | max-width: 300px;
879 | }
880 | body .ui-tooltip {
881 | border-width: 2px;
882 | }
883 |
884 | /* Component containers
885 | ----------------------------------*/
886 | .ui-widget {
887 | font-family: Segoe UI,Helvetica,Arial,sans-serif;
888 | font-size: 1.1em;
889 | }
890 | .ui-widget .ui-widget {
891 | font-size: 1em;
892 | }
893 | .ui-widget input,
894 | .ui-widget select,
895 | .ui-widget textarea,
896 | .ui-widget button {
897 | font-family: Segoe UI,Helvetica,Arial,sans-serif;
898 | font-size: 1em;
899 | }
900 | .ui-widget.ui-widget-content {
901 | border: 1px solid #333333;
902 | }
903 | .ui-widget-content {
904 | border: 1px solid #4a4a4a;
905 | background: #000000;
906 | color: #ffffff;
907 | }
908 | .ui-widget-content a {
909 | color: #ffffff;
910 | }
911 | .ui-widget-header {
912 | border: 1px solid #000000;
913 | background: #00498f url("images/ui-bg_inset-soft_40_00498f_1x100.png") 50% 50% repeat-x;
914 | color: #222222;
915 | font-weight: bold;
916 | }
917 | .ui-widget-header a {
918 | color: #222222;
919 | }
920 |
921 | /* Interaction states
922 | ----------------------------------*/
923 | .ui-state-default,
924 | .ui-widget-content .ui-state-default,
925 | .ui-widget-header .ui-state-default,
926 | .ui-button,
927 |
928 | /* We use html here because we need a greater specificity to make sure disabled
929 | works properly when clicked or hovered */
930 | html .ui-button.ui-state-disabled:hover,
931 | html .ui-button.ui-state-disabled:active {
932 | border: 1px solid #333333;
933 | background: #0a0a0a url("images/ui-bg_diagonals-small_40_0a0a0a_40x40.png") 50% 50% repeat;
934 | font-weight: bold;
935 | color: #75abff;
936 | }
937 | .ui-state-default a,
938 | .ui-state-default a:link,
939 | .ui-state-default a:visited,
940 | a.ui-button,
941 | a:link.ui-button,
942 | a:visited.ui-button,
943 | .ui-button {
944 | color: #75abff;
945 | text-decoration: none;
946 | }
947 | .ui-state-hover,
948 | .ui-widget-content .ui-state-hover,
949 | .ui-widget-header .ui-state-hover,
950 | .ui-state-focus,
951 | .ui-widget-content .ui-state-focus,
952 | .ui-widget-header .ui-state-focus,
953 | .ui-button:hover,
954 | .ui-button:focus {
955 | border: 1px solid #000000;
956 | background: #000000 url("images/ui-bg_diagonals-small_60_000000_40x40.png") 50% 50% repeat;
957 | font-weight: bold;
958 | color: #ffffff;
959 | }
960 | .ui-state-hover a,
961 | .ui-state-hover a:hover,
962 | .ui-state-hover a:link,
963 | .ui-state-hover a:visited,
964 | .ui-state-focus a,
965 | .ui-state-focus a:hover,
966 | .ui-state-focus a:link,
967 | .ui-state-focus a:visited,
968 | a.ui-button:hover,
969 | a.ui-button:focus {
970 | color: #ffffff;
971 | text-decoration: none;
972 | }
973 |
974 | .ui-visual-focus {
975 | box-shadow: 0 0 3px 1px rgb(94, 158, 214);
976 | }
977 | .ui-state-active,
978 | .ui-widget-content .ui-state-active,
979 | .ui-widget-header .ui-state-active,
980 | a.ui-button:active,
981 | .ui-button:active,
982 | .ui-button.ui-state-active:hover {
983 | border: 1px solid #696969;
984 | background: #4c4c4c;
985 | font-weight: bold;
986 | color: #ffffff;
987 | }
988 | .ui-icon-background,
989 | .ui-state-active .ui-icon-background {
990 | border: #696969;
991 | background-color: #ffffff;
992 | }
993 | .ui-state-active a,
994 | .ui-state-active a:link,
995 | .ui-state-active a:visited {
996 | color: #ffffff;
997 | text-decoration: none;
998 | }
999 |
1000 | /* Interaction Cues
1001 | ----------------------------------*/
1002 | .ui-state-highlight,
1003 | .ui-widget-content .ui-state-highlight,
1004 | .ui-widget-header .ui-state-highlight {
1005 | border: 1px solid #052f57;
1006 | background: #0b58a2 url("images/ui-bg_hexagon_30_0b58a2_12x10.png") 50% 50% repeat;
1007 | color: #030303;
1008 | }
1009 | .ui-state-checked {
1010 | border: 1px solid #052f57;
1011 | background: #0b58a2;
1012 | }
1013 | .ui-state-highlight a,
1014 | .ui-widget-content .ui-state-highlight a,
1015 | .ui-widget-header .ui-state-highlight a {
1016 | color: #030303;
1017 | }
1018 | .ui-state-error,
1019 | .ui-widget-content .ui-state-error,
1020 | .ui-widget-header .ui-state-error {
1021 | border: 1px solid #cd0a0a;
1022 | background: #a32d00 url("images/ui-bg_hexagon_30_a32d00_12x10.png") 50% 50% repeat;
1023 | color: #ffffff;
1024 | }
1025 | .ui-state-error a,
1026 | .ui-widget-content .ui-state-error a,
1027 | .ui-widget-header .ui-state-error a {
1028 | color: #ffffff;
1029 | }
1030 | .ui-state-error-text,
1031 | .ui-widget-content .ui-state-error-text,
1032 | .ui-widget-header .ui-state-error-text {
1033 | color: #ffffff;
1034 | }
1035 | .ui-priority-primary,
1036 | .ui-widget-content .ui-priority-primary,
1037 | .ui-widget-header .ui-priority-primary {
1038 | font-weight: bold;
1039 | }
1040 | .ui-priority-secondary,
1041 | .ui-widget-content .ui-priority-secondary,
1042 | .ui-widget-header .ui-priority-secondary {
1043 | opacity: .7;
1044 | filter:Alpha(Opacity=70); /* support: IE8 */
1045 | font-weight: normal;
1046 | }
1047 | .ui-state-disabled,
1048 | .ui-widget-content .ui-state-disabled,
1049 | .ui-widget-header .ui-state-disabled {
1050 | opacity: .35;
1051 | filter:Alpha(Opacity=35); /* support: IE8 */
1052 | background-image: none;
1053 | }
1054 | .ui-state-disabled .ui-icon {
1055 | filter:Alpha(Opacity=35); /* support: IE8 - See #6059 */
1056 | }
1057 |
1058 | /* Icons
1059 | ----------------------------------*/
1060 |
1061 | /* states and images */
1062 | .ui-icon {
1063 | width: 16px;
1064 | height: 16px;
1065 | }
1066 | .ui-icon,
1067 | .ui-widget-content .ui-icon {
1068 | background-image: url("images/ui-icons_75abff_256x240.png");
1069 | }
1070 | .ui-widget-header .ui-icon {
1071 | background-image: url("images/ui-icons_1f1f1f_256x240.png");
1072 | }
1073 | .ui-state-hover .ui-icon,
1074 | .ui-state-focus .ui-icon,
1075 | .ui-button:hover .ui-icon,
1076 | .ui-button:focus .ui-icon {
1077 | background-image: url("images/ui-icons_9ccdfc_256x240.png");
1078 | }
1079 | .ui-state-active .ui-icon,
1080 | .ui-button:active .ui-icon {
1081 | background-image: url("images/ui-icons_ffffff_256x240.png");
1082 | }
1083 | .ui-state-highlight .ui-icon,
1084 | .ui-button .ui-state-highlight.ui-icon {
1085 | background-image: url("images/ui-icons_000000_256x240.png");
1086 | }
1087 | .ui-state-error .ui-icon,
1088 | .ui-state-error-text .ui-icon {
1089 | background-image: url("images/ui-icons_ffffff_256x240.png");
1090 | }
1091 | .ui-button .ui-icon {
1092 | background-image: url("images/ui-icons_00498f_256x240.png");
1093 | }
1094 |
1095 | /* positioning */
1096 | .ui-icon-blank { background-position: 16px 16px; }
1097 | .ui-icon-caret-1-n { background-position: 0 0; }
1098 | .ui-icon-caret-1-ne { background-position: -16px 0; }
1099 | .ui-icon-caret-1-e { background-position: -32px 0; }
1100 | .ui-icon-caret-1-se { background-position: -48px 0; }
1101 | .ui-icon-caret-1-s { background-position: -65px 0; }
1102 | .ui-icon-caret-1-sw { background-position: -80px 0; }
1103 | .ui-icon-caret-1-w { background-position: -96px 0; }
1104 | .ui-icon-caret-1-nw { background-position: -112px 0; }
1105 | .ui-icon-caret-2-n-s { background-position: -128px 0; }
1106 | .ui-icon-caret-2-e-w { background-position: -144px 0; }
1107 | .ui-icon-triangle-1-n { background-position: 0 -16px; }
1108 | .ui-icon-triangle-1-ne { background-position: -16px -16px; }
1109 | .ui-icon-triangle-1-e { background-position: -32px -16px; }
1110 | .ui-icon-triangle-1-se { background-position: -48px -16px; }
1111 | .ui-icon-triangle-1-s { background-position: -65px -16px; }
1112 | .ui-icon-triangle-1-sw { background-position: -80px -16px; }
1113 | .ui-icon-triangle-1-w { background-position: -96px -16px; }
1114 | .ui-icon-triangle-1-nw { background-position: -112px -16px; }
1115 | .ui-icon-triangle-2-n-s { background-position: -128px -16px; }
1116 | .ui-icon-triangle-2-e-w { background-position: -144px -16px; }
1117 | .ui-icon-arrow-1-n { background-position: 0 -32px; }
1118 | .ui-icon-arrow-1-ne { background-position: -16px -32px; }
1119 | .ui-icon-arrow-1-e { background-position: -32px -32px; }
1120 | .ui-icon-arrow-1-se { background-position: -48px -32px; }
1121 | .ui-icon-arrow-1-s { background-position: -65px -32px; }
1122 | .ui-icon-arrow-1-sw { background-position: -80px -32px; }
1123 | .ui-icon-arrow-1-w { background-position: -96px -32px; }
1124 | .ui-icon-arrow-1-nw { background-position: -112px -32px; }
1125 | .ui-icon-arrow-2-n-s { background-position: -128px -32px; }
1126 | .ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
1127 | .ui-icon-arrow-2-e-w { background-position: -160px -32px; }
1128 | .ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
1129 | .ui-icon-arrowstop-1-n { background-position: -192px -32px; }
1130 | .ui-icon-arrowstop-1-e { background-position: -208px -32px; }
1131 | .ui-icon-arrowstop-1-s { background-position: -224px -32px; }
1132 | .ui-icon-arrowstop-1-w { background-position: -240px -32px; }
1133 | .ui-icon-arrowthick-1-n { background-position: 1px -48px; }
1134 | .ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
1135 | .ui-icon-arrowthick-1-e { background-position: -32px -48px; }
1136 | .ui-icon-arrowthick-1-se { background-position: -48px -48px; }
1137 | .ui-icon-arrowthick-1-s { background-position: -64px -48px; }
1138 | .ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
1139 | .ui-icon-arrowthick-1-w { background-position: -96px -48px; }
1140 | .ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
1141 | .ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
1142 | .ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
1143 | .ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
1144 | .ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
1145 | .ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
1146 | .ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
1147 | .ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
1148 | .ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
1149 | .ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
1150 | .ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
1151 | .ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
1152 | .ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
1153 | .ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
1154 | .ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
1155 | .ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
1156 | .ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
1157 | .ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
1158 | .ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
1159 | .ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
1160 | .ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
1161 | .ui-icon-arrow-4 { background-position: 0 -80px; }
1162 | .ui-icon-arrow-4-diag { background-position: -16px -80px; }
1163 | .ui-icon-extlink { background-position: -32px -80px; }
1164 | .ui-icon-newwin { background-position: -48px -80px; }
1165 | .ui-icon-refresh { background-position: -64px -80px; }
1166 | .ui-icon-shuffle { background-position: -80px -80px; }
1167 | .ui-icon-transfer-e-w { background-position: -96px -80px; }
1168 | .ui-icon-transferthick-e-w { background-position: -112px -80px; }
1169 | .ui-icon-folder-collapsed { background-position: 0 -96px; }
1170 | .ui-icon-folder-open { background-position: -16px -96px; }
1171 | .ui-icon-document { background-position: -32px -96px; }
1172 | .ui-icon-document-b { background-position: -48px -96px; }
1173 | .ui-icon-note { background-position: -64px -96px; }
1174 | .ui-icon-mail-closed { background-position: -80px -96px; }
1175 | .ui-icon-mail-open { background-position: -96px -96px; }
1176 | .ui-icon-suitcase { background-position: -112px -96px; }
1177 | .ui-icon-comment { background-position: -128px -96px; }
1178 | .ui-icon-person { background-position: -144px -96px; }
1179 | .ui-icon-print { background-position: -160px -96px; }
1180 | .ui-icon-trash { background-position: -176px -96px; }
1181 | .ui-icon-locked { background-position: -192px -96px; }
1182 | .ui-icon-unlocked { background-position: -208px -96px; }
1183 | .ui-icon-bookmark { background-position: -224px -96px; }
1184 | .ui-icon-tag { background-position: -240px -96px; }
1185 | .ui-icon-home { background-position: 0 -112px; }
1186 | .ui-icon-flag { background-position: -16px -112px; }
1187 | .ui-icon-calendar { background-position: -32px -112px; }
1188 | .ui-icon-cart { background-position: -48px -112px; }
1189 | .ui-icon-pencil { background-position: -64px -112px; }
1190 | .ui-icon-clock { background-position: -80px -112px; }
1191 | .ui-icon-disk { background-position: -96px -112px; }
1192 | .ui-icon-calculator { background-position: -112px -112px; }
1193 | .ui-icon-zoomin { background-position: -128px -112px; }
1194 | .ui-icon-zoomout { background-position: -144px -112px; }
1195 | .ui-icon-search { background-position: -160px -112px; }
1196 | .ui-icon-wrench { background-position: -176px -112px; }
1197 | .ui-icon-gear { background-position: -192px -112px; }
1198 | .ui-icon-heart { background-position: -208px -112px; }
1199 | .ui-icon-star { background-position: -224px -112px; }
1200 | .ui-icon-link { background-position: -240px -112px; }
1201 | .ui-icon-cancel { background-position: 0 -128px; }
1202 | .ui-icon-plus { background-position: -16px -128px; }
1203 | .ui-icon-plusthick { background-position: -32px -128px; }
1204 | .ui-icon-minus { background-position: -48px -128px; }
1205 | .ui-icon-minusthick { background-position: -64px -128px; }
1206 | .ui-icon-close { background-position: -80px -128px; }
1207 | .ui-icon-closethick { background-position: -96px -128px; }
1208 | .ui-icon-key { background-position: -112px -128px; }
1209 | .ui-icon-lightbulb { background-position: -128px -128px; }
1210 | .ui-icon-scissors { background-position: -144px -128px; }
1211 | .ui-icon-clipboard { background-position: -160px -128px; }
1212 | .ui-icon-copy { background-position: -176px -128px; }
1213 | .ui-icon-contact { background-position: -192px -128px; }
1214 | .ui-icon-image { background-position: -208px -128px; }
1215 | .ui-icon-video { background-position: -224px -128px; }
1216 | .ui-icon-script { background-position: -240px -128px; }
1217 | .ui-icon-alert { background-position: 0 -144px; }
1218 | .ui-icon-info { background-position: -16px -144px; }
1219 | .ui-icon-notice { background-position: -32px -144px; }
1220 | .ui-icon-help { background-position: -48px -144px; }
1221 | .ui-icon-check { background-position: -64px -144px; }
1222 | .ui-icon-bullet { background-position: -80px -144px; }
1223 | .ui-icon-radio-on { background-position: -96px -144px; }
1224 | .ui-icon-radio-off { background-position: -112px -144px; }
1225 | .ui-icon-pin-w { background-position: -128px -144px; }
1226 | .ui-icon-pin-s { background-position: -144px -144px; }
1227 | .ui-icon-play { background-position: 0 -160px; }
1228 | .ui-icon-pause { background-position: -16px -160px; }
1229 | .ui-icon-seek-next { background-position: -32px -160px; }
1230 | .ui-icon-seek-prev { background-position: -48px -160px; }
1231 | .ui-icon-seek-end { background-position: -64px -160px; }
1232 | .ui-icon-seek-start { background-position: -80px -160px; }
1233 | /* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
1234 | .ui-icon-seek-first { background-position: -80px -160px; }
1235 | .ui-icon-stop { background-position: -96px -160px; }
1236 | .ui-icon-eject { background-position: -112px -160px; }
1237 | .ui-icon-volume-off { background-position: -128px -160px; }
1238 | .ui-icon-volume-on { background-position: -144px -160px; }
1239 | .ui-icon-power { background-position: 0 -176px; }
1240 | .ui-icon-signal-diag { background-position: -16px -176px; }
1241 | .ui-icon-signal { background-position: -32px -176px; }
1242 | .ui-icon-battery-0 { background-position: -48px -176px; }
1243 | .ui-icon-battery-1 { background-position: -64px -176px; }
1244 | .ui-icon-battery-2 { background-position: -80px -176px; }
1245 | .ui-icon-battery-3 { background-position: -96px -176px; }
1246 | .ui-icon-circle-plus { background-position: 0 -192px; }
1247 | .ui-icon-circle-minus { background-position: -16px -192px; }
1248 | .ui-icon-circle-close { background-position: -32px -192px; }
1249 | .ui-icon-circle-triangle-e { background-position: -48px -192px; }
1250 | .ui-icon-circle-triangle-s { background-position: -64px -192px; }
1251 | .ui-icon-circle-triangle-w { background-position: -80px -192px; }
1252 | .ui-icon-circle-triangle-n { background-position: -96px -192px; }
1253 | .ui-icon-circle-arrow-e { background-position: -112px -192px; }
1254 | .ui-icon-circle-arrow-s { background-position: -128px -192px; }
1255 | .ui-icon-circle-arrow-w { background-position: -144px -192px; }
1256 | .ui-icon-circle-arrow-n { background-position: -160px -192px; }
1257 | .ui-icon-circle-zoomin { background-position: -176px -192px; }
1258 | .ui-icon-circle-zoomout { background-position: -192px -192px; }
1259 | .ui-icon-circle-check { background-position: -208px -192px; }
1260 | .ui-icon-circlesmall-plus { background-position: 0 -208px; }
1261 | .ui-icon-circlesmall-minus { background-position: -16px -208px; }
1262 | .ui-icon-circlesmall-close { background-position: -32px -208px; }
1263 | .ui-icon-squaresmall-plus { background-position: -48px -208px; }
1264 | .ui-icon-squaresmall-minus { background-position: -64px -208px; }
1265 | .ui-icon-squaresmall-close { background-position: -80px -208px; }
1266 | .ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
1267 | .ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
1268 | .ui-icon-grip-solid-vertical { background-position: -32px -224px; }
1269 | .ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
1270 | .ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
1271 | .ui-icon-grip-diagonal-se { background-position: -80px -224px; }
1272 |
1273 |
1274 | /* Misc visuals
1275 | ----------------------------------*/
1276 |
1277 | /* Corner radius */
1278 | .ui-corner-all,
1279 | .ui-corner-top,
1280 | .ui-corner-left,
1281 | .ui-corner-tl {
1282 | border-top-left-radius: 6px;
1283 | }
1284 | .ui-corner-all,
1285 | .ui-corner-top,
1286 | .ui-corner-right,
1287 | .ui-corner-tr {
1288 | border-top-right-radius: 6px;
1289 | }
1290 | .ui-corner-all,
1291 | .ui-corner-bottom,
1292 | .ui-corner-left,
1293 | .ui-corner-bl {
1294 | border-bottom-left-radius: 6px;
1295 | }
1296 | .ui-corner-all,
1297 | .ui-corner-bottom,
1298 | .ui-corner-right,
1299 | .ui-corner-br {
1300 | border-bottom-right-radius: 6px;
1301 | }
1302 |
1303 | /* Overlays */
1304 | .ui-widget-overlay {
1305 | background: #262626 url("images/ui-bg_diagonals-small_50_262626_40x40.png") 50% 50% repeat;
1306 | opacity: .3;
1307 | filter: Alpha(Opacity=30); /* support: IE8 */
1308 | }
1309 | .ui-widget-shadow {
1310 | -webkit-box-shadow: -6px -6px 6px #303030;
1311 | box-shadow: -6px -6px 6px #303030;
1312 | }
1313 |
--------------------------------------------------------------------------------