@massif/lancer-data 4.0.0-beta.9 → 4.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/weapons.json CHANGED
@@ -1711,7 +1711,10 @@
1711
1711
  "license_level": 3,
1712
1712
  "on_crit": {
1713
1713
  "detail": "Deal +1d6 bonus damage.",
1714
- "bonus_damage": "1d6"
1714
+ "bonus_damage": {
1715
+ "val": "1d6",
1716
+ "hit_type": "crit"
1717
+ }
1715
1718
  },
1716
1719
  "description": "The variable sword is a Smith-Shimano hallmark: a length of razor-sharp molecular wire attached to a handle and locked in place by a magnetic field, variable swords are invisible to the naked eye until they cut into their target. Designed in the early days of interstellar travel, variable swords were meant to allow for the precise gathering of samples, while also reducing the overall burden on a mech’s core.",
1717
1720
  "license_id": "mf_mourning_cloak"
@@ -2353,7 +2356,7 @@
2353
2356
  "no_core_bonus": true,
2354
2357
  "mount": "Heavy",
2355
2358
  "type": "Rifle",
2356
- "damage": [],
2359
+
2357
2360
  "range": [
2358
2361
  {
2359
2362
  "type": "Range",
@@ -2832,7 +2835,7 @@
2832
2835
  "name": "Latch Drone",
2833
2836
  "mount": "Main",
2834
2837
  "type": "Launcher",
2835
- "damage": [],
2838
+
2836
2839
  "range": [
2837
2840
  {
2838
2841
  "type": "Range",
package/package.json CHANGED
@@ -1,29 +1,32 @@
1
- {
2
- "name": "@massif/lancer-data",
3
- "version": "4.0.0-beta.9",
4
- "description": "Data for the LANCER TTRPG",
5
- "main": "index.js",
6
- "scripts": {
7
- "build": "node ./scripts/build.js",
8
- "test": "node ./scripts/test.js"
9
- },
10
- "repository": {
11
- "type": "git",
12
- "url": "https://github.com/massif-press/lancer-data.git"
13
- },
14
- "directories": {
15
- "lib": "lib"
16
- },
17
- "author": "Massif Press",
18
- "license": "GPL-3.0-or-later",
19
- "devDependencies": {
20
- "zip-lib": "^0.7.3"
21
- },
22
- "keywords": [
23
- "lancer",
24
- "compcon",
25
- "comp/con",
26
- "lancerrpg",
27
- "massif"
28
- ]
29
- }
1
+ {
2
+ "name": "@massif/lancer-data",
3
+ "version": "4.0.1",
4
+ "description": "Data for the LANCER TTRPG",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "build": "node ./scripts/build.js",
8
+ "build:dev": "node ./scripts/build.js --dev",
9
+ "test": "node ./scripts/test.js",
10
+ "validate": "node ./scripts/validate.js",
11
+ "audit": "node ./scripts/audit.js"
12
+ },
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "https://github.com/massif-press/lancer-data.git"
16
+ },
17
+ "directories": {
18
+ "lib": "lib"
19
+ },
20
+ "author": "Massif Press",
21
+ "license": "GPL-3.0-or-later",
22
+ "devDependencies": {
23
+ "zip-lib": "^0.7.3"
24
+ },
25
+ "keywords": [
26
+ "lancer",
27
+ "compcon",
28
+ "comp/con",
29
+ "lancerrpg",
30
+ "massif"
31
+ ]
32
+ }
@@ -1,134 +0,0 @@
1
- const fs = require('fs');
2
-
3
- const weapons = require('../lib/weapons.json');
4
- const systems = require('../lib/systems.json');
5
- const mods = require('../lib/mods.json');
6
- const frames = require('../lib/frames.json');
7
-
8
- // const items = [weapons, systems, mods, frames]
9
- const items = [weapons];
10
-
11
- function row(x) {
12
- if (x.data_type === 'weapon') return weaponRow(x);
13
- return `${x.source},${x.name},0,0,0,0,0,0\n`;
14
- // return `${x.data_type},${x.id},${x.source},${x.name},0,0,0,0,0,0\n`
15
- }
16
-
17
- function weaponRow(x) {
18
- // return `${x.data_type},${x.id},${x.source},${x.name},0,${rangedAp(x)},0,0,0,0\n`
19
- return `${x.source},${x.name},${closeAp(x)},${rangedAp(x)},0,0,0,0\n`;
20
- }
21
-
22
- function getDamage(d, tags) {
23
- let dscore = 0;
24
- let guaranteed = 0;
25
- let possible = 0;
26
- if (typeof d.val === 'string') {
27
- const dmgArr = d.val.split(/d|\+/g);
28
- if (dmgArr.length === 1) guaranteed = parseInt(dmgArr[0]); // straight value, no dice
29
- else {
30
- if (dmgArr.length === 3) guaranteed = parseInt(dmgArr[2]); // bonus val
31
- // handle dice
32
- guaranteed += parseInt(dmgArr[0]); // min number we can get on the dice
33
- possible = parseInt(dmgArr[0]) * parseInt(dmgArr[1]) - parseInt(dmgArr[0]);
34
- }
35
- // guaranteed = 1pt, possible = 0.5
36
- dscore += guaranteed * (possible / 2);
37
- } else {
38
- guaranteed = d.val;
39
- }
40
-
41
- if (tags.find((x) => x.id === 'tg_accurate')) dscore += possible * 0.25;
42
- if (tags.find((x) => x.id === 'tg_inaccurate')) dscore -= possible * 0.25;
43
- if (tags.find((x) => x.id === 'tg_reliable'))
44
- dscore += tags.find((x) => x.id === 'tg_reliable').val * 3;
45
-
46
- return dscore;
47
- }
48
-
49
- function closeAp(x) {
50
- let score = 0;
51
- const tags = x.tags || [];
52
- if (x.type.toLowerCase() !== 'melee' || !x.damage || !x.damage.length) return score.toString();
53
-
54
- x.damage.forEach((d) => {
55
- score += getDamage(d, tags);
56
- });
57
-
58
- const threat = x.range.find((y) => y.type.toLowerCase() === 'threat');
59
- // console.log(threat)
60
- if (threat) score *= (threat.val + 1) / 2;
61
-
62
- if (tags.find((x) => x.id === 'tg_ap')) score = score * 1.35;
63
- if (tags.find((x) => x.id === 'tg_arcing')) score = score * 1.15;
64
- if (tags.find((x) => x.id === 'tg_smart')) score = score * 1.15;
65
- if (tags.find((x) => x.id === 'tg_overkill')) score = score * 1.15;
66
- if (tags.find((x) => x.id === 'tg_loading')) score = score * 0.85;
67
-
68
- return Math.ceil(score).toString();
69
- }
70
-
71
- function rangedAp(x) {
72
- let score = 0;
73
- const tags = x.tags || [];
74
- if (
75
- x.type.toLowerCase() === 'melee' ||
76
- !x.range ||
77
- !x.range.length ||
78
- !x.damage ||
79
- !x.damage.length
80
- )
81
- return score.toString();
82
- x.damage.forEach((d) => {
83
- score += getDamage(d, tags);
84
- });
85
- //collect range pts
86
- x.range.forEach((r) => {
87
- let rscore = 0;
88
- if (typeof r.val === 'string') return 'XXXX';
89
- if (r.type.toLowerCase() === 'range') {
90
- if (r.val <= 10) score += r.val / 2;
91
- else {
92
- rscore += 5 + (r.val - 10);
93
- }
94
- }
95
- if (r.type.toLowerCase() === 'blast') {
96
- rscore += Math.pow(r.val * 3, 2) / 2;
97
- }
98
- if (r.type.toLowerCase() === 'burst') {
99
- rscore += Math.pow(r.val * 3, 2) / 3;
100
- }
101
- if (r.type.toLowerCase() === 'cone') {
102
- rscore += Math.pow(r.val, 2) / 2;
103
- }
104
- if (r.type.toLowerCase() === 'line') {
105
- rscore += r.val * 2;
106
- }
107
- if (r.type.toLowerCase() === 'thrown') {
108
- rscore = score / 3;
109
- }
110
- score += rscore;
111
- });
112
-
113
- if (tags.find((x) => x.id === 'tg_ap')) score = score * 1.35;
114
- if (tags.find((x) => x.id === 'tg_arcing')) score = score * 1.15;
115
- if (tags.find((x) => x.id === 'tg_smart')) score = score * 1.15;
116
- if (tags.find((x) => x.id === 'tg_overkill')) score = score * 1.15;
117
- if (tags.find((x) => x.id === 'tg_loading')) score = score * 0.85;
118
-
119
- return Math.ceil(score).toString();
120
- }
121
-
122
- // let output = 'TYPE,ID,SOURCE,NAME,CQB,RANGED,SURVIVABILITY,MANEUVERABILITY,SUPPORT,CONTROL\n'
123
- let output = 'SOURCE,NAME,MELEE,RANGED,SURVIVABILITY,MANEUVERABILITY,SUPPORT,CONTROL\n';
124
-
125
- items.forEach((e) => {
126
- e.forEach((x) => {
127
- output += row(x);
128
- });
129
- });
130
-
131
- fs.writeFile('./util/output/equipment.csv', output, function (err) {
132
- if (err) return console.log(err);
133
- console.log('Export Complete');
134
- });
package/scripts/build.js DELETED
@@ -1,14 +0,0 @@
1
- const zl = require('zip-lib');
2
-
3
- const info = require('../package.json');
4
-
5
- const filepath = './' + info.name + '-' + info.version + '.lcp';
6
-
7
- zl.archiveFolder('./lib', filepath).then(
8
- function () {
9
- console.log('done');
10
- },
11
- function (err) {
12
- console.log(err);
13
- }
14
- );
@@ -1,86 +0,0 @@
1
- SOURCE,NAME,MELEE,RANGED,SURVIVABILITY,MANUVERABILITY,SUPPORT,CONTROL
2
- GMS,ANTI-MATERIEL RIFLE,0,32,0,0,0,0
3
- GMS,ASSAULT RIFLE,0,14,0,0,0,0
4
- GMS,CHARGED BLADE,6,0,0,0,0,0
5
- GMS,CYCLONE PULSE RIFLE,0,63,0,0,0,0
6
- GMS,HEAVY CHARGED BLADE,14,0,0,0,0,0
7
- GMS,HEAVY MACHINE GUN,0,32,0,0,0,0
8
- GMS,HEAVY MELEE WEAPON,15,0,0,0,0,0
9
- GMS,HOWITZER,0,40,0,0,0,0
10
- GMS,MISSILE RACK,0,10,0,0,0,0
11
- GMS,MORTAR,0,21,0,0,0,0
12
- GMS,NEXUS (HUNTER-KILLER),0,9,0,0,0,0
13
- GMS,NEXUS (LIGHT),0,7,0,0,0,0
14
- GMS,PISTOL,0,7,0,0,0,0
15
- GMS,SEGMENT KNIFE,3,0,0,0,0,0
16
- GMS,ROCKET-PROPELLED GRENADE,0,24,0,0,0,0
17
- GMS,SHOTGUN,0,5,0,0,0,0
18
- GMS,TACTICAL KNIFE,2,0,0,0,0,0
19
- GMS,TACTICAL MELEE WEAPON,8,0,0,0,0,0
20
- GMS,THERMAL LANCE,0,30,0,0,0,0
21
- GMS,THERMAL PISTOL,0,10,0,0,0,0
22
- GMS,THERMAL RIFLE,0,8,0,0,0,0
23
- IPS-N,CHAIN AXE,9,0,0,0,0,0
24
- IPS-N,BRISTLECROWN FLECHETTE LAUNCHER,0,3,0,0,0,0
25
- IPS-N,NANOCARBON SWORD,33,0,0,0,0,0
26
- IPS-N,ASSAULT CANNON,0,14,0,0,0,0
27
- IPS-N,CONCUSSION MISSILES,0,4,0,0,0,0
28
- IPS-N,LEVIATHAN HEAVY ASSAULT CANNON,0,7,0,0,0,0
29
- IPS-N,CUTTER MKII PLASMA TORCH,0,0,0,0,0,0
30
- IPS-N,WAR PIKE,5,0,0,0,0,0
31
- IPS-N,POWER KNUCKLES,2,0,0,0,0,0
32
- IPS-N,HAND CANNON,0,7,0,0,0,0
33
- IPS-N,BOLT THROWER,0,25,0,0,0,0
34
- IPS-N,KINETIC HAMMER,32,0,0,0,0,0
35
- IPS-N,DECK-SWEEPER AUTOMATIC SHOTGUN,0,9,0,0,0,0
36
- IPS-N,DAISY CUTTER,0,47,0,0,0,0
37
- IPS-N,CATALYTIC HAMMER,6,0,0,0,0,0
38
- IPS-N,IMPACT LANCE,5,0,0,0,0,0
39
- IPS-N,IMPALER NAILGUN,0,8,0,0,0,0
40
- IPS-N,COMBAT DRILL,39,0,0,0,0,0
41
- SSC,MAGNETIC CANNON,0,18,0,0,0,0
42
- SSC,VULTURE DMR,0,19,0,0,0,0
43
- SSC,RAILGUN,0,71,0,0,0,0
44
- SSC,VEIL RIFLE,0,23,0,0,0,0
45
- SSC,BURST LAUNCHER,0,14,0,0,0,0
46
- SSC,RAIL RIFLE,0,25,0,0,0,0
47
- SSC,SHOCK KNIFE,0,0,0,0,0,0
48
- SSC,SHARANGA MISSILES,0,12,0,0,0,0
49
- SSC,GANDIVA MISSILES,0,25,0,0,0,0
50
- SSC,PINAKA MISSILES,0,34,0,0,0,0
51
- SSC,FOLD KNIFE,2,0,0,0,0,0
52
- SSC,VIJAYA ROCKETS,0,4,0,0,0,0
53
- SSC,VARIABLE SWORD,0,0,0,0,0,0
54
- SSC,ORACLE LMG-I,0,14,0,0,0,0
55
- HORUS,NANOBOT WHIP,23,0,0,0,0,0
56
- HORUS,SWARM/HIVE NANITES,0,3,0,0,0,0
57
- HORUS,AUTOPOD,0,10,0,0,0,0
58
- HORUS,VORPAL GUN,0,13,0,0,0,0
59
- HORUS,GHOUL NEXUS,0,10,0,0,0,0
60
- HORUS,GHAST NEXUS,0,18,0,0,0,0
61
- HORUS,ANNIHILATION NEXUS,0,35,0,0,0,0
62
- HORUS,CATALYST PISTOL,0,6,0,0,0,0
63
- HORUS,ARC PROJECTOR,0,8,0,0,0,0
64
- HORUS,SMARTGUN,0,12,0,0,0,0
65
- HORUS,MIMIC GUN,0,NaN,0,0,0,0
66
- HORUS,AUTOGUN,0,10,0,0,0,0
67
- HA,SIEGE CANNON,0,23,0,0,0,0
68
- HA,KRAKATOA THERMOBARIC FLAMETHROWER,0,13,0,0,0,0
69
- HA,PLASMA THROWER,0,52,0,0,0,0
70
- HA,STUB CANNON,0,3,0,0,0,0
71
- HA,GRAVITY GUN,0,0,0,0,0,0
72
- HA,DISPLACER,0,11,0,0,0,0
73
- HA,SHATTERHEAD COLONY MISSILES,0,14,0,0,0,0
74
- HA,SOL-PATTERN LASER RIFLE,0,7,0,0,0,0
75
- HA,ANDROMEDA-PATTERN HEAVY LASER RIFLE,0,17,0,0,0,0
76
- HA,TACHYON LANCE,0,25,0,0,0,0
77
- HA,ANNIHILATOR,0,8,0,0,0,0
78
- HA,TORCH,3,0,0,0,0,0
79
- ,Fuel Rod Gun,0,5,0,0,0,0
80
- ,Prototype Weapon,0,15,0,0,0,0
81
- ,Prototype Weapon,0,15,0,0,0,0
82
- ,Prototype Weapon,0,21,0,0,0,0
83
- undefined,Latch Drone,0,0,0,0,0,0
84
- undefined,M35 Mjolnir,0,0,0,0,0,0
85
- undefined,Apocalypse Rail,0,0,0,0,0,0
86
- undefined,ZF4 SOLIDCORE,0,0,0,0,0,0
package/scripts/test.js DELETED
@@ -1,19 +0,0 @@
1
- const fs = require('fs');
2
- var currentDir = process.cwd();
3
- var files = fs.readdirSync('./lib');
4
- let contents = ""
5
- let valid = true
6
-
7
- files.forEach(filename => {
8
- contents = fs.readFileSync(`./lib/${filename}`, 'utf-8')
9
- try {
10
- JSON.parse(contents)
11
- } catch (e) {
12
- console.error(`invalid JSON in ${filename}`);
13
- valid = false;
14
- }
15
- });
16
-
17
- if (!valid) {
18
- throw "One or more JSON files are invalid."
19
- }
package/scripts/util.js DELETED
@@ -1,232 +0,0 @@
1
- #!/usr/bin/env node
2
- const readline = require('readline');
3
-
4
- const fs = require('fs');
5
-
6
- const path = require('path');
7
-
8
- const folderPath = '../lib';
9
- const ignore = ['manufacturers.json', 'weapons.json', 'systems.json', 'mods.json'];
10
-
11
- const ignoreWords = ['{VAL}', '({VAL})', '{VAL}+', 'HP', 'GRIT', 'SP', 'AP', '(AP)'];
12
-
13
- function decapitalize() {
14
- function decap(string) {
15
- const words = string.split(' ');
16
- const capitalizedWords = words.map((word) => {
17
- if (ignoreWords.some((x) => x === word)) return word;
18
- else if (word === word.toUpperCase()) {
19
- return word.toLowerCase().replace(/\b\w/g, (l) => l.toUpperCase());
20
- } else {
21
- return word;
22
- }
23
- });
24
- const capitalizedString = capitalizedWords.join(' ');
25
- return capitalizedString;
26
- }
27
-
28
- fs.readdir(folderPath, (err, files) => {
29
- if (err) {
30
- console.error('Error reading folder:', err);
31
- return;
32
- }
33
-
34
- files.forEach((file) => {
35
- if (ignore.some((x) => x === file)) return;
36
- const filePath = path.join(folderPath, file);
37
- console.log(filePath);
38
-
39
- fs.readFile(filePath, 'utf8', (err, data) => {
40
- if (err) {
41
- console.error('Error reading file:', file, err);
42
- return;
43
- }
44
-
45
- try {
46
- let fixed = 0;
47
- const jsonObject = JSON.parse(data);
48
- if (Array.isArray(jsonObject)) {
49
- jsonObject.forEach((e) => {
50
- if (e.name) {
51
- if (e.name.includes('ERR:')) return;
52
- if (e.name === e.name.toUpperCase()) {
53
- const decapped = decap(e.name);
54
- if (e.name !== decapped) {
55
- // console.log('decapped:', e.name, decapped);
56
- e.name = decapped;
57
- fixed++;
58
- }
59
- }
60
- }
61
- });
62
- fs.writeFile(filePath, JSON.stringify(jsonObject, null, 2), (err) => {
63
- if (err) {
64
- console.error('Error writing file:', file, err);
65
- return;
66
- }
67
- });
68
-
69
- console.log(`fixed: ${fixed} in file: ${file}`);
70
- }
71
- } catch (err) {
72
- console.error('Error parsing JSON in file:', file, err);
73
- }
74
- });
75
- });
76
- });
77
- }
78
-
79
- function collapseEffect() {
80
- fs.readdir(folderPath, (err, files) => {
81
- if (err) {
82
- console.error('Error reading directory:', err);
83
- return;
84
- }
85
-
86
- files.forEach((file) => {
87
- if (path.extname(file) === '.json') {
88
- const filePath = path.join(folderPath, file);
89
-
90
- fs.readFile(filePath, 'utf8', (err, data) => {
91
- if (err) {
92
- console.error(`Error reading file ${file}:`, err);
93
- return;
94
- }
95
-
96
- try {
97
- let jsonData = JSON.parse(data);
98
- let modified = false;
99
-
100
- jsonData = jsonData.map((obj) => {
101
- if (obj.hasOwnProperty('effect') && typeof obj.effect === 'object') {
102
- obj.effect = obj.effect.description;
103
- modified = true;
104
- }
105
- return obj;
106
- });
107
-
108
- if (modified) {
109
- fs.writeFile(filePath, JSON.stringify(jsonData, null, 2), 'utf8', (err) => {
110
- if (err) {
111
- console.error(`Error writing file ${file}:`, err);
112
- } else {
113
- console.log(`Updated ${file}`);
114
- }
115
- });
116
- }
117
- } catch (parseError) {
118
- console.error(`Error parsing JSON in file ${file}:`, parseError);
119
- }
120
- });
121
- }
122
- });
123
- });
124
- }
125
-
126
- async function collectEffects() {
127
- const collection = {
128
- active_effects: [],
129
- add_status: [],
130
- add_special: [],
131
- remove_special: [],
132
- add_resist: [],
133
- damage: [],
134
- range: [],
135
- save: [],
136
- bonus_damage: [],
137
- };
138
-
139
- fs.readdir(folderPath, (err, files) => {
140
- if (err) {
141
- console.error('Error reading directory:', err);
142
- return;
143
- }
144
-
145
- const promises = [];
146
-
147
- files.forEach((file) => {
148
- if (path.extname(file) === '.json') {
149
- const filePath = path.join(folderPath, file);
150
- promises.push(
151
- fs.readFile(filePath, 'utf8', (err, data) => {
152
- if (err) {
153
- console.error(`Error reading file ${file}:`, err);
154
- return;
155
- }
156
- try {
157
- const jsonData = JSON.parse(data);
158
- if (!Array.isArray(jsonData)) return;
159
- console.log(`Processing ${file} with ${jsonData.length} entries`);
160
- jsonData.forEach((obj) => {
161
- const elemArr = [obj];
162
- if (obj.actions) elemArr.push(...obj.actions);
163
- if (obj.deployables) elemArr.push(...obj.deployables);
164
- elemArr.forEach((e) => {
165
- if (e.active_effects) {
166
- for (const effect of e.active_effects) {
167
- if (Object.keys(effect).length > 2) {
168
- collection.active_effects.push(effect);
169
- }
170
- }
171
- }
172
- if (e.add_status) collection.add_status.push(e.add_status);
173
- if (e.add_special) collection.add_special.push(e.add_special);
174
- if (e.remove_special) collection.remove_special.push(e.remove_special);
175
- if (e.add_resist) collection.add_resist.push(e.add_resist);
176
- if (e.damage) collection.damage.push(e.damage);
177
- if (e.range) collection.range.push(e.range);
178
- if (e.save) collection.save.push(e.save);
179
- if (e.bonus_damage) collection.bonus_damage.push(e.save);
180
- });
181
- });
182
- } catch (parseError) {
183
- console.error(`Error parsing JSON in file ${file}:`, parseError);
184
- }
185
- })
186
- );
187
- }
188
- });
189
- });
190
-
191
- await new Promise((resolve) => setTimeout(resolve, 2000)); // wait for all files to be processed
192
-
193
- await fs.writeFile(
194
- 'output/activeEffects.json',
195
- JSON.stringify(collection, null, 2),
196
- 'utf8',
197
- (err) => {
198
- if (err) {
199
- console.error('Error writing activeEffects.json:', err);
200
- } else {
201
- console.log('activeEffects.json updated');
202
- }
203
- }
204
- );
205
- }
206
-
207
- // run
208
-
209
- const functions = {
210
- 1: { name: 'Decapitalize Item names', fn: decapitalize },
211
- 2: { name: 'Collapse Effect Props', fn: collapseEffect },
212
- 3: { name: 'Collect Active Effects', fn: collectEffects },
213
- };
214
-
215
- console.log('Choose a function:');
216
- for (const key in functions) {
217
- console.log(`${key}. ${functions[key].name}`);
218
- }
219
-
220
- const rl = readline.createInterface({
221
- input: process.stdin,
222
- output: process.stdout,
223
- });
224
-
225
- rl.question('> ', (answer) => {
226
- if (functions[answer]) {
227
- functions[answer].fn();
228
- } else {
229
- console.log('Invalid choice.');
230
- }
231
- rl.close();
232
- });