Issues (1)

src/index.js (1 issue)

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
// Class Constructor
27
// Initiate Array
28
const PokeredMonDB = exports.PokeredMonDB = function() {
29
30
    // Do initial build
31
    this.rebuild();
32
}
33
34
// Build or Rebuild database
35
// This should normally never need to be called manually
36
// and is provided for special edge cases. Keep in mind calling this again
37
// might create memory leaks in certain browsers due to circular references.
38
PokeredMonDB.prototype.rebuild = function() {
39
    
40
    // Rr-create array
41
    this.items = [];
42
    this.moves = [];
43
    this.pokemon = [];
44
    this.types = [];
45
46
    // Setup primary index to be the internal index number
47
    this._indexPrimary(this.itemsRaw, this.items, "ind");
48
    this._indexPrimary(this.movesRaw, this.moves, "ind");
49
    this._indexPrimary(this.pokemonRaw, this.pokemon, "ind");
50
    this._indexPrimary(this.typesRaw, this.types, "ind");
51
52
    // Add in aliases for names as-is
53
    this._indexSecondary(this.itemsRaw, this.items, "name", "ind");
54
    this._indexSecondary(this.movesRaw, this.moves, "name", "ind");
55
    this._indexSecondary(this.pokemonRaw, this.pokemon, "name", "ind");
56
    this._indexSecondary(this.typesRaw, this.types, "name", "ind");
57
58
    // Add in aliases for names in snake_case form
59
    this._indexSecondarySnake(this.itemsRaw, this.items, "name", "ind");
60
    this._indexSecondarySnake(this.movesRaw, this.moves, "name", "ind");
61
    this._indexSecondarySnake(this.pokemonRaw, this.pokemon, "name", "ind");
62
    this._indexSecondarySnake(this.typesRaw, this.types, "name", "ind");
63
64
    // Add in aliases prefixed
65
    this._indexSecondaryPrefix(this.itemsRaw, this.items, "tm", "ind");
66
    this._indexSecondaryPrefix(this.itemsRaw, this.items, "hm", "ind");
67
68
    this._indexSecondaryPrefix(this.movesRaw, this.moves, "tm", "ind");
69
    this._indexSecondaryPrefix(this.movesRaw, this.moves, "hm", "ind");
70
71
    this._indexSecondaryPrefix(this.pokemonRaw, this.pokemon, "pokedex", "ind");
72
73
    // Circular Link everything
74
    this._processAllItems();
75
    this._processAllMoves();
76
    this._processAllPokemon();
77
}
78
79
// Indexes all items by deep cloning each item and adding it to the object under key value
80
PokeredMonDB.prototype._indexPrimary = function(from, to, key) {
81
    this._iterate(from, function(entry) {
82
        to[entry[key]] = _.cloneDeep(entry);
83
    });
84
}
85
86
// Creates a 2nd index which just aliases the first index
87
PokeredMonDB.prototype._indexSecondary = function(from, to, key, origKey) {
88
    this._iterate(from, function(entry) {
89
        to[entry[key]] = to[entry[origKey]];
90
    });
91
}
92
93
// Creates a 2nd index snake_case'd which just aliases the first index
94
PokeredMonDB.prototype._indexSecondarySnake = function(from, to, key, origKey) {
95
    this._iterate(from, function(entry) {
96
        to[_.snakeCase(entry[key])] = to[entry[origKey]];
97
    });
98
}
99
100
// Creates a 2nd index prefixed which just aliases the first index
101
// Only creates alias if key exists
102
PokeredMonDB.prototype._indexSecondaryPrefix = function(from, to, key, origKey) {
103
    this._iterate(from, function(entry) {
104
        if(entry[key] !== undefined)
105
            to['' + key + entry[key]] = to[entry[origKey]];
106
    });
107
}
108
109
// This interlinks certain item meta-data to other database entries
110
PokeredMonDB.prototype._processAllItems = function() {
111
    this._iterate(this.items, function(value) {
112
        this._processPrimitiveEntry(value, "tm", "tm", "moves", this);
113
        this._processPrimitiveEntry(value, "hm", "hm", "moves", this);
114
    });
115
}
116
117
// This interlinks certain move meta-data to other database entries
118
PokeredMonDB.prototype._processAllMoves = function() {
119
    this._iterate(this.moves, function(value) {
120
        this._processPrimitiveEntry(value, "type", "name", "types", this);
121
        this._processPrimitiveEntry(value, "tm", "_tm", "items", this);
122
        this._processPrimitiveEntry(value, "hm", "_hm", "items", this);
123
    });
124
}
125
126
// This interlinks certain pokemon meta-data to other database entries
127
PokeredMonDB.prototype._processAllPokemon = function() {
128
    this._iterate(this.pokemon, function(value) {
129
130
        this._iterate(value.moves, function(move) {
131
            this._processPrimitiveEntry(move, "move", "name", "moves", this);
132
        });
133
134
        this._processArrayEntry(value, "initial", "name", "moves", this);
135
        this._processArrayEntry(value, "tmHm", "_tm", "moves", this);
136
137
        this._processPrimitiveEntry(value, "type1", "name", "types", this);
138
        this._processPrimitiveEntry(value, "type2", "name", "types", this);
139
140
        this._processArrPrimEntry(value, "evolution", this._processEvolutionEntry);
141
    });
142
}
143
144
// This loops over an object or array if it's valid, checks each entry and calls a function
145
// if the entry is not defined. The function is binded to the class.
146
PokeredMonDB.prototype._iterate = function(v, fn) {
147
    _.each(v, function(value) {
148
        if(value === undefined)
149
            return;
0 ignored issues
show
Comprehensibility Best Practice introduced by
Are you sure this return statement is not missing an argument? If this is intended, consider adding an explicit undefined like return undefined;.
Loading history...
150
151
        return fn.call(this, value);
152
    }.bind(this));
153
}
154
155
// This loops over an evolution pokemon database entry and proccesses it
156
PokeredMonDB.prototype._processEvolutionEntry = function(entry, value) {
157
    this._processPrimitiveEntry(entry, "item", "name", "items");
158
159
    // Intentional to simplify code below
160
    if(entry._toName !== undefined)
161
        return;
162
163
    this._processPrimitiveEntry(entry, "toName", "name", "pokemon");
164
    entry.toName.deEvolution = value;
165
}
166
167
// This loops over a simple array if it exists and inter-links valid elements to other data
168
PokeredMonDB.prototype._processArrayEntry = function(value, from, key, where) {
169
    if(value[from] !== undefined && value['_' + from] === undefined) {
170
        value['_' + from] = _.cloneDeep(value[from]);
171
        for(let i = 0; i < value[from].length; i++) {
172
            const val = value[from][i];
173
            value[from][i] = _.find(this[where], [key, val]);
174
        }
175
    }
176
}
177
178
// This interlinks primitive data to other data
179
PokeredMonDB.prototype._processPrimitiveEntry = function(value, from, key, where) {
180
    if(value[from] !== undefined && value["_" + from] === undefined) {
181
        value["_" + from] = value[from];
182
        value[from] = _.find(this[where], [key, value[from]]);
183
    }
184
}
185
186
// This interlinks a variable that could be an array or primitive and calls given function
187
// with each item and original object item came from
188
PokeredMonDB.prototype._processArrPrimEntry = function(value, from, fn) {
189
    if(Array.isArray(value[from])) {
190
        this._iterate(value[from], function(arrEntry) {
191
            fn.call(this, arrEntry, value);
192
        });
193
    }
194
    else if(value[from] !== undefined)
195
        fn.call(this, value[from], value);
196
}
197
198
// Add several ways to access the raw data, nothings duplicated because it's just all copying
199
// by reference
200
PokeredMonDB.itemsRaw = PokeredMonDB.prototype.itemsRaw = itemsRaw;
201
PokeredMonDB.movesRaw = PokeredMonDB.prototype.movesRaw = movesRaw;
202
PokeredMonDB.typesRaw = PokeredMonDB.prototype.typesRaw = typesRaw;
203
PokeredMonDB.pokemonRaw = PokeredMonDB.prototype.pokemonRaw = pokemonRaw;
204