Passed
Push — master ( 3cfbdf...1ddf8c )
by June
01:25
created

PokemonDB._processPrimitiveEntry   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 4
c 1
b 0
f 0
nc 2
nop 5
dl 0
loc 6
rs 10
1
/****
2
 * This module creates an in-memory Pokemon Database for Generation 1 Games
3
 * It uses a very small memory footprint and speedy-quick navigation
4
 * This happens through circular references which come with a lot of pitfalls
5
 *      * Make sure to only keep a single instance of this in memory, Javascript
6
 *        has a lot of issues with garbage collecting circular references.
7
 *      * If deep navigating the database, be very careful about not ending up
8
 *        in infinite loops.
9
 *      * You cannot use JSON.stringify or certain other tools, at least not
10
 *        without some careful prep work beforehand
11
 *
12
 * This was created initially for the Pokered Save Editor which works just fine
13
 * with the pitfalls of circular references but for other uses caution may need
14
 * to be taken.
15
 */
16
17
// Import Dependencies
18
const _ = require("lodash");
19
20
// Import Raw JSON Files
21
const itemsRaw = require("./data/items.json");
22
const movesRaw = require("./data/moves.json");
23
const pokemonRaw = require("./data/pokemon.json");
24
const typesRaw = require("./data/types.json");
25
26
const PokemonDB = exports.PokemonDB = function() {
27
    this.items = [];
28
    this.moves = [];
29
    this.pokemon = [];
30
    this.types = [];
31
32
    this.rebuild();
33
}
34
35
PokemonDB.prototype.rebuild = function() {
36
    // Setup primary index
37
    this.indexPrimary(this.itemsRaw, this.items, "ind");
38
    this.indexPrimary(this.movesRaw, this.moves, "ind");
39
    this.indexPrimary(this.pokemonRaw, this.pokemon, "ind");
40
    this.indexPrimary(this.typesRaw, this.types, "ind");
41
42
    // Add in aliases for names as-is
43
    this.indexSecondary(this.itemsRaw, this.items, "name", "ind");
44
    this.indexSecondary(this.movesRaw, this.moves, "name", "ind");
45
    this.indexSecondary(this.pokemonRaw, this.pokemon, "name", "ind");
46
    this.indexSecondary(this.typesRaw, this.types, "name", "ind");
47
48
    // Add in aliases for names in snake_case form
49
    this.indexSecondarySnake(this.itemsRaw, this.items, "name", "ind");
50
    this.indexSecondarySnake(this.movesRaw, this.moves, "name", "ind");
51
    this.indexSecondarySnake(this.pokemonRaw, this.pokemon, "name", "ind");
52
    this.indexSecondarySnake(this.typesRaw, this.types, "name", "ind");
53
54
    // Add in aliases prefixed
55
    this.indexSecondaryPrefix(this.itemsRaw, this.items, "tm", "ind");
56
    this.indexSecondaryPrefix(this.itemsRaw, this.items, "hm", "ind");
57
58
    this.indexSecondaryPrefix(this.movesRaw, this.moves, "tm", "ind");
59
    this.indexSecondaryPrefix(this.movesRaw, this.moves, "hm", "ind");
60
61
    this.indexSecondaryPrefix(this.pokemonRaw, this.pokemon, "pokedex", "ind");
62
63
    // Circular Link everything
64
    // Yes I'm aware of the dangers and pitfalls of circular linking
65
    // I'm choosing it anyways
66
    this.processAllItems();
67
    this.processAllMoves();
68
    this.processAllPokemon();
69
}
70
71
// Indexes all items by deep cloning each item and adding it to the object under key value
72
PokemonDB.prototype.indexPrimary = function(from, to, key) {
73
    for(let i = 0; i < from.length; i++) {
74
        const entry = from[i];
75
        to[entry[key]] = _.cloneDeep(entry);
76
    }
77
}
78
79
// Creates a 2nd index which just aliases the first index
80
PokemonDB.prototype.indexSecondary = function(from, to, key, origKey) {
81
    for(let i = 0; i < from.length; i++) {
82
        const entry = from[i];
83
        to[entry[key]] = to[entry[origKey]];
84
    }
85
}
86
87
// Creates a 2nd index snake_case'd which just aliases the first index
88
PokemonDB.prototype.indexSecondarySnake = function(from, to, key, origKey) {
89
    for(let i = 0; i < from.length; i++) {
90
        const entry = from[i];
91
        to[_.snakeCase(entry[key])] = to[entry[origKey]];
92
    }
93
}
94
95
// Creates a 2nd index prefixed which just aliases the first index
96
// Only creates alias if key exists
97
PokemonDB.prototype.indexSecondaryPrefix = function(from, to, key, origKey) {
98
    for(let i = 0; i < from.length; i++) {
99
        const entry = from[i];
100
        if(entry[key] !== undefined)
101
            to['' + key + entry[key]] = to[entry[origKey]];
1 ignored issue
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
102
    }
103
}
104
105
PokemonDB.prototype.processAllItems = function() {
106
    const self = this;
107
108
    _.each(self.items, function(value) {
109
        if(value == undefined)
0 ignored issues
show
Best Practice introduced by
Comparing value to undefined using the == operator is not safe. Consider using === instead.
Loading history...
110
            return;
1 ignored issue
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
111
112
        self._processPrimitiveEntry(value, "tm", "tm", "moves", self);
113
        self._processPrimitiveEntry(value, "hm", "hm", "moves", self);
114
    });
115
}
116
117
PokemonDB.prototype.processAllMoves = function() {
118
    const self = this;
119
120
    _.each(self.moves, function(value) {
121
        if(value == undefined)
0 ignored issues
show
Best Practice introduced by
Comparing value to undefined using the == operator is not safe. Consider using === instead.
Loading history...
122
            return;
1 ignored issue
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
123
124
        self._processPrimitiveEntry(value, "type", "name", "types", self);
125
        self._processPrimitiveEntry(value, "tm", "_tm", "items", self);
126
        self._processPrimitiveEntry(value, "hm", "_hm", "items", self);
127
    });
128
}
129
130
PokemonDB.prototype.processAllPokemon = function() {
131
    const self = this;
132
133
    _.each(self.pokemon, function(value) {
134
        if(value == undefined)
0 ignored issues
show
Best Practice introduced by
Comparing value to undefined using the == operator is not safe. Consider using === instead.
Loading history...
135
            return;
1 ignored issue
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
136
137
        if(value.moves !== undefined) {
138
            for(let i = 0; i < value.moves.length; i++) {
139
                const move = value.moves[i];
140
                if(move._move !== undefined)
141
                    continue;
1 ignored issue
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
142
143
                move._move = move.move;
144
                move.move = _.find(self.moves, ['name',move.move]);
145
            }
146
        }
147
148
        self._processArrayEntry(value, "initial", "name", "moves", self);
149
        self._processArrayEntry(value, "tmHm", "_tm", "moves", self);
150
151
        self._processPrimitiveEntry(value, "type1", "name", "types", self);
152
        self._processPrimitiveEntry(value, "type2", "name", "types", self);
153
154
        if(value.evolution !== undefined) {
155
            if(Array.isArray(value.evolution)) {
156
                for(let i = 0; i < value.evolution.length; i++) {
157
                    self._processEvolutionEntry(value.evolution[i], value, self);
158
                }
159
            }
160
            else
161
                self._processEvolutionEntry(value.evolution, value, self);
1 ignored issue
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
162
        }
163
    });
164
}
165
166
PokemonDB.prototype._processEvolutionEntry = function(entry, value, self) {
167
    if(entry.item !== undefined && entry._item === undefined) {
168
        entry._item = entry.item;
169
        entry.item = _.find(self.items, ['name', entry.item]);
170
    }
171
172
    if(entry._toName !== undefined)
173
        return;
1 ignored issue
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
174
175
    entry._toName = entry.toName;
176
    entry.toName = _.find(self.pokemon, ['name', entry.toName]);
177
178
    entry.toName.deEvolution = value;
179
}
180
181
PokemonDB.prototype._processArrayEntry = function(value, from, key, where, self) {
182
    if(value[from] !== undefined && value['_' + from] === undefined) {
183
        value['_' + from] = _.cloneDeep(value[from]);
184
        for(let i = 0; i < value[from].length; i++) {
185
            const val = value[from][i];
186
            value[from][i] = _.find(self[where], [key, val]);
187
        }
188
    }
189
}
190
191
PokemonDB.prototype._processPrimitiveEntry = function(value, from, key, where, self) {
192
    if(value[from] !== undefined && value["_" + from] === undefined) {
193
        value["_" + from] = value[from];
194
        value[from] = _.find(self[where], [key, value[from]]);
195
    }
196
}
197
198
// Add several ways to access the raw data, nothings duplicated because it's just all copying
199
// by reference
200
PokemonDB.itemsRaw = PokemonDB.prototype.itemsRaw = itemsRaw;
201
PokemonDB.movesRaw = PokemonDB.prototype.movesRaw = movesRaw;
202
PokemonDB.typesRaw = PokemonDB.prototype.typesRaw = typesRaw;
203
PokemonDB.pokemonRaw = PokemonDB.prototype.pokemonRaw = pokemonRaw;
204