├── .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 | ![Factorio Sprite Resolution Error](https://user-images.githubusercontent.com/45295229/95661099-dd0cc400-0b2c-11eb-8ddd-6bc7c613e119.png) 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 --------------------------------------------------------------------------------