├── .gitattributes
├── LICENSE
├── README.md
├── changelog.txt
├── config.lua
├── data-final-fixes.lua
├── info.json
└── thumbnail.png
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Zangeti
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # TextureBase
2 | **TextureBase is a mod that make it easy to create Texture Packs in factorio.
**
3 | **It's tool help you override textures, but also sound .ogg files so you can create your own a custom experience!
**
4 |
5 | Follow the guide below to see how you can easily make your own texture pack!
6 | Don't be intimidated by the README length; a vast majority is dedicated towards explaining [Attributes](#attributes) & [Wild Cards](#wild-cards-), an optional tool you don't have to use for your texturepack & giving lots of examples!
7 |
8 | > Why not check out my discord @ [https://discord.gg/kC53xn2](https://discord.gg/kC53xn2) and the factorio mod portal @ [https://mods.factorio.com/mod/texturebase](https://mods.factorio.com/mod/texturebase) for more info
9 |
10 | #### Contents
11 | - **[Steps](#steps)**
12 | - [Download TextureBase!](#download-texturebase)
13 | - [Give your Texture Pack a cool name!](#give-your-texture-pack-a-cool-name)
14 | - [Create duplicate file tree in *\_\_texturepackfolder\_\_/data* to add your own textures & sounds!](#create-duplicate-file-tree-in-\_\_texturepackfolder\_\_data-to-add-your-own-textures--sounds)
15 | - [Let your texture pack know which files you have retextured!](#let-your-texture-pack-know-which-files-you-have-retextured)
16 | - *[Basic Syntax](#basic-syntax)*
17 | - *[Attributes __(optional)__](#attributes)*
18 | - *[Wild Cards (\*) __(optional)__](#wild-cards-)*
19 | - [You're done!!!](#youre-done)
20 | - **[Still having Issues, Bugfixes, Feature Requests](#still-having-issues-bugfixes-feature-requests)**
21 | - **[License](#license)**
22 |
23 | ---
24 |
25 |
26 | # Steps
27 |
28 | ## Download TextureBase!
29 |
30 | - Place TextureBase into the factorio/data mods folder
31 | - The mod does nothing on its own; follow the next steps to make a texturepack with it!
32 |
33 | ---
34 |
35 |
36 | ## Give your Texture Pack a cool name!
37 |
38 | - Rename the TextureBase mod folder to what you want to call your texture pack in the format of '_'. Your name must be lower case!
39 | - edit info.json
40 | - set "name" to the lower case mod name of your folder
41 | - set "version" to the version of your texturepack (usually 0.0.1 if you are creating a new texture pack); must be the same as the version written in the folder name
42 | - set "title" to the mod name! Here you may use uppercase letters & spaces
43 | - set "author" to the username of your [Factorio Account](https://factorio.com/login)
44 | - edit the other fields as you see fit... if you are retexturing another mod, I recommend listing that mod as an optional dependency, to guarantee your texture pack is loaded after the mod you are retexturing is.
45 | - edit config.lua
46 | - set resource_pack_name to what you wrote for "name" in info.json
47 |
48 | ---
49 |
50 |
51 | ## Create duplicate file tree in *\_\_texturepackfolder\_\_/data* to add your own textures & sounds!
52 |
53 | - For the texturepack to apply your textures correctly, you need to save them in a specific format & location. Follow the next steps:
54 | - Locate the data folder inside of your texturepack (the folder you just renamed). That data folder should be empty (we will from now on call this data folder *\_\_texturepackfolder\_\_/data*).
55 | - Think of this *\_\_texturepackfolder\_\_/data* directory as a duplicate of the *factorio/data* directory (mods folder). **Any textures created in this *\_\_texturepackfolder\_\_/data* must have the same relative paths as their originals to factorio/data**
56 | - for example, to retexture a nuclear explosion, you would:
57 | - find the directory in which the nuclear explosion sprites are stored, which is *factorio/data/base/graphics/entity/nuke-explosion/*
58 | - mirror the file tree in *\_\_texturepackfolder\_\_/data*, such that the directory *\_\_texturepackfolder\_\_/data/base/graphics/entity/nuke-explosion/* exists
59 | - place the .png files in *factorio/data/base/graphics/entity/nuke-explosion/* you have retextured in *\_\_texturepackfolder\_\_/data/base/graphics/entity/nuke-explosion/*. Make sure the filename & format of your texture is the same the original.
60 | - Mods may have file trees with different structures to base or core.
61 | - You must nonetheless mirror the file tree of the respective mod.
62 | - e.g. to change the logistic chest texture in [boblogistics](https://mods.factorio.com/mod/boblogistics) found at *factorio/data/boblogistics/graphics/entity/logistic-chest/*, you would have to add your retextured files to *\_\_texturepackfolder\_\_/data/boblogistics/graphics/entity/logistic-chest/*
63 | - Sounds work the same! To change the spidertron activation sound (*factorio/data/base/sound/spidertron/spidertron-activate.ogg*), place your new sound file to *\_\_texturepackfolder\_\_/data/base/sound/spidertron/spidertron-activate.ogg*
64 |
65 | - If you are planning on retexturing large parts of base or another mod, I would recommend copying the mod directory found in *factorio/data* to *\_\_texturepackfolder\_\_/data*, and deleting any files / directories that do not contain images or .ogg sound files (or textures/sounds you don't want to change)!
66 |
67 | ---
68 |
69 |
70 | ## Let your texture pack know which files you have retextured!
71 |
72 | ### Basic Syntax
73 |
74 | - Sadly, due to factorio lua not being able to read files, the texture pack currently has no way of knowing which files you put into *\_\_texturepackfolder\_\_/data*
75 | - Let's change that
76 | - edit config.lua
77 | - Here you let the resource pack know which textures & sounds you have created in *\_\_texturepackfolder\_\_/data* and hence would like changed in game. You do this by changing the 'data' table in config.lua
78 | - To let the program know you want *factorio/data/base/sound/spidertron/spidertron-activate.ogg* replaced by *\_\_texturepackfolder\_\_/data/base/sound/spidertron/spidertron-activate.ogg*, you write the following:
79 | ```lua
80 | data = {
81 | base = {
82 | sound = {
83 | spidertron = {
84 | ["spidertron-activate.ogg"] = { },
85 | },
86 | },
87 | },
88 | }
89 | ```
90 | > You can see how the path to the file to be changed is specified in config.lua, every directory and file being represented by a table (though files are always in the format ["\"] = {}, whilst this is only the case for folders with special character in their name \[see below in Key Rules]).
91 | - If you wanted to change both deactivation & activation sound of the spidertron, both of which are found in the *factorio/data/base/sound/spidertron/* directory, you would do so as follows
92 | ```lua
93 | data = {
94 | base = {
95 | sound = {
96 | spidertron = {
97 | ["spidertron-activate.ogg"] = { },
98 | ["spidertron-deactivate.ogg"] = { },
99 | },
100 | },
101 | },
102 | }
103 | ```
104 | > Keep in mind your own *spidertron-activate.ogg* and *spidertron-deactivate.ogg* both have to exist in *\_\_texturepackfolder\_\_/data/base/sound/spidertron/* for the mod to replace the original sound files with your own! If you specify files in the config.lua data table that do not exist in *\_\_texturepackfolder\_\_/data/* your texture pack will throw a 'file not found' error upon loading the game.
105 | - If you wanted to change every spidertron sound stored in *factorio/data/base/sound/spidertron*, you luckily don't have write all of them out! The format below tells the texture pack to replace every sound in the *factorio/data/base/sound/spidertron* directory (and any subdirectories) with their *\_\_texturepackfolder\_\_/data* equivalents!
106 | ```lua
107 | data = {
108 | base = {
109 | sound = {
110 | spidertron = {
111 |
112 | },
113 | },
114 | },
115 | }
116 | ```
117 | > Pay special attention to how folders with listed contents will exclusively have these contents loaded into the game as textures/sounds, whilst when folder contents are not specified, all .ogg, .png and .jpg files inside them are loaded into the game as textures. Once again, ensure that *\_\_texturepackfolder\_\_/data/base/sound/spidertron* contains a retextured file for every file in *factorio/data/base/sound/spidertron*, so your texture pack is not trying to assign textures to files that don't actually exist!
118 |
119 | - At this point you have learnt the basic syntax of using the config.lua data table to let your resource pack know which textures / sounds you are replacing!
120 |
121 | **The Key Rules are:**
122 | **1. Folders are always declared as tables (\ = {}, or if special characters (such as .,%+-/\*) are in the foldername ["\"] = {},)**
123 | **2. Files are always declared as tables in the following format; ["\"] = {}, like folders with special characters in their name**
124 | **3. Undefined table contents mean all of that folders/images .ogg, .png and .jpg contents have been retextured and exist the repective path in *\_\_texturepackfolder\_\_/data***
125 | **4. Folders with defined contents (whether that be files or other folders), contain retextured .ogg, .png and .jpg files in these defined folders / files**
126 | > A few more examples will follow, but they all follow the above fundamental guidelines.
127 |
128 | - The below example is telling the resource pack you are retexturing/changing *accumulator.png* and *hr-accumulator.png* in *factorio/data/base/graphics/entity/accumulator/*, along with all .ogg, .png and .jpg files in, *factorio/data/base/sound/spidertron/*, *factorio/data/core/sound/* and *factorio/data/boblogistics/graphics/gui/*
129 | ```lua
130 | data = {
131 | base = {
132 | graphics = {
133 | entity = {
134 | accumulator = {
135 | ["accumulator.png"] = { },
136 | ["hr-accumulator.png"] = { },
137 | },
138 | ["copper-ore"] = {
139 |
140 | },
141 | },
142 | },
143 | sound = {
144 | spidertron = {
145 |
146 | },
147 | },
148 | },
149 | core = {
150 | sound = {
151 |
152 | },
153 | },
154 | boblogistics = {
155 | graphics = {
156 | gui = {
157 |
158 | },
159 | },
160 | },
161 | }
162 | ```
163 | > Note that the use of '["copper-ore"]'. This is the format in which foldernames need to be written down that include special characters, in this case a dash ("-").
164 |
165 | > Also note that (as of version 1.0.0) boblogistics only has one .png file in *boblogistics/graphics/gui/* *(checkbox.png)*. data = {boblogistics = {graphics = {gui = {},},},} (as above) is therefore synonymous to writing data = {boblogistics = {graphics = {gui = {"checkbox.png",},},},}!
166 |
167 | - The this system for the data table in config.lua may seem overcomplicated. However, especially when resource packs replace a vast number of the games assets, the data table gets really simple. After all, if you wanted to replace all .ogg, .png and .jpg files in factorio/base, you would let the resourcepack know as follows:
168 | ```lua
169 | data = {
170 | base = {
171 |
172 | },
173 | }
174 | ```
175 | > Thats a pretty short data table considering the hundreds, if not thousands of images/.ogg sound files in *factorio/data/base/* that the resource pack will try to replace! One final reminder, that telling the mod you are replacing every .ogg, .png and .jpg file in *factorio/data/base/*, means you need a replacement for each of these files in the mirrored path in *\_\_texturepackfolder\_\_/data/base/*
176 |
177 | **[Back To Top](#texturebase)**
178 |
179 | ---
180 |
181 | ### Attributes
182 |
183 | **This is not necessary to get your texturepack working**. I recommend reading up on this however if you are intrested in what other things TextureBase can do for you (such as higher res pictures than the original or excluding certain filenames).
184 | If you are making your first texturepack, I recommend you **[SKIP THIS SECTION](#youre-done)**.
185 |
186 | Attributes are effectively custom keywords you can use in the config.lua data table for custom behaviour in terms of which files are retextured & which aren't
187 | ```lua
188 | data = {
189 | base = {
190 | graphics = {
191 | entity = {
192 | attribute_name = attribute_data_table1,
193 | attribute_name = attribute_data_table2,
194 | folder1 = folder_content_data_table1,
195 | folder2 = folder_content_data_table2,
196 | folder3 = folder_content_data_table3,
197 | folder4 = folder_content_data_table4,
198 | },
199 | },
200 | },
201 | }
202 | ```
203 | > You can see the format of declaring attributes is identical to that of declaring folders in the config.lua data table. YES, this does mean you can't retexture the contents of a folder with the same name as an attribute! Ask the creator of the mod you are retexturing to rename the folder!
204 |
205 | Current attributes are:
206 | - \_\_settings\_\_
207 |
208 |
209 |
210 | #### The \_\_settings\_\_ Attribute; Customising What / How the Resource Pack Retextures by Specific Settings
211 |
212 | - The use of the \_\_settings\_\_ attribute in the config.lua data table further customizes what/how the resource pack retextures.
213 | - See the below example for one use of \_\_settings\_\_. Here we are retexturing every .png, .jpg and .ogg file in *data/base/graphics*, except for those with "shadow" or "reflection" in their name. This is very useful when you are using a script to modify images in a folder, but are omitting those with a certain string in their name.
214 | ```lua
215 | data = {
216 | base = {
217 | graphics = {
218 | __settings__ = {
219 | exclude_names = {"shadow", "reflection"},
220 | },
221 | },
222 | },
223 | }
224 | ```
225 | > Think \_\_settings\_\_ as an attribute of all contents within the same folder as itself (in this case the contents of *data/base/graphics*).
226 |
227 | - **The Format of the \_\_settings\_\_ Attribute is as follows:**
228 | ```lua
229 | name_of_folder_whose_contents_are_affected_by_settings = {
230 | __settings__ = {
231 | setting_name_1 = setting_value_1,
232 | setting_name_2 = setting_value_2,
233 | setting_name_3 = setting_value_3,
234 | },
235 | }
236 | ```
237 |
238 | - The \_\_settings\_\_ attribute is applied to all files set for retexturing within the folder in which the \_\_settings\_\_ attribute is declared.
239 | This means that a folder with undeclared contents (\_\_settings\_\_ does not count), will have its \_\_settings\_\_ attribute applied to all .png, .jpg and .ogg files within it, whereas a folder with declared contents will only have the \_\_settings\_\_ attribute applied to these subdirectories / images.
240 |
241 |
242 | - **Simple Rules of how \_\_settings\_\_ Attributes work.**
243 |
244 | **1. \_\_settings\_\_ Attributes Apply to all Files inside the Folder / File in Which the Attribute is Declared.**
245 |
246 | *Therefore...*
247 | ```lua
248 | data = {
249 | __settings__ = {
250 | setting_name_1 = setting_value_1,
251 | },
252 |
253 | base = {
254 | ["example_image.png"] = { },
255 | },
256 | }
257 | ```
258 | *has the same result as...*
259 | ```lua
260 | data = {
261 | base = {
262 | __settings__ = {
263 | setting_name_1 = setting_value_1,
264 | },
265 | ["example_image.png"] = { },
266 | },
267 | }
268 | ```
269 | *which has the same result as...*
270 | ```lua
271 | data = {
272 | base = {
273 | ["example_image.png"] = {
274 | __settings__ = {
275 | setting_name_1 = setting_value_1
276 | },
277 | },
278 | },
279 | }
280 | ```
281 | In the top 2 examples above, *example_image.png* is in a folder whose \_\_settings\_\_ attribute prescibes for all of the retextured files inside of it have *setting_name_1* applied at *setting_name_1*. In the last example, the \_\_settings\_\_ attribute is directly applied to the image itself (and not the contents of a folder, the image happens to be in).
282 | Note *example_image.png* does not actually exist in *data/base/*; it only demonstrates how \_\_settings\_\_ attributes are applied to retextured images. *setting_name_1* and *setting_name_1* are also only placeholders for actual settings (see below)
283 |
284 |
285 | **2. \_\_settings\_\_ Attributes can 'Stack' Up, Down a Path.**
286 |
287 | *Therefore...*
288 | ```lua
289 | data = {
290 | __settings__ = {
291 | setting_name_1 = setting_value_1,
292 | },
293 |
294 | base = {
295 | __settings__ = {
296 | setting_name_2 = setting_value_2,
297 | },
298 |
299 | ["example_image.png"] = {
300 | __settings__ = {
301 | setting_name_3 = setting_value_3,
302 | },
303 | },
304 | },
305 | }
306 | ```
307 | *has the same result as...*
308 | ```lua
309 | data = {
310 | base = {
311 | ["example_image.png"] = {
312 | __settings__ = {
313 | setting_name_1 = setting_value_1,
314 | setting_name_2 = setting_value_2,
315 | setting_name_3 = setting_value_3,
316 | },
317 | },
318 | },
319 | }
320 | ```
321 | Different settings for an image accumulate along its path.
322 |
323 |
324 | **3. \_\_settings\_\_ Attributes can Overwrite each other Down a Path**
325 |
326 | *Therefore...*
327 | ```lua
328 | data = {
329 | __settings__ = {
330 | setting_name_1 = setting_value_1,
331 | },
332 |
333 | base = {
334 | __settings__ = {
335 | setting_name_1 = setting_value_2,
336 | },
337 |
338 | ["example_image.png"] = {
339 | __settings__ = {
340 | setting_name_1 = setting_value_3,
341 | },
342 | },
343 | },
344 | }
345 | ```
346 | *has the same result as...*
347 | ```lua
348 | data = {
349 | base = {
350 | ["example_image.png"] = {
351 | __settings__ = {
352 | setting_name_1 = setting_value_3,
353 | },
354 | },
355 | },
356 | }
357 | ```
358 | Same settings for an image overwrite each other along its path.
359 |
360 |
361 | - Remember the contents of a folder. Folders that do not contain anything (but a \_\_settings\_\_ attribute) have that \_\_settings\_\_ attribute applied to all of the .png, .jpg and .ogg files in it, as a folder with undefined contents means all of its contents have been retextured. A folder with defined contents (other than a \_\_settings\_\_ attribute), will only have these settings applied to its defined contents!
362 |
363 |
364 |
365 |
366 |
367 | **Setting List**
368 |
369 | A list of all accepted parameters in the \_\_settings\_\_ attribute
370 | Settings are given in the form of *\ = \ (default = \)*
371 | > Note that to exclude certain images images / folders from a setting defined in a superceding folder, you will have to set the settings in the \_\_settings\_\_ attribute to their defaults
372 |
373 |
374 | - ***exclude_names = list (default = {})***
375 | ```lua
376 | data = {
377 | base = {
378 | graphics = {
379 | __settings__ = {
380 | exclude_names = {"*shadow*", "*reflection*", "*mask*", "*particle*", "*fire*", "*.jpg", "hr-accumulator-discharge.png"},
381 | },
382 | },
383 | },
384 | }
385 | ```
386 | exclude_names lets you exclude files that would otherwise be included in those to be retextured. This is very useful when you are retexturing via script.
387 |
388 | **e.g. If you Write a Script to Change the Look of every Image in *data/base/graphics/entity/* that Does not Have the Strings "shadow", "reflection" or "mask" in it, You Can Tell the Texture Pack to Retexture all Contents of *data/base/graphics/entity/* except Files Containing "shadow", "reflection" or "mask" in the Following Way:
**
389 | ```lua
390 | data = {
391 | base = {
392 | graphics = {
393 | entity = {
394 | __settings__ = {
395 | exclude_names = {"*shadow*", "*reflection*", "*mask*"},
396 | },
397 | },
398 | },
399 | },
400 | }
401 | ```
402 | > Note the usage of \* surrounding the terms that are not allowed around filenames that would otherwise be retextured. See [Wild Cards](#wild-cards-) why this is necessary in order to exclude files whose names include these strings!
403 |
404 | > **CAVEAT:** exclude_names let you ommit images that contain whatever string you enter! typing in something like "a" WILL exclude all images that contain an a in their name. A "." would ommit all images, as every image has a file format of!
405 | It does not matter whether images that are excluded are specified or not!
406 |
407 | **In Spite of Being Manually Specified, *accumulator-shadow.png* and *hr-accumulator-shadow.png* Will not Actually Have their Paths Replaced in data.raw, as They are Excluded by having "shadow" in their Name, and being inside the Entity Directory to which the Setting Attribute containing 'exclude_names = {"shadow", "reflection", "mask"}' is Applied
408 | ```lua
409 | data = {
410 | base = {
411 | graphics = {
412 | __settings__ = {
413 | exclude_names = {"*shadow*", "*reflection*", "*mask*"},
414 | },
415 | entity = {
416 | accumulator = {
417 | ["accumulator-shadow.png"] = { },
418 | ["hr-accumulator-shadow.png"] = { },
419 | },
420 | },
421 | },
422 | },
423 | }
424 | ```
425 | > The above example does not result in any images being retextured. All images specified are excluded by the settings attribute
426 |
427 | **Only Images with exclude_names in their Filenames (Name and Format) are Excluded! The Image's Entire Path is not part of its Filename!**
428 | ```lua
429 | data = {
430 | base = {
431 | graphics = {
432 | __settings__ = {
433 | exclude_names = {"*entity*"},
434 | },
435 | entity = {
436 |
437 | },
438 | },
439 | },
440 | }
441 | ```
442 | > Therefore, this marks all files in *data/base/graphics/entity/* without "entity" in their filename for retexturing! NOT all of the contents of entity are excluded from retexturing, as it is only the filenames that are checked whether they contain "entity", and NOT an images entire path!
443 |
444 |
445 | - ***upscale = int (default = 1)***
446 | ```lua
447 | data = {
448 | base = {
449 | graphics = {
450 | entity = {
451 | accumulator = {
452 | __settings__ = {
453 | upscale = 2,
454 | },
455 | },
456 | },
457 | },
458 | },
459 | }
460 | ```
461 | The *upscale* setting allows retextured images to have a higher resolution than their original. *upscale = 2* means that all the retextured files have 2x resolution of their originals, and for the texturepack to account for this in the in-game scaling of the texture.
462 | **Note that upscale is somewhat experimental!** You may get a variety of errors when using upscale, such as that below:
463 |
464 | 
465 |
466 | > Feel free to report other errors than this related to texture scaling on the discord @ [https://discord.gg/kC53xn2](https://discord.gg/kC53xn2), but beware these errors are likely caused by restrictions imposed by the Factorio dev team, and are not a mod error.
467 |
468 |
469 |
470 |
471 |
472 | **Setting Attribute Examples**
473 |
474 | - **A Simple Example of Using the \_\_settings\_\_ Attribute:**
475 | ```lua
476 | data = {
477 | base = {
478 | graphics = {
479 | entity = {
480 | __settings__ = {
481 | exclude_names = {"*shadow*", "*reflection*", "*mask*"},
482 | },
483 |
484 | accumulator = {
485 | __settings__ = {
486 | upscale = 2,
487 | },
488 | },
489 | character = { },
490 | car = { },
491 | },
492 | },
493 | },
494 | }
495 | ```
496 | **The Following Files are retextured:**
497 | 1. The .png, .jpg and .ogg contents of *data/base/graphics/entity/accumulator/* are retextured at 2x original resolution as long as their filename doesn't include "shadow", "reflection" or "mask".
498 | 2. The .png, .jpg and .ogg contents of *data/base/graphics/entity/character/* and *data/base/graphics/entity/car/* are retextured at normal resolution as long as their filename doesn't include "shadow", "reflection" or "mask".
499 |
500 |
501 |
502 | - **A Sophisticated Example of Using the \_\_settings\_\_ Attribute**
503 | ```lua
504 | data = {
505 | __settings__ = {
506 | exclude_names = {"*shadow*", "*reflection*"},
507 | },
508 |
509 | base = {
510 | graphics = {
511 | entity = {
512 | __settings__ = {
513 | exclude_names = {"*shadow*", "*charge*"}
514 | },
515 | accumulator = {
516 | __settings__ = {
517 | upscale = 2,
518 | },
519 | },
520 | ["acid-projectile"] = {
521 | ["hr-acid-projectile-head.png"] = {
522 | __settings__ = {
523 | upscale = 3,
524 | },
525 | },
526 | },
527 | beacon = {
528 |
529 | },
530 |
531 | car = {
532 |
533 | },
534 | },
535 | terrain = {
536 |
537 | },
538 | technology = {
539 |
540 | },
541 | },
542 | },
543 | boblogistics = {
544 | graphics = {
545 |
546 | },
547 | },
548 | }
549 | ```
550 |
551 | **No reason to panic; lets walk through this step by step.**
552 | 1. **We are retexturing all .png, .jpg and .ogg files in *data/base/graphics/entity/accumulator/*.**
553 | All images retextured that are within the *data/* folder, should not contain "shadow" or "relection" in their name (given in the format of "\.\").
554 | However, the *data/base/graphics/entity/* overrides the exlude_names setting, so within *data/base/graphics/entity/* files that include "shadow" or "charge" are excluded.
555 | Inside the *data/base/graphics/entity/accumulator/* folder, the upscale setting is set to 2.
556 | This means that the textures in *\_\_texturepackfolder\_\_/data/base/graphics/entity/accumulator/* are twice the resolution of those in *factorio/data/base/graphics/entity/accumulator/*, and need to be handled by factorio as such.
557 | Therefore, we are retexturing all images in *data/base/graphics/entity/accumulator/*, apart from those including "shadow" or "charge" in their name (in this case *accumulator-charge.png, accumulator-discharge.png, hr-accumulator-charge.png, hr-accumulator-discharge.png, accumulator-shadow.png and hr-accumulator-shadow.png), at 2x the resolution of the images' originals.
558 | 2. **We are also retexturing *data/base/graphics/entity/acid-projectile/hr-acid-projectile-head.png*.**
559 | Once again the exclusion of "shadow" and "reflection" for all files in the *data/* folder is overwritten for files in the *data/base/graphics/entity/* folder to exclude files with "shadow" or "charge" in their names.
560 | From the folder *data/base/graphics/entity/acid-projectile/* we are retexturing *hr-acid-projectile-head.png*. This file has an upscale of 3 set in its settings (settings in the table of an image, only apply to that image).
561 | Since *hr-acid-projectile-head.png* does not include the strings "shadow" or "charge", it is retextured, at 3x original resolution.
562 | 3. **We are also retexturing all .png, .jpg and .ogg files in *data/base/graphics/entity/beacon/* and *data/base/graphics/entity/car/*.**
563 | Once again, the exclusion of files containing the strings "shadow" and "reflection" within *data/* is overwritten in *data/base/graphics/* so instead files including "shadow" or "charge" are excluded.
564 | Therefore all .png, .jpg and .ogg files, apart from those with "shadow" or "charge" in their name, in *data/base/graphics/entity/beacon/* and *data/base/graphics/entity/car/* are retextured.
565 | The upscale setting has not been specified, so the files have their same resolution as their originals.
566 | 4. **We are also retexturing all .png, .jpg and .ogg files in *data/base/graphics/terrain/* and *data/base/graphics/technology/*.**
567 | All files in *data/* including strings "shadow" and "reflection" are excluded from those being retextured.
568 | The exclude_names setting is not overwritten in a subdirectory in the path of *data/base/graphics/terrain/* or *data/base/graphics/technology/*, so all files from *data/base/graphics/terrain/* and *data/base/graphics/technology/* are retextured apart from those including "shadow" or "reflection" in their name.
569 | 5. **We are also retexturing all .png, .jpg and .ogg files in *data/boblogistics/graphics/*.**
570 | All files in *data/* including strings "shadow" and "reflection" are excluded from those being retextured.
571 | This is not overwritten, so all contents of *data/boblogistics/graphics/* are retextured that do not include *shadow* or *reflection* in their name.
572 |
573 | **[Back To Top](#texturebase)**
574 |
575 | ---
576 |
577 | ### Wild Cards (\*)
578 |
579 | **This is not necessary to get your texturepack working**. I recommend reading up on this however if you are interested in what other things TextureBase can do for you (such as Wild Cards in directory/fiile names in config.lua data table).
580 | If you are making your first texturepack, I recommend you **[SKIP THIS SECTION](#youre-done)**.
581 |
582 | **Wild Cards increase your flexibility in defining folders to be retextured in the config.lua data table**
583 |
584 | - Wild Cards allow for parts of a string to be unknown, which is denoted by an \* (asterix)
585 | - Here are some wild card examples:
586 | ```lua
587 | "*" -- the whole name can be anything; all strings are included
588 | "character*" -- the name must start with "character" and end with an unknown string (which may just be "")
589 | "*.png" -- the name must end with ".png" with an unknown string before that
590 | "circuit*-*.png" -- the name must start with "circuit" and end with ".png" in between these, "-" must also be in the name
591 | "*rocket*1*" -- the "rocket" must be in the name. "1" must also be in the name after rocket
592 | "water.png" -- just kidding; there is no wild card here. The entire name is known
593 | ```
594 | - **One Rule:** You **CANNOT** use wild cards in the selection of mod name level directories in the config.lua data table e.g. *data = {\["base\*"] = {}}* is **NOT** allowed
595 |
596 |
597 |
598 |
599 |
600 | **Use Cases**
601 |
602 | - **Say You Wanted to Retexture all of the Contents of *data/base/graphics/entity/*, but only Upscale the Contents of *data/base/graphics/entity/accumulator*.**
603 | ```lua
604 | data = {
605 | base = {
606 | graphics = {
607 | entity = {
608 | accumulator = {
609 | __settings__ = {
610 | upscale = 2,
611 | },
612 | },
613 | ["*"] = { },
614 | },
615 | },
616 | },
617 | }
618 | ```
619 | The above example of default_include does that exact thing!
620 | However, as is you would have to write out every folder in *data/base/graphics/entity/* to then specify in *data/base/graphics/entity/accumulator* upscale its contents (with a settings attribute).
621 |
622 | **See Below the Unpractical Nature of this Approach!**
623 | ```lua
624 | data = {
625 | base = {
626 | graphics = {
627 | entity = {
628 | accumulator = {
629 | __settings__ = {
630 | upscale = 2,
631 | },
632 | },
633 | ["acid-projectile"] = { },
634 | ["acid-splash"] = { },
635 | ["acid-sticker"] = { },
636 | ["artillery-cannon-muzzle-flash"] = { },
637 | ["artillery-projectile"] = { },
638 | ["artillery-turret"] = { },
639 | ["artillery-wagon"] = { },
640 | ["assembling-machine-1"] = { },
641 | ["assembling-machine-2"] = { },
642 | ["assembling-machine-3"] = { },
643 | beacon = { },
644 | beam = { },
645 | ["bigass-explosion"] = { },
646 | ["big-electric-pole"] = { },
647 | ["big-explosion"] = { },
648 | biter = { },
649 | ["blue-beam"] = { },
650 | ["blue-laser"] = { },
651 | boiler = { },
652 | bullet = { },
653 | ["burner-inserter"] = { },
654 | ["burner-mining-drill"] = { },
655 | car = { },
656 | ["cargo-wagon"] = { },
657 | centrifuge = { },
658 | character = { },
659 | ["chemical-plant"] = { },
660 |
661 | etc -- 'etc' is not valid syntax; this list is just continues for longer and looks reeeaaally unsightly.
662 |
663 |
664 | },
665 | },
666 | },
667 | }
668 | ```
669 | > the 'etc' is not actual lua syntax; there are many more folders in *data/base/graphics/entity/* that would need to be listed, but the list would be too unsightly.
670 |
671 |
672 |
673 |
674 |
675 | **Syntax & Examples**
676 |
677 | - **Order of Wild Cards in a Table affects How and Which Images are have their Paths Changed to their Retextured Versions
**
678 | The below example...
679 | ```lua
680 | data = {
681 | base = {
682 | graphics = {
683 | entity = {
684 | accumulator = {
685 | __settings__ = {
686 | upscale = 2,
687 | },
688 | },
689 | ["*"] = { },
690 | },
691 | },
692 | },
693 | }
694 | ```
695 | is **NOT** the same as ...
696 | ```lua
697 | data = {
698 | base = {
699 | graphics = {
700 | entity = {
701 |
702 | ["*"] = { },
703 |
704 | accumulator = {
705 | __settings__ = {
706 | upscale = 2,
707 | },
708 | },
709 | },
710 | },
711 | },
712 | }
713 | ```
714 | This is because Wild cards sort objects in the order in which they are written in the config.lua data table.
715 |
716 | - **In the first of the 2 code snippets**, when an image in data.raw is found whose path is inside *data/base/graphics/entity/*, the path is checked that the next dirctory down is the *accumulator/* dirctory. If so, the file's path is changed to the retextured file, and data.raw settings are changed to allow the replacement picture to be 1.5x the resolution of the original. Else if the next directory down the path has a name (wild card of "\*" represents an unspecified part of a string), which all directories do, it is loaded without the upscale applied.
717 | - **In the second of the 2 code snippets**, when an image in data.raw is found whose path is inside *data/base/graphics/entity/*, the path is checked that the next dirctory down has a name (wild card of "\*" represents an unspecified part of a string), which all directories do, it is loaded without the upscale applied. Even files inside the *accumulator/* folder are not upscaled, as the folder name "accumulator" these files are located in matches the "\*" wild card, which in example 2 is queried before the "accumulator" folder.
718 |
719 |
720 | - **A Slightly More Complex Example Involving Wild Cards**
721 | ```lua
722 | data = {
723 | base = {
724 | graphics = {
725 | achievement = {
726 | __settings__ = {
727 | exclude_names = {"*circuit-veteran*", "*computer-age*"},
728 | upscale = 2,
729 | },
730 | ["iron-throne-*.png"] = {
731 | __settings__ = {
732 | upscale = 1,
733 | },
734 | },
735 | ["*"] = {},
736 | },
737 | entity = {
738 | biter = {
739 | ["biter-attack-01.png"] = {
740 | __settings__ = {
741 | upscale = 1.5,
742 | },
743 | },
744 | ["*"] = {},
745 | },
746 | ["*"] = {
747 | __settings__ = {
748 | upscale = 2,
749 | },
750 | },
751 | },
752 | ["*"] = {},
753 | },
754 | },
755 | }
756 | ```
757 | **Here the following is happening:**
758 | 1. We are retexturing all the .png, .jpg and .ogg files in *data/base/graphics/*, except from those in *data/base/graphics/achievement/* whose names contain "circuit-veteran" or "computer-age".
759 | 2. All .png, .jpg and .ogg files other than those matching the "iron-throne-\*.png" wildcard in *data/base/graphics/achievement/* are retextured at 2x original resolution.
760 | 3. *data/base/graphics/entity/biter/biter-attack-01.png* is retextured at 1.5x original resolution, whilst all other .png, .jpg and .ogg files in *data/base/graphics/entity/biter/* are retextured at normal resolution.
761 | 4. All .png, .jpg and .ogg in *data/base/graphics/entity/* but not in *data/base/graphics/entity/biter/* are retexture at 2x original resolution. All .png, .jpg and .ogg in *data/base/graphics/* and not in *data/base/graphics/achievement/* or *data/base/graphics/entity/* are retextured at normal resolution.
762 |
763 | **[Back To Top](#texturebase)**
764 |
765 |
766 | ---
767 |
768 | ## You're done!!!
769 |
770 | **Congrats! you have created a factorio resource pack with TextureBase!**
771 |
772 | - There is no obligation to do this, but I would be soooo happy if you spread the word & include e.g. "made with TextureBase @ https://mods.factorio.com/mod/texturebase" in your resource pack description :)
773 | - You might also consider removing / modifying *README.md*, *LICENCE*, *.gitattributes*, *.git* and *changelog.txt* from your texturepack.
774 | - If you haven't already, exchange the default thumbnail.png with a texturepack icon of your own (144x144 resolution).
775 | - Next Steps:
776 | - Upload your creation. You might have to use Gdrive for this, as GitHub and [https://mods.factorio.com/](https://mods.factorio.com/) do not allow uploading a bigger texturepack than 100Mb. You'll likely have to create a placeholder mod on [https://mods.factorio.com/](https://mods.factorio.com/) with a texturepack download link.
777 | - Let me check out your texturepack by contacting me via my modding discord @ [https://discord.gg/kC53xn2](https://discord.gg/kC53xn2), or on the dedicated Discussion channel on the TextureBase mod page @ [https://mods.factorio.com/mod/texturebase](https://mods.factorio.com/mod/texturebase)
778 | - Start over! You can make more than one texturepack (though they can't be loaded at the same time) ;)
779 |
780 | **[Back To Top](#texturebase)**
781 |
782 | ---
783 |
784 | ## Still having Issues, Bugfixes, Feature Requests
785 |
786 | If no error is thrown on startup but your textures have not appeared in-game, first insure your texturepack is enabled in the factorio mod list *cough cough...*.
787 | If so, check the factorio log files (*%AppData%/Roaming/Factorio/factorio-current.log*). If your textures are not in-game & no error is thrown when the game starts, the reason of this is likely logged by your texturepack!
788 |
789 | If you are still having issues creating your resourcepack, download my example resourcepacks:
790 | - **Shadow World** @ [https://drive.google.com/file/d/1bN3yiFb1y8rMiTiCTyGbhklPDw5AfF1K/view?usp=sharing](https://drive.google.com/file/d/1bN3yiFb1y8rMiTiCTyGbhklPDw5AfF1K/view?usp=sharing)
791 | - **Hologram World** @ [https://drive.google.com/file/d/1jZhFcircOzxt0ejexdXds5Khsw75Vaby/view?usp=sharing](https://drive.google.com/file/d/1jZhFcircOzxt0ejexdXds5Khsw75Vaby/view?usp=sharing)
792 | - **Pixel Perfect** @ [https://drive.google.com/file/d/14dEJA84MVQ3Z4UhZfryU5rCKtoEiw2hr/view?usp=sharing](https://drive.google.com/file/d/14dEJA84MVQ3Z4UhZfryU5rCKtoEiw2hr/view?usp=sharing)
793 |
794 | I tried uploading them to https://mods.factorio.com/ but a 100Mb mod size limit prevented this. Google Drive comes for the rescue!
795 |
796 | Should you have skipped the optional sections (***[Wild Cards (\*)](#wild-cards-)*** and ***[Attributes](#attributes)***) you might want to read up on those.
797 |
798 | Also check out my **discord @ [https://discord.gg/kC53xn2](https://discord.gg/kC53xn2)**. Here you can get further help, report bugs, request features and find people to play with!
799 |
800 | **[Back To Top](#texturebase)**
801 |
802 | ---
803 |
804 | # License
805 |
806 | MIT License
807 |
808 | Copyright (c) 2020 Zangeti
809 |
810 | Permission is hereby granted, free of charge, to any person obtaining a copy
811 | of this software and associated documentation files (the "Software"), to deal
812 | in the Software without restriction, including without limitation the rights
813 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
814 | copies of the Software, and to permit persons to whom the Software is
815 | furnished to do so, subject to the following conditions:
816 |
817 | The above copyright notice and this permission notice shall be included in all
818 | copies or substantial portions of the Software.
819 |
820 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
821 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
822 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
823 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
824 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
825 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
826 | SOFTWARE.
827 |
828 | **[Back To Top](#texturebase)**
829 |
--------------------------------------------------------------------------------
/changelog.txt:
--------------------------------------------------------------------------------
1 | ---------------------------------------------------------------------------------------------------
2 | Version: 0.0.2
3 | Date: 17.10.2020
4 | Additions:
5 | - Settings Attribute
6 | - Upscaling of many textures from their original sizes
7 | - Exclusion of file names
8 | - Wild Cards
9 |
10 | Changes:
11 | - Definition of images in config.lua data table
12 | ---------------------------------------------------------------------------------------------------
13 | Version: 0.0.1
14 | Date: 26.09.2020
15 | Additions:
16 | - The texture pack creator (everything)
--------------------------------------------------------------------------------
/config.lua:
--------------------------------------------------------------------------------
1 | return {
2 | -- lower case resource pack name, same of mod folder name (exclusing version) and "name" in info.json; no double underscores ("__")!
3 | resource_pack_name = "texturebase",
4 |
5 | -- let the mod know what you will be retexturing
6 | data = {
7 |
8 | }
9 | }
--------------------------------------------------------------------------------
/data-final-fixes.lua:
--------------------------------------------------------------------------------
1 | local config = require("config")
2 |
3 |
4 |
5 | -- utility functions
6 |
7 | -- split string into list via delimiter
8 | local split = function(in_str, delim)
9 | local list = {};
10 | for match in (in_str..delim):gmatch("(.-)"..delim) do
11 | table.insert(list, match);
12 | end
13 | return list;
14 | end
15 |
16 | local mlog = function(a, b)
17 | return math.log(a) / math.log(b)
18 | end
19 |
20 | local firstIndex = function(in_table)
21 | for index,item in pairs(in_table) do
22 | return index
23 | end
24 | end
25 |
26 | local in_table = function(in_table, comp_table)
27 | for index,obj in pairs(in_table) do
28 | for _index,compObj in pairs(comp_table) do
29 | if (index == compObj) or (obj == compObj) then
30 | return true
31 | end
32 | end
33 | end
34 | return false
35 | end
36 |
37 | -- concatenate two lists
38 | local concat = function(list1, list2)
39 | local new_list = {}
40 | for index,item in pairs(list1) do
41 | new_list[#new_list + 1] = item
42 | end
43 | for index,item in pairs(list2) do
44 | new_list[#new_list + 1] = item
45 | end
46 | return new_list
47 | end
48 |
49 |
50 |
51 | -- wild card string parsing functions
52 |
53 | -- return a list of indices at which the target string can be found in the item string
54 | local findIndices = function(item, target)
55 | local count = 1
56 | local offset = 1
57 | local indiceList = {}
58 |
59 | for i=1,#item do
60 | local letter = string.sub(item, i, i)
61 | count = count + 1
62 |
63 | if not(letter == string.sub(target, count-1, count-1)) then
64 | offset = offset + count - 1
65 | count = 1
66 | end
67 |
68 | if (count == #target + 1) and (letter == string.sub(target, count-1, count-1)) then
69 | indiceList[#indiceList + 1] = offset
70 | offset = offset + count - 1
71 | count = 1
72 | end
73 | end
74 | return indiceList
75 | end
76 |
77 | -- separates the items in search by wild cards (*). Ensures these separated terms can be found in the specified order in the final item string, and that if there are no wild cards at the beginning/end of the serach string, the beginning / end of the search string are also the beginning / end of the item string
78 | local isValidTerm = function(search, item)
79 | -- separate search by wild cards
80 | local split_search = split(search, "*")
81 | -- last index is used to store the index position of the previous term in split_search, ensuring that the next has a higher index in the item string
82 | local lastIndex = 0
83 |
84 | -- loop through elements of split_search (the text between the wildcards) to see if and where they are found in the string
85 | for matchObjIndex=1,#split_search do
86 | -- ensure element is not "" (happens when '*' is at beginning / end of string, or people write '**' for some reason)
87 | if not(split_search[matchObjIndex] == "") then
88 | indiceList = findIndices(item, split_search[matchObjIndex])
89 |
90 | if (#indiceList == 0) then
91 | -- the element of split_search can't be found in the item string
92 | return false
93 | end
94 |
95 | -- select the earliest occurence of the search element in item that comes after the index of a prior search element for thisIndex
96 | -- respectively change lastIndex for future search elements
97 | local thisIndex = nil
98 | for index,itemIndex in pairs(indiceList) do
99 | if itemIndex > lastIndex then
100 | thisIndex = itemIndex
101 | lastIndex = thisIndex
102 | break
103 | end
104 | end
105 |
106 | if thisIndex == nil then
107 | -- An occurence of the search element can't be found in the item string AFTER the prior search element
108 | return false
109 |
110 | elseif matchObjIndex == 1 and not(split_search[matchObjIndex] == "") and not(thisIndex == 1) then
111 | -- This is the first search element, and it doesn't have a wildcard (*) in front of it! Yet the item string does not start with this search element
112 | return false
113 |
114 | elseif matchObjIndex == #split_search and not(split_search[matchObjIndex] == "") and not(thisIndex == #item - #split_search[matchObjIndex] + 1) then
115 | -- This is the last search element, and it doesn't have a wildcard (*) in after it! Yet the item string does not end with this search element
116 | return false
117 | end
118 | end
119 | end
120 | return true
121 | end
122 |
123 |
124 |
125 | -- recursive data.lua & config.lua data table parsing, editing and __settings__ attribute finding code
126 |
127 | -- path down the tree in config.lua data = {} (configdata) to find which setting apply to an object
128 | -- split path is the path to the object split by "/"
129 | -- maxstep tells the function how far down the path to the object is defined in configdata and hence to what depth it has to parse configdata for __settings__ keys
130 | local lookForTreeSettings = function(configdata, split_path, maxstep)
131 | local settings = {}
132 |
133 | for i=1,maxstep do
134 | -- accumulate & overwrite settings as we travel down the path in config.lua data = {}
135 | for key,val in pairs(configdata["__settings__"] or {}) do
136 | settings[key] = val
137 | end
138 | -- move down a step in config.lua data = {} It does so by comparing every key in config.lua with respective directory in the data.raw path. If the wildcard comparison makes the strings out to be equal, that key's contents are compared to the next step of the path next!
139 | for key,step in pairs(configdata) do
140 | if not(key == "__settings__") then
141 | if (isValidTerm(key, split_path[i]) == true) then
142 | step_name = key
143 | end
144 | end
145 | end
146 | configdata = configdata[step_name]
147 | end
148 |
149 | return settings
150 | end
151 |
152 | -- See if a path from data.raw is among those declared in the config.lua data = {} table as one to be retextured & hence if the path needs to be changed to the retextured version
153 | isReplaceItem = function(path, step)
154 | step = step or 1
155 | local split_path = split(path:gsub("__", ""), "/")
156 | -- not sure of whether lua keeps variable reference if passed as arguments into a function; hence I pass a key list to the function, and then have it path to where that keylist goes
157 | local configdata = config.data
158 | for i=1,step do
159 | if not(i == step) then
160 | local step_name = ""
161 | for key,step in pairs(configdata) do
162 | -- move down a step in config.lua data = {} It does so by comparing every key in config.lua with respective directory in the data.raw path. If the wildcard comparison makes the strings out to be equal, that key's contents are compared to the next step of the path next!
163 | if not(key == "__settings__") then
164 | if (isValidTerm(key, split_path[i]) == true) then
165 | step_name = key
166 | end
167 | end
168 | end
169 | configdata = configdata[step_name]
170 | end
171 | end
172 |
173 | -- count measures the number of items denoted in the current config.lua data = {} marked directory
174 | local count = 0
175 | for key,item in pairs(configdata) do
176 | -- look through the contents of the current config.lua data = {} marked directory
177 | if not(key == "__settings__") then
178 | count = count + 1
179 | -- if there is another folder denoted inside config.lua data = {} which has the same name as the folder path of the image that is being replaced then increment step to go into the next directory down the path, and recursively call this function again to explore the contents of the next folder down
180 | if (isValidTerm(key, split_path[step])) then
181 | return isReplaceItem(path, step + 1)
182 | end
183 | end
184 | end
185 | -- when count is 0, there are no subdirectories marked down in config.lua data = {}, either a file or undefined folder has been reached
186 | -- if its a file, that the path to that file should be changed
187 | -- if its an undefined folder, than the path given is of an item inside of it. All contents of undefined folders are retextured, so this file falls among that umbrella, and its path should be changed
188 | if (count == 0) then
189 | -- find the settings that apply down this config.lua data = {} path
190 | return lookForTreeSettings(config.data, split_path, step)
191 | end
192 |
193 | -- this is reached if the path is not among those denoted in config.lua data = {}
194 | return
195 | end
196 |
197 | -- adjust metadata of importing image/sound at path in data.raw as set by the settings accumulated in lookForTreeSettings
198 | local adjustDataDotRaw = function(path, filename_key, new_path, settings)
199 | local pathed_data = data.raw
200 | local generic_data = data.raw
201 |
202 | for pathIndex=1,#path do
203 | pathed_data = pathed_data[path[pathIndex]]
204 | if pathIndex < #path-1 then
205 | generic_data = generic_data[path[pathIndex]]
206 | end
207 | end
208 |
209 | -- do not overwrite image path if image name contains an element of exclude_names declared in settings
210 | if type(settings["exclude_names"]) == "table" then
211 | local split_path = split(pathed_data[filename_key], "/")
212 |
213 | for index,name in pairs(settings["exclude_names"]) do
214 |
215 | if(isValidTerm(name, split_path[#split_path])) then
216 | -- return before overwriting path
217 | return
218 | end
219 | end
220 | end
221 |
222 |
223 | -- adjust image width, height and scale & other properties depending on what the upscale setting is
224 | if not(settings["upscale"] == nil) and not(settings["upscale"] == 0) then
225 |
226 |
227 | -- Primary Checks: Ensures images are not changed if they exceed the sprite size limit (note, does not work for all sprites)
228 |
229 | if type(pathed_data["size"]) == "number" then
230 | if (math.max(math.floor(pathed_data["size"] * settings["upscale"]), 1) >= 8192) then
231 | log("[ERROR]: Image @ " .. new_path .. " is wider / taller than 8192 px (maximum)! It can't be loaded into the game!")
232 | return
233 | end
234 | elseif type(pathed_data["size"]) == "table" then
235 | if (math.max(math.floor(pathed_data["size"][1] * settings["upscale"]), 1) >= 8192 or math.max(math.floor(pathed_data["size"][2] * settings["upscale"]), 1) > 8192) then
236 | log("[ERROR]: Image @ " .. new_path .. " is wider / taller than 8192 px (maximum)! It can't be loaded into the game!")
237 | return
238 | end
239 | end
240 |
241 | if type(pathed_data["width"]) == "number" then
242 | if (math.max(math.floor(pathed_data["width"] * settings["upscale"]), 1) >= 8192) then
243 | log("[ERROR]: Image @ " .. new_path .. " is wider / taller than 8192 px (maximum)! It can't be loaded into the game!")
244 | return
245 | end
246 | elseif type(pathed_data["height"]) == "number" then
247 | if (math.max(math.floor(pathed_data["height"] * settings["upscale"]), 1) >= 8192) then
248 | log("[ERROR]: Image @ " .. new_path .. " is wider / taller than 8192 px (maximum)! It can't be loaded into the game!")
249 | return
250 | end
251 | end
252 |
253 | -- find width of animations
254 | if type(pathed_data["width"]) == "number" and type(pathed_data["line_length"]) == "number" then
255 | if (math.max(math.floor(pathed_data["line_length"] * pathed_data["width"] * settings["upscale"]), 1) >= 8192) then
256 | log("[ERROR]: Image @ " .. new_path .. " is wider / taller than 8192 px (maximum)! It can't be loaded into the game!")
257 | return
258 | end
259 | end
260 | -- find height of animations
261 | if type(pathed_data["height"]) == "number" and type(pathed_data["line_length"]) == "number" then
262 | if (math.max(math.floor(math.ceil((pathed_data["frame_count"] or 1) / pathed_data["line_length"]) * pathed_data["height"] * settings["upscale"]), 1) >= 8192) then
263 | log("[ERROR]: Image @ " .. new_path .. " is wider / taller than 8192 px (maximum)! It can't be loaded into the game!")
264 | return
265 | end
266 | end
267 |
268 |
269 | -- Secondary Checks: Ensure no values are manipulated twice (and hence deviate 2x as far from the original value as they should)
270 |
271 | if type(pathed_data["stripes"]) == "table" then
272 | return
273 | end
274 |
275 | -- ensure animation with term filenames={} is below max resolution of 8192 x / y after rescaling
276 | if (path[#path] == "filenames" and type(generic_data[path[#path-1]]["slice"]) == "number") then
277 | if filename_key == 1 then
278 | if (type(generic_data[path[#path-1]]["width"]) == "number" and math.max(math.floor(generic_data[path[#path-1]]["width"] * generic_data[path[#path-1]]["slice"] * settings["upscale"]), 1) >= 8192) then
279 | log("[ERROR]: Image @ " .. new_path .. " is wider / taller than 8192 px (maximum)! It can't be loaded into the game!")
280 | return
281 | end
282 | if (type(generic_data[path[#path-1]]["height"]) == "number" and math.max(math.floor(generic_data[path[#path-1]]["height"] * generic_data[path[#path-1]]["slice"] * settings["upscale"]), 1) >= 8192) then
283 | log("[ERROR]: Image @ " .. new_path .. " is wider / taller than 8192 px (maximum)! It can't be loaded into the game!")
284 | return
285 | end
286 | else
287 | -- check if the first element of animation has already been retextured; if not the animation sprite paths are not replaced as its upscaled width is higher than or equal to the max texture size of 8192
288 | local split_element_one_path = split(generic_data[path[#path-1]]["filenames"][1], "/")
289 | if not(split_element_one_path[1] == "__" .. config.resource_pack_name .. "__") then
290 | log("[ERROR]: Image @ " .. new_path .. " is wider / taller than 8192 px (maximum)! It can't be loaded into the game!")
291 | return
292 | end
293 | end
294 | end
295 |
296 | -- ensure animation with term stripes={} is below max resolution of 8192 x / y after rescaling
297 | if (path[#path-1] == "stripes") then
298 | if (path[#path] == 1) then
299 | if (not(generic_data["slice_x"] == nil) and not(generic_data["width"] == nil) and math.max(math.floor(generic_data["width"] * generic_data["slice_x"] * settings["upscale"]), 1) >= 8192) then
300 | log("[ERROR]: Image @ " .. new_path .. " is wider / taller than 8192 px (maximum)! It can't be loaded into the game!")
301 | return
302 | end
303 | if (not(generic_data["slice_y"] == nil) and not(generic_data["height"] == nil) and math.max(math.floor(generic_data["height"] * generic_data["slice_y"] * settings["upscale"]), 1) >= 8192) then
304 | log("[ERROR]: Image @ " .. new_path .. " is wider / taller than 8192 px (maximum)! It can't be loaded into the game!")
305 | return
306 | end
307 | if (not(generic_data["dice_x"] == nil) and not(generic_data["width"] == nil) and math.max(math.floor(generic_data["width"] * generic_data["dice_x"] * settings["upscale"]), 1) >= 8192) then
308 | log("[ERROR]: Image @ " .. new_path .. " is wider / taller than 8192 px (maximum)! It can't be loaded into the game!")
309 | return
310 | end
311 | if (not(generic_data["dice_y"] == nil) and not(generic_data["height"] == nil) and math.max(math.floor(generic_data["height"] * generic_data["dice_y"] * settings["upscale"]), 1) >= 8192) then
312 | log("[ERROR]: Image @ " .. new_path .. " is wider / taller than 8192 px (maximum)! It can't be loaded into the game!")
313 | return
314 | end
315 | else
316 | -- check if the first element of animation has already been retextured; if not the animation sprite paths are not replaced as its upscaled width is higher than or equal to the max texture size of 8192
317 | local split_element_one_path = split(generic_data["stripes"][1][filename_key], "/")
318 | if not(split_element_one_path[1] == "__" .. config.resource_pack_name .. "__") then
319 | log("[ERROR]: Image @ " .. new_path .. " is wider / taller than 8192 px (maximum)! It can't be loaded into the game!")
320 | return
321 | end
322 | end
323 | end
324 |
325 |
326 | -- General Value Manipulation: Change image scaling etc. stored in same table as image
327 |
328 | pathed_data["scale"] = (pathed_data["scale"] or 1) / settings["upscale"]
329 | --log("scale: " .. pathed_data["scale"])
330 |
331 | if not(pathed_data["width"] == nil) then
332 | pathed_data["width"] = math.max(math.floor(pathed_data["width"] * settings["upscale"]), 1)
333 | --log("width: " .. pathed_data["width"])
334 | end
335 | if not(pathed_data["height"] == nil) then
336 | pathed_data["height"] = math.max(math.floor(pathed_data["height"] * settings["upscale"]), 1)
337 | --log("height: " .. pathed_data["height"])
338 | end
339 |
340 | if not(pathed_data["x"] == nil) then
341 | pathed_data["x"] = math.floor(pathed_data["x"] * settings["upscale"])
342 | --log("x: " .. pathed_data["x"])
343 | end
344 |
345 | if not(pathed_data["y"] == nil) then
346 | pathed_data["y"] = math.floor(pathed_data["y"] * settings["upscale"])
347 | --log("y: " .. pathed_data["y"])
348 | end
349 |
350 | -- used when x and y property are 0
351 | if not(pathed_data["position"] == nil) then
352 | pathed_data["position"] = {math.floor(pathed_data["position"][1] * settings["upscale"]), math.floor(pathed_data["position"][2] * settings["upscale"])}
353 | --log("x: " .. tostring(pathed_data["position"][1]) .. ", " .. tostring(pathed_data["position"][2]))
354 | end
355 |
356 | -- sets icon_size if no icons={} or pictures={} table are specified in icon prototype
357 | if (not(pathed_data["icon_size"] == nil) and filename_key == "icon" and pathed_data["icons"] == nil and pathed_data["pictures"] == nil) then
358 | pathed_data["icon_size"] = math.max(math.floor(pathed_data["icon_size"] * settings["upscale"]), 1)
359 | end
360 |
361 |
362 | -- Type specific value manipulation: Certain types of sprite importing e.g. as part of an animation, need different values
363 |
364 | if (generic_data["type"]) == "item" then
365 | if (type(pathed_data["size"]) == "table") then
366 | pathed_data["size"] = {pathed_data["size"][1] * settings["upscale"], pathed_data["size"][2] / settings["upscale"]}
367 | elseif (type(pathed_data["size"]) == "number") then
368 | pathed_data["size"] = pathed_data["size"] * settings["upscale"]
369 | end
370 | end
371 |
372 | -- scales shortcut GUI elements
373 | if (generic_data[path[#path-1]]["type"] == "shortcut") then
374 | pathed_data["size"] = math.max(math.floor(pathed_data["size"] * settings["upscale"]))
375 | end
376 |
377 | -- sets item / icon prototype icon_size when images are specified under pictures={} or icons={} keys
378 | if (generic_data["type"] == "icon" or generic_data["type"] == "item") and (path[#path] == firstIndex(generic_data[path[#path-1]])) then
379 | generic_data["icon_size"] = math.max(math.floor(generic_data["icon_size"] * settings["upscale"]), 1)
380 | end
381 |
382 | -- prevents black tiles in scaled terrain (may cause some texture tileability issues)
383 | if not(pathed_data["probability"] == nil) then
384 | pathed_data["probability"] = 1
385 | --log("y: " .. pathed_data["probability"])
386 | end
387 |
388 |
389 | -- upscales resources
390 | if (path[#path] == "sheet" and path[#path-1] == "stages") or (path[#path-1] == "sheet" and path[#path] == "hr_version") then
391 | if not(pathed_data["size"] == nil) then
392 | pathed_data["size"] = pathed_data["size"] * settings["upscale"]
393 | end
394 | end
395 |
396 | -- Adjusts properties for animations making use of the stripes attribute
397 | if (path[#path-1] == "stripes" and path[#path] == 1) then
398 | pathed_data["scale"] = (pathed_data["scale"] or 1) / settings["upscale"]
399 | if not(generic_data["scale"] == nil) then
400 | generic_data["scale"] = generic_data["scale"] / settings["upscale"]
401 | end
402 | if not(generic_data["width"] == nil) then
403 | generic_data["width"] = math.max(math.floor(generic_data["width"] * settings["upscale"]), 1)
404 | end
405 | if not(generic_data["height"] == nil) then
406 | generic_data["height"] = math.max(math.floor(generic_data["height"] * settings["upscale"]), 1)
407 | end
408 | if not(generic_data["x"] == nil) then
409 | generic_data["x"] = math.floor(generic_data["y"] * settings["upscale"])
410 | end
411 | if not(generic_data["y"] == nil) then
412 | generic_data["y"] = math.floor(generic_data["y"] * settings["upscale"])
413 | end
414 | --[[if not(generic_data["size"] == nil) then
415 | if (type(generic_data["size"]) == "table") then
416 | generic_data["size"] = {math.pow(2, math.floor(mlog(2, math.max(generic_data["size"][1] * settings["upscale"], 1)))), math.pow(2, math.floor(mlog(2, math.max(generic_data["size"][2] * settings["upscale"], 1))))}
417 | elseif (type(pathed_data["size"]) == "number") then
418 | generic_data["size"] = math.pow(2, math.floor(mlog(2, math.max(generic_data["size"] * settings["upscale"], 1))))
419 | end
420 | end]]--
421 | end
422 | -- Adjusts properties for animations making use of the layers TileTransitions filenames attribute
423 | if (path[#path] == "filenames" and filename_key == 1) then
424 | if not(generic_data[path[#path-1]]["scale"] == nil) then
425 | generic_data[path[#path-1]]["scale"] = generic_data[path[#path-1]]["scale"] / settings["upscale"]
426 | end
427 | if not(generic_data[path[#path-1]]["width"] == nil) then
428 | generic_data[path[#path-1]]["width"] = math.max(math.floor(generic_data[path[#path-1]]["width"] * settings["upscale"]), 1)
429 | end
430 | if not(generic_data[path[#path-1]]["height"] == nil) then
431 | generic_data[path[#path-1]]["height"] = math.max(math.floor(generic_data[path[#path-1]]["height"] * settings["upscale"]), 1)
432 | end
433 | if not(generic_data[path[#path-1]]["x"] == nil) then
434 | generic_data[path[#path-1]]["x"] = math.floor(generic_data[path[#path-1]]["y"] * settings["upscale"])
435 | end
436 | if not(generic_data[path[#path-1]]["y"] == nil) then
437 | generic_data[path[#path-1]]["y"] = math.floor(generic_data[path[#path-1]]["y"] * settings["upscale"])
438 | end
439 | end
440 | end
441 |
442 | -- overwrite old path with path to retextured version
443 | pathed_data[filename_key] = new_path
444 |
445 | return
446 | end
447 |
448 | -- recursively loop through data.raw to find strings (and the list of keys that leads to them in data.raw) which resemble file paths ending in .png, .jpg and .ogg
449 | checkData = function(path)
450 |
451 | -- path is a keylist in data.raw in which this fuction will look for paths that match the description above
452 | -- navigate to the location in data.raw denoted by path keylist
453 | local pathed_data = data.raw
454 | for index,key in pairs(path) do
455 | pathed_data = pathed_data[key]
456 | end
457 |
458 | -- look for strings that match the description above in the current data.raw table
459 | for key,item in pairs(pathed_data) do
460 | if (type(item) == "string") then
461 | local split_path = split(item, "/")
462 | -- ensure this file has not already been retextured
463 | if not(split_path[1] == "__" .. config.resource_pack_name .. "__") then
464 | local itemname = split(split_path[#split_path], "%p")
465 | local itemtype = itemname[#itemname]
466 | -- ensure the filetype matches .png, .jpg or .ogg
467 | if (itemtype == "png" or itemtype == "jpg" or itemtype == "ogg") then
468 | -- use the isReplaceItem to check if config.lua marks this data.raw path to be replaced by a retextured version
469 | local settings = isReplaceItem(item)
470 | if type(settings) == "table" then
471 | -- if so, a settings table is returned containing any other changes that need to be made to data.raw in regards to the file being retextured
472 | local changed_item_path = "__" .. config.resource_pack_name .. "__/data/" .. item:gsub("__", "")
473 | -- assign new file path and adjust data.raw according to settings using adjustDataDotRaw function
474 | adjustDataDotRaw(path, key, changed_item_path, settings)
475 | if pathed_data == nil then
476 | return
477 | end
478 | end
479 | end
480 | end
481 | elseif (type(item) == "table") then
482 | -- if this folder in data.raw contains other folders, add these to the path and recursively call this function to explore the contents of these folders as well for .png, .jpg and .ogg file paths
483 | checkData(concat(path, {key}))
484 | end
485 | end
486 | end
487 |
488 | -- only trigger checkdata to look for paths that fit config.lua data = {} table if that table is not empty (__settings__ doesn't count as a directory entry) / has modnames that include a wildcard!
489 | -- Important, as if it were & checkData ran, all files of all actively running mods would need to be retextured (all of data.raw), which is unsustainable, as it would make texturepacks break if it was run with mods that weren't retextured!
490 | local dircount = 0
491 | for key,content in pairs(config.data) do
492 | if not(key == "__settings__") then
493 | dircount = dircount + 1
494 | end
495 | if string.match(key, "*") then
496 | dircount = 0
497 | break
498 | end
499 | end
500 |
501 | if dircount > 0 then
502 | checkData({})
503 | else
504 | log("[ERROR] : TEXTURE PACK NOT LOADED. You must explicitly state in the config.lua data table which mods you are retexturing! Your data table is either emtpy, or the mod names contain wild cards (*)! This is NOT allowed! See documentation @ for how to specify what you are retexturing in config.lua!")
505 | end
506 |
--------------------------------------------------------------------------------
/info.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "texturebase",
3 | "version": "0.0.2",
4 | "title": "TextureBase",
5 | "author": "Zangeti",
6 | "contact": "",
7 | "homepage": "https://discord.gg/kC53xn2",
8 | "factorio_version": "1.0",
9 | "dependencies": ["base >= 1.0"],
10 | "description": "TextureBase makes it easy to create your own Factorio Texturepacks! Effortlessly change Sounds & Textures of the Base Game & Mods with this streamlined Tool!"
11 | }
--------------------------------------------------------------------------------
/thumbnail.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Zangeti/TextureBase/a29e3a186954c7f0249e43a8dd6cdf55588d8570/thumbnail.png
--------------------------------------------------------------------------------