Skip to content

Commit 7697ccf

Browse files
author
Saurav Tripathi
committed
refactored code - added engine.js file, which contains logic common to AoE2 and RoR, similar to main.js
added more charge types and armor classes
1 parent 3fda527 commit 7697ccf

File tree

13 files changed

+547
-723
lines changed

13 files changed

+547
-723
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
.idea
2+
.vscode
23
bin
34
.directory
45
/scripts/venv

index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
<script defer src="js/svg.min.js"></script>
3030
<script defer src="js/techtree.js"></script>
3131
<script defer src="js/main.js"></script>
32+
<script defer src="js/engine.js"></script>
3233
</head>
3334

3435
<body>

js/engine.js

Lines changed: 306 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,306 @@
1+
/**
2+
* The main.js file is supposed to contain logic specific to the website, like those modifying the HTML elements, and the event listeners.
3+
* To reduce size of the main.js file, this file includes the logic of the game engine-specific logic, logic for the 'Caret' class, or any other simple logic.
4+
*/
5+
6+
const TYPES = Object.freeze({
7+
'BUILDING': {colour: '#ff0000', type: 'BUILDING', name: 'Building'},
8+
'UNIT': {colour: '#ff0000', type: 'UNIT', name: 'Unit'},
9+
'UNIQUEUNIT': {colour: '#703b7a', type: 'UNIQUEUNIT', name: 'Unique Unit'},
10+
'TECHNOLOGY': {colour: '#2c5729', type: 'TECHNOLOGY', name: 'Technology'}
11+
});
12+
13+
const PREFIX = Object.freeze({
14+
'BUILDING': 'building_',
15+
'UNIT': 'unit_',
16+
'UNIQUEUNIT': 'unit_',
17+
'TECHNOLOGY': 'tech_'
18+
});
19+
20+
const armorClasses = {
21+
0: 'Wonders',
22+
1: 'Infantry',
23+
2: 'Heavy Warships',
24+
3: 'Base Pierce',
25+
4: 'Base Melee',
26+
5: 'Elephants',
27+
8: '<abbr title="except Camels">Mounted Units</abbr>',
28+
11: 'All Buildings',
29+
13: '<abbr title="except Castles and Kreposts">Stone Defense & Harbors</abbr>',
30+
14: 'Predator Animals',
31+
15: 'All Archers',
32+
16: '<abbr title="except Fishing Ships">Ships</abbr>',
33+
17: 'High Pierce Armor Siege Units',
34+
18: 'Trees',
35+
19: 'Unique Units',
36+
20: 'Siege Units',
37+
21: '<abbr title="All buildings except Wonders">Standard Buildings</abbr>',
38+
22: 'Walls & Gates',
39+
23: 'Gunpowder Units',
40+
24: 'Aggressive Huntable Animals',
41+
25: 'Monastery Units',
42+
26: 'Castles & Kreposts',
43+
27: 'Spearmen',
44+
28: 'Mounted Archers',
45+
29: 'Shock Infantry',
46+
30: 'Camels',
47+
31: '<abbr title="Armor-ignoring melee attack against units, but not against buildings">Unblockable Melee</abbr>',
48+
32: 'Condottieri',
49+
34: 'Fishing Ships',
50+
35: 'Mamelukes',
51+
36: 'Heroes & Kings',
52+
37: 'Heavy Siege',
53+
38: 'Skirmishers',
54+
39: 'Cavalry Resistance',
55+
40: 'Houses',
56+
60: 'Long-Range Warships'
57+
};
58+
59+
const animation_duration = 50;
60+
61+
class Caret {
62+
constructor(type, name, id, colour = null) {
63+
this.type = type;
64+
this.name = name;
65+
this.id = PREFIX[type.type] + formatId(id);
66+
this.width = 100;
67+
this.height = 100;
68+
this.x = 0;
69+
this.y = 0;
70+
this.colour = colour;
71+
}
72+
73+
isBuilding() {
74+
return this.type === TYPES.BUILDING;
75+
}
76+
}
77+
78+
function imagePrefix(name) {
79+
return name.replace('_copy', '')
80+
.replace('building_', 'Buildings/')
81+
.replace('unit_', 'Units/')
82+
.replace('tech_', 'Techs/');
83+
}
84+
85+
function cost(cost_object) {
86+
let value = '';
87+
if (cost_object.Food) {
88+
value += `<span class="cost food" title="${cost_object.Food} Food">${cost_object.Food}</span>`;
89+
}
90+
if (cost_object.Wood) {
91+
value += `<span class="cost wood" title="${cost_object.Wood} Wood">${cost_object.Wood}</span>`;
92+
}
93+
if (cost_object.Gold) {
94+
value += `<span class="cost gold" title="${cost_object.Gold} Gold">${cost_object.Gold}</span>`;
95+
}
96+
if (cost_object.Stone) {
97+
value += `<span class="cost stone" title="${cost_object.Stone} Stone">${cost_object.Stone}</span>`;
98+
}
99+
if (value === '') return 'free';
100+
return value;
101+
}
102+
103+
function formatId(string) {
104+
return string.toString().replace(/\s/g, '_').replace(/\//g, '_').toLowerCase();
105+
}
106+
107+
function checkIdUnique(tree) {
108+
let ids = new Set();
109+
for (let lane of tree.lanes) {
110+
for (let r of Object.keys(lane.rows)) {
111+
for (let caret of lane.rows[r]) {
112+
if (ids.has(caret.id)) {
113+
console.error('ID ' + caret.id + ' is not unique!');
114+
}
115+
ids.add(caret.id);
116+
}
117+
}
118+
}
119+
}
120+
121+
function getColourForNodeType(nodeType) {
122+
switch (nodeType) {
123+
case 'BuildingTech':
124+
case 'BuildingNonTech':
125+
return '#b54e18';
126+
case 'RegionalBuilding':
127+
return '#cc4422';
128+
case 'UniqueBuilding':
129+
return '#d43652';
130+
case 'Unit':
131+
case 'UnitUpgrade':
132+
return '#00739c';
133+
case 'RegionalUnit':
134+
return '#515ae3';
135+
case 'UniqueUnit':
136+
return '#703b7a';
137+
case 'Technology':
138+
return '#397139';
139+
default:
140+
return '#ff0000';
141+
}
142+
}
143+
144+
function chargeText(type) {
145+
switch (type) {
146+
case 1: return 'Charge Attack:&nbsp;';
147+
case 2: return 'Charge Hit Points:&nbsp;';
148+
case 3: return 'Charged Area Attack:&nbsp;';
149+
case 4: return 'Projectile Dodging:&nbsp;';
150+
case 5: return 'Melee Attack Blocking:&nbsp;';
151+
case 6: return 'Charged Ranged Attack (type 1):&nbsp;';
152+
case 7: return 'Charged Ranged Attack (type 2):&nbsp;';
153+
default: return 'Unknown Charge:&nbsp;';
154+
}
155+
}
156+
157+
function getEntityType(type) {
158+
switch (type) {
159+
case 'TECHNOLOGY':
160+
return 'techs';
161+
case 'UNIT':
162+
case 'UNIQUEUNIT':
163+
return 'units';
164+
}
165+
return 'buildings';
166+
}
167+
168+
/**
169+
* @param {number} trait
170+
* @return {number[]}
171+
*/
172+
function splitTrait(trait) {
173+
const traits = [];
174+
for (let x of [1, 2, 4, 8, 16, 32, 64, 128]) {
175+
if ((trait & x) > 0) {
176+
traits.push(x);
177+
}
178+
}
179+
return traits;
180+
}
181+
182+
/**
183+
* @param {number} traitId
184+
* @param {number} traitPiece
185+
* @return {string}
186+
*/
187+
function getTraitDefinition(traitId, traitPiece) {
188+
switch (traitId) {
189+
case 1: return 'Garrison Unit';
190+
case 2: return 'Ship Unit';
191+
case 4: return 'Builds:&nbsp;' + data.strings[data.data['buildings'][traitPiece]['LanguageNameId']];
192+
case 8: return 'Transforms into:&nbsp;' + data.strings[(data.data['buildings'][traitPiece] || data.data['units'][traitPiece])['LanguageNameId']];
193+
case 16: return '<abbr title="has auto-scout behaviour">Scout Unit</abbr>';
194+
default: return 'Unknown Trait:&nbsp;' + traitId;
195+
}
196+
}
197+
198+
/**
199+
* @param {number} trait
200+
* @param {string} traitPiece
201+
* @return {*[]|boolean}
202+
*/
203+
function traitsIfDefined(trait, traitPiece) {
204+
let traitdescriptions = [];
205+
if (trait === undefined || trait === 0) {
206+
return false;
207+
}
208+
const traits = splitTrait(trait);
209+
for (let singleTrait of traits) {
210+
traitdescriptions.push(getTraitDefinition(singleTrait, Number(traitPiece)));
211+
}
212+
return traitdescriptions;
213+
}
214+
215+
function getName(id, itemtype) {
216+
// ToDo handle unique stuff properly
217+
if (id.toString().startsWith('UNIQUE')) {
218+
return id;
219+
}
220+
try {
221+
const languageNameId = data['data'][itemtype][id]['LanguageNameId'];
222+
return data['strings'][languageNameId];
223+
} catch {
224+
console.log(id, itemtype);
225+
}
226+
}
227+
228+
function getColour(id, itemtype) {
229+
let nodeType = data['data']?.['node_types']?.[itemtype]?.[id];
230+
if (!nodeType) {
231+
nodeType = itemtype === 'units' ? 'Unit' : 'BuildingTech';
232+
}
233+
return getColourForNodeType(nodeType);
234+
}
235+
236+
function building(id) {
237+
return new Caret(TYPES.BUILDING, getName(id, 'buildings'), id, getColour(id, 'buildings'));
238+
}
239+
240+
function unit(id) {
241+
return new Caret(TYPES.UNIT, getName(id, 'units'), id, getColour(id, 'units'));
242+
}
243+
244+
function tech(id) {
245+
return new Caret(TYPES.TECHNOLOGY, getName(id, 'techs'), id);
246+
}
247+
248+
function u(unit) {
249+
return 'unit_' + formatId(unit);
250+
}
251+
252+
function b(building) {
253+
return 'building_' + formatId(building);
254+
}
255+
256+
function t(tech) {
257+
return 'tech_' + formatId(tech);
258+
}
259+
260+
function formatName(originalname) {
261+
if (!originalname) return "";
262+
let name = originalname.toString().replace(/<br>/g, '\n').replace(/\n+/g, '\n');
263+
const items = name.split('\n');
264+
for (let i = 0; i < items.length; i++) {
265+
const item = items[i];
266+
if (items[i].length > 10) {
267+
let space = item.indexOf(' ');
268+
if (space !== -1) {
269+
items[i] = item.slice(0, space) + '\n' + item.slice(space + 1);
270+
let alternativeSpace = space + 1 + item.slice(space + 1).indexOf(' ');
271+
if (alternativeSpace !== -1) {
272+
if (Math.abs((item.length / 2) - alternativeSpace) < Math.abs((item.length / 2) - space)) {
273+
items[i] = item.slice(0, alternativeSpace) + '\n' + item.slice(alternativeSpace + 1);
274+
}
275+
}
276+
} else {
277+
let hyphen = item.indexOf('-');
278+
if (hyphen !== -1) {
279+
items[i] = item.slice(0, hyphen) + '-\n' + item.slice(hyphen + 1);
280+
let alternativeHyphen = hyphen + 1 + item.slice(hyphen + 1).indexOf('-');
281+
if (alternativeHyphen !== -1) {
282+
if (Math.abs((item.length / 2) - alternativeHyphen) < Math.abs((item.length / 2) - hyphen)) {
283+
items[i] = item.slice(0, alternativeHyphen) + '-\n' + item.slice(alternativeHyphen + 1);
284+
}
285+
}
286+
}
287+
}
288+
}
289+
}
290+
return items.join('\n');
291+
}
292+
293+
function getConnectionPoints(tree) {
294+
let points = new Map();
295+
for (let lane of tree.lanes) {
296+
for (let r of Object.keys(lane.rows)) {
297+
for (let caret of lane.rows[r]) {
298+
points.set(caret.id, {
299+
x: caret.x + (caret.width / 2),
300+
y: caret.y + (caret.height / 2)
301+
});
302+
}
303+
}
304+
}
305+
return points;
306+
}

0 commit comments

Comments
 (0)