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
![]() |
|||
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 |