diff --git a/.gitignore b/.gitignore index 2e469f22..3adf65ae 100644 --- a/.gitignore +++ b/.gitignore @@ -58,7 +58,6 @@ cmake-build-debug/ *.exe *.pdb *.rar -vcpkg_installed/ -*.ilk -remeres.exe.manifest -remeres +*.lzma +*.dat +*.manifest diff --git a/CMakeLists.txt b/CMakeLists.txt index 24a0d977..7076d5bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,17 @@ cmake_minimum_required(VERSION 3.22) project(cmake) +# ***************************************************************************** +# Append cmake search path +# ***************************************************************************** +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) + +# ***************************************************************************** +# Include cmake tools +# ***************************************************************************** +include(MessageColors) +include(LoggingHelper) + # ***************************************************************************** # Vcpkg Configs # ***************************************************************************** @@ -41,6 +52,20 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) option(OPTIONS_ENABLE_CCACHE "Enable ccache" OFF) option(OPTIONS_ENABLE_SCCACHE "Use sccache to speed up compilation process" OFF) option(OPTIONS_ENABLE_IPO "Check and Enable interprocedural optimization (IPO/LTO)" ON) +option(ASAN_ENABLED "Build this target with AddressSanitizer" ON) + +# === ASAN === +if(ASAN_ENABLED AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")) + log_option_enabled("asan") + if(MSVC) + add_compile_options(/fsanitize=address) + else() + add_compile_options(-fsanitize=address) + link_libraries(-fsanitize=address) + endif() +else() + log_option_disabled("asan") +endif() # ***************************************************************************** # Set Sanity Check @@ -67,12 +92,6 @@ endif() # ***************************************************************************** list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) -# ***************************************************************************** -# Include cmake tools -# ***************************************************************************** -include(MessageColors) -include(LoggingHelper) - # ***************************************************************************** # Options Code # ***************************************************************************** @@ -119,4 +138,5 @@ endif() # ***************************************************************************** # Add source project # ***************************************************************************** +add_subdirectory(source/protobuf) add_subdirectory(source) diff --git a/CMakePresets.json b/CMakePresets.json index d67b075a..fe8e351e 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -8,7 +8,7 @@ "configurePresets": [ { "name": "windows-release", - "displayName": "Windows - Release", + "displayName": "1-Windows (Release)", "description": "Sets Ninja generator, compilers, build and install directory and set build type as release", "generator": "Ninja", "binaryDir": "${sourceDir}/build/${presetName}", @@ -36,6 +36,22 @@ "rhs": "Windows" } }, + { + "name": "windows-release-asan", + "inherits": "windows-release", + "displayName": "2-Windows (ReleaseDebInfo + ASAN)", + "description": "Release Mode with ASAN", + "cacheVariables": { + "ASAN_ENABLED": "ON", + "DEBUG_LOG": "ON", + "BUILD_STATIC_LIBRARY": "OFF", + "VCPKG_TARGET_TRIPLET": "x64-windows" + }, + "architecture": { + "value": "x64", + "strategy": "external" + } + }, { "name": "linux-release", "displayName": "Linux - Release", @@ -79,7 +95,7 @@ { "name": "windows-debug", "inherits": "windows-release", - "displayName": "Windows - Debug", + "displayName": "3-Windows (Debug)", "description": "Build Debug Mode", "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug", @@ -120,14 +136,6 @@ "name": "linux-debug", "configurePreset": "linux-debug" }, - { - "name": "windows-release", - "configurePreset": "windows-release" - }, - { - "name": "windows-Xdebug", - "configurePreset": "windows-debug" - }, { "name": "macos-release", "configurePreset": "macos-release" diff --git a/data/items/items.otb b/data/items/items.otb deleted file mode 100644 index c064af60..00000000 Binary files a/data/items/items.otb and /dev/null differ diff --git a/data/items/items.xml b/data/items/items.xml index e21ac7eb..f514e683 100644 --- a/data/items/items.xml +++ b/data/items/items.xml @@ -225,7 +225,6 @@ - @@ -730,7 +729,6 @@ - @@ -765,7 +763,6 @@ - @@ -851,7 +848,6 @@ - @@ -886,7 +882,6 @@ - @@ -901,11 +896,9 @@ - - @@ -1028,8 +1021,6 @@ - - @@ -2377,7 +2368,6 @@ - @@ -2566,7 +2556,6 @@ - @@ -2578,7 +2567,6 @@ - @@ -2739,7 +2727,6 @@ - @@ -3425,8 +3412,6 @@ - - @@ -3793,7 +3778,6 @@ - @@ -3801,9 +3785,6 @@ - - - @@ -3828,9 +3809,6 @@ - - - @@ -3871,7 +3849,6 @@ - @@ -3912,7 +3889,6 @@ - @@ -4383,7 +4359,6 @@ - @@ -4663,7 +4638,6 @@ - @@ -12109,7 +12083,8 @@ - + + @@ -12179,7 +12154,6 @@ - @@ -13711,8 +13685,6 @@ - - @@ -18333,7 +18305,6 @@ - @@ -20074,7 +20045,6 @@ - @@ -21260,7 +21230,6 @@ - @@ -21589,7 +21558,6 @@ - @@ -21884,7 +21852,6 @@ - @@ -22087,7 +22054,6 @@ - @@ -26136,7 +26102,6 @@ - @@ -26222,7 +26187,6 @@ - @@ -26299,7 +26263,6 @@ - @@ -26324,7 +26287,6 @@ - @@ -26373,7 +26335,6 @@ - @@ -27284,7 +27245,6 @@ - @@ -27425,7 +27385,6 @@ - @@ -27604,7 +27563,6 @@ - @@ -27614,12 +27572,10 @@ - - @@ -27675,7 +27631,6 @@ - @@ -27751,20 +27706,16 @@ - - - - @@ -27819,7 +27770,6 @@ - @@ -27947,9 +27897,7 @@ - - @@ -28042,9 +27990,7 @@ - - @@ -28069,7 +28015,6 @@ - @@ -28203,7 +28148,6 @@ - @@ -28242,7 +28186,6 @@ - @@ -28288,7 +28231,6 @@ - @@ -28556,7 +28498,6 @@ - @@ -28584,7 +28525,6 @@ - @@ -28612,12 +28552,10 @@ - - @@ -28886,7 +28824,6 @@ - @@ -28945,7 +28882,6 @@ - @@ -29013,7 +28949,6 @@ - @@ -29224,7 +29159,6 @@ - @@ -29260,7 +29194,6 @@ - @@ -29380,13 +29313,11 @@ - - @@ -29395,7 +29326,6 @@ - @@ -29467,13 +29397,11 @@ - - @@ -29482,7 +29410,6 @@ - @@ -29565,7 +29492,6 @@ - @@ -29728,7 +29654,6 @@ - @@ -29789,7 +29714,6 @@ - @@ -29824,23 +29748,18 @@ - - - - - @@ -29850,7 +29769,6 @@ - @@ -29858,15 +29776,12 @@ - - - @@ -29887,7 +29802,6 @@ - @@ -29910,7 +29824,6 @@ - @@ -30080,7 +29993,6 @@ - @@ -30104,7 +30016,6 @@ - @@ -30329,7 +30240,6 @@ - @@ -30388,7 +30298,6 @@ - @@ -30456,7 +30365,6 @@ - @@ -30647,7 +30555,6 @@ - @@ -31007,7 +30914,6 @@ - @@ -31231,14 +31137,12 @@ - - @@ -31275,25 +31179,20 @@ - - - - - @@ -31324,7 +31223,6 @@ - @@ -31344,7 +31242,6 @@ - @@ -31354,7 +31251,6 @@ - @@ -31380,9 +31276,7 @@ - - @@ -31394,7 +31288,6 @@ - @@ -31436,7 +31329,6 @@ - @@ -31472,22 +31364,17 @@ - - - - - @@ -31527,14 +31414,12 @@ - - @@ -31618,7 +31503,6 @@ - @@ -31628,11 +31512,9 @@ - - @@ -31772,7 +31654,6 @@ - @@ -32007,7 +31888,6 @@ - @@ -32019,7 +31899,6 @@ - @@ -32100,7 +31979,6 @@ - @@ -32370,7 +32248,6 @@ - @@ -32712,7 +32589,6 @@ - @@ -32725,7 +32601,6 @@ - @@ -32739,15 +32614,12 @@ - - - @@ -32758,7 +32630,6 @@ - @@ -32773,7 +32644,6 @@ - @@ -32853,7 +32723,6 @@ - @@ -32886,7 +32755,6 @@ - @@ -34592,13 +34460,11 @@ - - @@ -34606,7 +34472,6 @@ - @@ -34672,11 +34537,9 @@ - - @@ -34738,7 +34601,6 @@ - @@ -34756,7 +34618,6 @@ - @@ -34772,7 +34633,6 @@ - @@ -34806,7 +34666,6 @@ - @@ -35125,7 +34984,6 @@ - @@ -35158,7 +35016,6 @@ - @@ -35287,7 +35144,6 @@ - @@ -35447,7 +35303,6 @@ - @@ -35619,21 +35474,18 @@ - - - @@ -35705,7 +35557,6 @@ - @@ -35729,7 +35580,6 @@ - @@ -35757,9 +35607,7 @@ - - @@ -35805,7 +35653,6 @@ - @@ -35844,7 +35691,6 @@ - @@ -35856,11 +35702,9 @@ - - @@ -35903,7 +35747,6 @@ - @@ -35929,13 +35772,11 @@ - - @@ -35947,7 +35788,6 @@ - @@ -35962,13 +35802,11 @@ - - @@ -36089,14 +35927,12 @@ - - @@ -36105,7 +35941,6 @@ - @@ -36119,7 +35954,6 @@ - @@ -36631,7 +36465,6 @@ - @@ -36650,7 +36483,6 @@ - @@ -36820,7 +36652,6 @@ - @@ -36852,10 +36683,8 @@ - - @@ -36950,7 +36779,6 @@ - @@ -37035,7 +36863,6 @@ - @@ -37218,11 +37045,9 @@ - - @@ -37231,13 +37056,11 @@ - - @@ -37259,11 +37082,9 @@ - - @@ -37803,7 +37624,6 @@ - @@ -37841,7 +37661,6 @@ - @@ -37909,7 +37728,6 @@ - @@ -37919,12 +37737,10 @@ - - @@ -38076,7 +37892,6 @@ - @@ -38175,7 +37990,6 @@ - @@ -38219,7 +38033,6 @@ - @@ -38270,7 +38083,6 @@ - @@ -38288,7 +38100,6 @@ - @@ -38299,14 +38110,12 @@ - - @@ -38316,7 +38125,6 @@ - @@ -38324,7 +38132,6 @@ - @@ -38346,9 +38153,7 @@ - - @@ -38362,7 +38167,6 @@ - @@ -38377,12 +38181,10 @@ - - @@ -38546,7 +38348,6 @@ - @@ -38617,7 +38418,6 @@ - @@ -38756,7 +38556,6 @@ - @@ -38766,16 +38565,12 @@ - - - - @@ -38791,7 +38586,6 @@ - @@ -38841,7 +38635,6 @@ - @@ -38854,12 +38647,10 @@ - - @@ -38878,20 +38669,16 @@ - - - - @@ -38899,7 +38686,6 @@ - @@ -38951,7 +38737,6 @@ - @@ -39082,7 +38867,6 @@ - @@ -39153,7 +38937,6 @@ - @@ -39324,7 +39107,6 @@ - @@ -39344,7 +39126,6 @@ - @@ -39355,12 +39136,10 @@ value="It is said to be a symbol of perseverance and luck. Awarded by TibiaMagazine.com.ve"/> - - @@ -39429,7 +39208,6 @@ - @@ -39458,7 +39236,6 @@ - @@ -39787,7 +39564,6 @@ - @@ -39948,7 +39724,6 @@ - @@ -39977,7 +39752,6 @@ - @@ -39998,7 +39772,6 @@ - @@ -40178,7 +39951,6 @@ - @@ -40224,9 +39996,7 @@ - - @@ -40310,7 +40080,6 @@ - @@ -40418,7 +40187,6 @@ - @@ -40634,15 +40402,12 @@ - - - @@ -40683,13 +40448,11 @@ - - @@ -40829,7 +40592,6 @@ - @@ -40876,7 +40638,6 @@ - @@ -41243,7 +41004,6 @@ - @@ -41311,7 +41071,6 @@ - @@ -41394,12 +41153,10 @@ - - @@ -41410,7 +41167,6 @@ - @@ -41418,73 +41174,54 @@ - - - - - - - - - - - - - - - - - - - @@ -41498,24 +41235,17 @@ - - - - - - - @@ -41615,7 +41345,6 @@ - @@ -42402,7 +42131,6 @@ - @@ -42459,12 +42187,10 @@ - - @@ -42689,7 +42415,6 @@ - @@ -42829,7 +42554,6 @@ - @@ -42902,19 +42626,15 @@ - - - - @@ -42929,9 +42649,7 @@ - - @@ -43162,7 +42880,6 @@ - @@ -43283,7 +43000,6 @@ - @@ -43295,13 +43011,10 @@ - - - @@ -43316,7 +43029,6 @@ - @@ -43345,7 +43057,6 @@ - @@ -43406,7 +43117,6 @@ - @@ -43486,10 +43196,8 @@ - - @@ -43506,9 +43214,7 @@ - - @@ -43714,7 +43420,6 @@ - @@ -43738,10 +43443,8 @@ - - @@ -43832,13 +43535,11 @@ - - @@ -44169,27 +43870,21 @@ - - - - - - @@ -44232,7 +43927,6 @@ - @@ -44323,7 +44017,6 @@ - @@ -44334,7 +44027,6 @@ - @@ -44453,7 +44145,6 @@ - @@ -44547,7 +44238,6 @@ - @@ -44564,13 +44254,11 @@ - - @@ -44738,7 +44426,6 @@ - @@ -44845,7 +44532,6 @@ - @@ -46314,7 +46000,6 @@ - @@ -46327,10 +46012,8 @@ - - @@ -46448,7 +46131,6 @@ - @@ -46497,7 +46179,6 @@ - @@ -46506,39 +46187,30 @@ - - - - - - - - - @@ -46620,7 +46292,6 @@ - @@ -46649,7 +46320,6 @@ - @@ -46839,7 +46509,8 @@ - + + @@ -47002,7 +46673,6 @@ - @@ -47025,7 +46695,6 @@ - @@ -47048,11 +46717,9 @@ - - @@ -47118,18 +46785,14 @@ - - - - @@ -47210,7 +46873,6 @@ - @@ -47359,7 +47021,6 @@ - @@ -47532,7 +47193,6 @@ - @@ -47542,7 +47202,6 @@ - @@ -47593,7 +47252,6 @@ - @@ -47679,7 +47337,6 @@ - @@ -47748,11 +47405,9 @@ - - @@ -47768,7 +47423,6 @@ - @@ -47804,7 +47458,6 @@ - @@ -47870,7 +47523,6 @@ - @@ -47885,7 +47537,6 @@ - @@ -47901,9 +47552,7 @@ - - @@ -47911,7 +47560,6 @@ - @@ -47960,7 +47608,6 @@ - @@ -48014,7 +47661,6 @@ - @@ -48036,7 +47682,6 @@ - @@ -48070,7 +47715,6 @@ - @@ -48098,7 +47742,6 @@ - @@ -48167,7 +47810,6 @@ - @@ -48391,7 +48033,6 @@ - @@ -48690,7 +48331,6 @@ - @@ -48736,7 +48376,6 @@ - @@ -48905,7 +48544,6 @@ - @@ -49163,7 +48801,6 @@ - @@ -49196,7 +48833,6 @@ - @@ -49306,7 +48942,6 @@ - @@ -49452,7 +49087,6 @@ - @@ -49567,7 +49201,6 @@ - @@ -49598,7 +49231,6 @@ - @@ -49628,7 +49260,6 @@ - @@ -49650,12 +49281,10 @@ - - @@ -49706,7 +49335,6 @@ - @@ -49737,7 +49365,6 @@ - @@ -49850,13 +49477,10 @@ - - - @@ -50087,7 +49711,6 @@ - @@ -50359,7 +49982,6 @@ - @@ -50370,7 +49992,6 @@ - @@ -50490,7 +50111,6 @@ - @@ -50585,7 +50205,6 @@ - @@ -50811,7 +50430,6 @@ - @@ -50856,7 +50474,6 @@ - @@ -50901,7 +50518,6 @@ - @@ -51231,7 +50847,6 @@ - @@ -51305,7 +50920,6 @@ - @@ -51635,7 +51249,6 @@ - @@ -51770,7 +51383,6 @@ - @@ -51792,7 +51404,6 @@ - @@ -51850,7 +51461,6 @@ - @@ -51993,7 +51603,6 @@ - @@ -52070,7 +51679,6 @@ - @@ -52272,7 +51880,6 @@ - @@ -52283,7 +51890,6 @@ - @@ -52480,7 +52086,6 @@ - @@ -52703,7 +52308,6 @@ - @@ -52835,7 +52439,6 @@ - @@ -52946,7 +52549,6 @@ - @@ -53013,7 +52615,6 @@ - @@ -53056,11 +52657,9 @@ - - @@ -53242,7 +52841,6 @@ - @@ -53317,7 +52915,6 @@ - @@ -53384,7 +52981,6 @@ - @@ -53964,11 +53560,9 @@ - - @@ -53987,7 +53581,6 @@ - @@ -54078,7 +53671,6 @@ - @@ -54140,7 +53732,6 @@ - @@ -54172,7 +53763,6 @@ - @@ -54217,14 +53807,11 @@ - - - @@ -54320,7 +53907,6 @@ - @@ -54335,13 +53921,11 @@ - - @@ -54382,7 +53966,6 @@ - @@ -54405,7 +53988,6 @@ - @@ -54479,14 +54061,12 @@ - - @@ -54805,7 +54385,6 @@ - @@ -54823,7 +54402,6 @@ - @@ -54896,9 +54474,7 @@ - - @@ -54906,7 +54482,6 @@ - @@ -56240,7 +55815,6 @@ - @@ -56326,7 +55900,6 @@ - diff --git a/data/materials/brushs/doodads.xml b/data/materials/brushs/doodads.xml index 1f1a6e49..bb8dc842 100644 --- a/data/materials/brushs/doodads.xml +++ b/data/materials/brushs/doodads.xml @@ -7044,12 +7044,10 @@ - - diff --git a/data/materials/tilesets.xml b/data/materials/tilesets.xml index 8c062c16..a0036348 100644 --- a/data/materials/tilesets.xml +++ b/data/materials/tilesets.xml @@ -5,7 +5,6 @@ - diff --git a/data/materials/tilesets/addons_and_quest_items.xml b/data/materials/tilesets/addons_and_quest_items.xml index d0feef09..f729f0b9 100644 --- a/data/materials/tilesets/addons_and_quest_items.xml +++ b/data/materials/tilesets/addons_and_quest_items.xml @@ -113,7 +113,8 @@ - + + diff --git a/data/materials/tilesets/interior.xml b/data/materials/tilesets/interior.xml index 1b003915..45da140c 100644 --- a/data/materials/tilesets/interior.xml +++ b/data/materials/tilesets/interior.xml @@ -148,7 +148,6 @@ - diff --git a/data/menubar.xml b/data/menubar.xml index 2adc40bd..7546e6e5 100644 --- a/data/menubar.xml +++ b/data/menubar.xml @@ -196,7 +196,6 @@ - diff --git a/extensions/8.4_additions.xml b/extensions/8.4_additions.xml deleted file mode 100644 index 1c224311..00000000 --- a/extensions/8.4_additions.xml +++ /dev/null @@ -1,2283 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- Flat roof border down -- - - - - - - - - - - - - - - - -- Flat roof border right -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- south -- - - - - - - - - - - - - - - -- west -- - - - - - - - - - - - -- north -- - - - - - - - - - - - -- east -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- south -- - - - -- east -- - - - - - - -- south -- - - - -- east -- - - - - - - -- south -- - - - -- north -- - - - -- west -- - - - -- east -- - - - - - - -- south -- - - - -- east -- - - - - - - -- south -- - - - -- east -- - - - - - - -- south -- - - - -- east -- - - - - - - -- south -- - - - -- east -- - - - - - - -- south -- - - - -- east -- - - - - - - -- south -- - - - -- east -- - - - - - - -- south -- - - - -- east -- - - - - - - -- south -- - - - -- east -- - - - - - - -- south -- - - - -- east -- - - - - - - -- south -- - - - -- east -- - - - - - - -- south -- - - - - - - -- south -- - - - -- east -- - - - - - - -- east -- - - - -- south -- - - - - - - -- east -- - - - -- south -- - - - - - - -- east -- - - - -- south -- - - - - - - -- east -- - - - -- south -- - - - - - - -- east -- - - - -- south -- - - - - - - -- east -- - - - -- south -- - - - - - - -- east -- - - - -- south -- - - - - - - -- east -- - - - -- south -- - - - - - - -- east -- - - - -- south -- - - - - - - -- east -- - - - -- south -- - - - - - - -- east -- - - - -- south -- - - - - - - -- east -- - - - -- south -- - - - - - - -- east -- - - - -- south -- - - - - - - -- east -- - - - -- south -- - - - - - - -- east -- - - - -- south -- - - - - - - -- east -- - - - -- south -- - - - - - - -- east -- - - - -- south -- - - - - - - -- east -- - - - -- south -- - - - - - - -- east -- - - - -- south -- - - - - - - -- east -- - - - -- south -- - - - - - - -- north -- - - - -- east -- - - - -- south -- - - - -- west -- - - - - - - -- east -- - - - -- south -- - - - - - - -- east -- - - - -- south -- - - - - - - -- south -- - - - -- east -- - - - - - - -- south -- - - - -- east -- - - - - - - -- south -- - - - -- east -- - - - - - - -- south -- - - - -- east -- - - - - - - -- south -- - - - -- east -- - - - - - - -- south -- - - - -- east -- - - - - - - -- south -- - - - -- east -- - - - - - - -- south -- - - - -- east -- - - - - - - -- south -- - - - -- east -- - - - - - - -- south -- - - - -- east -- - - - - - - -- south -- - - - -- east -- - - - - - - -- south -- - - - -- east -- - - - - - - -- south -- - - - -- north -- - - - -- west -- - - - -- east -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- south -- - - - - - - -- east -- - - - -- south -- - - - - - - -- east -- - - - -- south -- - - - - - - -- east -- - - - -- south -- - - - - - - -- east -- - - - -- south -- - - - - - - -- east -- - - - -- south -- - - - - - - -- east -- - - - -- south -- - - - - - - - - - - - - - - - - - - - - - - - - - - - -- south -- - - - -- east -- - - - - - - -- south -- - - - - - - - -- rope brigde east -- - - - - - - - - - - - - - - -- rope brigde south -- - - - - - - - - - - - - - - - -- rope brigde border -- - - - - - - -- 12 books -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- 9 special books -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- Yalahar wooden border -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- raft border -- - - - - - - - - - - - - - - - - - - - - - - - - -- snow vs. grass/whatever with normal sea border -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- snow vs. frozen mud border -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- West -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- East -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- North -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- South -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- Corkscrew stairs -- - - - - - - - - - - - - - - -- Corkscrew stairs -- - - - - - - - - - - - - - - -- south -- - - - - - - - - - - - -- east -- - - - - - - - - - - - - - - -- south -- - - - - - - - - - - - - - - -- east -- - - - - - - - - - - - - - - - - - -- lol -- - - - - - - - - - - - - - - - - - - - - -- south -- - - - - - - - - - - - -- east -- - - - - - - - - - - - - - - -- south -- - - - - - - - - - - - - - - -- east -- - - - - - - - - - - - - - - - - - -- south -- - - - - - - - - - - - - - - -- east -- - - - - - - - - - - - - - - - - - -- south -- - - - - - - - - - - - -- east -- - - - - - - - - - - - - - - -- south -- - - - - - - - - - - - -- east -- - - - - - - - - - - - - - - -- south -- - - - - - - - - - - - -- east -- - - - - - - - - - - - - - - -- south -- - - - - - - - - - - - -- east -- - - - - - - - - - - - - - - -- south -- - - - - - - - - - - - -- east -- - - - - - - - - - - - - - - - - - - - - - - - - - -- south -- - - - - - - - - -- east -- - - - - - - - - - - - -- south -- - - - - - - - - -- east -- - - - - - - - - - - - -- south -- - - - - - - - - -- east -- - - - - - - - - - - - -- south -- - - - - - - - - - - - -- south -- - - - - - - - - -- east -- - - - - - - - - - - - -- south -- - - - - - - - - -- east -- - - - - - - - - - - - -- south -- - - - - - - - - -- east -- - - - - - - - - - - - -- south -- - - - - - - - - -- east -- - - - - - - - - - - - -- south -- - - - - - - - - -- east -- - - - - - - - - - - - -- south -- - - - - - - - - -- east -- - - - - - - - - - - - -- south -- - - - - - - - - -- east -- - - - - - - - - - - - -- south -- - - - - - - - - -- east -- - - - - - - - - - - - -- south -- - - - - - - - - - - - -- south -- - - - - - - - - - - - -- south -- - - - - - - - - - - - -- south -- - - - - - - - - - - - -- east -- - - - - - - - - - - - - - - -- 8.40 walls/grounds -- - - - - - - - - - - - - - - - -- 8.40 roof -- - - - - - - - - - - -- 8.40 pilars -- - - - - - - - - - - - - - - - -- Interior -- - - - - -- Note that it can not go to the south or north -- - - - - - - - - - - - - -- Exterior -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- 8.40 wall decorations -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- 8.40 Stairs -- - - - - - - - - -- 8.40 statues -- - - - - - - - - - - - - - - - diff --git a/extensions/8.5_additions.xml b/extensions/8.5_additions.xml deleted file mode 100644 index fb0ae3fc..00000000 --- a/extensions/8.5_additions.xml +++ /dev/null @@ -1,1330 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- east -- - - - - - - - - - - - - - - - - - - -- east -- - - - - - - - - - - - - - - - - - - -- east -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- east -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- east -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- east -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- east -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- east -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- closed -- - - - - - - - - -- open -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/extensions/beach.xml b/extensions/beach.xml deleted file mode 100644 index 3530f08c..00000000 --- a/extensions/beach.xml +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/extensions/better_creatures 760.xml b/extensions/better_creatures 760.xml deleted file mode 100644 index a7d6fd26..00000000 --- a/extensions/better_creatures 760.xml +++ /dev/null @@ -1,182 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/extensions/better_creatures 840.xml b/extensions/better_creatures 840.xml deleted file mode 100644 index be6ec464..00000000 --- a/extensions/better_creatures 840.xml +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/extensions/better_creatures.xml b/extensions/better_creatures.xml deleted file mode 100644 index 925fd181..00000000 --- a/extensions/better_creatures.xml +++ /dev/null @@ -1,312 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/extensions/frozen_furniture.xml b/extensions/frozen_furniture.xml deleted file mode 100644 index a7d39b40..00000000 --- a/extensions/frozen_furniture.xml +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/extensions/jungle_doodads.xml b/extensions/jungle_doodads.xml deleted file mode 100644 index 798af5d0..00000000 --- a/extensions/jungle_doodads.xml +++ /dev/null @@ -1,217 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/extensions/marquees.xml b/extensions/marquees.xml deleted file mode 100644 index 3aa99e8a..00000000 --- a/extensions/marquees.xml +++ /dev/null @@ -1,167 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/extensions/more_creatures.xml b/extensions/more_creatures.xml deleted file mode 100644 index 40aa006d..00000000 --- a/extensions/more_creatures.xml +++ /dev/null @@ -1,1376 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/extensions/more_stuff_9.10.xml b/extensions/more_stuff_9.10.xml deleted file mode 100644 index 31106dd2..00000000 --- a/extensions/more_stuff_9.10.xml +++ /dev/null @@ -1,435 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/extensions/new_doodads.xml b/extensions/new_doodads.xml deleted file mode 100644 index 7640d72e..00000000 --- a/extensions/new_doodads.xml +++ /dev/null @@ -1,368 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/extensions/new_walls.xml b/extensions/new_walls.xml deleted file mode 100644 index 1172dc10..00000000 --- a/extensions/new_walls.xml +++ /dev/null @@ -1,167 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/extensions/sample.xml b/extensions/sample.xml deleted file mode 100644 index 68ac57dc..00000000 --- a/extensions/sample.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/extensions/slaying_world_additions.xml b/extensions/slaying_world_additions.xml deleted file mode 100644 index 7c85cd98..00000000 --- a/extensions/slaying_world_additions.xml +++ /dev/null @@ -1,1330 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/extensions/smithing.xml b/extensions/smithing.xml deleted file mode 100644 index 971a8b57..00000000 --- a/extensions/smithing.xml +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/extensions/underwater_800.xml b/extensions/underwater_800.xml deleted file mode 100644 index 1545391e..00000000 --- a/extensions/underwater_800.xml +++ /dev/null @@ -1,178 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/extensions/underwater_810.xml b/extensions/underwater_810.xml deleted file mode 100644 index ba29117b..00000000 --- a/extensions/underwater_810.xml +++ /dev/null @@ -1,181 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/extensions/wall_and_railing.xml b/extensions/wall_and_railing.xml deleted file mode 100644 index fae2c038..00000000 --- a/extensions/wall_and_railing.xml +++ /dev/null @@ -1,334 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/extensions/weapons_fur.xml b/extensions/weapons_fur.xml deleted file mode 100644 index ca0204c1..00000000 --- a/extensions/weapons_fur.xml +++ /dev/null @@ -1,116 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 752de380..13402e53 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -1,8 +1,8 @@ # ***************************************************************************** -# Project remeres +# Project canary map editor # ***************************************************************************** -project(remeres) +project(canary-map-editor) find_package(asio CONFIG REQUIRED) find_package(fmt CONFIG REQUIRED) @@ -13,20 +13,12 @@ find_package(Threads REQUIRED) find_package(wxWidgets COMPONENTS html aui gl adv core net base CONFIG REQUIRED) find_package(ZLIB REQUIRED) find_package(pugixml CONFIG REQUIRED) +find_package(liblzma CONFIG REQUIRED) option(TOGGLE_BIN_FOLDER "Use build/bin folder for generate compilation files" OFF) option(OPTIONS_ENABLE_OPENMP "Enable Open Multi-Processing support." ON) option(DEBUG_LOG "Enable Debug Log" OFF) option(BUILD_STATIC_LIBRARY "Build using static libraries" ON) -option(SPEED_UP_BUILD_UNITY "Compile using build unity for speed up build" ON) - -# LibArchive disabled in compilation level by default, see "#define OTGZ_SUPPORT" in the "definitions.h" file -#if(APPLE) -# set(CMAKE_PREFIX_PATH /usr/local/opt/libarchive) -#endif() -# If you need use, enable this: -#find_package(LibArchive REQUIRED) -#${LibArchive_INCLUDE_DIRS} ${LibArchive_LIBRARIES} # Build static libs if(BUILD_STATIC_LIBRARY) @@ -89,11 +81,6 @@ endif() # === PRECOMPILED HEADER === target_precompile_headers(${PROJECT_NAME} PRIVATE main.h) -# === UNITY BUILD (compile time reducer) === -if(SPEED_UP_BUILD_UNITY) - set_target_properties(${PROJECT_NAME} PROPERTIES UNITY_BUILD ON) - log_option_enabled("Build unity for speed up compilation") -endif() target_sources(${PROJECT_NAME} @@ -109,7 +96,7 @@ target_sources(${PROJECT_NAME} browse_tile_window.cpp positionctrl.cpp carpet_brush.cpp - client_version.cpp + client_assets.cpp common.cpp common_windows.cpp complexitem.cpp @@ -124,8 +111,6 @@ target_sources(${PROJECT_NAME} editor.cpp editor_tabs.cpp eraser_brush.cpp - extension.cpp - extension_window.cpp find_item_window.cpp filehandle.cpp graphics.cpp @@ -187,6 +172,7 @@ target_sources(${PROJECT_NAME} spawn_monster.cpp spawn_npc.cpp spawn_npc_brush.cpp + sprite_appearances.cpp table_brush.cpp templatemap76-74.cpp templatemap81.cpp @@ -222,6 +208,8 @@ target_link_libraries(${PROJECT_NAME} nlohmann_json::nlohmann_json pugixml::pugixml wx::base wx::core wx::net wx::gl wx::html wx::aui wx::adv + liblzma::liblzma + protobuf ) ## Link compilation files to build/bin folder, else link to the main dir diff --git a/source/about_window.cpp b/source/about_window.cpp index 293479bf..ddc84ed6 100644 --- a/source/about_window.cpp +++ b/source/about_window.cpp @@ -489,7 +489,7 @@ void TetrisPanel::NewGame() { void TetrisPanel::AddScore(int lines_added) { lines += lines_added; score += lines_added * lines_added * 10; - wxString title = "Remere's Tetris : "; + wxString title = "Canary Map Editor Tetris : "; title << score << " points "; title << lines << " lines"; ((wxTopLevelWindow*)GetParent())->SetTitle(title); @@ -785,7 +785,7 @@ void SnakePanel::NewGame() { } void SnakePanel::UpdateTitle() { - wxString title = "Remere's Snake : "; + wxString title = "Canary Map Editor Snake : "; title << length << " segments"; ((wxTopLevelWindow*)GetParent())->SetTitle(title); } diff --git a/source/application.cpp b/source/application.cpp index 86f129b3..db72d960 100644 --- a/source/application.cpp +++ b/source/application.cpp @@ -97,9 +97,12 @@ bool Application::OnInit() { _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); #endif - std::cout << "This is free software: you are free to change and redistribute it." << std::endl; - std::cout << "There is NO WARRANTY, to the extent permitted by law." << std::endl; - std::cout << "Review COPYING in RME distribution for details." << std::endl; + spdlog::info("This is free software: you are free to change and redistribute it"); + spdlog::info("There is NO WARRANTY, to the extent permitted by law"); + spdlog::info("Review COPYING in RME distribution for details"); + spdlog::info("Visit our website for updates, support, and resources: https://docs.opentibiabr.com/"); + spdlog::info("Application started sucessfull!\n"); + mt_seed(time(nullptr)); srand(time(nullptr)); @@ -120,9 +123,8 @@ bool Application::OnInit() { // Load some internal stuff g_settings.load(); - FixVersionDiscrapencies(); g_gui.LoadHotkeys(); - ClientVersion::loadVersions(); + ClientAssets::load(); #ifdef _USE_PROCESS_COM m_single_instance_checker = newd wxSingleInstanceChecker; // Instance checker has to stay alive throughout the applications lifetime @@ -159,9 +161,6 @@ bool Application::OnInit() { #ifndef __DEBUG_MODE__ // wxHandleFatalExceptions(true); #endif - // Load all the dependency files - std::string error; - StringVector warnings; m_file_to_open = wxEmptyString; ParseCommandLineMap(m_file_to_open); @@ -283,11 +282,6 @@ void Application::OnEventLoopEnter(wxEventLoopBase* loop) { } m_startup = false; - // Don't try to create a map if we didn't load the client map. - if (ClientVersion::getLatestVersion() == nullptr) { - return; - } - // Open a map. if (m_file_to_open != wxEmptyString) { g_gui.LoadMap(FileName(m_file_to_open)); @@ -303,37 +297,12 @@ void Application::MacOpenFiles(const wxArrayString &fileNames) { } } -void Application::FixVersionDiscrapencies() { - // Here the registry should be fixed, if the version has been changed - if (g_settings.getInteger(Config::VERSION_ID) < MAKE_VERSION_ID(1, 0, 5)) { - g_settings.setInteger(Config::USE_MEMCACHED_SPRITES_TO_SAVE, 0); - } - - if (g_settings.getInteger(Config::VERSION_ID) < __RME_VERSION_ID__ && ClientVersion::getLatestVersion() != nullptr) { - g_settings.setInteger(Config::DEFAULT_CLIENT_VERSION, ClientVersion::getLatestVersion()->getID()); - } - - wxString ss = wxstr(g_settings.getString(Config::SCREENSHOT_DIRECTORY)); - if (ss.empty()) { - ss = wxStandardPaths::Get().GetDocumentsDir(); -#ifdef __WINDOWS__ - ss += "/My Pictures/RME/"; -#endif - } - g_settings.setString(Config::SCREENSHOT_DIRECTORY, nstr(ss)); - - // Set registry to newest version - g_settings.setInteger(Config::VERSION_ID, __RME_VERSION_ID__); -} - void Application::Unload() { g_gui.CloseAllEditors(); - g_gui.UnloadVersion(); g_gui.SaveHotkeys(); g_gui.SavePerspective(); g_gui.root->SaveRecentFiles(); - ClientVersion::saveVersions(); - ClientVersion::unloadVersions(); + ClientAssets::save(); g_settings.save(true); g_gui.root = nullptr; } @@ -549,6 +518,26 @@ bool MainFrame::DoQuerySave(bool doclose) { return true; } +void MainFrame::ShowMissingMonsters() { + wxArrayString missingMonsters = g_monsters.getMissingMonsterNames(); + + if (!missingMonsters.IsEmpty()) { + wxString missingMonstersStr = "Missing Monsters:\n" + wxJoin(missingMonsters, '\n'); + wxMessageDialog dialog(this, missingMonstersStr, "Missing Monsters Outfit (data/monsters.xml)", wxOK | wxICON_INFORMATION); + dialog.ShowModal(); + } +} + +void MainFrame::ShowMissingNpcs() { + wxArrayString missingMonsters = g_npcs.getMissingNpcNames(); + + if (!missingMonsters.IsEmpty()) { + wxString missingMonstersStr = "Missing Npcs:\n" + wxJoin(missingMonsters, '\n'); + wxMessageDialog dialog(this, missingMonstersStr, "Missing Npcs Outfit (data/npcs.xml)", wxOK | wxICON_INFORMATION); + dialog.ShowModal(); + } +} + bool MainFrame::DoQueryImportCreatures() { // Monsters if (g_monsters.hasMissing()) { @@ -574,6 +563,8 @@ bool MainFrame::DoQueryImportCreatures() { } } while (g_monsters.hasMissing()); } + + ShowMissingMonsters(); } // Npcs if (g_npcs.hasMissing()) { @@ -599,6 +590,8 @@ bool MainFrame::DoQueryImportCreatures() { } } while (g_npcs.hasMissing()); } + + ShowMissingNpcs(); } g_gui.RefreshPalettes(); return true; diff --git a/source/application.h b/source/application.h index 80910183..42327abc 100644 --- a/source/application.h +++ b/source/application.h @@ -48,7 +48,6 @@ class Application : public wxApp { private: bool m_startup; wxString m_file_to_open; - void FixVersionDiscrapencies(); bool ParseCommandLineMap(wxString &fileName); virtual void OnFatalException(); @@ -69,6 +68,8 @@ class MainFrame : public wxFrame { void UpdateMenubar(); bool DoQueryClose(); bool DoQuerySave(bool doclose = true); + void ShowMissingMonsters(); + void ShowMissingNpcs(); bool DoQueryImportCreatures(); bool LoadMap(FileName name); diff --git a/source/browse_tile_window.cpp b/source/browse_tile_window.cpp index de399fb4..5ef8d7e6 100644 --- a/source/browse_tile_window.cpp +++ b/source/browse_tile_window.cpp @@ -59,7 +59,7 @@ void BrowseTileListBox::OnDrawItem(wxDC &dc, const wxRect &rect, size_t n) const ItemsMap::const_iterator item_iterator = items.find(int(n)); Item* item = item_iterator->second; - Sprite* sprite = g_gui.gfx.getSprite(item->getClientID()); + Sprite* sprite = g_gui.gfx.getSprite(item->getID()); if (sprite) { sprite->DrawTo(&dc, SPRITE_SIZE_32x32, rect.GetX(), rect.GetY(), rect.GetWidth(), rect.GetHeight()); } diff --git a/source/brush.cpp b/source/brush.cpp index 949a24cf..00cc8528 100644 --- a/source/brush.cpp +++ b/source/brush.cpp @@ -139,7 +139,7 @@ bool Brushes::unserializeBrush(pugi::xml_node node, wxArrayString &warnings) { } if (!node.first_child()) { - brushes.insert(std::make_pair(brush->getName(), brush)); + addBrush(brush); return true; } @@ -167,7 +167,7 @@ bool Brushes::unserializeBrush(pugi::xml_node node, wxArrayString &warnings) { } } - brushes.insert(std::make_pair(brush->getName(), brush)); + addBrush(brush); return true; } @@ -191,7 +191,9 @@ bool Brushes::unserializeBorder(pugi::xml_node node, wxArrayString &warnings) { } void Brushes::addBrush(Brush* brush) { - brushes.insert(std::make_pair(brush->getName(), brush)); + if (brush) { + brushes.insert(std::make_pair(brush->getName(), brush)); + } } Brush* Brushes::getBrush(const std::string &name) const { diff --git a/source/client_assets.cpp b/source/client_assets.cpp new file mode 100644 index 00000000..51ea59d0 --- /dev/null +++ b/source/client_assets.cpp @@ -0,0 +1,187 @@ +////////////////////////////////////////////////////////////////////// +// This file is part of Canary Map Editor +////////////////////////////////////////////////////////////////////// +// Canary Map Editor is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Canary Map Editor is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +////////////////////////////////////////////////////////////////////// + +#include "main.h" + +#include "client_assets.h" + +#include "settings.h" +#include "filehandle.h" +#include "preferences.h" +#include "sprite_appearances.h" +#include "gui.h" +#include "otml.h" + +using json = nlohmann::json; + +std::string ClientAssets::version_name; +wxString ClientAssets::data_path; +wxString ClientAssets::assets_path; +bool ClientAssets::loaded = false; + +void ClientAssets::load() { + // Load the data directory info + try { + auto dataDirs = g_settings.getString(Config::ASSETS_DATA_DIRS); + if (!dataDirs.empty()) { + json read_obj = json::parse(dataDirs); + auto ver_obj = read_obj.at(0).get(); + auto path = ver_obj.at("path").get(); + setPath(wxstr(path)); + } + } catch ([[maybe_unused]] const json::exception &e) { + spdlog::info("Json exception with error code {}", e.what()); + } +} + +bool ClientAssets::loadAppearanceProtobuf(wxString &error, wxArrayString &warnings) { + using namespace canary::protobuf::appearances; + using json = nlohmann::json; + + auto clientDirectory = ClientAssets::getPath().ToStdString() + "/"; + if (!wxDirExists(wxString(clientDirectory))) { + error = "The client directory is not valid path, please set a valid path"; + spdlog::error("The client directory is not valid path, please set a valid path"); + return false; + } + + auto assetsDirectory = clientDirectory + "/assets/"; + if (!wxDirExists(wxString(assetsDirectory))) { + error = "The assets directory is not valid path, please set a valid path"; + spdlog::error("The assets directory is not valid path, please set a valid path"); + return false; + } + + if (!g_spriteAppearances.loadCatalogContent(assetsDirectory, false)) { + error = "The client directory is not valid path, please set a valid path"; + spdlog::error("[{}] Cannot open catalog content file", __func__); + return false; + } + + using json = nlohmann::json; + std::filesystem::path packagesPath = std::filesystem::path(clientDirectory) / std::filesystem::path("package.json"); + if (!std::filesystem::exists(packagesPath)) { + error = "The file package.json is not present in the client directory."; + spdlog::error("The file package.json is not present in the client directory. {}", packagesPath.string().c_str()); + return false; + } + + std::ifstream file(packagesPath, std::ios::in); + if (!file.is_open()) { + error = "Failed to open packages.json"; + spdlog::error("Failed to open packages.json"); + return false; + } + + json document = json::parse(file, nullptr, false); + file.close(); + // Save version from package.json + std::string version = document.at("version").get(); + version_name = version; + + const std::string appearanceFileName = g_spriteAppearances.getAppearanceFileName(); + + std::fstream fileStream(assetsDirectory + appearanceFileName, std::ios::in | std::ios::binary); + if (!fileStream.is_open()) { + error = "Failed to load " + appearanceFileName + " from the client folder, file cannot be oppened"; + spdlog::error("[{}] - Failed to load {}, file cannot be oppened", __func__, appearanceFileName); + fileStream.close(); + return false; + } + + // Verify that the version of the library that we linked against is + // compatible with the version of the headers we compiled against. + GOOGLE_PROTOBUF_VERIFY_VERSION; + g_gui.appearances = Appearances(); + if (!g_gui.appearances.ParseFromIstream(&fileStream)) { + error = "Failed to parse binary file " + appearanceFileName + ", file is invalid"; + spdlog::error("[{}] - Failed to parse binary file {}, file is invalid", __func__, appearanceFileName); + fileStream.close(); + return false; + } + + // Parsing all items into ItemType + bool rt = g_items.loadFromProtobuf(error, warnings, g_gui.appearances); + if (!rt) { + error = "Failed to parse item types from protobuf"; + spdlog::error("[{}] - Failed to parse item types from protobuf", __func__); + fileStream.close(); + return false; + } + + // Load looktypes + for (int i = 0; i < g_gui.appearances.outfit().size(); i++) { + const auto &outfit = g_gui.appearances.outfit().Get(i); + if (!g_gui.gfx.loadOutfitSpriteMetadata(outfit, error, warnings)) { + error = "Failed to parse outfit types from protobuf"; + spdlog::error("[{}] - Failed to parse outfit types from protobuf", __func__); + fileStream.close(); + return false; + } + } + + fileStream.close(); + + // Disposing allocated objects. + google::protobuf::ShutdownProtobufLibrary(); + + // Client loaded + setLoaded(true); + return true; +} + +void ClientAssets::save() { + try { + json vers_obj; + + json ver_obj; + ver_obj["id"] = getVersionName(); + wxFileName fileName; + fileName.Assign(getPath()); + ver_obj["path"] = fileName.GetFullPath().ToStdString(); + auto path = fileName.GetFullPath().ToStdString(); + vers_obj.push_back(ver_obj); + + std::ostringstream out; + out << vers_obj; + g_settings.setString(Config::ASSETS_DATA_DIRS, out.str()); + } catch ([[maybe_unused]] const json::exception &e) { + // pass + } +} + +FileName ClientAssets::getDataPath() { + wxString basePath = g_gui.GetDataDirectory(); + if (!wxFileName(basePath).DirExists()) { + basePath = g_gui.getFoundDataDirectory(); + } + return basePath + data_path + FileName::GetPathSeparator(); +} + +FileName ClientAssets::getLocalPath() { + FileName f = g_gui.GetLocalDataDirectory() + data_path + FileName::GetPathSeparator(); + f.Mkdir(0755, wxPATH_MKDIR_FULL); + return f; +} + +wxString ClientAssets::getPath() { + return assets_path; +} + +std::string ClientAssets::getVersionName() { + return version_name; +} diff --git a/source/client_assets.h b/source/client_assets.h new file mode 100644 index 00000000..defeeeb8 --- /dev/null +++ b/source/client_assets.h @@ -0,0 +1,81 @@ +////////////////////////////////////////////////////////////////////// +// This file is part of Canary Map Editor +////////////////////////////////////////////////////////////////////// +// Canary Map Editor is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Canary Map Editor is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +////////////////////////////////////////////////////////////////////// + +#ifndef RME_CLIENT_VERSION_H_ +#define RME_CLIENT_VERSION_H_ + +#include "settings.h" + +// OTBM versions +enum MapVersionID { + MAP_OTBM_UNKNOWN = -1, + MAP_OTBM_1 = 0, + MAP_OTBM_2 = 1, + MAP_OTBM_3 = 2, + MAP_OTBM_4 = 3, +}; + +// The composed version of a otbm file (otbm version, client version) +struct MapVersion { + MapVersion() : + otbm(MAP_OTBM_1) { } + MapVersion(MapVersionID m) : + otbm(m) { } + MapVersionID otbm; +}; + +class ClientAssets { +public: + ClientAssets() = default; + ~ClientAssets() = default; + + // Ensures we don't accidentally copy it. + ClientAssets(const ClientAssets &) = delete; + ClientAssets &operator=(const ClientAssets &) = delete; + + static void load(); + // Load protobuf appearance file and catalog-content json with sprites + static bool loadAppearanceProtobuf(wxString &error, wxArrayString &warnings); + static void save(); + + static std::string getVersionName(); + + static FileName getDataPath(); + static FileName getLocalPath(); + static wxString getPath(); + static void setPath(wxString newPath) { + assets_path = newPath; + } + static void setLoaded(bool newLoaded) { + loaded = newLoaded; + } + + static bool isLoaded() { + return loaded; + } + +private: + static std::string version_name; + + static wxString assets_path; + static wxString data_path; + static bool loaded; + + static void loadVersion(pugi::xml_node client_node); +}; + +#endif diff --git a/source/client_version.cpp b/source/client_version.cpp deleted file mode 100644 index f8a67c21..00000000 --- a/source/client_version.cpp +++ /dev/null @@ -1,550 +0,0 @@ -////////////////////////////////////////////////////////////////////// -// This file is part of Remere's Map Editor -////////////////////////////////////////////////////////////////////// -// Remere's Map Editor is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Remere's Map Editor is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -////////////////////////////////////////////////////////////////////// - -#include "main.h" - -#include "settings.h" -#include "filehandle.h" - -#include "gui.h" - -#include "client_version.h" -#include "otml.h" - -using json = nlohmann::json; - -// Static methods to load/save - -ClientVersion::VersionMap ClientVersion::client_versions; -ClientVersion* ClientVersion::latest_version = nullptr; -ClientVersion::OtbMap ClientVersion::otb_versions; - -void ClientVersion::loadVersions() { - // Clean up old stuff - ClientVersion::unloadVersions(); - - // Locate the clients.xml file - wxFileName file_to_load; - - wxFileName exec_dir_client_xml; - exec_dir_client_xml.Assign(g_gui.GetExecDirectory()); - exec_dir_client_xml.SetFullName("clients.xml"); - - wxFileName data_dir_client_xml; - data_dir_client_xml.Assign(g_gui.GetDataDirectory()); - data_dir_client_xml.SetFullName("clients.xml"); - - wxFileName work_dir_client_xml; - work_dir_client_xml.Assign(g_gui.getFoundDataDirectory()); - work_dir_client_xml.SetFullName("clients.xml"); - - file_to_load = exec_dir_client_xml; - if (!file_to_load.FileExists()) { - file_to_load = data_dir_client_xml; - if (!file_to_load.FileExists()) { - file_to_load = work_dir_client_xml; - if (!file_to_load.FileExists()) { - file_to_load.Clear(); - } - } - } - - if (!file_to_load.FileExists()) { - wxLogError(wxString() + "Could not load clients.xml, editor will NOT be able to load any client data files.\n" + "Checked paths:\n" + exec_dir_client_xml.GetFullPath() + "\n" + data_dir_client_xml.GetFullPath() + "\n" + work_dir_client_xml.GetFullPath()); - return; - } - - // Parse the file - pugi::xml_document doc; - pugi::xml_parse_result result = doc.load_file(file_to_load.GetFullPath().mb_str()); - if (result) { - pugi::xml_node node = doc.child("client_config"); - if (!node) { - wxLogError("Could not load clients.xml (syntax error), editor will NOT be able to load any client data files."); - return; - } - - for (pugi::xml_node childNode = node.first_child(); childNode; childNode = childNode.next_sibling()) { - const std::string &childName = as_lower_str(childNode.name()); - if (childName == "otbs") { - for (pugi::xml_node otbNode = childNode.first_child(); otbNode; otbNode = otbNode.next_sibling()) { - if (as_lower_str(otbNode.name()) == "otb") { - loadOTBInfo(otbNode); - } - } - } else if (childName == "clients") { - for (pugi::xml_node versionNode = childNode.first_child(); versionNode; versionNode = versionNode.next_sibling()) { - if (as_lower_str(versionNode.name()) == "client") { - loadVersion(versionNode); - } - } - } - } - - for (pugi::xml_node childNode = node.first_child(); childNode; childNode = childNode.next_sibling()) { - if (as_lower_str(childNode.name()) != "clients") { - continue; - } - - for (pugi::xml_node versionNode = childNode.first_child(); versionNode; versionNode = versionNode.next_sibling()) { - if (as_lower_str(versionNode.name()) == "client") { - loadVersionExtensions(versionNode); - } - } - } - } - - // Assign a default if there isn't one. - if (!latest_version && !client_versions.empty()) { - latest_version = client_versions.begin()->second; - } - - // Load the data directory info - try { - json read_obj = json::parse(g_settings.getString(Config::ASSETS_DATA_DIRS)); - auto vers_obj = read_obj.get>(); - for (const auto &ver_iter : vers_obj) { - const auto &ver_obj = ver_iter.get(); - auto version = get(ver_obj.at("id").get()); - if (version == nullptr) { - continue; - } - version->setClientPath(wxstr(ver_obj.at("path").get())); - } - } catch ([[maybe_unused]] const json::exception &e) { - // pass - } -} - -void ClientVersion::unloadVersions() { - for (VersionMap::iterator it = client_versions.begin(); it != client_versions.end(); ++it) { - delete it->second; - } - client_versions.clear(); - latest_version = nullptr; - otb_versions.clear(); -} - -void ClientVersion::loadOTBInfo(pugi::xml_node otbNode) { - if (as_lower_str(otbNode.name()) != "otb") { - return; - } - - pugi::xml_attribute attribute; - if (!(attribute = otbNode.attribute("client"))) { - wxLogError("Node 'otb' must contain 'client' tag."); - return; - } - - OtbVersion otb = { "", OTB_VERSION_3, CLIENT_VERSION_NONE }; - otb.name = attribute.as_string(); - if (!(attribute = otbNode.attribute("id"))) { - wxLogError("Node 'otb' must contain 'id' tag."); - return; - } - - otb.id = static_cast(attribute.as_int()); - if (!(attribute = otbNode.attribute("version"))) { - wxLogError("Node 'otb' must contain 'version' tag."); - return; - } - - OtbFormatVersion versionId = static_cast(attribute.as_uint()); - if (versionId < OTB_VERSION_1 || versionId > OTB_VERSION_3) { - wxLogError("Node 'otb' unrecognized format version (version 1..3 supported)."); - return; - } - - otb.format_version = versionId; - otb_versions[otb.name] = otb; -} - -void ClientVersion::loadVersion(pugi::xml_node versionNode) { - pugi::xml_attribute attribute; - if (!(attribute = versionNode.attribute("name"))) { - wxLogError("Node 'client' must contain 'name', 'data_directory' and 'otb' tags."); - return; - } - - const std::string &versionName = attribute.as_string(); - if (!(attribute = versionNode.attribute("data_directory"))) { - wxLogError("Node 'client' must contain 'name', 'data_directory' and 'otb' tags."); - return; - } - - const std::string &dataPath = attribute.as_string(); - if (!(attribute = versionNode.attribute("otb"))) { - wxLogError("Node 'client' must contain 'name', 'data_directory' and 'otb' tags."); - return; - } - - const std::string &otbVersionName = attribute.as_string(); - if (otb_versions.find(otbVersionName) == otb_versions.end()) { - wxLogError("Node 'client' 'otb' tag is invalid (couldn't find this otb version)."); - return; - } - - ClientVersion* version = newd ClientVersion(otb_versions[otbVersionName], versionName, wxstr(dataPath)); - - bool should_be_default = versionNode.attribute("default").as_bool(); - version->visible = versionNode.attribute("visible").as_bool(); - - for (pugi::xml_node childNode = versionNode.first_child(); childNode; childNode = childNode.next_sibling()) { - const std::string &childName = as_lower_str(childNode.name()); - if (childName == "otbm") { - if (!(attribute = childNode.attribute("version"))) { - wxLogError("Node 'otbm' missing version."); - continue; - } - - int32_t otbmVersion = attribute.as_int() - 1; - if (otbmVersion < MAP_OTBM_1 || otbmVersion > MAP_OTBM_4) { - wxLogError("Node 'otbm' unsupported version."); - continue; - } - - if (childNode.attribute("preffered").as_bool() || version->preferred_map_version == MAP_OTBM_UNKNOWN) { - version->preferred_map_version = static_cast(otbmVersion); - } - version->map_versions_supported.push_back(version->preferred_map_version); - } else if (childName == "fucked_up_charges") { - version->usesFuckedUpCharges = true; - } else if (childName == "data") { - - if (!(attribute = childNode.attribute("format"))) { - wxLogError("Node 'data' does not have 'format' tag."); - continue; - } - - const std::string &format = attribute.as_string(); - ClientData client_data = { DAT_FORMAT_11, 0, 0 }; - if (format == "11") { - client_data.datFormat = DAT_FORMAT_11; - } else { - wxLogError("Node 'data' 'format' is invalid (only 11.00 are supported)"); - continue; - } - - if (!(attribute = childNode.attribute("dat")) || !wxString(attribute.as_string(), wxConvUTF8).ToULong((unsigned long*)&client_data.datSignature, 16)) { - wxLogError("Node 'data' 'dat' tag is not hex-formatted."); - continue; - } - - if (!(attribute = childNode.attribute("spr")) || !wxString(attribute.as_string(), wxConvUTF8).ToULong((unsigned long*)&client_data.sprSignature, 16)) { - wxLogError("Node 'data' 'spr' tag is not hex-formatted."); - continue; - } - - version->data_versions.push_back(client_data); - } - } - - if (client_versions[version->getID()]) { - wxLogError("Duplicate version id %i, discarding version '%s'.", version->getID(), version->name); - delete version; - return; - } - - client_versions[version->getID()] = version; - if (should_be_default) { - latest_version = version; - } -} - -void ClientVersion::loadVersionExtensions(pugi::xml_node versionNode) { - pugi::xml_attribute attribute; - if (!(attribute = versionNode.attribute("name"))) { - // Error has already been displayed earlier, no need to show another error about the same thing - return; - } - - ClientVersion* version = get(attribute.as_string()); - if (!version) { - // Same rationale as above - return; - } - - for (pugi::xml_node childNode = versionNode.first_child(); childNode; childNode = childNode.next_sibling()) { - if (as_lower_str(childNode.name()) != "extensions") { - continue; - } - - const std::string &from = childNode.attribute("from").as_string(); - const std::string &to = childNode.attribute("to").as_string(); - - ClientVersion* fromVersion = get(from); - ClientVersion* toVersion = get(to); - - if (!fromVersion && !toVersion) { - wxLogError("Unknown client extension data."); - continue; - } - - if (!fromVersion) { - fromVersion = client_versions.begin()->second; - } - - if (!toVersion) { - toVersion = client_versions.rbegin()->second; - } - - for (const auto &versionEntry : client_versions) { - ClientVersion* version = versionEntry.second; - if (version->getID() >= fromVersion->getID() && version->getID() <= toVersion->getID()) { - version->extension_versions.push_back(version); - } - } - - std::sort(version->extension_versions.begin(), version->extension_versions.end(), VersionComparisonPredicate); - } -} - -void ClientVersion::saveVersions() { - try { - json vers_obj; - - for (auto &[id, version] : client_versions) { - json ver_obj; - ver_obj["id"] = version->getName(); - ver_obj["path"] = version->getClientPath().GetFullPath().ToStdString(); - vers_obj.push_back(ver_obj); - } - - std::ostringstream out; - out << vers_obj; - g_settings.setString(Config::ASSETS_DATA_DIRS, out.str()); - } catch ([[maybe_unused]] const json::exception &e) { - // pass - } -} - -// Client version class - -ClientVersion::ClientVersion(OtbVersion otb, std::string versionName, wxString path) : - otb(otb), - name(versionName), - visible(false), - preferred_map_version(MAP_OTBM_UNKNOWN), - data_path(path) { - //// -} - -ClientVersion* ClientVersion::get(ClientVersionID id) { - VersionMap::iterator i = client_versions.find(id); - if (i == client_versions.end()) { - return nullptr; - } - return i->second; -} - -ClientVersion* ClientVersion::get(std::string id) { - for (VersionMap::iterator i = client_versions.begin(); i != client_versions.end(); ++i) { - if (i->second->getName() == id) { - return i->second; - } - } - return nullptr; -} - -ClientVersionList ClientVersion::getAll() { - ClientVersionList l; - for (VersionMap::iterator i = client_versions.begin(); i != client_versions.end(); ++i) { - l.push_back(i->second); - } - return l; -} - -ClientVersionList ClientVersion::getAllVisible() { - ClientVersionList l; - for (VersionMap::iterator i = client_versions.begin(); i != client_versions.end(); ++i) { - if (i->second->isVisible()) { - l.push_back(i->second); - } - } - return l; -} - -ClientVersionList ClientVersion::getAllForOTBMVersion(MapVersionID id) { - ClientVersionList list; - for (VersionMap::iterator i = client_versions.begin(); i != client_versions.end(); ++i) { - if (i->second->isVisible()) { - for (std::vector::iterator v = i->second->map_versions_supported.begin(); v != i->second->map_versions_supported.end(); ++v) { - if (*v == id) { - list.push_back(i->second); - } - } - } - } - return list; -} - -ClientVersion* ClientVersion::getLatestVersion() { - return latest_version; -} - -FileName ClientVersion::getDataPath() const { - wxString basePath = g_gui.GetDataDirectory(); - if (!wxFileName(basePath).DirExists()) { - basePath = g_gui.getFoundDataDirectory(); - } - return basePath + data_path + FileName::GetPathSeparator(); -} - -FileName ClientVersion::getLocalDataPath() const { - FileName f = g_gui.GetLocalDataDirectory() + data_path + FileName::GetPathSeparator(); - f.Mkdir(0755, wxPATH_MKDIR_FULL); - return f; -} - -bool ClientVersion::hasValidPaths() { - if (!client_path.DirExists()) { - return false; - } - - wxDir dir(client_path.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR)); - wxString otfi_file; - metadata_path = wxFileName(client_path.GetFullPath(), wxString(ASSETS_NAME) + ".dat"); - sprites_path = wxFileName(client_path.GetFullPath(), wxString(ASSETS_NAME) + ".spr"); - - if (dir.GetFirst(&otfi_file, "*.otfi", wxDIR_FILES)) { - wxFileName otfi(client_path.GetFullPath(), otfi_file); - OTMLDocumentPtr doc = OTMLDocument::parse(otfi.GetFullPath().ToStdString()); - if (doc->size() != 0 && doc->hasChildAt("DatSpr")) { - OTMLNodePtr node = doc->get("DatSpr"); - std::string metadata = node->valueAt("metadata-file", std::string(ASSETS_NAME) + ".dat"); - std::string sprites = node->valueAt("sprites-file", std::string(ASSETS_NAME) + ".spr"); - metadata_path = wxFileName(client_path.GetFullPath(), wxString(metadata)); - sprites_path = wxFileName(client_path.GetFullPath(), wxString(sprites)); - } - } - - if (!metadata_path.FileExists() || !sprites_path.FileExists()) { - return false; - } - - if (!g_settings.getInteger(Config::CHECK_SIGNATURES)) { - return true; - } - - // Peek the version of the files - FileReadHandle dat_file(static_cast(metadata_path.GetFullPath().mb_str())); - if (!dat_file.isOk()) { - wxLogError("Could not open metadata file."); - return false; - } - - uint32_t datSignature; - dat_file.getU32(datSignature); - dat_file.close(); - - FileReadHandle spr_file(static_cast(sprites_path.GetFullPath().mb_str())); - if (!spr_file.isOk()) { - wxLogError("Could not open sprites file."); - return false; - } - - uint32_t sprSignature; - spr_file.getU32(sprSignature); - spr_file.close(); - - for (const auto &clientData : data_versions) { - if (clientData.sprSignature == sprSignature && clientData.datSignature == datSignature) { - return true; - } - } - - wxString message = "Signatures are incorrect.\n"; - message << "Metadata signature: %X\n"; - message << "Sprites signature: %X"; - wxLogError(wxString::Format(message, datSignature, sprSignature)); - return false; -} - -bool ClientVersion::loadValidPaths() { - while (!hasValidPaths()) { - wxString message = "Could not locate metadata and/or sprite files, please navigate to your client assets %s installation folder.\n"; - message << "Attempted metadata file: %s\n"; - message << "Attempted sprites file: %s\n"; - - g_gui.PopupDialog("Error", wxString::Format(message, name, metadata_path.GetFullPath(), sprites_path.GetFullPath()), wxOK); - - wxString dirHelpText("Select assets directory."); - wxDirDialog file_dlg(nullptr, dirHelpText, "", wxDD_DIR_MUST_EXIST); - int ok = file_dlg.ShowModal(); - if (ok == wxID_CANCEL) { - return false; - } - - client_path.Assign(file_dlg.GetPath() + FileName::GetPathSeparator()); - } - - ClientVersion::saveVersions(); - - return true; -} - -DatFormat ClientVersion::getDatFormatForSignature(uint32_t signature) const { - for (std::vector::const_iterator iter = data_versions.begin(); iter != data_versions.end(); ++iter) { - if (iter->datSignature == signature) { - return iter->datFormat; - } - } - - return DAT_FORMAT_UNKNOWN; -} - -std::string ClientVersion::getName() const { - return name; -} - -ClientVersionID ClientVersion::getID() const { - return otb.id; -} - -bool ClientVersion::isVisible() const { - return visible; -} - -void ClientVersion::setClientPath(const FileName &dir) { - client_path.Assign(dir); -} - -MapVersionID ClientVersion::getPrefferedMapVersionID() const { - return preferred_map_version; -} - -OtbVersion ClientVersion::getOTBVersion() const { - return otb; -} - -ClientVersionList ClientVersion::getExtensionsSupported() const { - return extension_versions; -} - -ClientVersionList ClientVersion::getAllVersionsSupportedForClientVersion(ClientVersion* clientVersion) { - ClientVersionList versionList; - for (const auto &versionEntry : client_versions) { - ClientVersion* version = versionEntry.second; - for (ClientVersion* checkVersion : version->getExtensionsSupported()) { - if (clientVersion == checkVersion) { - versionList.push_back(version); - } - } - } - std::sort(versionList.begin(), versionList.end(), VersionComparisonPredicate); - return versionList; -} diff --git a/source/client_version.h b/source/client_version.h deleted file mode 100644 index 8cac4bc8..00000000 --- a/source/client_version.h +++ /dev/null @@ -1,273 +0,0 @@ -////////////////////////////////////////////////////////////////////// -// This file is part of Remere's Map Editor -////////////////////////////////////////////////////////////////////// -// Remere's Map Editor is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Remere's Map Editor is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -////////////////////////////////////////////////////////////////////// - -#ifndef RME_CLIENT_VERSION_H_ -#define RME_CLIENT_VERSION_H_ - -#include "main.h" -#include "settings.h" - -typedef int ClientVersionID; - -// Client versions -enum ClientVersions { - CLIENT_VERSION_NONE = -1, - CLIENT_VERSION_ALL = -2, - CLIENT_VERSION_750 = 1, - CLIENT_VERSION_755 = 2, - CLIENT_VERSION_760 = 3, - CLIENT_VERSION_770 = 3, - CLIENT_VERSION_780 = 4, - CLIENT_VERSION_790 = 5, - CLIENT_VERSION_792 = 6, - CLIENT_VERSION_800 = 7, - CLIENT_VERSION_810 = 8, - CLIENT_VERSION_811 = 9, - CLIENT_VERSION_820 = 10, // After this version, OTBM stores charges as an attribute - CLIENT_VERSION_830 = 11, - CLIENT_VERSION_840 = 12, - CLIENT_VERSION_841 = 13, - CLIENT_VERSION_842 = 14, - CLIENT_VERSION_850 = 15, - CLIENT_VERSION_854_BAD = 16, - CLIENT_VERSION_854 = 17, - CLIENT_VERSION_855 = 18, - CLIENT_VERSION_860_OLD = 19, - CLIENT_VERSION_860 = 20, - CLIENT_VERSION_861 = 21, - CLIENT_VERSION_862 = 22, - CLIENT_VERSION_870 = 23, - CLIENT_VERSION_871 = 24, - CLIENT_VERSION_872 = 25, - CLIENT_VERSION_873 = 26, - CLIENT_VERSION_900 = 27, - CLIENT_VERSION_910 = 28, - CLIENT_VERSION_920 = 29, - CLIENT_VERSION_940 = 30, - CLIENT_VERSION_944_V1 = 31, - CLIENT_VERSION_944_V2 = 32, - CLIENT_VERSION_944_V3 = 33, - CLIENT_VERSION_944_V4 = 34, - CLIENT_VERSION_946 = 35, - CLIENT_VERSION_950 = 36, - CLIENT_VERSION_952 = 37, - CLIENT_VERSION_953 = 38, - CLIENT_VERSION_954 = 39, - CLIENT_VERSION_960 = 40, - CLIENT_VERSION_961 = 41, - CLIENT_VERSION_963 = 42, - CLIENT_VERSION_970 = 43, - CLIENT_VERSION_980 = 44, - CLIENT_VERSION_981 = 45, - CLIENT_VERSION_982 = 46, - CLIENT_VERSION_983 = 47, - CLIENT_VERSION_985 = 48, - CLIENT_VERSION_986 = 49, - CLIENT_VERSION_1010 = 50, - CLIENT_VERSION_1020 = 51, - CLIENT_VERSION_1021 = 52 -}; - -// OTBM versions -enum MapVersionID { - MAP_OTBM_UNKNOWN = -1, - MAP_OTBM_1 = 0, - MAP_OTBM_2 = 1, - MAP_OTBM_3 = 2, - MAP_OTBM_4 = 3, -}; - -// The composed version of a otbm file (otbm version, client version) -struct MapVersion { - MapVersion() : - otbm(MAP_OTBM_1), client(CLIENT_VERSION_NONE) { } - MapVersion(MapVersionID m, ClientVersionID c) : - otbm(m), client(c) { } - MapVersionID otbm; - ClientVersionID client; -}; - -enum OtbFormatVersion : uint32_t { - OTB_VERSION_1 = 1, - OTB_VERSION_2 = 2, - OTB_VERSION_3 = 3, -}; - -// Represents an OTB version -struct OtbVersion { - // '8.60', '7.40' etc. - std::string name; - // What file format the OTB is in (version 1..3) - OtbFormatVersion format_version; - // The minor version ID of the OTB (maps to CLIENT_VERSION in OTServ) - ClientVersionID id; -}; - -// Formats for the metadata file -enum DatFormat { - DAT_FORMAT_UNKNOWN, - DAT_FORMAT_11 -}; - -enum DatFlags : uint8_t { - DatFlagGround = 0, - DatFlagGroundBorder = 1, - DatFlagOnBottom = 2, - DatFlagOnTop = 3, - DatFlagContainer = 4, - DatFlagStackable = 5, - DatFlagForceUse = 6, - DatFlagMultiUse = 7, - DatFlagWritable = 8, - DatFlagWritableOnce = 9, - DatFlagFluidContainer = 10, - DatFlagSplash = 11, - DatFlagNotWalkable = 12, - DatFlagNotMoveable = 13, - DatFlagBlockProjectile = 14, - DatFlagNotPathable = 15, - DatFlagPickupable = 16, - DatFlagHangable = 17, - DatFlagHookSouth = 18, - DatFlagHookEast = 19, - DatFlagRotateable = 20, - DatFlagLight = 21, - DatFlagDontHide = 22, - DatFlagTranslucent = 23, - DatFlagDisplacement = 24, - DatFlagElevation = 25, - DatFlagLyingCorpse = 26, - DatFlagAnimateAlways = 27, - DatFlagMinimapColor = 28, - DatFlagLensHelp = 29, - DatFlagFullGround = 30, - DatFlagLook = 31, - DatFlagCloth = 32, - DatFlagMarket = 33, - DatFlagUsable = 34, - DatFlagWrappable = 35, - DatFlagUnwrappable = 36, - DatFlagTopEffect = 37, - - DatFlagFloorChange = 252, - DatFlagNoMoveAnimation = 253, // 10.10: real value is 16, but we need to do this for backwards compatibility - DatFlagChargeable = 254, - DatFlagLast = 255 -}; - -// Represents a client file version -struct ClientData { - DatFormat datFormat; - uint32_t datSignature; - uint32_t sprSignature; -}; - -// typedef the client version -class ClientVersion; -typedef std::vector ClientVersionList; - -class ClientVersion { -public: - ClientVersion(OtbVersion otb, std::string versionName, wxString path); - ~ClientVersion() = default; - - // Ensures we don't accidentally copy it. - ClientVersion(const ClientVersion &) = delete; - ClientVersion &operator=(const ClientVersion &) = delete; - - static void loadVersions(); - static void unloadVersions(); - static void saveVersions(); - - static ClientVersion* get(ClientVersionID id); - static ClientVersion* get(std::string name); - static ClientVersionList getVisible(std::string from, std::string to); - static ClientVersionList getAll(); - static ClientVersionList getAllVisible(); - static ClientVersionList getAllForOTBMVersion(MapVersionID map_version); - static ClientVersionList getAllVersionsSupportedForClientVersion(ClientVersion* v); - static ClientVersion* getLatestVersion(); - - bool operator==(const ClientVersion &o) const { - return otb.id == o.otb.id; - } - - bool hasValidPaths(); - bool loadValidPaths(); - void setClientPath(const FileName &dir); - - bool isVisible() const; - std::string getName() const; - - ClientVersionID getID() const; - MapVersionID getPrefferedMapVersionID() const; - OtbVersion getOTBVersion() const; - DatFormat getDatFormatForSignature(uint32_t signature) const; - ClientVersionList getExtensionsSupported() const; - - FileName getDataPath() const; - FileName getLocalDataPath() const; - FileName getClientPath() const { - return client_path; - } - wxFileName getMetadataPath() const { - return metadata_path; - } - wxFileName getSpritesPath() const { - return sprites_path; - } - -private: - OtbVersion otb; - - std::string name; - bool visible; - bool usesFuckedUpCharges; - - std::vector map_versions_supported; - MapVersionID preferred_map_version; - std::vector data_versions; - std::vector extension_versions; - - wxString data_path; - FileName client_path; - wxFileName metadata_path; - wxFileName sprites_path; - -private: - static void loadOTBInfo(pugi::xml_node otb_nodes); - static void loadVersion(pugi::xml_node client_node); - static void loadVersionExtensions(pugi::xml_node client_node); - - // All versions - using VersionMap = std::map; - static VersionMap client_versions; - static ClientVersion* latest_version; - - // All otbs - typedef std::map OtbMap; - static OtbMap otb_versions; -}; - -inline int VersionComparisonPredicate(ClientVersion* a, ClientVersion* b) { - if (a->getID() < b->getID()) { - return 1; - } - return 0; -} - -#endif diff --git a/source/common_windows.cpp b/source/common_windows.cpp index f49d4b30..aa68ac02 100644 --- a/source/common_windows.cpp +++ b/source/common_windows.cpp @@ -32,6 +32,7 @@ #include "application.h" #include "common_windows.h" #include "positionctrl.h" +#include "preferences.h" #include "iominimap.h" @@ -92,11 +93,11 @@ MapPropertiesWindow::MapPropertiesWindow(wxWindow* parent, MapTab* view, Editor grid_sizer->Add(version_choice, wxSizerFlags(1).Expand()); - // Version - grid_sizer->Add(newd wxStaticText(this, wxID_ANY, "Client Version")); + // Client directory + grid_sizer->Add(newd wxStaticText(this, wxID_ANY, "Client Folder")); protocol_choice = newd wxChoice(this, wxID_ANY); - protocol_choice->SetStringSelection(wxstr(g_gui.GetCurrentVersion().getName())); + protocol_choice->SetStringSelection(wxstr(ClientAssets::getVersionName())); grid_sizer->Add(protocol_choice, wxSizerFlags(1).Expand()); @@ -157,8 +158,7 @@ MapPropertiesWindow::MapPropertiesWindow(wxWindow* parent, MapTab* view, Editor Centre(wxBOTH); UpdateProtocolList(); - ClientVersion* current_version = ClientVersion::get(map.getVersion().client); - protocol_choice->SetStringSelection(wxstr(current_version->getName())); + protocol_choice->SetStringSelection(wxstr(ClientAssets::getVersionName())); } void MapPropertiesWindow::UpdateProtocolList() { @@ -166,27 +166,6 @@ void MapPropertiesWindow::UpdateProtocolList() { wxString client = protocol_choice->GetStringSelection(); protocol_choice->Clear(); - - ClientVersionList versions; - if (g_settings.getInteger(Config::USE_OTBM_4_FOR_ALL_MAPS)) { - versions = ClientVersion::getAllVisible(); - } else { - MapVersionID map_version = MAP_OTBM_1; - if (ver.Contains("0.5.0")) { - map_version = MAP_OTBM_1; - } else if (ver.Contains("0.6.0")) { - map_version = MAP_OTBM_2; - } else if (ver.Contains("0.6.1")) { - map_version = MAP_OTBM_3; - } else if (ver.Contains("0.7.0")) { - map_version = MAP_OTBM_4; - } - - ClientVersionList protocols = ClientVersion::getAllForOTBMVersion(map_version); - for (ClientVersionList::const_iterator p = protocols.begin(); p != protocols.end(); ++p) { - protocol_choice->Append(wxstr((*p)->getName())); - } - } protocol_choice->SetSelection(0); protocol_choice->SetStringSelection(client); } @@ -241,7 +220,6 @@ void MapPropertiesWindow::OnClickOK(wxCommandEvent &WXUNUSED(event)) { wxString ver = version_choice->GetStringSelection(); - new_ver.client = ClientVersion::get(nstr(protocol_choice->GetStringSelection()))->getID(); if (ver.Contains("0.5.0")) { new_ver.otbm = MAP_OTBM_1; } else if (ver.Contains("0.6.0")) { @@ -252,7 +230,7 @@ void MapPropertiesWindow::OnClickOK(wxCommandEvent &WXUNUSED(event)) { new_ver.otbm = MAP_OTBM_4; } - if (new_ver.client != old_ver.client) { + if (new_ver.otbm != old_ver.otbm) { if (g_gui.GetOpenMapCount() > 1) { g_gui.PopupDialog(this, "Error", "You can not change editor version with multiple maps open", wxOK); return; @@ -264,7 +242,7 @@ void MapPropertiesWindow::OnClickOK(wxCommandEvent &WXUNUSED(event)) { g_gui.GetCurrentEditor()->getSelection().clear(); g_gui.GetCurrentEditor()->clearActions(); - if (new_ver.client < old_ver.client) { + if (new_ver.otbm < old_ver.otbm) { int ret = g_gui.PopupDialog(this, "Notice", "Converting to a previous version may have serious side-effects, are you sure you want to do this?", wxYES | wxNO); if (ret != wxID_YES) { return; @@ -279,11 +257,19 @@ void MapPropertiesWindow::OnClickOK(wxCommandEvent &WXUNUSED(event)) { map.convert(new_ver, true); // Load the new version - if (!g_gui.LoadVersion(new_ver.client, error, warnings)) { + if (!g_gui.LoadVersion(error, warnings)) { g_gui.ListDialog(this, "Warnings", warnings); g_gui.PopupDialog(this, "Map Loader Error", error, wxOK); g_gui.PopupDialog(this, "Conversion Error", "Could not convert map. The map will now be closed.", wxOK); + auto clientDirectory = ClientAssets::getPath().ToStdString() + "/"; + if (clientDirectory.empty() || !wxDirExists(wxString(clientDirectory))) { + PreferencesWindow dialog(nullptr); + dialog.getBookCtrl().SetSelection(4); + dialog.ShowModal(); + dialog.Destroy(); + } + EndModal(0); return; } @@ -331,11 +317,19 @@ void MapPropertiesWindow::OnClickOK(wxCommandEvent &WXUNUSED(event)) { map.cleanInvalidTiles(true); } else { UnnamedRenderingLock(); - if (!g_gui.LoadVersion(new_ver.client, error, warnings)) { + if (!g_gui.LoadVersion(error, warnings)) { g_gui.ListDialog(this, "Warnings", warnings); g_gui.PopupDialog(this, "Map Loader Error", error, wxOK); g_gui.PopupDialog(this, "Conversion Error", "Could not convert map. The map will now be closed.", wxOK); + auto clientDirectory = ClientAssets::getPath().ToStdString() + "/"; + if (clientDirectory.empty() || !wxDirExists(wxString(clientDirectory))) { + PreferencesWindow dialog(nullptr); + dialog.getBookCtrl().SetSelection(4); + dialog.ShowModal(); + dialog.Destroy(); + } + EndModal(0); return; } diff --git a/source/complexitem.h b/source/complexitem.h index e9423f88..2269337f 100644 --- a/source/complexitem.h +++ b/source/complexitem.h @@ -58,8 +58,6 @@ class Container : public Item { virtual bool unserializeItemNode_OTBM(const IOMap &maphandle, BinaryNode* node) override; virtual bool serializeItemNode_OTBM(const IOMap &maphandle, NodeFileWriteHandle &f) const override; - // virtual bool unserializeItemNode_OTMM(const IOMap& maphandle, BinaryNode* node); - // virtual bool serializeItemNode_OTMM(const IOMap& maphandle, NodeFileWriteHandle& f) const; protected: ItemVector contents; @@ -76,8 +74,6 @@ class Teleport : public Item { virtual void serializeItemAttributes_OTBM(const IOMap &maphandle, NodeFileWriteHandle &f) const override; virtual bool readItemAttribute_OTBM(const IOMap &maphandle, OTBM_ItemAttribute attr, BinaryNode* node) override; - // virtual void serializeItemAttributes_OTMM(const IOMap& maphandle, NodeFileWriteHandle& f) const; - // virtual bool readItemAttribute_OTMM(const IOMap& maphandle, OTMM_ItemAttribute attr, BinaryNode* node); const Position &getDestination() const noexcept { return destination; @@ -120,8 +116,6 @@ class Door : public Item { virtual void serializeItemAttributes_OTBM(const IOMap &maphandle, NodeFileWriteHandle &f) const override; virtual bool readItemAttribute_OTBM(const IOMap &maphandle, OTBM_ItemAttribute attr, BinaryNode* node) override; - // virtual void serializeItemAttributes_OTMM(const IOMap& maphandle, NodeFileWriteHandle& f) const; - // virtual bool readItemAttribute_OTMM(const IOMap& maphandle, OTMM_ItemAttribute attr, BinaryNode* node); protected: uint8_t doorId; @@ -145,8 +139,6 @@ class Depot : public Item { virtual void serializeItemAttributes_OTBM(const IOMap &maphandle, NodeFileWriteHandle &f) const override; virtual bool readItemAttribute_OTBM(const IOMap &maphandle, OTBM_ItemAttribute attr, BinaryNode* node) override; - // virtual void serializeItemAttributes_OTMM(const IOMap& maphandle, NodeFileWriteHandle& f) const; - // virtual bool readItemAttribute_OTMM(const IOMap& maphandle, OTMM_ItemAttribute attr, BinaryNode* node); protected: uint8_t depotId; diff --git a/source/container_properties_window.cpp b/source/container_properties_window.cpp index d2266eb9..db689549 100644 --- a/source/container_properties_window.cpp +++ b/source/container_properties_window.cpp @@ -44,7 +44,7 @@ EVT_MENU(CONTAINER_POPUP_MENU_REMOVE, ContainerItemButton::OnRemoveItem) END_EVENT_TABLE() ContainerItemButton::ContainerItemButton(wxWindow* parent, bool large, int _index, const Map* map, Item* item) : - ItemButton(parent, (large ? RENDER_SIZE_32x32 : RENDER_SIZE_16x16), (item ? item->getClientID() : 0)), + ItemButton(parent, (large ? RENDER_SIZE_32x32 : RENDER_SIZE_16x16), (item ? item->getID() : 0)), edit_map(map), edit_item(item), index(_index) { @@ -158,7 +158,7 @@ void ContainerItemButton::OnRemoveItem(wxCommandEvent &WXUNUSED(event)) { void ContainerItemButton::setItem(Item* item) { edit_item = item; if (edit_item) { - SetSprite(edit_item->getClientID()); + SetSprite(edit_item->getID()); } else { SetSprite(0); } diff --git a/source/dcbutton.cpp b/source/dcbutton.cpp index b0a34c8b..6b084574 100644 --- a/source/dcbutton.cpp +++ b/source/dcbutton.cpp @@ -92,10 +92,6 @@ bool DCButton::GetValue() const { void DCButton::OnPaint(wxPaintEvent &event) { wxBufferedPaintDC pdc(this); - if (g_gui.gfx.isUnloaded()) { - return; - } - static std::unique_ptr highlight_pen; static std::unique_ptr dark_highlight_pen; static std::unique_ptr light_shadow_pen; diff --git a/source/definitions.h b/source/definitions.h index 2744b995..dc543eb7 100644 --- a/source/definitions.h +++ b/source/definitions.h @@ -50,6 +50,7 @@ // OS #define OTGZ_SUPPORT 0 +#define CLIENT_VERSION 1100 #define ASSETS_NAME "Tibia" diff --git a/source/editor.cpp b/source/editor.cpp index 2ce4c9e7..74b81513 100644 --- a/source/editor.cpp +++ b/source/editor.cpp @@ -20,6 +20,7 @@ #include "editor.h" #include "materials.h" #include "map.h" +#include "client_assets.h" #include "complexitem.h" #include "settings.h" #include "gui.h" @@ -36,6 +37,7 @@ #include "npc_brush.h" #include "spawn_monster_brush.h" #include "spawn_npc_brush.h" +#include "preferences.h" #include "live_server.h" #include "live_client.h" @@ -52,28 +54,21 @@ Editor::Editor(CopyBuffer ©buffer) : wxArrayString warnings; bool ok = true; - ClientVersionID defaultVersion = ClientVersionID(g_settings.getInteger(Config::DEFAULT_CLIENT_VERSION)); - if (defaultVersion == CLIENT_VERSION_NONE) { - defaultVersion = ClientVersion::getLatestVersion()->getID(); - } + if (g_gui.CloseAllEditors()) { + ok = g_gui.LoadVersion(error, warnings); + g_gui.PopupDialog("Error", error, wxOK); + g_gui.ListDialog("Warnings", warnings); - if (g_gui.GetCurrentVersionID() != defaultVersion) { - if (g_gui.CloseAllEditors()) { - ok = g_gui.LoadVersion(defaultVersion, error, warnings); - g_gui.PopupDialog("Error", error, wxOK); - g_gui.ListDialog("Warnings", warnings); - } else { - throw std::runtime_error("All maps of different versions were not closed."); + auto clientDirectory = ClientAssets::getPath().ToStdString() + "/"; + if (!wxDirExists(wxString(clientDirectory))) { + PreferencesWindow dialog(nullptr); + dialog.getBookCtrl().SetSelection(4); + dialog.ShowModal(); + dialog.Destroy(); } } - if (!ok) { - throw std::runtime_error("Couldn't load client version"); - } - MapVersion version; - version.otbm = g_gui.GetCurrentVersion().getPrefferedMapVersionID(); - version.client = g_gui.GetCurrentVersionID(); map.convert(version); map.height = 2048; @@ -102,49 +97,36 @@ Editor::Editor(CopyBuffer ©buffer, const FileName &fn) : replace_brush(nullptr) { MapVersion ver; if (!IOMapOTBM::getVersionInfo(fn, ver)) { - // g_gui.PopupDialog("Error", "Could not open file \"" + fn.GetFullPath() + "\".", wxOK); + g_gui.PopupDialog("Error", "Could not open file \"" + fn.GetFullPath() + "\".", wxOK); + spdlog::error("Could not open file {}. This is not a valid OTBM file or it does not exist.", nstr(fn.GetFullPath())); throw std::runtime_error("Could not open file \"" + nstr(fn.GetFullPath()) + "\".\nThis is not a valid OTBM file or it does not exist."); } - /* - if(ver < CLIENT_VERSION_760) { - long b = g_gui.PopupDialog("Error", "Unsupported Client Version (pre 7.6), do you want to try to load the map anyways?", wxYES | wxNO); - if(b == wxID_NO) { - valid_state = false; - return; - } - } - */ - bool success = true; - if (g_gui.GetCurrentVersionID() != ver.client) { - wxString error; - wxArrayString warnings; - if (g_gui.CloseAllEditors()) { - success = g_gui.LoadVersion(ver.client, error, warnings); - if (!success) { - g_gui.PopupDialog("Error", error, wxOK); - } else { - g_gui.ListDialog("Warnings", warnings); + wxString error; + wxArrayString warnings; + if (g_gui.CloseAllEditors()) { + success = g_gui.LoadVersion(error, warnings); + if (!success) { + g_gui.PopupDialog("Error", error, wxOK); + auto clientDirectory = ClientAssets::getPath().ToStdString() + "/"; + if (!wxDirExists(wxString(clientDirectory))) { + PreferencesWindow dialog(nullptr); + dialog.getBookCtrl().SetSelection(4); + dialog.ShowModal(); + dialog.Destroy(); } } else { - throw std::runtime_error("All maps of different versions were not closed."); + g_gui.ListDialog("Warnings", warnings); } + } else { + spdlog::error("All maps of different versions were not closed."); + throw std::runtime_error("All maps of different versions were not closed."); } if (success) { ScopedLoadingBar LoadingBar("Loading OTBM map..."); success = map.open(nstr(fn.GetFullPath())); - /* TODO - if(success && ver.client == CLIENT_VERSION_854_BAD) { - int ok = g_gui.PopupDialog("Incorrect OTB", "This map has been saved with an incorrect OTB version, do you want to convert it to the new OTB version?\n\nIf you are not sure, click Yes.", wxYES | wxNO); - - if(ok == wxID_YES){ - ver.client = CLIENT_VERSION_854; - map.convert(ver); - } - } - */ } } @@ -154,9 +136,7 @@ Editor::Editor(CopyBuffer ©buffer, LiveClient* client) : actionQueue(newd NetworkedActionQueue(*this)), selection(*this), copybuffer(copybuffer), - replace_brush(nullptr) { - ; -} + replace_brush(nullptr) { } Editor::~Editor() { if (IsLive()) { @@ -560,22 +540,7 @@ bool Editor::importMap(FileName filename, int import_x_offset, int import_y_offs map.towns.addTown(imported_town); -#ifdef __VISUALC__ // C++0x compliance to some degree :) tit = imported_map.towns.erase(tit); -#else // Bulky, slow way - TownMap::iterator tmp_iter = tit; - ++tmp_iter; - uint32_t next_key = 0; - if (tmp_iter != imported_map.towns.end()) { - next_key = tmp_iter->first; - } - imported_map.towns.erase(tit); - if (next_key != 0) { - tit = imported_map.towns.find(next_key); - } else { - tit = imported_map.towns.end(); - } -#endif } for (HouseMap::iterator hit = imported_map.houses.begin(); hit != imported_map.houses.end();) { @@ -646,22 +611,7 @@ bool Editor::importMap(FileName filename, int import_x_offset, int import_y_offs } map.houses.addHouse(imported_house); -#ifdef __VISUALC__ // C++0x compliance to some degree :) hit = imported_map.houses.erase(hit); -#else // Bulky, slow way - HouseMap::iterator tmp_iter = hit; - ++tmp_iter; - uint32_t next_key = 0; - if (tmp_iter != imported_map.houses.end()) { - next_key = tmp_iter->first; - } - imported_map.houses.erase(hit); - if (next_key != 0) { - hit = imported_map.houses.find(next_key); - } else { - hit = imported_map.houses.end(); - } -#endif } } @@ -996,22 +946,7 @@ void Editor::clearInvalidHouseTiles(bool showdialog) { while (iter != houses.end()) { House* h = iter->second; if (map.towns.getTown(h->townid) == nullptr) { -#ifdef __VISUALC__ // C++0x compliance to some degree :) iter = houses.erase(iter); -#else // Bulky, slow way - HouseMap::iterator tmp_iter = iter; - ++tmp_iter; - uint32_t next_key = 0; - if (tmp_iter != houses.end()) { - next_key = tmp_iter->first; - } - houses.erase(iter); - if (next_key != 0) { - iter = houses.find(next_key); - } else { - iter = houses.end(); - } -#endif } else { ++iter; } diff --git a/source/extension.cpp b/source/extension.cpp deleted file mode 100644 index e87002b1..00000000 --- a/source/extension.cpp +++ /dev/null @@ -1,87 +0,0 @@ -////////////////////////////////////////////////////////////////////// -// This file is part of Remere's Map Editor -////////////////////////////////////////////////////////////////////// -// Remere's Map Editor is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Remere's Map Editor is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -////////////////////////////////////////////////////////////////////// - -#include "main.h" - -#include "extension.h" - -MaterialsExtension::MaterialsExtension(std::string name, std::string author, std::string description) : - name(name), - author(author), - description(description), - for_all_versions(false) { - //// -} - -MaterialsExtension::~MaterialsExtension() { - //// -} - -void MaterialsExtension::addVersion(const std::string &versionString) { - if (versionString == "all") { - for_all_versions = true; - } else { - ClientVersion* client = ClientVersion::get(versionString); - if (client) { - ClientVersionList supported_versions = ClientVersion::getAllVersionsSupportedForClientVersion(client); - version_list.insert(version_list.end(), supported_versions.begin(), supported_versions.end()); - } - } -} - -bool MaterialsExtension::isForVersion(uint16_t versionId) { - if (for_all_versions) { - return true; - } - - for (ClientVersion* version : version_list) { - if (version->getID() == versionId) { - return true; - } - } - return false; -} - -std::string MaterialsExtension::getVersionString() { - if (for_all_versions) { - return "All"; - } - - std::string versions; - std::string last; - for (ClientVersion* version : version_list) { - if (!last.empty()) { - if (!versions.empty()) { - versions += ", " + last; - } else { - versions = last; - } - } - last = version->getName(); - } - - if (!last.empty()) { - if (!versions.empty()) { - versions += " and " + last; - } else { - versions = last; - } - } else { - return "None"; - } - return versions; -} diff --git a/source/extension.h b/source/extension.h deleted file mode 100644 index 38a9ad0d..00000000 --- a/source/extension.h +++ /dev/null @@ -1,48 +0,0 @@ -////////////////////////////////////////////////////////////////////// -// This file is part of Remere's Map Editor -////////////////////////////////////////////////////////////////////// -// Remere's Map Editor is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Remere's Map Editor is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -////////////////////////////////////////////////////////////////////// - -#ifndef RME_EXTENSION_H_ -#define RME_EXTENSION_H_ - -#include "tileset.h" -#include "client_version.h" - -class MaterialsExtension { -public: - MaterialsExtension(std::string name, std::string author, std::string description); - ~MaterialsExtension(); - - void addVersion(const std::string &versionString); - bool isForVersion(uint16_t versionId); - std::string getVersionString(); - - std::string name; - std::string url; - std::string author; - std::string author_url; - std::string description; - bool for_all_versions; - ClientVersionList version_list; - -private: - MaterialsExtension(const MaterialsExtension &); - MaterialsExtension &operator=(const MaterialsExtension &); -}; - -typedef std::vector MaterialsExtensionList; - -#endif diff --git a/source/extension_window.cpp b/source/extension_window.cpp deleted file mode 100644 index 1a7ace6c..00000000 --- a/source/extension_window.cpp +++ /dev/null @@ -1,135 +0,0 @@ -////////////////////////////////////////////////////////////////////// -// This file is part of Remere's Map Editor -////////////////////////////////////////////////////////////////////// -// Remere's Map Editor is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Remere's Map Editor is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -////////////////////////////////////////////////////////////////////// - -#include "main.h" - -#include "extension_window.h" - -#include "gui.h" -#include "materials.h" - -extern Materials g_materials; - -BEGIN_EVENT_TABLE(ExtensionsDialog, wxDialog) -EVT_HTML_LINK_CLICKED(wxID_ANY, ExtensionsDialog::OnClickLink) -EVT_BUTTON(wxID_OK, ExtensionsDialog::OnClickOK) -EVT_BUTTON(EXTENSIONS_OPEN_FOLDER_BUTTON, ExtensionsDialog::OnClickOpenFolder) -END_EVENT_TABLE() - -ExtensionsDialog::ExtensionsDialog(wxWindow* parent) : - wxDialog(parent, wxID_ANY, "Extensions", wxDefaultPosition, wxSize(600, 500), wxRESIZE_BORDER | wxCAPTION) { - wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); - - wxHtmlWindow* htmlWindow = new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition, wxSize(550, 400)); - htmlWindow->SetPage(HTML()); - topSizer->Add(htmlWindow, wxSizerFlags(1).DoubleBorder().Expand()); - - wxSizer* buttonSizer = newd wxBoxSizer(wxHORIZONTAL); - buttonSizer->Add(newd wxButton(this, wxID_OK, "OK"), wxSizerFlags(1).Center()); - buttonSizer->Add(newd wxButton(this, EXTENSIONS_OPEN_FOLDER_BUTTON, "Open Extensions Folder"), wxSizerFlags(1).Center()); - topSizer->Add(buttonSizer, 0, wxCENTER | wxLEFT | wxRIGHT | wxBOTTOM, 20); - - SetSizerAndFit(topSizer); - Centre(wxBOTH); -} - -ExtensionsDialog::~ExtensionsDialog() { - //// -} - -void ExtensionsDialog::OnClickLink(wxHtmlLinkEvent &evt) { - ::wxLaunchDefaultBrowser(evt.GetLinkInfo().GetHref(), wxBROWSER_NEW_WINDOW); -} - -void ExtensionsDialog::OnClickOK(wxCommandEvent &evt) { - EndModal(0); -} - -void ExtensionsDialog::OnClickOpenFolder(wxCommandEvent &evt) { - wxString cmd, extensionsDir = g_gui.GetExtensionsDirectory(); -#if defined __WINDOWS__ - cmd << "explorer"; -#elif defined __APPLE__ - cmd << "open"; - extensionsDir.Replace(" ", "\\ "); // Escape spaces -#elif defined __linux - cmd << "xdg-open"; -#else - #error "NOT IMPLEMENTED" -#endif - - cmd << " " << extensionsDir; - - wxExecute(cmd); -} - -wxString ExtensionsDialog::HTML() const { - wxString markup; - for (MaterialsExtensionList::const_iterator me = g_materials.getExtensions().begin(); me != g_materials.getExtensions().end(); ++me) { - markup << HTMLForExtension(*me); - markup << "
"; - } - return markup; -} - -wxString ExtensionsDialog::HTMLForExtension(MaterialsExtension* me) const { - wxString markup; - markup - << "" - - << "< background='#ff0000'>" - << "" - << "" - << "" - - << "" - << "" - << "" - << "" - - << "" - << "" - << "" - << "" - - << "" - << "" - << "" - << "" - - << "
Extension"; - - if (me->url.empty()) { - markup << me->name; - } else { - markup << "" << me->name << ""; - } - - markup - << "
Author"; - - if (me->author_url.empty()) { - markup << me->author; - } else { - markup << "" << me->author << ""; - } - - markup - << "
Description" << me->description << "
Clients" << me->getVersionString() << "
"; - - return markup; -} diff --git a/source/extension_window.h b/source/extension_window.h deleted file mode 100644 index 58b597c0..00000000 --- a/source/extension_window.h +++ /dev/null @@ -1,39 +0,0 @@ -////////////////////////////////////////////////////////////////////// -// This file is part of Remere's Map Editor -////////////////////////////////////////////////////////////////////// -// Remere's Map Editor is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Remere's Map Editor is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -////////////////////////////////////////////////////////////////////// - -#ifndef RME_EXTENSION_WINDOW_H -#define RME_EXTENSION_WINDOW_H - -class MaterialsExtension; - -class ExtensionsDialog : public wxDialog { -public: - ExtensionsDialog(wxWindow* parent); - virtual ~ExtensionsDialog(); - - void OnClickOK(wxCommandEvent &evt); - void OnClickOpenFolder(wxCommandEvent &evt); - void OnClickLink(wxHtmlLinkEvent &evt); - - DECLARE_EVENT_TABLE(); - -private: - wxString HTML() const; - wxString HTMLForExtension(MaterialsExtension* me) const; -}; - -#endif diff --git a/source/find_item_window.cpp b/source/find_item_window.cpp index 9faa3b9b..82fde686 100644 --- a/source/find_item_window.cpp +++ b/source/find_item_window.cpp @@ -49,19 +49,13 @@ FindItemDialog::FindItemDialog(wxWindow* parent, const wxString &title, bool onl int radio_boxNChoices = sizeof(radio_boxChoices) / sizeof(wxString); options_radio_box = newd wxRadioBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, radio_boxNChoices, radio_boxChoices, 1, wxRA_SPECIFY_COLS); - options_radio_box->SetSelection(SearchMode::ServerIDs); + options_radio_box->SetSelection(SearchMode::ItemIDs); options_box_sizer->Add(options_radio_box, 0, wxALL | wxEXPAND, 5); - wxStaticBoxSizer* server_id_box_sizer = newd wxStaticBoxSizer(newd wxStaticBox(this, wxID_ANY, "Server ID"), wxVERTICAL); - server_id_spin = newd wxSpinCtrl(server_id_box_sizer->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, g_items.getMinID(), g_items.getMaxID(), g_items.getMinID()); - server_id_box_sizer->Add(server_id_spin, 0, wxALL | wxEXPAND, 5); - options_box_sizer->Add(server_id_box_sizer, 1, wxALL | wxEXPAND, 5); - - wxStaticBoxSizer* client_id_box_sizer = newd wxStaticBoxSizer(newd wxStaticBox(this, wxID_ANY, "Client ID"), wxVERTICAL); - client_id_spin = newd wxSpinCtrl(client_id_box_sizer->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, g_gui.gfx.getItemSpriteMinID(), g_gui.gfx.getItemSpriteMaxID(), g_gui.gfx.getItemSpriteMinID()); - client_id_spin->Enable(false); - client_id_box_sizer->Add(client_id_spin, 0, wxALL | wxEXPAND, 5); - options_box_sizer->Add(client_id_box_sizer, 1, wxALL | wxEXPAND, 5); + wxStaticBoxSizer* item_id_box_sizer = newd wxStaticBoxSizer(newd wxStaticBox(this, wxID_ANY, "Item ID"), wxVERTICAL); + item_id_spin = newd wxSpinCtrl(item_id_box_sizer->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 100, g_items.getMaxID(), 100); + item_id_box_sizer->Add(item_id_spin, 0, wxALL | wxEXPAND, 5); + options_box_sizer->Add(item_id_box_sizer, 1, wxALL | wxEXPAND, 5); wxStaticBoxSizer* name_box_sizer = newd wxStaticBoxSizer(newd wxStaticBox(this, wxID_ANY, "Name"), wxVERTICAL); name_text_input = newd wxTextCtrl(name_box_sizer->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); @@ -173,10 +167,8 @@ FindItemDialog::FindItemDialog(wxWindow* parent, const wxString &title, bool onl // Connect Events options_radio_box->Connect(wxEVT_COMMAND_RADIOBOX_SELECTED, wxCommandEventHandler(FindItemDialog::OnOptionChange), NULL, this); - server_id_spin->Connect(wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler(FindItemDialog::OnServerIdChange), NULL, this); - server_id_spin->Connect(wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler(FindItemDialog::OnServerIdChange), NULL, this); - client_id_spin->Connect(wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler(FindItemDialog::OnClientIdChange), NULL, this); - client_id_spin->Connect(wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler(FindItemDialog::OnClientIdChange), NULL, this); + item_id_spin->Connect(wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler(FindItemDialog::OnItemIdChange), NULL, this); + item_id_spin->Connect(wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler(FindItemDialog::OnItemIdChange), NULL, this); name_text_input->Connect(wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler(FindItemDialog::OnText), NULL, this); types_radio_box->Connect(wxEVT_COMMAND_RADIOBOX_SELECTED, wxCommandEventHandler(FindItemDialog::OnTypeChange), NULL, this); @@ -201,10 +193,8 @@ FindItemDialog::FindItemDialog(wxWindow* parent, const wxString &title, bool onl FindItemDialog::~FindItemDialog() { // Disconnect Events options_radio_box->Disconnect(wxEVT_COMMAND_RADIOBOX_SELECTED, wxCommandEventHandler(FindItemDialog::OnOptionChange), NULL, this); - server_id_spin->Disconnect(wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler(FindItemDialog::OnServerIdChange), NULL, this); - server_id_spin->Disconnect(wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler(FindItemDialog::OnServerIdChange), NULL, this); - client_id_spin->Disconnect(wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler(FindItemDialog::OnClientIdChange), NULL, this); - client_id_spin->Disconnect(wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler(FindItemDialog::OnClientIdChange), NULL, this); + item_id_spin->Disconnect(wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler(FindItemDialog::OnItemIdChange), NULL, this); + item_id_spin->Disconnect(wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler(FindItemDialog::OnItemIdChange), NULL, this); name_text_input->Disconnect(wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler(FindItemDialog::OnText), NULL, this); types_radio_box->Disconnect(wxEVT_COMMAND_RADIOBOX_SELECTED, wxCommandEventHandler(FindItemDialog::OnTypeChange), NULL, this); @@ -235,19 +225,15 @@ void FindItemDialog::setSearchMode(FindItemDialog::SearchMode mode) { options_radio_box->SetSelection(mode); } - server_id_spin->Enable(mode == SearchMode::ServerIDs); - client_id_spin->Enable(mode == SearchMode::ClientIDs); + item_id_spin->Enable(mode == SearchMode::ItemIDs); name_text_input->Enable(mode == SearchMode::Names); types_radio_box->Enable(mode == SearchMode::Types); EnableProperties(mode == SearchMode::Properties); RefreshContentsInternal(); - if (mode == SearchMode::ServerIDs) { - server_id_spin->SetFocus(); - server_id_spin->SetSelection(-1, -1); - } else if (mode == SearchMode::ClientIDs) { - client_id_spin->SetFocus(); - client_id_spin->SetSelection(-1, -1); + if (mode == SearchMode::ItemIDs) { + item_id_spin->SetFocus(); + item_id_spin->SetSelection(-1, -1); } else if (mode == SearchMode::Names) { name_text_input->SetFocus(); } @@ -278,31 +264,11 @@ void FindItemDialog::RefreshContentsInternal() { SearchMode selection = (SearchMode)options_radio_box->GetSelection(); bool found_search_results = false; - if (selection == SearchMode::ServerIDs) { - uint16_t serverID = (uint16_t)server_id_spin->GetValue(); + if (selection == SearchMode::ItemIDs) { + uint16_t itemID = (uint16_t)item_id_spin->GetValue(); for (int id = g_items.getMinID(); id <= g_items.getMaxID(); ++id) { const ItemType &item = g_items.getItemType(id); - if (item.id != serverID) { - continue; - } - - RAWBrush* raw_brush = item.raw_brush; - if (!raw_brush) { - continue; - } - - if (only_pickupables && !item.pickupable) { - continue; - } - - found_search_results = true; - items_list->AddBrush(raw_brush); - } - } else if (selection == SearchMode::ClientIDs) { - uint16_t clientID = static_cast(client_id_spin->GetValue()); - for (int id = g_items.getMinID(); id <= g_items.getMaxID(); ++id) { - const ItemType &item = g_items.getItemType(id); - if (item.id == 0 || item.clientID != clientID) { + if (item.id == 0 || item.id != itemID) { continue; } @@ -407,11 +373,7 @@ void FindItemDialog::OnOptionChange(wxCommandEvent &WXUNUSED(event)) { setSearchMode((SearchMode)options_radio_box->GetSelection()); } -void FindItemDialog::OnServerIdChange(wxCommandEvent &WXUNUSED(event)) { - RefreshContentsInternal(); -} - -void FindItemDialog::OnClientIdChange(wxCommandEvent &WXUNUSED(event)) { +void FindItemDialog::OnItemIdChange(wxCommandEvent &WXUNUSED(event)) { RefreshContentsInternal(); } diff --git a/source/find_item_window.h b/source/find_item_window.h index ca378ece..68afccc9 100644 --- a/source/find_item_window.h +++ b/source/find_item_window.h @@ -32,8 +32,7 @@ class FindDialogListBox; class FindItemDialog : public wxDialog { public: enum SearchMode { - ServerIDs = 0, - ClientIDs, + ItemIDs = 0, Names, Types, Properties, @@ -69,8 +68,7 @@ class FindItemDialog : public wxDialog { void RefreshContentsInternal(); void OnOptionChange(wxCommandEvent &event); - void OnServerIdChange(wxCommandEvent &event); - void OnClientIdChange(wxCommandEvent &event); + void OnItemIdChange(wxCommandEvent &event); void OnText(wxCommandEvent &event); void OnTypeChange(wxCommandEvent &event); void OnPropertyChange(wxCommandEvent &event); @@ -82,8 +80,7 @@ class FindItemDialog : public wxDialog { wxRadioBox* types_radio_box; - wxSpinCtrl* server_id_spin; - wxSpinCtrl* client_id_spin; + wxSpinCtrl* item_id_spin; wxTextCtrl* name_text_input; wxTimer input_timer; wxCheckBox* unpassable; diff --git a/source/graphics.cpp b/source/graphics.cpp index f02cfd67..ff270447 100644 --- a/source/graphics.cpp +++ b/source/graphics.cpp @@ -17,16 +17,23 @@ #include "main.h" -#include "sprites.h" #include "graphics.h" + +#include "client_assets.h" #include "artprovider.h" #include "filehandle.h" #include "settings.h" #include "gui.h" #include "otml.h" -#include +#include "sprite_appearances.h" +#include "sprites.h" #include "pngfiles.h" +#include + +GraphicManager g_graphics; +GameSprite g_gameSprite; + // All 133 template colors static uint32_t TemplateOutfitLookupTable[] = { 0xFFFFFF, @@ -165,12 +172,8 @@ static uint32_t TemplateOutfitLookupTable[] = { }; GraphicManager::GraphicManager() : - client_version(nullptr), - unloaded(true), - dat_format(DAT_FORMAT_UNKNOWN), otfi_found(false), is_extended(false), - has_transparency(false), has_frame_durations(false), has_frame_groups(false), loaded_textures(0), @@ -182,24 +185,16 @@ GraphicManager::GraphicManager() : GraphicManager::~GraphicManager() { for (SpriteMap::iterator iter = sprite_space.begin(); iter != sprite_space.end(); ++iter) { delete iter->second; + iter->second = nullptr; } for (ImageMap::iterator iter = image_space.begin(); iter != image_space.end(); ++iter) { delete iter->second; + iter->second = nullptr; } - sprite_space.clear(); - image_space.clear(); - delete animation_timer; -} - -bool GraphicManager::hasTransparency() const { - return has_transparency; -} - -bool GraphicManager::isUnloaded() const { - return unloaded; + animation_timer = nullptr; } GLuint GraphicManager::getFreeTextureID() { @@ -212,6 +207,7 @@ void GraphicManager::clear() { for (SpriteMap::iterator iter = sprite_space.begin(); iter != sprite_space.end(); ++iter) { if (iter->first >= 0) { // Don't clean internal sprites delete iter->second; + iter->second = nullptr; } else { new_sprite_space.insert(std::make_pair(iter->first, iter->second)); } @@ -219,6 +215,7 @@ void GraphicManager::clear() { for (ImageMap::iterator iter = image_space.begin(); iter != image_space.end(); ++iter) { delete iter->second; + iter->second = nullptr; } sprite_space.swap(new_sprite_space); @@ -229,9 +226,6 @@ void GraphicManager::clear() { creature_count = 0; loaded_textures = 0; lastclean = time(nullptr); - spritefile = ""; - - unloaded = true; } void GraphicManager::cleanSoftwareSprites() { @@ -255,13 +249,21 @@ GameSprite* GraphicManager::getCreatureSprite(int id) { return nullptr; } - SpriteMap::iterator it = sprite_space.find(id + item_count); + SpriteMap::iterator it = sprite_space.find(id + getItemSpriteMaxID()); if (it != sprite_space.end()) { return static_cast(it->second); } return nullptr; } +uint16_t GraphicManager::getItemSpriteMaxID() const { + return item_count; +} + +uint16_t GraphicManager::getCreatureSpriteMaxID() const { + return creature_count; +} + GameSprite* GraphicManager::getEditorSprite(int id) { if (id >= 0) { return nullptr; @@ -416,6 +418,7 @@ bool GraphicManager::loadEditorSprites() { return true; } +#if CLIENT_VERSION < 1100 bool GraphicManager::loadOTFI(const FileName &filename, wxString &error, wxArrayString &warnings) { wxDir dir(filename.GetFullPath()); wxString otfi_file; @@ -719,13 +722,13 @@ bool GraphicManager::loadSpriteData(const FileName &datafile, wxString &error, w return false; } -#define safe_get(func, ...) \ - do { \ - if (!fh.get##func(__VA_ARGS__)) { \ - error = wxstr(fh.getErrorMessage()); \ - return false; \ - } \ - } while (false) + #define safe_get(func, ...) \ + do { \ + if (!fh.get##func(__VA_ARGS__)) { \ + error = wxstr(fh.getErrorMessage()); \ + return false; \ + } \ + } while (false) uint32_t sprSignature; safe_get(U32, sprSignature); @@ -772,8 +775,8 @@ bool GraphicManager::loadSpriteData(const FileName &datafile, wxString &error, w } else { spr->id = id; spr->size = size; - spr->dump = newd uint8_t[size]; - if (!fh.getRAW(spr->dump, size)) { + spr->m_cachedData = newd uint8_t[size]; + if (!fh.getRAW(spr->m_cachedData, size)) { error = wxstr(fh.getErrorMessage()); return false; } @@ -783,7 +786,7 @@ bool GraphicManager::loadSpriteData(const FileName &datafile, wxString &error, w fh.skip(size); } } -#undef safe_get + #undef safe_get unloaded = false; return true; } @@ -826,6 +829,128 @@ bool GraphicManager::loadSpriteDump(uint8_t*&target, uint16_t &size, int sprite_ } return false; } +#endif + +bool GraphicManager::loadItemSpriteMetadata(ItemType* t, wxString &error, wxArrayString &warnings) { + GameSprite* sType = newd GameSprite(); + sType->id = t->id; + sprite_space[t->id] = sType; + item_count = std::max(item_count, t->id); + + // Number of blendframes (some sprites consist of several merged sprites + sType->layers = t->layers; + sType->pattern_x = t->pattern_width; + sType->pattern_y = t->pattern_height; + sType->pattern_z = t->pattern_depth; + + // Length of animation + sType->sprite_phase_size = t->m_animationPhases.size(); + has_frame_durations = t->m_animationPhases.size() > 0; + + if (sType->sprite_phase_size > 0) { + sType->animator = newd Animator(sType->sprite_phase_size, t->start_frame, t->loop_count, t->async_animation); + if (has_frame_durations) { + int frameIndex = 0; + for (const auto phase : t->m_animationPhases) { + FrameDuration* frame_duration = sType->animator->getFrameDuration(frameIndex); + frame_duration->setValues(phase.first, phase.second); + frameIndex++; + } + sType->animator->reset(); + } + } + + sType->numsprites = (int)sType->layers * (int)sType->pattern_x * (int)sType->pattern_y * sType->pattern_z * std::max(1, sType->sprite_phase_size); + + // Read the sprite ids + for (uint32_t i = 0; i < sType->numsprites; ++i) { + uint32_t sprite_id = t->m_sprites[i]; + + if (image_space[sprite_id] == nullptr) { + GameSprite::NormalImage* img = newd GameSprite::NormalImage(); + img->id = sprite_id; + image_space[sprite_id] = img; + } + sType->spriteList.push_back(static_cast(image_space[sprite_id])); + } + return true; +} + +bool GraphicManager::loadOutfitSpriteMetadata(canary::protobuf::appearances::Appearance outfit, wxString &error, wxArrayString &warnings) { + GameSprite* sType = newd GameSprite(); + sType->id = outfit.id() + getItemSpriteMaxID(); + sprite_space[outfit.id() + getItemSpriteMaxID()] = sType; + creature_count = std::max(creature_count, outfit.id()); + + // We dont need to worry about IDLE or MOVING frame group + const auto &frameGroup = outfit.frame_group().Get(0); + const auto &spriteInfo = frameGroup.sprite_info(); + const auto &animation = spriteInfo.animation(); + + // Number of blendframes (some sprites consist of several merged sprites + sType->layers = spriteInfo.layers(); + sType->pattern_x = spriteInfo.pattern_width(); + sType->pattern_y = spriteInfo.pattern_height(); + sType->pattern_z = spriteInfo.pattern_depth(); + + // Length of animation + sType->sprite_phase_size = animation.sprite_phase().size(); + has_frame_durations = animation.sprite_phase().size() > 0; + + if (sType->sprite_phase_size > 0) { + sType->animator = newd Animator(sType->sprite_phase_size, animation.default_start_phase(), animation.loop_count(), !animation.synchronized()); + if (has_frame_durations) { + int frameIndex = 0; + for (const auto &phase : animation.sprite_phase()) { + FrameDuration* frame_duration = sType->animator->getFrameDuration(frameIndex); + frame_duration->setValues(phase.duration_min(), phase.duration_max()); + frameIndex++; + } + sType->animator->reset(); + } + } + + sType->numsprites = (int)sType->layers * (int)sType->pattern_x * (int)sType->pattern_y * sType->pattern_z * std::max(1, sType->sprite_phase_size); + + sType->minimap_color = outfit.flags().has_automap() ? static_cast(outfit.flags().automap().color()) : 0; + sType->draw_height = outfit.flags().has_height() ? static_cast(outfit.flags().height().elevation()) : 0; + if (outfit.flags().has_shift()) { + sType->drawoffset_x = static_cast(outfit.flags().shift().x()); + sType->drawoffset_y = static_cast(outfit.flags().shift().y()); + sType->isDrawOffsetLoaded = true; + } + + // Read the sprite ids + for (uint32_t i = 0; i < sType->numsprites; ++i) { + uint32_t sprite_id = spriteInfo.sprite_id().Get(i); + + if (image_space[sprite_id] == nullptr) { + GameSprite::NormalImage* img = newd GameSprite::NormalImage(); + img->id = sprite_id; + image_space[sprite_id] = img; + } + sType->spriteList.push_back(static_cast(image_space[sprite_id])); + } + return true; +} + +bool GraphicManager::loadSpriteDump(uint8_t*&target, uint16_t &size, int sprite_id) { + // Empty GameSprite + if (sprite_id == 0) { + size = 0; + target = nullptr; + return true; + } + + const auto &spritePtr = g_spriteAppearances.getSprite(sprite_id); + if (!spritePtr) { + return false; + } + + size = spritePtr->pixels.size(); + target = spritePtr->pixels.data(); + return true; +} void GraphicManager::addSpriteToCleanup(GameSprite* spr) { cleanup_list.push_back(spr); @@ -847,14 +972,6 @@ void GraphicManager::garbageCollection() { iit->second->clean(t); ++iit; } - SpriteMap::iterator sit = sprite_space.begin(); - while (sit != sprite_space.end()) { - GameSprite* gs = dynamic_cast(sit->second); - if (gs) { - gs->clean(t); - } - ++sit; - } lastclean = t; } } @@ -891,50 +1008,63 @@ GameSprite::GameSprite() : pattern_x(0), pattern_y(0), pattern_z(0), - frames(0), + sprite_phase_size(0), numsprites(0), animator(nullptr), - ground_speed(0), draw_height(0), + drawoffset_x(0), + drawoffset_y(0), minimap_color(0) { - dc[SPRITE_SIZE_16x16] = nullptr; - dc[SPRITE_SIZE_32x32] = nullptr; + m_wxMemoryDc[SPRITE_SIZE_16x16] = nullptr; + m_wxMemoryDc[SPRITE_SIZE_32x32] = nullptr; } GameSprite::~GameSprite() { unloadDC(); - for (std::list::iterator iter = instanced_templates.begin(); iter != instanced_templates.end(); ++iter) { - delete *iter; - } - delete animator; + animator = nullptr; +} + +void GameSprite::unloadDC() { + delete m_wxMemoryDc[SPRITE_SIZE_16x16]; + delete m_wxMemoryDc[SPRITE_SIZE_32x32]; + m_wxMemoryDc[SPRITE_SIZE_16x16] = nullptr; + m_wxMemoryDc[SPRITE_SIZE_32x32] = nullptr; } -void GameSprite::clean(int time) { - for (std::list::iterator iter = instanced_templates.begin(); - iter != instanced_templates.end(); - ++iter) { - (*iter)->clean(time); +uint16_t GameSprite::getDrawHeight() const { + return draw_height; +} + +wxPoint GameSprite::getDrawOffset() { + if (!isDrawOffsetLoaded && spriteList.size() > 0) { + const auto &sheet = g_spriteAppearances.getSheetBySpriteId(spriteList[0]->getHardwareID()); + if (!sheet) { + return wxPoint(0, 0); + } + + drawoffset_x += sheet->getSpriteSize().width - 32; + drawoffset_y += sheet->getSpriteSize().height - 32; + isDrawOffsetLoaded = true; } + + return wxPoint(drawoffset_x, drawoffset_y); } -void GameSprite::unloadDC() { - delete dc[SPRITE_SIZE_16x16]; - delete dc[SPRITE_SIZE_32x32]; - dc[SPRITE_SIZE_16x16] = nullptr; - dc[SPRITE_SIZE_32x32] = nullptr; +uint8_t GameSprite::getMiniMapColor() const { + return minimap_color; } int GameSprite::getIndex(int width, int height, int layer, int pattern_x, int pattern_y, int pattern_z, int frame) const { - return ((((((frame % this->frames) * this->pattern_z + pattern_z) * this->pattern_y + pattern_y) * this->pattern_x + pattern_x) * this->layers + layer) * this->height + height) * this->width + width; + return ((((frame % this->sprite_phase_size) * this->pattern_z + pattern_z) * this->pattern_y + pattern_y) * this->pattern_x + pattern_x) * this->layers + layer; } -GLuint GameSprite::getHardwareID(int _x, int _y, int _layer, int _count, int _pattern_x, int _pattern_y, int _pattern_z, int _frame) { +GLuint GameSprite::getHardwareID(int _layer, int _count, int _pattern_x, int _pattern_y, int _pattern_z, int _frame) { uint32_t v; - if (_count >= 0 && height <= 1 && width <= 1) { + if (_count >= 0) { v = _count; } else { - v = ((((((_frame)*pattern_y + _pattern_y) * pattern_x + _pattern_x) * layers + _layer) * height + _y) * width + _x); + v = (((_frame)*pattern_y + _pattern_y) * pattern_x + _pattern_x) * layers + _layer; } if (v >= numsprites) { if (numsprites == 1) { @@ -946,100 +1076,100 @@ GLuint GameSprite::getHardwareID(int _x, int _y, int _layer, int _count, int _pa return spriteList[v]->getHardwareID(); } -GameSprite::TemplateImage* GameSprite::getTemplateImage(int sprite_index, const Outfit &outfit) { +std::shared_ptr GameSprite::getOutfitImage(int spriteId, Direction direction, const Outfit &outfit) { + uint32_t spriteIndex = direction * layers; + if (layers > 1 && spriteIndex >= numsprites) { + if (numsprites == 1) { + spriteIndex = 0; + } else { + spriteIndex %= numsprites; + } + } + if (instanced_templates.empty()) { - TemplateImage* img = newd TemplateImage(this, sprite_index, outfit); + auto img = std::make_shared(this, spriteIndex, spriteId, outfit); + if (!img) { + return nullptr; + } instanced_templates.push_back(img); return img; } // While this is linear lookup, it is very rare for the list to contain more than 4-8 entries, so it's faster than a hashmap anyways. - for (std::list::iterator iter = instanced_templates.begin(); iter != instanced_templates.end(); ++iter) { - TemplateImage* img = *iter; - if (img->sprite_index == sprite_index) { - uint32_t lookHash = img->lookHead << 24 | img->lookBody << 16 | img->lookLegs << 8 | img->lookFeet; + for (auto iter = instanced_templates.begin(); iter != instanced_templates.end(); ++iter) { + auto img = *iter; + if (img->m_spriteId == spriteId) { + uint32_t lookHash = img->m_lookHead << 24 | img->m_lookBody << 16 | img->m_lookLegs << 8 | img->m_lookFeet; if (outfit.getColorHash() == lookHash) { return img; } } } - TemplateImage* img = newd TemplateImage(this, sprite_index, outfit); + + auto img = std::make_shared(this, spriteIndex, spriteId, outfit); instanced_templates.push_back(img); return img; } -GLuint GameSprite::getHardwareID(int _x, int _y, int _dir, int _addon, int _pattern_z, const Outfit &_outfit, int _frame) { - uint32_t v = getIndex(_x, _y, 0, _dir, _addon, _pattern_z, _frame); - if (v >= numsprites) { - if (numsprites == 1) { - v = 0; - } else { - v %= numsprites; - } - } - if (layers > 1) { // Template - TemplateImage* img = getTemplateImage(v, _outfit); - return img->getHardwareID(); - } - return spriteList[v]->getHardwareID(); -} - -wxMemoryDC* GameSprite::getDC(SpriteSize size) { - ASSERT(size == SPRITE_SIZE_16x16 || size == SPRITE_SIZE_32x32); - - if (!dc[size]) { - ASSERT(width >= 1 && height >= 1); - +wxMemoryDC* GameSprite::getDC(SpriteSize spriteSize) { + if (!m_wxMemoryDc[spriteSize]) { const int bgshade = g_settings.getInteger(Config::ICON_BACKGROUND); + wxImage background(getWidth(), getHeight()); - int image_size = std::max(width, height) * rme::SpritePixels; - wxImage image(image_size, image_size); - image.Clear(bgshade); - - for (uint8_t l = 0; l < layers; l++) { - for (uint8_t w = 0; w < width; w++) { - for (uint8_t h = 0; h < height; h++) { - const int i = getIndex(w, h, l, 0, 0, 0, 0); - uint8_t* data = spriteList[i]->getRGBData(); - if (data) { - wxImage img(rme::SpritePixels, rme::SpritePixels, data); - img.SetMaskColour(0xFF, 0x00, 0xFF); - image.Paste(img, (width - w - 1) * rme::SpritePixels, (height - h - 1) * rme::SpritePixels); - img.Destroy(); - } - } - } - } + auto backgroundBmp = wxBitmap(background); + m_wxMemoryDc[spriteSize] = new wxMemoryDC(backgroundBmp); + + m_wxMemoryDc[spriteSize]->SelectObject(wxNullBitmap); + auto spriteId = spriteList[0]->getHardwareID(); + wxImage wxImage = g_spriteAppearances.getWxImageBySpriteId(spriteId); - // Now comes the resizing / antialiasing - if (size == SPRITE_SIZE_16x16 || image.GetWidth() > rme::SpritePixels || image.GetHeight() > rme::SpritePixels) { - int new_size = SPRITE_SIZE_16x16 ? 16 : 32; - image.Rescale(new_size, new_size); + // Resize image to 32x32 + if (getWidth() > rme::SpritePixels || getHeight() > rme::SpritePixels) { + wxImage.Rescale(rme::SpritePixels, rme::SpritePixels); } - wxBitmap bmp(image); - dc[size] = newd wxMemoryDC(bmp); + auto bitMap = wxBitmap(wxImage); + m_wxMemoryDc[spriteSize]->SelectObject(bitMap); g_gui.gfx.addSpriteToCleanup(this); - image.Destroy(); } - return dc[size]; + + return m_wxMemoryDc[spriteSize]; } -void GameSprite::DrawTo(wxDC* dc, SpriteSize sz, int start_x, int start_y, int width, int height) { - if (width == -1) { - width = sz == SPRITE_SIZE_32x32 ? 32 : 16; - } - if (height == -1) { - height = sz == SPRITE_SIZE_32x32 ? 32 : 16; +void GameSprite::DrawTo(wxDC* dcWindow, SpriteSize spriteSize, int start_x, int start_y, int width, int height) { + if (width == -1 || height == -1) { + if (spriteList.size() == 0) { + return; + } + const auto &sheet = g_spriteAppearances.getSheetBySpriteId(spriteList[0]->getHardwareID()); + if (!sheet) { + return; + } + + width = sheet->getSpriteSize().width; + height = sheet->getSpriteSize().height; } - wxDC* sdc = getDC(sz); + + wxMemoryDC* sdc = getDC(spriteSize); if (sdc) { - dc->Blit(start_x, start_y, width, height, sdc, 0, 0, wxCOPY, true); + dcWindow->Blit(start_x, start_y, width, height, sdc, 0, 0, wxCOPY, true); } else { - const wxBrush &b = dc->GetBrush(); - dc->SetBrush(*wxRED_BRUSH); - dc->DrawRectangle(start_x, start_y, width, height); - dc->SetBrush(b); + const wxBrush &b = dcWindow->GetBrush(); + dcWindow->SetBrush(*wxRED_BRUSH); + dcWindow->DrawRectangle(start_x, start_y, width, height); + dcWindow->SetBrush(b); + } +} + +uint8_t* GameSprite::invertGLColors(int spriteHeight, int spriteWidth, uint8_t* rgba) { + uint8_t* rgba_inverted = new uint8_t[spriteWidth * spriteHeight * 4]; + for (int i = 0; i < spriteWidth * spriteHeight; i++) { + rgba_inverted[i * 4 + 0] = rgba[i * 4 + 2]; // R -> B + rgba_inverted[i * 4 + 1] = rgba[i * 4 + 1]; // G + rgba_inverted[i * 4 + 2] = rgba[i * 4 + 0]; // B -> R + rgba_inverted[i * 4 + 3] = rgba[i * 4 + 3]; // A } + + return rgba_inverted; } GameSprite::Image::Image() : @@ -1052,7 +1182,7 @@ GameSprite::Image::~Image() { unloadGLTexture(0); } -void GameSprite::Image::createGLTexture(GLuint textureId) { +void GameSprite::Image::createGLTexture(GLuint whatid) { ASSERT(!isGLLoaded); uint8_t* rgba = getRGBAData(); @@ -1060,23 +1190,30 @@ void GameSprite::Image::createGLTexture(GLuint textureId) { return; } + const auto &sheet = g_spriteAppearances.getSheetBySpriteId(whatid); + if (!sheet) { + return; + } + + auto spriteWidth = sheet->getSpriteSize().width; + auto spriteHeight = sheet->getSpriteSize().height; + auto invertedBuffer = invertGLColors(spriteHeight, spriteWidth, rgba); + isGLLoaded = true; g_gui.gfx.loaded_textures += 1; - glBindTexture(GL_TEXTURE_2D, textureId); + glBindTexture(GL_TEXTURE_2D, whatid); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // Linear Filtering glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // Linear Filtering glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, 0x812F); // GL_CLAMP_TO_EDGE glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, 0x812F); // GL_CLAMP_TO_EDGE - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, rme::SpritePixels, rme::SpritePixels, 0, GL_RGBA, GL_UNSIGNED_BYTE, rgba); - - delete[] rgba; + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, spriteWidth, spriteHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, invertedBuffer); } -void GameSprite::Image::unloadGLTexture(GLuint textureId) { +void GameSprite::Image::unloadGLTexture(GLuint whatid) { isGLLoaded = false; g_gui.gfx.loaded_textures -= 1; - glDeleteTextures(1, &textureId); + glDeleteTextures(1, &whatid); } void GameSprite::Image::visit() { @@ -1092,125 +1229,31 @@ void GameSprite::Image::clean(int time) { GameSprite::NormalImage::NormalImage() : id(0), size(0), - dump(nullptr) { + m_cachedData(nullptr) { //// } GameSprite::NormalImage::~NormalImage() { - delete[] dump; + m_cachedData = nullptr; } void GameSprite::NormalImage::clean(int time) { Image::clean(time); - if (time - lastaccess > 5 && !g_settings.getInteger(Config::USE_MEMCACHED_SPRITES)) { // We keep dumps around for 5 seconds. - delete[] dump; - dump = nullptr; - } -} - -uint8_t* GameSprite::NormalImage::getRGBData() { - if (!dump) { - if (g_settings.getInteger(Config::USE_MEMCACHED_SPRITES)) { - return nullptr; - } - - if (!g_gui.gfx.loadSpriteDump(dump, size, id)) { - return nullptr; - } - } - - const int pixels_data_size = rme::SpritePixels * rme::SpritePixels * 3; - uint8_t* data = newd uint8_t[pixels_data_size]; - uint8_t bpp = g_gui.gfx.hasTransparency() ? 4 : 3; - int write = 0; - int read = 0; - - // decompress pixels - while (read < size && write < pixels_data_size) { - int transparent = dump[read] | dump[read + 1] << 8; - read += 2; - for (int i = 0; i < transparent && write < pixels_data_size; i++) { - data[write + 0] = 0xFF; // red - data[write + 1] = 0x00; // green - data[write + 2] = 0xFF; // blue - write += 3; - } - - int colored = dump[read] | dump[read + 1] << 8; - read += 2; - for (int i = 0; i < colored && write < pixels_data_size; i++) { - data[write + 0] = dump[read + 0]; // red - data[write + 1] = dump[read + 1]; // green - data[write + 2] = dump[read + 2]; // blue - write += 3; - read += bpp; - } - } - - // fill remaining pixels - while (write < pixels_data_size) { - data[write + 0] = 0xFF; // red - data[write + 1] = 0x00; // green - data[write + 2] = 0xFF; // blue - write += 3; + // We keep dumps around for 5 seconds. + if (time - lastaccess > 5) { + m_cachedData = nullptr; } - return data; } uint8_t* GameSprite::NormalImage::getRGBAData() { - if (!dump) { - if (g_settings.getInteger(Config::USE_MEMCACHED_SPRITES)) { - return nullptr; - } - - if (!g_gui.gfx.loadSpriteDump(dump, size, id)) { + if (!m_cachedData) { + if (!g_gui.gfx.loadSpriteDump(m_cachedData, size, id)) { + spdlog::info("[GameSprite::NormalImage::getRGBAData] - Failed when parsing sprite id {}", id); return nullptr; } } - const int pixels_data_size = rme::SpritePixelsSize * 4; - uint8_t* data = newd uint8_t[pixels_data_size]; - bool use_alpha = g_gui.gfx.hasTransparency(); - uint8_t bpp = use_alpha ? 4 : 3; - int write = 0; - int read = 0; - - // decompress pixels - while (read < size && write < pixels_data_size) { - int transparent = dump[read] | dump[read + 1] << 8; - if (use_alpha && transparent >= rme::SpritePixelsSize) { // Corrupted sprite? - break; - } - read += 2; - for (int i = 0; i < transparent && write < pixels_data_size; i++) { - data[write + 0] = 0x00; // red - data[write + 1] = 0x00; // green - data[write + 2] = 0x00; // blue - data[write + 3] = 0x00; // alpha - write += 4; - } - - int colored = dump[read] | dump[read + 1] << 8; - read += 2; - for (int i = 0; i < colored && write < pixels_data_size; i++) { - data[write + 0] = dump[read + 0]; // red - data[write + 1] = dump[read + 1]; // green - data[write + 2] = dump[read + 2]; // blue - data[write + 3] = use_alpha ? dump[read + 3] : 0xFF; // alpha - write += 4; - read += bpp; - } - } - - // fill remaining pixels - while (write < pixels_data_size) { - data[write + 0] = 0x00; // red - data[write + 1] = 0x00; // green - data[write + 2] = 0x00; // blue - data[write + 3] = 0x00; // alpha - write += 4; - } - return data; + return m_cachedData; } GLuint GameSprite::NormalImage::getHardwareID() { @@ -1221,11 +1264,11 @@ GLuint GameSprite::NormalImage::getHardwareID() { return id; } -void GameSprite::NormalImage::createGLTexture(GLuint textureId) { +void GameSprite::NormalImage::createGLTexture(GLuint ignored) { Image::createGLTexture(id); } -void GameSprite::NormalImage::unloadGLTexture(GLuint textureId) { +void GameSprite::NormalImage::unloadGLTexture(GLuint ignored) { Image::unloadGLTexture(id); } @@ -1281,161 +1324,169 @@ void GameSprite::EditorImage::createGLTexture(GLuint textureId) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, 0x812F); // GL_CLAMP_TO_EDGE glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, 0x812F); // GL_CLAMP_TO_EDGE glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, rme::SpritePixels, rme::SpritePixels, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData); - - delete[] imageData; } void GameSprite::EditorImage::unloadGLTexture(GLuint textureId) { Image::unloadGLTexture(id); } -GameSprite::TemplateImage::TemplateImage(GameSprite* parent, int v, const Outfit &outfit) : - gl_tid(0), - parent(parent), - sprite_index(v), - lookHead(outfit.lookHead), - lookBody(outfit.lookBody), - lookLegs(outfit.lookLegs), - lookFeet(outfit.lookFeet) { - //// +uint8_t GameSprite::getWidth() { + if (width <= 0) { + const auto &sheet = g_spriteAppearances.getSheetBySpriteId(spriteList[0]->getHardwareID(), false); + if (sheet) { + width = sheet->getSpriteSize().width; + height = sheet->getSpriteSize().height; + } + } + + return width; +} + +uint8_t GameSprite::getHeight() { + if (height <= 0) { + const auto &sheet = g_spriteAppearances.getSheetBySpriteId(spriteList[0]->getHardwareID(), false); + if (sheet) { + width = sheet->getSpriteSize().width; + height = sheet->getSpriteSize().height; + } + } + + return height; } -GameSprite::TemplateImage::~TemplateImage() { +// OutfitImage +GameSprite::OutfitImage::OutfitImage(GameSprite* initParent, int initSpriteIndex, GLuint initSpriteId, const Outfit &initOutfit) : + m_spriteId(initSpriteId), + m_spriteIndex(initSpriteIndex), + m_parent(initParent), + m_name(initOutfit.name), + m_lookHead(initOutfit.lookHead), + m_lookBody(initOutfit.lookBody), + m_lookLegs(initOutfit.lookLegs), + m_lookFeet(initOutfit.lookFeet) { //// } -void GameSprite::TemplateImage::colorizePixel(uint8_t color, uint8_t &red, uint8_t &green, uint8_t &blue) { - // Thanks! Khaos, or was it mips? Hmmm... =) +GameSprite::OutfitImage::~OutfitImage() { + m_cachedOutfitData = nullptr; +} + +void GameSprite::OutfitImage::unloadGLTexture(GLuint ignored) { + Image::unloadGLTexture(m_spriteId); +} + +void GameSprite::OutfitImage::colorizePixel(uint8_t color, uint8_t &red, uint8_t &green, uint8_t &blue) { uint8_t ro = (TemplateOutfitLookupTable[color] & 0xFF0000) >> 16; // rgb outfit uint8_t go = (TemplateOutfitLookupTable[color] & 0xFF00) >> 8; uint8_t bo = (TemplateOutfitLookupTable[color] & 0xFF); - red = (uint8_t)(red * (ro / 255.f)); - green = (uint8_t)(green * (go / 255.f)); - blue = (uint8_t)(blue * (bo / 255.f)); + + red = (uint8_t)(red * ((float)ro / 255.f)); + green = (uint8_t)(green * ((float)go / 255.f)); + blue = (uint8_t)(blue * ((float)bo / 255.f)); } -uint8_t* GameSprite::TemplateImage::getRGBData() { - uint8_t* rgbdata = parent->spriteList[sprite_index]->getRGBData(); - uint8_t* template_rgbdata = parent->spriteList[sprite_index + parent->height * parent->width]->getRGBData(); +uint8_t* GameSprite::OutfitImage::getRGBAData() { + if (m_cachedOutfitData) { + return m_cachedOutfitData; + } - if (!rgbdata) { - delete[] template_rgbdata; + const auto &sprite = g_spriteAppearances.getSprite(m_parent->spriteList[m_spriteIndex]->getHardwareID()); + if (!sprite) { return nullptr; } - if (!template_rgbdata) { - delete[] rgbdata; + + const auto &spriteTemplate = g_spriteAppearances.getSprite(m_parent->spriteList[m_spriteIndex + 1]->getHardwareID()); + if (!spriteTemplate) { return nullptr; } - if (lookHead > (sizeof(TemplateOutfitLookupTable) / sizeof(TemplateOutfitLookupTable[0]))) { - lookHead = 0; + uint8_t* rgbadata = sprite->pixels.data(); + uint8_t* template_rgbadata = spriteTemplate->pixels.data(); + + if (m_lookHead > (sizeof(TemplateOutfitLookupTable) / sizeof(TemplateOutfitLookupTable[0]))) { + m_lookHead = 0; } - if (lookBody > (sizeof(TemplateOutfitLookupTable) / sizeof(TemplateOutfitLookupTable[0]))) { - lookBody = 0; + if (m_lookBody > (sizeof(TemplateOutfitLookupTable) / sizeof(TemplateOutfitLookupTable[0]))) { + m_lookBody = 0; } - if (lookLegs > (sizeof(TemplateOutfitLookupTable) / sizeof(TemplateOutfitLookupTable[0]))) { - lookLegs = 0; + if (m_lookLegs > (sizeof(TemplateOutfitLookupTable) / sizeof(TemplateOutfitLookupTable[0]))) { + m_lookLegs = 0; } - if (lookFeet > (sizeof(TemplateOutfitLookupTable) / sizeof(TemplateOutfitLookupTable[0]))) { - lookFeet = 0; + if (m_lookFeet > (sizeof(TemplateOutfitLookupTable) / sizeof(TemplateOutfitLookupTable[0]))) { + m_lookFeet = 0; } - for (int y = 0; y < rme::SpritePixels; ++y) { - for (int x = 0; x < rme::SpritePixels; ++x) { - uint8_t &red = rgbdata[y * rme::SpritePixels * 3 + x * 3 + 0]; - uint8_t &green = rgbdata[y * rme::SpritePixels * 3 + x * 3 + 1]; - uint8_t &blue = rgbdata[y * rme::SpritePixels * 3 + x * 3 + 2]; + int height = sprite->size.height; + int width = sprite->size.width; + for (int y = 0; y < height; ++y) { + for (int x = 0; x < width; ++x) { + const int index = (y * width + x) * 4; + uint8_t &red = rgbadata[index + 2]; + uint8_t &green = rgbadata[index + 1]; + uint8_t &blue = rgbadata[index]; - uint8_t &tred = template_rgbdata[y * rme::SpritePixels * 3 + x * 3 + 0]; - uint8_t &tgreen = template_rgbdata[y * rme::SpritePixels * 3 + x * 3 + 1]; - uint8_t &tblue = template_rgbdata[y * rme::SpritePixels * 3 + x * 3 + 2]; + const uint8_t &tred = template_rgbadata[index + 2]; + const uint8_t &tgreen = template_rgbadata[index + 1]; + const uint8_t &tblue = template_rgbadata[index]; if (tred && tgreen && !tblue) { // yellow => head - colorizePixel(lookHead, red, green, blue); + colorizePixel(m_lookHead, red, green, blue); } else if (tred && !tgreen && !tblue) { // red => body - colorizePixel(lookBody, red, green, blue); + colorizePixel(m_lookBody, red, green, blue); } else if (!tred && tgreen && !tblue) { // green => legs - colorizePixel(lookLegs, red, green, blue); + colorizePixel(m_lookLegs, red, green, blue); } else if (!tred && !tgreen && tblue) { // blue => feet - colorizePixel(lookFeet, red, green, blue); + colorizePixel(m_lookFeet, red, green, blue); } } } - delete[] template_rgbdata; - return rgbdata; -} -uint8_t* GameSprite::TemplateImage::getRGBAData() { - uint8_t* rgbadata = parent->spriteList[sprite_index]->getRGBAData(); - uint8_t* template_rgbdata = parent->spriteList[sprite_index + parent->height * parent->width]->getRGBData(); + spdlog::debug("outfit name: {}, pattern_x: {}, pattern_y: {}, pattern_z: {}, sprite_phase_size: {}, layers: {}, draw height: {}, drawx: {}, drawy: {}", m_name, m_parent->pattern_x, m_parent->pattern_y, m_parent->pattern_z, m_parent->sprite_phase_size, m_parent->layers, m_parent->draw_height, m_parent->drawoffset_x, m_parent->drawoffset_y); - if (!rgbadata) { - delete[] template_rgbdata; - return nullptr; - } - if (!template_rgbdata) { - delete[] rgbadata; - return nullptr; - } + m_cachedOutfitData = rgbadata; + return m_cachedOutfitData; +} - if (lookHead > (sizeof(TemplateOutfitLookupTable) / sizeof(TemplateOutfitLookupTable[0]))) { - lookHead = 0; - } - if (lookBody > (sizeof(TemplateOutfitLookupTable) / sizeof(TemplateOutfitLookupTable[0]))) { - lookBody = 0; - } - if (lookLegs > (sizeof(TemplateOutfitLookupTable) / sizeof(TemplateOutfitLookupTable[0]))) { - lookLegs = 0; - } - if (lookFeet > (sizeof(TemplateOutfitLookupTable) / sizeof(TemplateOutfitLookupTable[0]))) { - lookFeet = 0; +GLuint GameSprite::OutfitImage::getHardwareID() { + if (!m_isGLLoaded) { + if (m_spriteId == 0) { + m_spriteId = g_gui.gfx.getFreeTextureID(); + } + createGLTexture(m_spriteId); + if (!m_isGLLoaded) { + return 0; + } } - for (int y = 0; y < rme::SpritePixels; ++y) { - for (int x = 0; x < rme::SpritePixels; ++x) { - uint8_t &red = rgbadata[y * rme::SpritePixels * 4 + x * 4 + 0]; - uint8_t &green = rgbadata[y * rme::SpritePixels * 4 + x * 4 + 1]; - uint8_t &blue = rgbadata[y * rme::SpritePixels * 4 + x * 4 + 2]; + return m_spriteId; +} - uint8_t &tred = template_rgbdata[y * rme::SpritePixels * 3 + x * 3 + 0]; - uint8_t &tgreen = template_rgbdata[y * rme::SpritePixels * 3 + x * 3 + 1]; - uint8_t &tblue = template_rgbdata[y * rme::SpritePixels * 3 + x * 3 + 2]; +void GameSprite::OutfitImage::createGLTexture(GLuint spriteId) { + ASSERT(!m_isGLLoaded); - if (tred && tgreen && !tblue) { // yellow => head - colorizePixel(lookHead, red, green, blue); - } else if (tred && !tgreen && !tblue) { // red => body - colorizePixel(lookBody, red, green, blue); - } else if (!tred && tgreen && !tblue) { // green => legs - colorizePixel(lookLegs, red, green, blue); - } else if (!tred && !tgreen && tblue) { // blue => feet - colorizePixel(lookFeet, red, green, blue); - } - } + uint8_t* rgba = getRGBAData(); + if (!rgba) { + return; } - delete[] template_rgbdata; - return rgbadata; -} -GLuint GameSprite::TemplateImage::getHardwareID() { - if (!isGLLoaded) { - if (gl_tid == 0) { - gl_tid = g_gui.gfx.getFreeTextureID(); - } - createGLTexture(gl_tid); - if (!isGLLoaded) { - return 0; - } + const auto &sheet = g_spriteAppearances.getSheetBySpriteId(spriteId); + if (!sheet) { + return; } - visit(); - return gl_tid; -} -void GameSprite::TemplateImage::createGLTexture(GLuint unused) { - Image::createGLTexture(gl_tid); -} + auto spriteWidth = sheet->getSpriteSize().width; + auto spriteHeight = sheet->getSpriteSize().height; + auto invertedBuffer = m_parent->invertGLColors(spriteHeight, spriteWidth, rgba); -void GameSprite::TemplateImage::unloadGLTexture(GLuint unused) { - Image::unloadGLTexture(gl_tid); + m_isGLLoaded = true; + g_gui.gfx.loaded_textures += 1; + + glBindTexture(GL_TEXTURE_2D, spriteId); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // Linear Filtering + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // Linear Filtering + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, 0x812F); // GL_CLAMP_TO_EDGE + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, 0x812F); // GL_CLAMP_TO_EDGE + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, spriteWidth, spriteHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, invertedBuffer); } GameSprite* GameSprite::createFromBitmap(const wxArtID &bitmapId) { @@ -1448,7 +1499,7 @@ GameSprite* GameSprite::createFromBitmap(const wxArtID &bitmapId) { sprite->pattern_x = 1; sprite->pattern_y = 1; sprite->pattern_z = 1; - sprite->frames = 1; + sprite->sprite_phase_size = 1; sprite->numsprites = 1; sprite->spriteList.push_back(image); return sprite; @@ -1481,6 +1532,7 @@ Animator::Animator(int frame_count, int start_frame, int loop_count, bool async) Animator::~Animator() { for (int i = 0; i < frame_count; i++) { delete durations[i]; + durations[i] = nullptr; } durations.clear(); } diff --git a/source/graphics.h b/source/graphics.h index 4c6a3a22..b5a14362 100644 --- a/source/graphics.h +++ b/source/graphics.h @@ -20,14 +20,17 @@ #include "outfit.h" #include "common.h" +#include "enums.h" -#include "client_version.h" #include +#include + enum SpriteSize { SPRITE_SIZE_16x16, // SPRITE_SIZE_24x24, SPRITE_SIZE_32x32, + SPRITE_SIZE_64x64, SPRITE_SIZE_COUNT }; @@ -80,23 +83,17 @@ class GameSprite : public Sprite { virtual ~GameSprite(); int getIndex(int width, int height, int layer, int pattern_x, int pattern_y, int pattern_z, int frame) const; - GLuint getHardwareID(int _x, int _y, int _layer, int _subtype, int _pattern_x, int _pattern_y, int _pattern_z, int _frame); - GLuint getHardwareID(int _x, int _y, int _dir, int _addon, int _pattern_z, const Outfit &_outfit, int _frame); // CreatureDatabase + GLuint getHardwareID(int _layer, int _count, int _pattern_x, int _pattern_y, int _pattern_z, int _frame); virtual void DrawTo(wxDC* dc, SpriteSize sz, int start_x, int start_y, int width = -1, int height = -1); virtual void unloadDC(); - void clean(int time); - - uint16_t getDrawHeight() const noexcept { - return draw_height; - } - const wxPoint &getDrawOffset() const noexcept { - return draw_offset; - } - uint8_t getMiniMapColor() const noexcept { - return minimap_color; - } + uint16_t getDrawHeight() const; + wxPoint getDrawOffset(); + uint8_t getWidth(); + uint8_t getHeight(); + static uint8_t* invertGLColors(int spriteHeight, int spriteWidth, uint8_t* rgba); + uint8_t getMiniMapColor() const; bool hasLight() const noexcept { return has_light; @@ -110,10 +107,9 @@ class GameSprite : public Sprite { protected: class Image; class NormalImage; - class TemplateImage; + class OutfitImage; - wxMemoryDC* getDC(SpriteSize size); - TemplateImage* getTemplateImage(int sprite_index, const Outfit &outfit); + wxMemoryDC* getDC(SpriteSize spriteSize); class Image { public: @@ -127,7 +123,9 @@ class GameSprite : public Sprite { virtual void clean(int time); virtual GLuint getHardwareID() = 0; +#if CLIENT_VERSION < 1100 virtual uint8_t* getRGBData() = 0; +#endif virtual uint8_t* getRGBAData() = 0; protected: @@ -145,12 +143,14 @@ class GameSprite : public Sprite { // This contains the pixel data uint16_t size; - uint8_t* dump; + uint8_t* m_cachedData; virtual void clean(int time); virtual GLuint getHardwareID(); - virtual uint8_t* getRGBData(); +#if CLIENT_VERSION < 1100 + virtual uint8_t* getRGBData() = 0; +#endif virtual uint8_t* getRGBAData(); protected: @@ -170,34 +170,44 @@ class GameSprite : public Sprite { wxArtID bitmapId; }; - class TemplateImage : public Image { + class OutfitImage : public Image { public: - TemplateImage(GameSprite* parent, int v, const Outfit &outfit); - virtual ~TemplateImage(); + OutfitImage(GameSprite* initParent, int initSpriteIndex, GLuint initSpriteId, const Outfit &initOutfit); + ~OutfitImage(); virtual GLuint getHardwareID(); - virtual uint8_t* getRGBData(); virtual uint8_t* getRGBAData(); - GLuint gl_tid; - GameSprite* parent; - int sprite_index; - uint8_t lookHead; - uint8_t lookBody; - uint8_t lookLegs; - uint8_t lookFeet; + GLuint m_spriteId = 0; + GameSprite* m_parent = 0; + int m_spriteIndex = 0; + std::string m_name; + uint8_t m_lookHead = 0; + uint8_t m_lookBody = 0; + uint8_t m_lookLegs = 0; + uint8_t m_lookFeet = 0; + bool m_isGLLoaded = false; + uint8_t* m_cachedOutfitData = nullptr; - protected: void colorizePixel(uint8_t color, uint8_t &r, uint8_t &b, uint8_t &g); + uint8_t* getOutfitData(int spriteId); - virtual void createGLTexture(GLuint ignored = 0); + virtual void createGLTexture(GLuint spriteId = 0); virtual void unloadGLTexture(GLuint ignored = 0); }; uint32_t id; - wxMemoryDC* dc[SPRITE_SIZE_COUNT]; + wxMemoryDC* m_wxMemoryDc[SPRITE_SIZE_COUNT]; public: + std::shared_ptr getOutfitImage(int spriteId, Direction direction, const Outfit &outfit); + + uint32_t getID() const { + return id; + } + + bool isDrawOffsetLoaded = false; + // GameSprite info uint8_t height; uint8_t width; @@ -205,7 +215,7 @@ class GameSprite : public Sprite { uint8_t pattern_x; uint8_t pattern_y; uint8_t pattern_z; - uint8_t frames; + uint8_t sprite_phase_size; uint32_t numsprites; Animator* animator; @@ -213,6 +223,8 @@ class GameSprite : public Sprite { uint16_t ground_speed; uint16_t draw_height; wxPoint draw_offset; + uint16_t drawoffset_x; + uint16_t drawoffset_y; uint16_t minimap_color; @@ -220,11 +232,13 @@ class GameSprite : public Sprite { SpriteLight light; std::vector spriteList; - std::list instanced_templates; // Templates that use this sprite + std::list> instanced_templates; // Templates that use this sprite friend class GraphicManager; }; +extern GameSprite g_gameSprite; + struct FrameDuration { int min; int max; @@ -301,12 +315,8 @@ class GraphicManager { uint16_t getItemSpriteMinID() const noexcept { return 100; } - uint16_t getItemSpriteMaxID() const noexcept { - return item_count; - } - uint16_t getCreatureSpriteMaxID() const noexcept { - return creature_count; - } + uint16_t getItemSpriteMaxID() const; + uint16_t getCreatureSpriteMaxID() const; // Get an unused texture id (this is acquired by simply increasing a value starting from 0x10000000) GLuint getFreeTextureID(); @@ -320,6 +330,9 @@ class GraphicManager { bool loadSpriteMetadataFlags(FileReadHandle &file, GameSprite* sType, wxString &error, wxArrayString &warnings); bool loadSpriteData(const FileName &datafile, wxString &error, wxArrayString &warnings); + bool loadItemSpriteMetadata(ItemType* t, wxString &error, wxArrayString &warnings); + bool loadOutfitSpriteMetadata(canary::protobuf::appearances::Appearance outfit, wxString &error, wxArrayString &warnings); + // Cleans old & unused textures according to config settings void garbageCollection(); void addSpriteToCleanup(GameSprite* spr); @@ -334,8 +347,6 @@ class GraphicManager { bool hasTransparency() const; bool isUnloaded() const; - ClientVersion* client_version; - private: bool unloaded; // This is used if memcaching is NOT on @@ -348,7 +359,6 @@ class GraphicManager { ImageMap image_space; std::deque cleanup_list; - DatFormat dat_format; uint16_t item_count; uint16_t creature_count; bool otfi_found; @@ -367,7 +377,9 @@ class GraphicManager { friend class GameSprite::Image; friend class GameSprite::NormalImage; friend class GameSprite::EditorImage; - friend class GameSprite::TemplateImage; + friend class GameSprite::OutfitImage; }; +extern GraphicManager g_graphics; + #endif diff --git a/source/gui.cpp b/source/gui.cpp index a1e044aa..11025dc5 100644 --- a/source/gui.cpp +++ b/source/gui.cpp @@ -18,6 +18,9 @@ #include "main.h" #include "gui.h" + +#include "application.h" +#include "client_assets.h" #include "main_menubar.h" #include "editor.h" @@ -37,6 +40,8 @@ #include "welcome_dialog.h" #include "spawn_npc_brush.h" #include "actions_history_window.h" +#include "sprite_appearances.h" +#include "preferences.h" #include "live_client.h" #include "live_tab.h" @@ -46,6 +51,8 @@ #include #endif +#include + const wxEventType EVT_UPDATE_MENUS = wxNewEventType(); const wxEventType EVT_UPDATE_ACTIONS = wxNewEventType(); @@ -76,7 +83,6 @@ GUI::GUI() : window_door_brush(nullptr), OGLContext(nullptr), - loaded_version(CLIENT_VERSION_NONE), mode(SELECTION_MODE), pasting(false), hotkeys_enabled(true), @@ -195,25 +201,6 @@ wxString GUI::GetLocalDirectory() { } } -wxString GUI::GetExtensionsDirectory() { - std::string cfg_str = g_settings.getString(Config::EXTENSIONS_DIRECTORY); - if (!cfg_str.empty()) { - FileName dir; - dir.Assign(wxstr(cfg_str)); - wxString path; - if (dir.DirExists()) { - path = dir.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR); - return path; - } - } - - // Silently reset directory - FileName local_directory = GetLocalDirectory(); - local_directory.AppendDir("extensions"); - local_directory.Mkdir(0755, wxPATH_MKDIR_FULL); - return local_directory.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR); -} - void GUI::discoverDataDirectory(const wxString &existentFile) { wxString currentDir = wxGetCwd(); wxString execDir = GetExecDirectory(); @@ -243,45 +230,24 @@ void GUI::discoverDataDirectory(const wxString &existentFile) { } } -bool GUI::LoadVersion(ClientVersionID version, wxString &error, wxArrayString &warnings, bool force) { - if (ClientVersion::get(version) == nullptr) { - error = "Unsupported client version! (8)"; - return false; - } - - if (version != loaded_version || force) { - if (getLoadedVersion() != nullptr) { - // There is another version loaded right now, save window layout - g_gui.SavePerspective(); - } - - // Disable all rendering so the data is not accessed while reloading - UnnamedRenderingLock(); - DestroyPalettes(); - DestroyMinimap(); - - // Destroy the previous version - UnloadVersion(); +bool GUI::LoadVersion(wxString &error, wxArrayString &warnings) { + // There is another version loaded right now, save window layout + g_gui.SavePerspective(); - loaded_version = version; - if (!getLoadedVersion()->hasValidPaths()) { - if (!getLoadedVersion()->loadValidPaths()) { - error = "Couldn't load relevant asset files"; - loaded_version = CLIENT_VERSION_NONE; - return false; - } - } + // Disable all rendering so the data is not accessed while reloading + UnnamedRenderingLock(); + DestroyPalettes(); + DestroyMinimap(); - bool ret = LoadDataFiles(error, warnings); - if (ret) { - g_gui.LoadPerspective(); - } else { - loaded_version = CLIENT_VERSION_NONE; - } + // Destroy the previous version + UnloadVersion(); - return ret; + bool ret = LoadDataFiles(error, warnings); + if (ret) { + g_gui.LoadPerspective(); } - return true; + + return ret; } void GUI::EnableHotkeys() { @@ -296,26 +262,12 @@ bool GUI::AreHotkeysEnabled() const { return hotkeys_enabled; } -ClientVersionID GUI::GetCurrentVersionID() const { - if (loaded_version != CLIENT_VERSION_NONE) { - return getLoadedVersion()->getID(); - } - return CLIENT_VERSION_NONE; -} - -const ClientVersion &GUI::GetCurrentVersion() const { - assert(loaded_version); - return *getLoadedVersion(); -} - void GUI::CycleTab(bool forward) { tabbook->CycleTab(forward); } bool GUI::LoadDataFiles(wxString &error, wxArrayString &warnings) { - FileName data_path = getLoadedVersion()->getDataPath(); - FileName client_path = getLoadedVersion()->getClientPath(); - FileName extension_path = GetExtensionsDirectory(); + FileName data_path = GetDataDirectory(); FileName exec_directory; try { @@ -325,57 +277,40 @@ bool GUI::LoadDataFiles(wxString &error, wxArrayString &warnings) { return false; } - g_gui.gfx.client_version = getLoadedVersion(); + g_spriteAppearances.init(); - if (!g_gui.gfx.loadOTFI(client_path.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR), error, warnings)) { - error = "Couldn't load otfi file: " + error; - g_gui.DestroyLoadBar(); - UnloadVersion(); - return false; - } - - g_gui.CreateLoadBar("Loading asset files"); - g_gui.SetLoadDone(0, "Loading metadata file..."); + g_gui.CreateLoadBar("Loading assets files"); + g_gui.SetLoadDone(0, "Loading assets file"); + spdlog::info("Loading assets"); - wxFileName metadata_path = g_gui.gfx.getMetadataFileName(); - if (!g_gui.gfx.loadSpriteMetadata(metadata_path, error, warnings)) { - error = "Couldn't load metadata: " + error; - g_gui.DestroyLoadBar(); - UnloadVersion(); - return false; - } - - g_gui.SetLoadDone(10, "Loading sprites file..."); - - wxFileName sprites_path = g_gui.gfx.getSpritesFileName(); - if (!g_gui.gfx.loadSpriteData(sprites_path.GetFullPath(), error, warnings)) { - error = "Couldn't load sprites: " + error; - g_gui.DestroyLoadBar(); - UnloadVersion(); - return false; - } - - g_gui.SetLoadDone(20, "Loading items.otb file..."); - if (!g_items.loadFromOtb(wxString("data/items/items.otb"), error, warnings)) { - error = "Couldn't load items.otb: " + error; + g_gui.SetLoadDone(20, "Loading client assets..."); + spdlog::info("Loading appearances"); + if (!ClientAssets::loadAppearanceProtobuf(error, warnings)) { + error = "Couldn't load catalog-content.json, error: " + error; + spdlog::error("[GUI::LoadDataFiles] {}", error.ToStdString()); g_gui.DestroyLoadBar(); UnloadVersion(); return false; } g_gui.SetLoadDone(30, "Loading items.xml ..."); + spdlog::info("Loading items"); if (!g_items.loadFromGameXml(wxString("data/items/items.xml"), error, warnings)) { warnings.push_back("Couldn't load items.xml: " + error); + spdlog::warn("[GUI::LoadDataFiles] {}: {}", wxString("data/items/items.xml").ToStdString(), error.ToStdString()); } g_gui.SetLoadDone(45, "Loading monsters.xml ..."); + spdlog::info("Loading monsters"); if (!g_monsters.loadFromXML(wxString("data/creatures/monsters.xml"), true, error, warnings)) { warnings.push_back("Couldn't load monsters.xml: " + error); + spdlog::warn("[GUI::LoadDataFiles] {}: {}", wxString("data/creatures/monsters.xml").ToStdString(), error.ToStdString()); } g_gui.SetLoadDone(45, "Loading user monsters.xml ..."); + spdlog::info("Loading user monsters"); { - FileName cdb = getLoadedVersion()->getLocalDataPath(); + FileName cdb = ClientAssets::getLocalPath(); cdb.SetFullName("monsters.xml"); wxString nerr; wxArrayString nwarn; @@ -383,13 +318,16 @@ bool GUI::LoadDataFiles(wxString &error, wxArrayString &warnings) { } g_gui.SetLoadDone(45, "Loading npcs.xml ..."); + spdlog::info("Loading npcs"); if (!g_npcs.loadFromXML(wxString("data/creatures/npcs.xml"), true, error, warnings)) { warnings.push_back("Couldn't load npcs.xml: " + error); + spdlog::warn("[GUI::LoadDataFiles] {}: {}", wxString("data/creatures/npcs.xml").ToStdString(), error.ToStdString()); } g_gui.SetLoadDone(45, "Loading user npcs.xml ..."); + spdlog::info("Loading user npcs"); { - FileName cdb = getLoadedVersion()->getLocalDataPath(); + FileName cdb = ClientAssets::getLocalPath(); cdb.SetFullName("npcs.xml"); wxString nerr; wxArrayString nwarn; @@ -397,21 +335,22 @@ bool GUI::LoadDataFiles(wxString &error, wxArrayString &warnings) { } g_gui.SetLoadDone(50, "Loading materials.xml ..."); - if (!g_materials.loadMaterials(wxString(data_path.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR) + "materials.xml"), error, warnings)) { + spdlog::info("Loading materials"); + auto materialsPath = wxString(data_path.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR) + "materials/materials.xml"); + if (!g_materials.loadMaterials(materialsPath, error, warnings)) { warnings.push_back("Couldn't load materials.xml: " + error); - } - - g_gui.SetLoadDone(70, "Loading extensions..."); - if (!g_materials.loadExtensions(extension_path, error, warnings)) { - // warnings.push_back("Couldn't load extensions: " + error); + spdlog::warn("[GUI::LoadDataFiles] {}: {}", materialsPath.ToStdString(), error.ToStdString()); } g_gui.SetLoadDone(70, "Finishing..."); + spdlog::info("Finishing load map..."); + g_brushes.init(); g_materials.createOtherTileset(); g_materials.createNpcTileset(); g_gui.DestroyLoadBar(); + spdlog::info("Assets loaded"); return true; } @@ -433,24 +372,20 @@ void GUI::UnloadVersion() { hatch_door_brush = nullptr; window_door_brush = nullptr; - if (loaded_version != CLIENT_VERSION_NONE) { - // g_gui.UnloadVersion(); - g_materials.clear(); - g_brushes.clear(); - g_items.clear(); - gfx.clear(); - - FileName cdb = getLoadedVersion()->getLocalDataPath(); - cdb.SetFullName("monsters.xml"); - g_monsters.saveToXML(cdb); - g_monsters.clear(); + // g_gui.UnloadVersion(); + g_materials.clear(); + g_brushes.clear(); + g_items.clear(); + gfx.clear(); - cdb.SetFullName("npcs.xml"); - g_npcs.saveToXML(cdb); - g_npcs.clear(); + FileName cdb = ClientAssets::getLocalPath(); + cdb.SetFullName("monsters.xml"); + g_monsters.saveToXML(cdb); + g_monsters.clear(); - loaded_version = CLIENT_VERSION_NONE; - } + cdb.SetFullName("npcs.xml"); + g_npcs.saveToXML(cdb); + g_npcs.clear(); } void GUI::SaveCurrentMap(FileName filename, bool showdialog) { @@ -535,7 +470,7 @@ bool GUI::NewMap() { } void GUI::OpenMap() { - wxString wildcard = g_settings.getInteger(Config::USE_OTGZ) != 0 ? MAP_LOAD_FILE_WILDCARD_OTGZ : MAP_LOAD_FILE_WILDCARD; + wxString wildcard = MAP_LOAD_FILE_WILDCARD; wxFileDialog dialog(root, "Open map file", wxEmptyString, wxEmptyString, wildcard, wxFD_OPEN | wxFD_FILE_MUST_EXIST); if (dialog.ShowModal() == wxID_OK) { @@ -551,7 +486,7 @@ void GUI::SaveMap() { if (GetCurrentMap().hasFile()) { SaveCurrentMap(true); } else { - wxString wildcard = g_settings.getInteger(Config::USE_OTGZ) != 0 ? MAP_SAVE_FILE_WILDCARD_OTGZ : MAP_SAVE_FILE_WILDCARD; + wxString wildcard = MAP_SAVE_FILE_WILDCARD; wxFileDialog dialog(root, "Save...", wxEmptyString, wxEmptyString, wildcard, wxFD_SAVE | wxFD_OVERWRITE_PROMPT); if (dialog.ShowModal() == wxID_OK) { @@ -565,7 +500,7 @@ void GUI::SaveMapAs() { return; } - wxString wildcard = g_settings.getInteger(Config::USE_OTGZ) != 0 ? MAP_SAVE_FILE_WILDCARD_OTGZ : MAP_SAVE_FILE_WILDCARD; + wxString wildcard = MAP_SAVE_FILE_WILDCARD; wxFileDialog dialog(root, "Save As...", "", "", wildcard, wxFD_SAVE | wxFD_OVERWRITE_PROMPT); if (dialog.ShowModal() == wxID_OK) { @@ -744,7 +679,7 @@ void GUI::NewMapView() { } void GUI::LoadPerspective() { - if (!IsVersionLoaded()) { + if (!ClientAssets::isLoaded()) { if (g_settings.getInteger(Config::WINDOW_MAXIMIZED)) { root->Maximize(); } else { @@ -967,7 +902,7 @@ void GUI::RefreshOtherPalettes(PaletteWindow* p) { } PaletteWindow* GUI::CreatePalette() { - if (!IsVersionLoaded()) { + if (!ClientAssets::isLoaded()) { return nullptr; } @@ -1042,7 +977,7 @@ void GUI::SelectPalettePage(PaletteType pt) { // Minimap Window Interface Implementation void GUI::CreateMinimap() { - if (!IsVersionLoaded()) { + if (!ClientAssets::isLoaded()) { return; } diff --git a/source/gui.h b/source/gui.h index 9ed11227..364d0e8f 100644 --- a/source/gui.h +++ b/source/gui.h @@ -28,9 +28,16 @@ #include "editor_tabs.h" #include "map_tab.h" #include "palette_window.h" -#include "client_version.h" #include "zone_brush.h" +namespace canary { + namespace protobuf { + namespace appearances { + class Appearances; + } + } +} + class BaseMap; class Map; @@ -296,23 +303,13 @@ class GUI { static wxString GetDataDirectory(); static wxString GetLocalDataDirectory(); static wxString GetLocalDirectory(); - static wxString GetExtensionsDirectory(); void discoverDataDirectory(const wxString &existentFile); wxString getFoundDataDirectory() { return m_dataDirectory; } - // Load/unload a client version (takes care of dialogs aswell) - void UnloadVersion(); - bool LoadVersion(ClientVersionID ver, wxString &error, wxArrayString &warnings, bool force = false); - // The current version loaded (returns CLIENT_VERSION_NONE if no version is loaded) - const ClientVersion &GetCurrentVersion() const; - ClientVersionID GetCurrentVersionID() const; - // If any version is loaded at all - bool IsVersionLoaded() const { - return loaded_version != CLIENT_VERSION_NONE; - } + bool LoadVersion(wxString &error, wxArrayString &warnings); // Centers current view on position void SetScreenCenterPosition(const Position &position, bool showIndicator = true); @@ -370,9 +367,7 @@ class GUI { protected: bool LoadDataFiles(wxString &error, wxArrayString &warnings); - ClientVersion* getLoadedVersion() const { - return loaded_version == CLIENT_VERSION_NONE ? nullptr : ClientVersion::get(loaded_version); - } + void UnloadVersion(); //========================================================================= // Palette Interface @@ -446,6 +441,8 @@ class GUI { FlagBrush* pvp_brush; ZoneBrush* zone_brush; + canary::protobuf::appearances::Appearances appearances; // Protobuf appearances file parsed + protected: //========================================================================= // Global GUI state @@ -455,7 +452,6 @@ class GUI { wxGLContext* OGLContext; - ClientVersionID loaded_version; EditorMode mode; bool pasting; diff --git a/source/gui_ids.h b/source/gui_ids.h index c413540a..efe657d1 100644 --- a/source/gui_ids.h +++ b/source/gui_ids.h @@ -50,8 +50,7 @@ enum EditorActionID { MAP_POPUP_MENU_PASTE, MAP_POPUP_MENU_DELETE, - MAP_POPUP_MENU_COPY_SERVER_ID, - MAP_POPUP_MENU_COPY_CLIENT_ID, + MAP_POPUP_MENU_COPY_ITEM_ID, MAP_POPUP_MENU_COPY_NAME, MAP_POPUP_MENU_BROWSE_TILE, @@ -92,8 +91,6 @@ enum EditorActionID { MAP_STATISTICS_EXPORT_XML, - EXTENSIONS_OPEN_FOLDER_BUTTON, - MAP_WINDOW_FILE_BUTTON, PALETTE_ITEM_CHOICEBOOK, diff --git a/source/house.h b/source/house.h index 35664aec..143057c9 100644 --- a/source/house.h +++ b/source/house.h @@ -84,15 +84,9 @@ class Houses { HouseMap::const_iterator end() const { return houses.end(); } -#ifdef __VISUALC__ // C++0x compliance to some degree :) HouseMap::iterator erase(HouseMap::iterator iter) { return houses.erase(iter); } -#else - void erase(HouseMap::iterator iter) { - houses.erase(iter); - } -#endif HouseMap::iterator find(uint32_t val) { return houses.find(val); } diff --git a/source/iomap.h b/source/iomap.h index bccbe80e..8c9cd8df 100644 --- a/source/iomap.h +++ b/source/iomap.h @@ -18,7 +18,7 @@ #ifndef RME_MAP_IO_H_ #define RME_MAP_IO_H_ -#include "client_version.h" +#include "client_assets.h" enum ImportType { IMPORT_DONT, @@ -41,7 +41,6 @@ class IOMap { public: IOMap() { version.otbm = MAP_OTBM_1; - version.client = CLIENT_VERSION_NONE; } virtual ~IOMap() { } diff --git a/source/iomap_otbm.cpp b/source/iomap_otbm.cpp index 04a23ead..3fe3669e 100644 --- a/source/iomap_otbm.cpp +++ b/source/iomap_otbm.cpp @@ -17,9 +17,11 @@ #include "main.h" +#include "iomap_otbm.h" + #include "settings.h" #include "gui.h" // Loadbar - +#include "client_assets.h" #include "monsters.h" #include "monster.h" #include "npcs.h" @@ -30,8 +32,6 @@ #include "complexitem.h" #include "town.h" -#include "iomap_otbm.h" - typedef uint8_t attribute_t; typedef uint32_t flags_t; @@ -181,7 +181,7 @@ void Item::serializeItemAttributes_OTBM(const IOMap &maphandle, NodeFileWriteHan serializeAttributeMap(maphandle, stream); } } else { - if (g_items.MinorVersion >= CLIENT_VERSION_820 && isCharged()) { + if (isCharged()) { stream.addU8(OTBM_ATTR_CHARGES); stream.addU16(getSubtype()); } @@ -389,6 +389,10 @@ bool Container::serializeItemNode_OTBM(const IOMap &maphandle, NodeFileWriteHand |--- OTBM_ITEM_DEF (not implemented) */ +IOMapOTBM::IOMapOTBM(MapVersion ver) { + version = ver; +} + bool IOMapOTBM::getVersionInfo(const FileName &filename, MapVersion &out_ver) { #if OTGZ_SUPPORT > 0 if (filename.GetExt() == "otgz") { @@ -453,17 +457,13 @@ bool IOMapOTBM::getVersionInfo(NodeFileReadHandle* f, MapVersion &out_ver) { if (!root->getU32(u32)) { // Version return false; } + out_ver.otbm = (MapVersionID)u32; root->getU16(u16); root->getU16(u16); root->getU32(u32); - if (!root->getU32(u32)) { // OTB minor version - return false; - } - - out_ver.client = ClientVersionID(u32); return true; } @@ -689,23 +689,6 @@ bool IOMapOTBM::loadMap(Map &map, NodeFileReadHandle &f) { map.height = u16; - if (!root->getU32(u32) || u32 > (unsigned long)g_items.MajorVersion) { // OTB major version - if (g_gui.PopupDialog("Map error", "The loaded map appears to be a items.otb format that deviates from the " - "items.otb loaded by the editor. Do you still want to attempt to load the map?", - wxYES | wxNO) - == wxID_YES) { - warning("Unsupported or damaged map version"); - } else { - error("Outdated items.otb, could not load map"); - return false; - } - } - - if (!root->getU32(u32) || u32 > (unsigned long)g_items.MinorVersion) { // OTB minor version - warning("This editor needs an updated items.otb version"); - } - version.client = (ClientVersionID)u32; - BinaryNode* mapHeaderNode = root->getChild(); if (mapHeaderNode == nullptr || !mapHeaderNode->getByte(u8) || u8 != OTBM_MAP_DATA) { error("Could not get root child node. Cannot recover from fatal error!"); @@ -1552,13 +1535,13 @@ bool IOMapOTBM::saveMap(Map &map, NodeFileWriteHandle &f) { f.addNode(0); { - f.addU32(mapVersion.otbm); // Version + f.addU32(2); // Version (deprecated) f.addU16(map.width); f.addU16(map.height); - f.addU32(g_items.MajorVersion); - f.addU32(g_items.MinorVersion); + f.addU32(4); // Major otb version (deprecated) + f.addU32(4); // Minor otb version (deprecated) f.addNode(OTBM_MAP_DATA); { @@ -1654,18 +1637,30 @@ bool IOMapOTBM::saveMap(Map &map, NodeFileWriteHandle &f) { } if (!found) { + if (ground->getID() == 0) { + continue; + } ground->serializeItemNode_OTBM(self, f); } } else if (ground->isComplex()) { + if (ground->getID() == 0) { + continue; + } ground->serializeItemNode_OTBM(self, f); } else { f.addByte(OTBM_ATTR_ITEM); + if (ground->getID() == 0) { + continue; + } ground->serializeItemCompact_OTBM(self, f); } } for (Item* item : save_tile->items) { if (!item->isMetaItem()) { + if (item->getID() == 0) { + continue; + } item->serializeItemNode_OTBM(self, f); } } @@ -1772,8 +1767,9 @@ bool IOMapOTBM::saveSpawns(Map &map, pugi::xml_document &doc) { monsterNode.append_attribute("x") = x; monsterNode.append_attribute("y") = y; monsterNode.append_attribute("z") = spawnPosition.z; - auto monsterSpawnTime = monster->getSpawnMonsterTime(); - if (monsterSpawnTime > std::numeric_limits::max() || monsterSpawnTime < std::numeric_limits::min()) { + int monsterSpawnTime = monster->getSpawnMonsterTime(); + uint16_t maxUint16 = std::numeric_limits::max(); + if (std::cmp_greater(monsterSpawnTime, maxUint16)) { monsterSpawnTime = 60; } diff --git a/source/iomap_otbm.h b/source/iomap_otbm.h index c8ea8e39..63b23784 100644 --- a/source/iomap_otbm.h +++ b/source/iomap_otbm.h @@ -20,9 +20,6 @@ #include "iomap.h" -// Pragma pack is VERY important since otherwise it won't be able to load the structs correctly -#pragma pack(1) - enum OTBM_ItemAttribute { OTBM_ATTR_DESCRIPTION = 1, OTBM_ATTR_EXT_FILE = 2, @@ -111,13 +108,14 @@ struct OTBM_HouseTile_coords { uint32_t houseid; }; -#pragma pack() +struct MapVersion; +class NodeFileReadHandle; +class NodeFileWriteHandle; +class Map; class IOMapOTBM : public IOMap { public: - IOMapOTBM(MapVersion ver) { - version = ver; - } + IOMapOTBM(MapVersion ver); ~IOMapOTBM() { } static bool getVersionInfo(const FileName &identifier, MapVersion &out_ver); diff --git a/source/item.cpp b/source/item.cpp index f18d5fba..7f8b1bad 100644 --- a/source/item.cpp +++ b/source/item.cpp @@ -40,6 +40,10 @@ Item* Item::Create(uint16_t id, uint16_t subtype /*= 0xFFFF*/) { return newd Item(id, subtype); } + if (!type.sprite) { + return nullptr; + } + if (type.isDepot()) { return new Depot(id); } else if (type.isContainer()) { @@ -224,7 +228,7 @@ bool Item::hasProperty(enum ITEMPROPERTY prop) const { wxPoint Item::getDrawOffset() const { const ItemType &type = g_items.getItemType(id); if (type.sprite) { - return type.sprite->getDrawOffset(); + return wxPoint(type.sprite->getDrawOffset().x, type.sprite->getDrawOffset().y); } return wxPoint(0, 0); } diff --git a/source/item.h b/source/item.h index 8b148133..f1b88c75 100644 --- a/source/item.h +++ b/source/item.h @@ -151,9 +151,6 @@ class Item : public ItemAttributes { uint16_t getID() const { return id; } - uint16_t getClientID() const { - return g_items.getItemType(id).clientID; - } // NOTE: This is very volatile, do NOT use this unless you know exactly what you're doing // which you probably don't so avoid it like the plague! diff --git a/source/items.cpp b/source/items.cpp index 9e9049f6..a5c7e657 100644 --- a/source/items.cpp +++ b/source/items.cpp @@ -22,77 +22,11 @@ #include "items.h" #include "item.h" +#include "sprite_appearances.h" -ItemDatabase g_items; +#include -ItemType::ItemType() : - sprite(nullptr), - id(0), - clientID(0), - brush(nullptr), - doodad_brush(nullptr), - raw_brush(nullptr), - is_metaitem(false), - has_raw(false), - in_other_tileset(false), - group(ITEM_GROUP_NONE), - type(ITEM_TYPE_NONE), - volume(0), - maxTextLen(0), - // writeOnceItemID(0), - ground_equivalent(0), - border_group(0), - has_equivalent(false), - wall_hate_me(false), - name(""), - description(""), - weight(0.0f), - attack(0), - defense(0), - armor(0), - charges(0), - client_chargeable(false), - extra_chargeable(false), - ignoreLook(false), - - isHangable(false), - hookEast(false), - hookSouth(false), - canReadText(false), - canWriteText(false), - replaceable(true), - decays(false), - stackable(false), - moveable(true), - alwaysOnBottom(false), - pickupable(false), - rotable(false), - isBorder(false), - isOptionalBorder(false), - isWall(false), - isBrushDoor(false), - isOpen(false), - isTable(false), - isCarpet(false), - - floorChangeDown(false), - floorChangeNorth(false), - floorChangeSouth(false), - floorChangeEast(false), - floorChangeWest(false), - floorChange(false), - - unpassable(false), - blockPickupable(false), - blockMissiles(false), - blockPathfinder(false), - hasElevation(false), - - alwaysOnTopOrder(0), - rotateTo(0), - border_alignment(BORDER_NONE) { - //// -} +ItemDatabase g_items; bool ItemType::isFloorChange() const noexcept { return floorChange @@ -103,25 +37,6 @@ bool ItemType::isFloorChange() const noexcept { || floorChangeWest; } -ItemDatabase::ItemDatabase() : - // Version information - MajorVersion(0), - MinorVersion(0), - BuildNumber(0), - - // Count of GameSprite types - item_count(0), - effect_count(0), - monster_count(0), - distance_count(0), - - minClientID(0), - maxClientID(0), - - maxItemId(0) { - //// -} - ItemDatabase::~ItemDatabase() { clear(); } @@ -133,6 +48,7 @@ void ItemDatabase::clear() { } } +#if CLIENT_VERSION < 1100 bool ItemDatabase::loadFromOtbVer1(BinaryNode* itemNode, wxString &error, wxArrayString &warnings) { for (; itemNode != nullptr; itemNode = itemNode->advance()) { uint8_t group; @@ -184,7 +100,7 @@ bool ItemDatabase::loadFromOtbVer1(BinaryNode* itemNode, wxString &error, wxArra if (itemNode->getU32(flags)) { item->unpassable = ((flags & FLAG_UNPASSABLE) == FLAG_UNPASSABLE); item->blockMissiles = ((flags & FLAG_BLOCK_MISSILES) == FLAG_BLOCK_MISSILES); - item->blockPathfinder = ((flags & FLAG_BLOCK_PATHFINDER) == FLAG_BLOCK_PATHFINDER); + item->blockPathfinderer = ((flags & FLAG_BLOCK_PATHFINDER) == FLAG_BLOCK_PATHFINDER); item->hasElevation = ((flags & FLAG_HAS_ELEVATION) == FLAG_HAS_ELEVATION); // t->useable = ((flags & FLAG_USEABLE) == FLAG_USEABLE); item->pickupable = ((flags & FLAG_PICKUPABLE) == FLAG_PICKUPABLE); @@ -457,7 +373,7 @@ bool ItemDatabase::loadFromOtbVer2(BinaryNode* itemNode, wxString &error, wxArra if (itemNode->getU32(flags)) { item->unpassable = ((flags & FLAG_UNPASSABLE) == FLAG_UNPASSABLE); item->blockMissiles = ((flags & FLAG_BLOCK_MISSILES) == FLAG_BLOCK_MISSILES); - item->blockPathfinder = ((flags & FLAG_BLOCK_PATHFINDER) == FLAG_BLOCK_PATHFINDER); + item->blockPathfinderer = ((flags & FLAG_BLOCK_PATHFINDER) == FLAG_BLOCK_PATHFINDER); item->hasElevation = ((flags & FLAG_HAS_ELEVATION) == FLAG_HAS_ELEVATION); item->pickupable = ((flags & FLAG_PICKUPABLE) == FLAG_PICKUPABLE); item->moveable = ((flags & FLAG_MOVEABLE) == FLAG_MOVEABLE); @@ -612,7 +528,7 @@ bool ItemDatabase::loadFromOtbVer3(BinaryNode* itemNode, wxString &error, wxArra if (itemNode->getU32(flags)) { item->unpassable = ((flags & FLAG_UNPASSABLE) == FLAG_UNPASSABLE); item->blockMissiles = ((flags & FLAG_BLOCK_MISSILES) == FLAG_BLOCK_MISSILES); - item->blockPathfinder = ((flags & FLAG_BLOCK_PATHFINDER) == FLAG_BLOCK_PATHFINDER); + item->blockPathfinderer = ((flags & FLAG_BLOCK_PATHFINDER) == FLAG_BLOCK_PATHFINDER); item->hasElevation = ((flags & FLAG_HAS_ELEVATION) == FLAG_HAS_ELEVATION); item->pickupable = ((flags & FLAG_PICKUPABLE) == FLAG_PICKUPABLE); item->moveable = ((flags & FLAG_MOVEABLE) == FLAG_MOVEABLE); @@ -746,13 +662,13 @@ bool ItemDatabase::loadFromOtb(const FileName &datafile, wxString &error, wxArra BinaryNode* root = f.getRootNode(); -#define safe_get(node, func, ...) \ - do { \ - if (!node->get##func(__VA_ARGS__)) { \ - error = wxstr(f.getErrorMessage()); \ - return false; \ - } \ - } while (false) + #define safe_get(node, func, ...) \ + do { \ + if (!node->get##func(__VA_ARGS__)) { \ + error = wxstr(f.getErrorMessage()); \ + return false; \ + } \ + } while (false) // Read root flags root->skip(1); // Type info @@ -800,18 +716,130 @@ bool ItemDatabase::loadFromOtb(const FileName &datafile, wxString &error, wxArra } return true; } +#endif -bool ItemDatabase::loadItemFromGameXml(pugi::xml_node itemNode, uint16_t id) { - ClientVersionID clientVersion = g_gui.GetCurrentVersionID(); - if (clientVersion < CLIENT_VERSION_980 && id > 20000 && id < 20100) { - itemNode = itemNode.next_sibling(); - return true; - } else if (id > 30000 && id < 30100) { - itemNode = itemNode.next_sibling(); - return true; +bool ItemDatabase::loadFromProtobuf(wxString &error, wxArrayString &warnings, canary::protobuf::appearances::Appearances appearances) { + using namespace canary::protobuf::appearances; + + for (uint32_t it = 0; it < static_cast(appearances.object_size()); ++it) { + Appearance object = appearances.object(it); + + // This scenario should never happen but on custom assets this can break the loader. + if (!object.has_flags()) { + spdlog::error("[ItemDatabase::loadFromProtobuf] - Item with id {} is invalid and was ignored.", object.id()); + wxLogError("[ItemDatabase::loadFromProtobuf] - Item with id %i is invalid and was ignored.", object.id()); + continue; + } + + if (object.id() >= items.size()) { + items.resize(object.id() + 1); + } + + if (!object.has_id()) { + continue; + } + + ItemType* t = newd ItemType(); + t->id = static_cast(object.id()); + t->clientID = static_cast(object.id()); + t->name = object.name(); + t->description = object.description(); + + if (object.flags().container()) { + t->type = ITEM_TYPE_CONTAINER; + t->group = ITEM_GROUP_CONTAINER; + } else if (object.flags().has_bank()) { + t->group = ITEM_GROUP_GROUND; + } else if (object.flags().liquidcontainer()) { + t->group = ITEM_GROUP_FLUID; + } else if (object.flags().liquidpool()) { + t->group = ITEM_GROUP_SPLASH; + } + + if (object.flags().clip()) { + t->alwaysOnTopOrder = 1; + } else if (object.flags().top()) { + t->alwaysOnTopOrder = 3; + } else if (object.flags().bottom()) { + t->alwaysOnTopOrder = 2; + } + + // now lets parse sprite data + t->m_animationPhases.clear(); + + for (const auto &framegroup : object.frame_group()) { + const auto &frameGroupType = framegroup.fixed_frame_group(); + const auto &spriteInfo = framegroup.sprite_info(); + const auto &animation = spriteInfo.animation(); + const auto &sprites = spriteInfo.sprite_id(); + + t->pattern_width = spriteInfo.pattern_width(); + t->pattern_height = spriteInfo.pattern_height(); + t->pattern_depth = spriteInfo.pattern_depth(); + t->layers = spriteInfo.layers(); + + if (animation.sprite_phase().size() > 0) { + const auto &spritesPhases = animation.sprite_phase(); + t->start_frame = animation.default_start_phase(); + t->loop_count = animation.loop_count(); + t->async_animation = !animation.synchronized(); + for (int k = 0; k < spritesPhases.size(); k++) { + t->m_animationPhases.push_back(std::pair(static_cast(spritesPhases[k].duration_min()), static_cast(spritesPhases[k].duration_max()))); + } + } + + t->sprite_id = spriteInfo.sprite_id(0); + + t->m_sprites.clear(); + t->m_sprites.resize(sprites.size()); + for (int i = 0; i < sprites.size(); i++) { + t->m_sprites[i] = sprites[i]; + } + } + + t->noMoveAnimation = object.flags().no_movement_animation(); + t->isCorpse = object.flags().corpse() || object.flags().player_corpse(); + t->forceUse = object.flags().forceuse(); + t->hasHeight = object.flags().has_height(); + t->unpassable = object.flags().unpass(); + t->blockMissiles = object.flags().unsight(); + t->blockPathfinder = object.flags().avoid(); + t->pickupable = object.flags().take(); + t->moveable = object.flags().unmove() == false; + t->canReadText = (object.flags().has_lenshelp() && object.flags().lenshelp().id() == 1112) || (object.flags().has_write() && object.flags().write().max_text_length() != 0) || (object.flags().has_write_once() && object.flags().write_once().max_text_length_once() != 0); + t->canReadText = object.flags().has_write() || object.flags().has_write_once(); + t->isVertical = object.flags().has_hook() && object.flags().hook().south(); + t->isHorizontal = object.flags().has_hook() && object.flags().hook().east(); + t->isHangable = object.flags().hang(); + t->stackable = object.flags().cumulative(); + t->isPodium = object.flags().show_off_socket(); + + g_gui.gfx.loadItemSpriteMetadata(t, error, warnings); + t->sprite = static_cast(g_gui.gfx.getSprite(t->id)); + if (t->sprite) { + t->sprite->minimap_color = object.flags().has_automap() ? static_cast(object.flags().automap().color()) : 0; + t->sprite->draw_height = object.flags().has_height() ? static_cast(object.flags().height().elevation()) : 0; + if (object.flags().has_shift()) { + t->sprite->drawoffset_x = static_cast(object.flags().shift().x()); + t->sprite->drawoffset_y = static_cast(object.flags().shift().y()); + } + } + + // Save max item id from the object size iteraction + maxItemId = it; + + if (t) { + if (items[t->id]) { + wxLogWarning("appearances.dat: Duplicate items"); + delete items[t->id]; + } + items.set(t->id, t); + } } + return true; +} - // Não verificar isValidID se o ID estiver entre 1 e 18, mas verificar para outros IDs. +bool ItemDatabase::loadItemFromGameXml(pugi::xml_node itemNode, uint16_t id) { if (!(id >= 1 && id <= 18) && !isValidID(id)) { return false; } @@ -981,17 +1009,14 @@ bool ItemDatabase::loadFromGameXml(const FileName &identifier, wxString &error, } if (fromId == 0 || toId == 0) { - error = "Could not read item id from item node, fromid " + std::to_string(fromId) + ", toid " + std::to_string(toId) + "."; - return false; + error += "Could not read item id from item node, fromid " + std::to_string(fromId) + ", toid " + std::to_string(toId) + "."; } for (uint16_t id = fromId; id <= toId; ++id) { - if (!loadItemFromGameXml(itemNode, id)) { - error = wxString::Format("Could not load item id %d. Item id not found.", id); - return false; - } + loadItemFromGameXml(itemNode, id); } } + return true; } diff --git a/source/items.h b/source/items.h index e1417dc0..928da40c 100644 --- a/source/items.h +++ b/source/items.h @@ -21,6 +21,14 @@ #include "filehandle.h" #include "brush_enums.h" +namespace canary { + namespace protobuf { + namespace appearances { + class Appearances; + } + } +} + class Brush; class GroundBrush; class WallBrush; @@ -240,7 +248,7 @@ class ItemType { ItemType(const ItemType &) { } public: - ItemType(); + ItemType() = default; bool isGroundTile() const noexcept { return group == ITEM_GROUP_GROUND; @@ -303,94 +311,110 @@ class ItemType { return volume; } - // editor related -public: - Brush* brush; - Brush* doodad_brush; - RAWBrush* raw_brush; - bool is_metaitem; - // This is needed as a consequence of the item palette & the raw palette - // using the same brushes ("others" category consists of items with this - // flag set to false) - bool has_raw; - bool in_other_tileset; - - uint16_t ground_equivalent; - uint32_t border_group; - bool has_equivalent; // True if any item has this as ground_equivalent - bool wall_hate_me; // (For wallbrushes, regard this as not part of the wall) - - bool isBorder; - bool isOptionalBorder; - bool isWall; - bool isBrushDoor; - bool isOpen; - bool isTable; - bool isCarpet; - public: - GameSprite* sprite; - - uint16_t id; - uint16_t clientID; - - ItemGroup_t group; - ItemTypes_t type; - - uint16_t volume; - uint16_t maxTextLen; - // uint16_t writeOnceItemId; + GameSprite* sprite = nullptr; + Brush* brush = nullptr; + Brush* doodad_brush = nullptr; + RAWBrush* raw_brush = nullptr; + + std::vector> m_animationPhases; + int m_numPatternX { 0 }, m_numPatternY { 0 }, m_numPatternZ { 0 }; + int m_layers { 0 }; + std::vector m_sprites; + + uint8_t sprite_phase_size = 0; + + uint16_t id = 0; + uint16_t clientID = 0; + uint16_t volume = 0; + uint16_t maxTextLen = 0; + uint16_t write_once_item_id = 0; + uint16_t ground_equivalent = 0; + uint16_t rotateTo = 0; + + uint32_t border_group = 0; + uint32_t pattern_width = 0; + uint32_t pattern_height = 0; + uint32_t pattern_depth = 0; + uint32_t layers = 0; + uint32_t width = 0; + uint32_t height = 0; + uint32_t sprite_id = 0; + uint32_t loop_count = 0; + uint32_t start_frame = 0; + uint32_t patternWidth = 0; + uint32_t charges = 0; std::string name; std::string editorsuffix; std::string description; - float weight; - // It might be useful to be able to extrapolate this information in the future - int attack; - int defense; - int armor; - uint32_t charges; - bool client_chargeable; - bool extra_chargeable; - bool ignoreLook; - - bool isHangable; - bool hookEast; - bool hookSouth; - bool canReadText; - bool canWriteText; - bool allowDistRead; - bool replaceable; - bool decays; - - bool stackable; - bool moveable; - bool alwaysOnBottom; - bool pickupable; - bool rotable; - - bool floorChangeDown; - bool floorChangeNorth; - bool floorChangeSouth; - bool floorChangeEast; - bool floorChangeWest; - bool floorChange; - - bool unpassable; - bool blockPickupable; - bool blockMissiles; - bool blockPathfinder; - bool hasElevation; - - int alwaysOnTopOrder; - uint16_t rotateTo; - BorderType border_alignment; + float weight = 0.0; + int attack = 0; + int defense = 0; + int armor = 0; + int alwaysOnTopOrder = 0; + + bool is_metaitem = false; + // This is needed as a consequence of the item palette & the raw palette + // using the same brushes ("others" category consists of items with this + // flag set to false) + bool has_raw = false; + bool in_other_tileset = false; + bool async_animation = false; + bool has_equivalent = false; // True if any item has this as ground_equivalent + bool wall_hate_me = false; // (For wallbrushes, regard this as not part of the wall) + bool client_chargeable = false; + bool extra_chargeable = false; + bool ignoreLook = false; + bool isHangable = false; + bool isCorpse = false; + bool isVertical = false; + bool isHorizontal = false; + bool isPodium = false; + bool hookEast = false; + bool hookSouth = false; + bool canReadText = false; + bool canWriteText = false; + bool allowDistRead = false; + bool replaceable = true; + bool decays = false; + bool stackable = false; + bool moveable = true; + bool alwaysOnBottom = false; + bool pickupable = false; + bool rotable = false; + bool isBorder = false; + bool isOptionalBorder = false; + bool isWall = false; + bool isBrushDoor = false; + bool isOpen = false; + bool isTable = false; + bool isCarpet = false; + bool floorChangeDown = false; + bool floorChangeNorth = false; + bool floorChangeSouth = false; + bool floorChangeEast = false; + bool floorChangeWest = false; + bool floorChange = false; + bool unpassable = false; + bool blockPickupable = false; + bool blockMissiles = false; + bool blockPathfinder = false; + bool hasElevation = false; + bool forceUse = false; + bool hasHeight = false; + bool walkStack = false; + bool spriteInfo = false; + bool noMoveAnimation = false; + + BorderType border_alignment = BORDER_NONE; + ItemGroup_t group = ITEM_GROUP_NONE; + ItemTypes_t type = ITEM_TYPE_NONE; }; class ItemDatabase { public: - ItemDatabase(); ~ItemDatabase(); void clear(); @@ -407,6 +431,7 @@ class ItemDatabase { bool isValidID(uint16_t id) const; bool loadFromOtb(const FileName &datafile, wxString &error, wxArrayString &warnings); + bool loadFromProtobuf(wxString &error, wxArrayString &warnings, canary::protobuf::appearances::Appearances appearances); bool loadFromGameXml(const FileName &datafile, wxString &error, wxArrayString &warnings); bool loadItemFromGameXml(pugi::xml_node itemNode, uint16_t id); bool loadMetaItem(pugi::xml_node node); @@ -429,14 +454,11 @@ class ItemDatabase { ItemMap items; // Count of GameSprite types - uint16_t item_count; - uint16_t effect_count; - uint16_t monster_count; - uint16_t distance_count; - - uint16_t minClientID; - uint16_t maxClientID; - uint16_t maxItemId; + uint16_t item_count = 0; + uint16_t effect_count = 0; + uint16_t monster_count = 0; + uint16_t distance_count = 0; + uint16_t maxItemId = 0; ItemType dummy; diff --git a/source/live_client.cpp b/source/live_client.cpp index 1d763db3..3d0d7423 100644 --- a/source/live_client.cpp +++ b/source/live_client.cpp @@ -246,7 +246,7 @@ void LiveClient::sendHello() { message.write(PACKET_HELLO_FROM_CLIENT); message.write(__RME_VERSION_ID__); message.write(__LIVE_NET_VERSION__); - message.write(g_gui.GetCurrentVersionID()); + message.write(0); message.write(nstr(name)); message.write(nstr(password)); @@ -385,7 +385,6 @@ void LiveClient::parseClientAccepted(NetworkMessage &message) { } void LiveClient::parseChangeClientVersion(NetworkMessage &message) { - ClientVersionID clientVersion = static_cast(message.read()); if (!g_gui.CloseAllEditors()) { close(); return; @@ -393,7 +392,7 @@ void LiveClient::parseChangeClientVersion(NetworkMessage &message) { wxString error; wxArrayString warnings; - g_gui.LoadVersion(clientVersion, error, warnings); + g_gui.LoadVersion(error, warnings); sendReady(); } diff --git a/source/live_peer.cpp b/source/live_peer.cpp index 971ce114..ae629391 100644 --- a/source/live_peer.cpp +++ b/source/live_peer.cpp @@ -199,12 +199,7 @@ void LivePeer::parseHello(NetworkMessage &message) { log->Message(name + " (" + getHostName() + ") connected."); NetworkMessage outMessage; - if (static_cast(clientVersion) != g_gui.GetCurrentVersionID()) { - outMessage.write(PACKET_CHANGE_CLIENT_VERSION); - outMessage.write(g_gui.GetCurrentVersionID()); - } else { - outMessage.write(PACKET_ACCEPTED_CLIENT); - } + outMessage.write(PACKET_ACCEPTED_CLIENT); send(outMessage); } diff --git a/source/live_socket.cpp b/source/live_socket.cpp index 5550a1b2..15fbae02 100644 --- a/source/live_socket.cpp +++ b/source/live_socket.cpp @@ -17,6 +17,8 @@ #include "main.h" #include "live_socket.h" + +#include "client_assets.h" #include "map_region.h" #include "iomap_otbm.h" #include "live_tab.h" @@ -24,7 +26,7 @@ LiveSocket::LiveSocket() : cursors(), mapReader(nullptr, 0), mapWriter(), - mapVersion(MapVersion(MAP_OTBM_4, CLIENT_VERSION_NONE)), log(nullptr), + mapVersion(MapVersion(MAP_OTBM_4)), log(nullptr), name("User"), password("") { // } diff --git a/source/main.h b/source/main.h index 7b985622..e7a5a069 100644 --- a/source/main.h +++ b/source/main.h @@ -103,6 +103,8 @@ _Ret_bytecap_(_Size) inline void* __CRTDECL operator new[](size_t _Size, const c // PugiXML #include +#include + // Libarchive, for OTGZ #if OTGZ_SUPPORT > 0 #include diff --git a/source/main_menubar.cpp b/source/main_menubar.cpp index 20a54b8c..0357ad05 100644 --- a/source/main_menubar.cpp +++ b/source/main_menubar.cpp @@ -24,7 +24,6 @@ #include "minimap_window.h" #include "dat_debug_view.h" #include "result_window.h" -#include "extension_window.h" #include "find_item_window.h" #include "settings.h" @@ -197,7 +196,6 @@ MainMenuBar::MainMenuBar(MainFrame* frame) : MAKE_ACTION(FLOOR_15, wxITEM_RADIO, OnChangeFloor); MAKE_ACTION(DEBUG_VIEW_DAT, wxITEM_NORMAL, OnDebugViewDat); - MAKE_ACTION(EXTENSIONS, wxITEM_NORMAL, OnListExtensions); MAKE_ACTION(GOTO_WEBSITE, wxITEM_NORMAL, OnGotoWebsite); MAKE_ACTION(ABOUT, wxITEM_NORMAL, OnAbout); @@ -323,7 +321,7 @@ void MainMenuBar::Update() { EnableItem(PASTE, false); } - bool loaded = g_gui.IsVersionLoaded(); + bool loaded = ClientAssets::isLoaded(); bool has_map = editor != nullptr; bool has_selection = editor && editor->hasSelection(); bool is_live = editor && editor->IsLive(); @@ -853,14 +851,16 @@ void MainMenuBar::OnDebugViewDat(wxCommandEvent &WXUNUSED(event)) { void MainMenuBar::OnReloadDataFiles(wxCommandEvent &WXUNUSED(event)) { wxString error; wxArrayString warnings; - g_gui.LoadVersion(g_gui.GetCurrentVersionID(), error, warnings, true); + g_gui.LoadVersion(error, warnings); g_gui.PopupDialog("Error", error, wxOK); g_gui.ListDialog("Warnings", warnings); -} - -void MainMenuBar::OnListExtensions(wxCommandEvent &WXUNUSED(event)) { - ExtensionsDialog exts(frame); - exts.ShowModal(); + auto clientDirectory = ClientAssets::getPath().ToStdString() + "/"; + if (clientDirectory.empty() || !wxDirExists(wxString(clientDirectory))) { + PreferencesWindow dialog(nullptr); + dialog.getBookCtrl().SetSelection(4); + dialog.ShowModal(); + dialog.Destroy(); + } } void MainMenuBar::OnGotoWebsite(wxCommandEvent &WXUNUSED(event)) { @@ -945,7 +945,7 @@ void MainMenuBar::OnSearchForItem(wxCommandEvent &WXUNUSED(event)) { } void MainMenuBar::OnReplaceItems(wxCommandEvent &WXUNUSED(event)) { - if (!g_gui.IsVersionLoaded()) { + if (!ClientAssets::isLoaded()) { return; } @@ -1101,7 +1101,7 @@ void MainMenuBar::OnSearchForItemOnSelection(wxCommandEvent &WXUNUSED(event)) { } void MainMenuBar::OnReplaceItemsOnSelection(wxCommandEvent &WXUNUSED(event)) { - if (!g_gui.IsVersionLoaded()) { + if (!ClientAssets::isLoaded()) { return; } @@ -1230,7 +1230,7 @@ void MainMenuBar::OnRandomizeMap(wxCommandEvent &WXUNUSED(event)) { } void MainMenuBar::OnJumpToBrush(wxCommandEvent &WXUNUSED(event)) { - if (!g_gui.IsVersionLoaded()) { + if (!ClientAssets::isLoaded()) { return; } @@ -1249,7 +1249,7 @@ void MainMenuBar::OnJumpToBrush(wxCommandEvent &WXUNUSED(event)) { } void MainMenuBar::OnJumpToItemBrush(wxCommandEvent &WXUNUSED(event)) { - if (!g_gui.IsVersionLoaded()) { + if (!ClientAssets::isLoaded()) { return; } @@ -2298,7 +2298,7 @@ namespace SearchDuplicatedItems { struct condition { std::unordered_set foundTiles; - void operator()(Map& map, Tile* tile, Item* item, long long done) { + void operator()(Map &map, Tile* tile, Item* item, long long done) { if (done % 0x8000 == 0) { g_gui.SetLoadDone((unsigned int)(100 * done / map.getTileCount())); } @@ -2329,7 +2329,7 @@ namespace SearchDuplicatedItems { }; } -void MainMenuBar::SearchDuplicatedItems(bool onSelection/* = false*/) { +void MainMenuBar::SearchDuplicatedItems(bool onSelection /* = false*/) { if (!g_gui.IsEditorOpen()) { return; } @@ -2343,7 +2343,7 @@ void MainMenuBar::SearchDuplicatedItems(bool onSelection/* = false*/) { SearchDuplicatedItems::condition finder; foreach_ItemOnMap(g_gui.GetCurrentMap(), finder, onSelection); - std::unordered_set& foundTiles = finder.foundTiles; + std::unordered_set &foundTiles = finder.foundTiles; g_gui.DestroyLoadBar(); @@ -2363,7 +2363,7 @@ void MainMenuBar::SearchDuplicatedItems(bool onSelection/* = false*/) { namespace RemoveDuplicatesItems { struct condition { - bool operator()(Map& map, Tile* tile, Item* item, long long removed, long long done) { + bool operator()(Map &map, Tile* tile, Item* item, long long removed, long long done) { if (done % 0x8000 == 0) { g_gui.SetLoadDone((unsigned int)(100 * done / map.getTileCount())); } @@ -2401,14 +2401,14 @@ namespace RemoveDuplicatesItems { }; } -void MainMenuBar::RemoveDuplicatesItems(bool onSelection/* = false*/) { +void MainMenuBar::RemoveDuplicatesItems(bool onSelection /* = false*/) { if (!g_gui.IsEditorOpen()) { return; } int ok = g_gui.PopupDialog("Remove Duplicate Items", "Do you want to remove all duplicates items from the map?", wxYES | wxNO); - if(ok == wxID_YES) { + if (ok == wxID_YES) { g_gui.GetCurrentEditor()->getSelection().clear(); g_gui.GetCurrentEditor()->clearActions(); diff --git a/source/main_menubar.h b/source/main_menubar.h index 788ae74f..570501ec 100644 --- a/source/main_menubar.h +++ b/source/main_menubar.h @@ -154,7 +154,6 @@ namespace MenuBar { FLOOR_14, FLOOR_15, DEBUG_VIEW_DAT, - EXTENSIONS, GOTO_WEBSITE, ABOUT, SEARCH_ON_MAP_DUPLICATE, @@ -295,7 +294,6 @@ class MainMenuBar : public wxEvtHandler { // About Menu void OnDebugViewDat(wxCommandEvent &event); - void OnListExtensions(wxCommandEvent &event); void OnGotoWebsite(wxCommandEvent &event); void OnAbout(wxCommandEvent &event); void OnSearchForDuplicateItemsOnMap(wxCommandEvent &event); @@ -311,6 +309,7 @@ class MainMenuBar : public wxEvtHandler { void SearchItems(bool unique, bool action, bool container, bool writable, bool onSelection = false); void SearchDuplicatedItems(bool onSelection = false); void RemoveDuplicatesItems(bool onSelection = false); + protected: MainFrame* frame; wxMenuBar* menubar; diff --git a/source/map.cpp b/source/map.cpp index 28eb274e..4b73fce2 100644 --- a/source/map.cpp +++ b/source/map.cpp @@ -17,10 +17,11 @@ #include "main.h" -#include "gui.h" // loadbar - +#include "gui.h" #include "map.h" +#include "client_assets.h" + Map::Map() : BaseMap(), width(512), @@ -33,7 +34,6 @@ Map::Map() : // Earliest version possible // Caller is responsible for converting us to proper version mapVersion.otbm = MAP_OTBM_1; - mapVersion.client = CLIENT_VERSION_NONE; } Map::~Map() { @@ -114,27 +114,6 @@ bool Map::open(const std::string file) { } bool Map::convert(MapVersion to, bool showdialog) { - if (mapVersion.client == to.client) { - // Only OTBM version differs - // No changes necessary - mapVersion = to; - return true; - } - - /* TODO - - if(to.otbm == MAP_OTBM_4 && to.client < CLIENT_VERSION_850) - return false; - - if(mapVersion.client >= CLIENT_VERSION_760 && to.client < CLIENT_VERSION_760) - convert(getReplacementMapFrom760To740(), showdialog); - - if(mapVersion.client < CLIENT_VERSION_810 && to.client >= CLIENT_VERSION_810) - convert(getReplacementMapFrom800To810(), showdialog); - - if(mapVersion.client == CLIENT_VERSION_854_BAD && to.client >= CLIENT_VERSION_854) - convert(getReplacementMapFrom854To854(), showdialog); - */ mapVersion = to; return true; diff --git a/source/map_display.cpp b/source/map_display.cpp index 06168797..abcfa518 100644 --- a/source/map_display.cpp +++ b/source/map_display.cpp @@ -74,8 +74,7 @@ EVT_MENU(MAP_POPUP_MENU_COPY_POSITION, MapCanvas::OnCopyPosition) EVT_MENU(MAP_POPUP_MENU_PASTE, MapCanvas::OnPaste) EVT_MENU(MAP_POPUP_MENU_DELETE, MapCanvas::OnDelete) //---- -EVT_MENU(MAP_POPUP_MENU_COPY_SERVER_ID, MapCanvas::OnCopyServerId) -EVT_MENU(MAP_POPUP_MENU_COPY_CLIENT_ID, MapCanvas::OnCopyClientId) +EVT_MENU(MAP_POPUP_MENU_COPY_ITEM_ID, MapCanvas::OnCopyItemId) EVT_MENU(MAP_POPUP_MENU_COPY_NAME, MapCanvas::OnCopyName) // ---- EVT_MENU(MAP_POPUP_MENU_ROTATE, MapCanvas::OnRotateItem) @@ -416,7 +415,6 @@ void MapCanvas::UpdatePositionStatus(int x, int y) { } else if (Item* item = tile->getTopItem()) { ss << "Item \"" << wxstr(item->getName()) << "\""; ss << " id:" << item->getID(); - ss << " cid:" << item->getClientID(); if (item->getUniqueID()) { ss << " uid:" << item->getUniqueID(); } @@ -2008,7 +2006,7 @@ void MapCanvas::OnCopyPosition(wxCommandEvent &WXUNUSED(event)) { } } -void MapCanvas::OnCopyServerId(wxCommandEvent &WXUNUSED(event)) { +void MapCanvas::OnCopyItemId(wxCommandEvent &WXUNUSED(event)) { ASSERT(editor.getSelection().size() == 1); if (wxTheClipboard->Open()) { @@ -2026,24 +2024,6 @@ void MapCanvas::OnCopyServerId(wxCommandEvent &WXUNUSED(event)) { } } -void MapCanvas::OnCopyClientId(wxCommandEvent &WXUNUSED(event)) { - ASSERT(editor.getSelection().size() == 1); - - if (wxTheClipboard->Open()) { - Tile* tile = editor.getSelection().getSelectedTile(); - ItemVector selected_items = tile->getSelectedItems(); - ASSERT(selected_items.size() == 1); - - const Item* item = selected_items.front(); - - wxTextDataObject* obj = new wxTextDataObject(); - obj->SetText(i2ws(item->getClientID())); - wxTheClipboard->SetData(obj); - - wxTheClipboard->Close(); - } -} - void MapCanvas::OnCopyName(wxCommandEvent &WXUNUSED(event)) { ASSERT(editor.getSelection().size() == 1); @@ -2524,8 +2504,7 @@ void MapPopupMenu::Update() { AppendSeparator(); if (topSelectedItem) { - Append(MAP_POPUP_MENU_COPY_SERVER_ID, "Copy Item Server Id", "Copy the server id of this item"); - Append(MAP_POPUP_MENU_COPY_CLIENT_ID, "Copy Item Client Id", "Copy the client id of this item"); + Append(MAP_POPUP_MENU_COPY_ITEM_ID, "Copy Item Id", "Copy the id of this item"); Append(MAP_POPUP_MENU_COPY_NAME, "Copy Item Name", "Copy the name of this item"); AppendSeparator(); } @@ -2776,7 +2755,7 @@ void AnimationTimer::Notify() { if (map_canvas->GetZoom() <= 2.0) { map_canvas->Refresh(); } -}; +} void AnimationTimer::Start() { if (!started) { diff --git a/source/map_display.h b/source/map_display.h index 5a58ebbc..d472ff55 100644 --- a/source/map_display.h +++ b/source/map_display.h @@ -68,8 +68,7 @@ class MapCanvas : public wxGLCanvas { void OnCut(wxCommandEvent &event); void OnCopy(wxCommandEvent &event); void OnCopyPosition(wxCommandEvent &event); - void OnCopyServerId(wxCommandEvent &event); - void OnCopyClientId(wxCommandEvent &event); + void OnCopyItemId(wxCommandEvent &event); void OnCopyName(wxCommandEvent &event); void OnBrowseTile(wxCommandEvent &event); void OnPaste(wxCommandEvent &event); diff --git a/source/map_drawer.cpp b/source/map_drawer.cpp index ad9f5eaf..e31ceae0 100644 --- a/source/map_drawer.cpp +++ b/source/map_drawer.cpp @@ -35,6 +35,7 @@ #include "house_exit_brush.h" #include "house_brush.h" #include "spawn_monster_brush.h" +#include "sprite_appearances.h" #include "npc_brush.h" #include "spawn_npc_brush.h" #include "wall_brush.h" @@ -1204,19 +1205,13 @@ void MapDrawer::BlitItem(int &draw_x, int &draw_y, const Tile* tile, const Item* } } - if (!ephemeral && options.transparent_items && (!type.isGroundTile() || sprite->width > 1 || sprite->height > 1) && !type.isSplash() && (!type.isBorder || sprite->width > 1 || sprite->height > 1)) { + if (!ephemeral && options.transparent_items && (!type.isGroundTile() || sprite->getWidth() > 1 || sprite->getHeight() > 1) && !type.isSplash() && (!type.isBorder || sprite->getWidth() > 1 || sprite->getHeight() > 1)) { alpha /= 2; } int frame = item->getFrame(); - for (int cx = 0; cx != sprite->width; cx++) { - for (int cy = 0; cy != sprite->height; cy++) { - for (int cf = 0; cf != sprite->layers; cf++) { - int texnum = sprite->getHardwareID(cx, cy, cf, subtype, pattern_x, pattern_y, pattern_z, frame); - glBlitTexture(screenx - cx * rme::TileSize, screeny - cy * rme::TileSize, texnum, red, green, blue, alpha); - } - } - } + int texnum = sprite->getHardwareID(0, subtype, pattern_x, pattern_y, pattern_z, frame); + glBlitTexture(screenx, screeny, texnum, red, green, blue, alpha); if (options.show_hooks && (type.hookSouth || type.hookEast)) { DrawHookIndicator(draw_x, draw_y, type); @@ -1304,19 +1299,13 @@ void MapDrawer::BlitItem(int &draw_x, int &draw_y, const Position &pos, const It } } - if (!ephemeral && options.transparent_items && (!type.isGroundTile() || sprite->width > 1 || sprite->height > 1) && !type.isSplash() && (!type.isBorder || sprite->width > 1 || sprite->height > 1)) { + if (!ephemeral && options.transparent_items && (!type.isGroundTile() || sprite->getWidth() > 1 || sprite->getHeight() > 1) && !type.isSplash() && (!type.isBorder || sprite->getWidth() > 1 || sprite->getHeight() > 1)) { alpha /= 2; } int frame = item->getFrame(); - for (int cx = 0; cx != sprite->width; ++cx) { - for (int cy = 0; cy != sprite->height; ++cy) { - for (int cf = 0; cf != sprite->layers; ++cf) { - int texnum = sprite->getHardwareID(cx, cy, cf, subtype, pattern_x, pattern_y, pattern_z, frame); - glBlitTexture(screenx - cx * rme::TileSize, screeny - cy * rme::TileSize, texnum, red, green, blue, alpha); - } - } - } + int texnum = sprite->getHardwareID(0, subtype, pattern_x, pattern_y, pattern_z, frame); + glBlitTexture(screenx, screeny, texnum, red, green, blue, alpha); if (options.show_hooks && (type.hookSouth || type.hookEast) && zoom <= 3.0) { DrawHookIndicator(draw_x, draw_y, type); @@ -1338,14 +1327,8 @@ void MapDrawer::BlitSpriteType(int screenx, int screeny, uint32_t spriteid, int screeny -= sprite->getDrawOffset().y; int frame = 0; - for (int cx = 0; cx != sprite->width; ++cx) { - for (int cy = 0; cy != sprite->height; ++cy) { - for (int cf = 0; cf != sprite->layers; ++cf) { - int texnum = sprite->getHardwareID(cx, cy, cf, -1, 0, 0, 0, frame); - glBlitTexture(screenx - cx * rme::TileSize, screeny - cy * rme::TileSize, texnum, red, green, blue, alpha); - } - } - } + int texnum = sprite->getHardwareID(0, -1, 0, 0, 0, 0); + glBlitTexture(screenx, screeny, texnum, red, green, blue, alpha); } void MapDrawer::BlitSpriteType(int screenx, int screeny, GameSprite* sprite, int red, int green, int blue, int alpha) { @@ -1357,57 +1340,33 @@ void MapDrawer::BlitSpriteType(int screenx, int screeny, GameSprite* sprite, int screeny -= sprite->getDrawOffset().y; int frame = 0; - for (int cx = 0; cx != sprite->width; ++cx) { - for (int cy = 0; cy != sprite->height; ++cy) { - for (int cf = 0; cf != sprite->layers; ++cf) { - int texnum = sprite->getHardwareID(cx, cy, cf, -1, 0, 0, 0, frame); - glBlitTexture(screenx - cx * rme::TileSize, screeny - cy * rme::TileSize, texnum, red, green, blue, alpha); - } - } - } + int texnum = sprite->getHardwareID(0, -1, 0, 0, 0, 0); + glBlitTexture(screenx, screeny, texnum, red, green, blue, alpha); } void MapDrawer::BlitCreature(int screenx, int screeny, const Outfit &outfit, Direction dir, int red, int green, int blue, int alpha) { if (outfit.lookItem != 0) { const ItemType &type = g_items.getItemType(outfit.lookItem); BlitSpriteType(screenx, screeny, type.sprite, red, green, blue, alpha); - } else { - GameSprite* sprite = g_gui.gfx.getCreatureSprite(outfit.lookType); - if (!sprite || outfit.lookType == 0) { - return; - } - - // mount and addon drawing thanks to otc code - int pattern_z = 0; - if (outfit.lookMount != 0) { - if (GameSprite* mountSpr = g_gui.gfx.getCreatureSprite(outfit.lookMount)) { - for (int cx = 0; cx != mountSpr->width; ++cx) { - for (int cy = 0; cy != mountSpr->height; ++cy) { - int texnum = mountSpr->getHardwareID(cx, cy, 0, 0, (int)dir, 0, 0, 0); - glBlitTexture(screenx - cx * rme::TileSize, screeny - cy * rme::TileSize, texnum, red, green, blue, alpha); - } - } - pattern_z = std::min(1, sprite->pattern_z - 1); - } - } + return; + } - int frame = 0; + if (outfit.lookType == 0) { + return; + } - // pattern_y => creature addon - for (int pattern_y = 0; pattern_y < sprite->pattern_y; pattern_y++) { + GameSprite* spr = g_gui.gfx.getCreatureSprite(outfit.lookType); + if (!spr || outfit.lookType == 0) { + return; + } - // continue if we dont have this addon - if (pattern_y > 0 && !(outfit.lookAddon & (1 << (pattern_y - 1)))) { - continue; - } + screenx -= spr->getDrawOffset().x; + screeny -= spr->getDrawOffset().y; - for (int cx = 0; cx != sprite->width; ++cx) { - for (int cy = 0; cy != sprite->height; ++cy) { - int texnum = sprite->getHardwareID(cx, cy, (int)dir, pattern_y, pattern_z, outfit, frame); - glBlitTexture(screenx - cx * rme::TileSize, screeny - cy * rme::TileSize, texnum, red, green, blue, alpha); - } - } - } + auto spriteId = spr->spriteList[0]->getHardwareID(); + auto outfitImage = spr->getOutfitImage(spriteId, dir, outfit); + if (outfitImage) { + glBlitTexture(screenx, screeny, outfitImage->getHardwareID(), red, green, blue, alpha, false, outfit); } } @@ -1771,7 +1730,7 @@ void MapDrawer::DrawIndicator(int x, int y, int indicator, uint8_t r, uint8_t g, return; } - int textureId = sprite->getHardwareID(0, 0, 0, -1, 0, 0, 0, 0); + int textureId = sprite->getHardwareID(0, 0, 0, -1, 0, 0); glBlitTexture(x, y, textureId, r, g, b, a, true); } @@ -1981,48 +1940,39 @@ void MapDrawer::ShowPositionIndicator(const Position &position) { pos_indicator_timer.Start(); } -void MapDrawer::glBlitTexture(int x, int y, int textureId, int red, int green, int blue, int alpha, bool adjustZoom) { - if (textureId <= 0) { - return; - } - - glBindTexture(GL_TEXTURE_2D, textureId); - glColor4ub(uint8_t(red), uint8_t(green), uint8_t(blue), uint8_t(alpha)); - glBegin(GL_QUADS); +void MapDrawer::glBlitTexture(int sx, int sy, int texture_number, int red, int green, int blue, int alpha, bool adjustZoom /* = false*/, const Outfit &outfit /* = {}*/) { + if (texture_number != 0) { + SpriteSheetPtr sheet = g_spriteAppearances.getSheetBySpriteId(texture_number); + if (!sheet) { + return; + } - if (adjustZoom) { - float size = rme::TileSize; - if (zoom < 1.0f) { - float offset = 10 / (10 * zoom); - size = std::max(16, rme::TileSize * zoom); - x += offset; - y += offset; - } else if (zoom > 1.f) { - float offset = (10 * zoom); - size = rme::TileSize + offset; - x -= offset; - y -= offset; + auto spriteWidth = sheet->getSpriteSize().width; + auto spriteHeight = sheet->getSpriteSize().height; + // Fix the outfit offset 8x8 sprites being drawn offset + if (spriteWidth == 64 && spriteHeight == 64 && (outfit.lookType > 0 || outfit.lookItem > 0)) { + GameSprite* spr = g_gui.gfx.getCreatureSprite(outfit.lookType); + if (spr && spr->drawoffset_x == 8 && spr->drawoffset_y == 8) { + sx -= spriteWidth / 2; + sy -= spriteWidth / 2; + } } + + spdlog::debug("Blitting outfit {} at ({}, {})", outfit.name, sx, sy); + + glBindTexture(GL_TEXTURE_2D, texture_number); + glColor4ub(uint8_t(red), uint8_t(green), uint8_t(blue), uint8_t(alpha)); + glBegin(GL_QUADS); glTexCoord2f(0.f, 0.f); - glVertex2f(x, y); - glTexCoord2f(1.f, 0.f); - glVertex2f(x + size, y); - glTexCoord2f(1.f, 1.f); - glVertex2f(x + size, y + size); - glTexCoord2f(0.f, 1.f); - glVertex2f(x, y + size); - } else { - glTexCoord2f(0.f, 0.f); - glVertex2f(x, y); + glVertex2f(sx, sy); glTexCoord2f(1.f, 0.f); - glVertex2f(x + rme::TileSize, y); + glVertex2f(sx + spriteWidth, sy); glTexCoord2f(1.f, 1.f); - glVertex2f(x + rme::TileSize, y + rme::TileSize); + glVertex2f(sx + spriteWidth, sy + spriteHeight); glTexCoord2f(0.f, 1.f); - glVertex2f(x, y + rme::TileSize); + glVertex2f(sx, sy + spriteHeight); + glEnd(); } - - glEnd(); } void MapDrawer::glBlitSquare(int x, int y, int red, int green, int blue, int alpha) { diff --git a/source/map_drawer.h b/source/map_drawer.h index aaa5d5ee..1d5b2572 100644 --- a/source/map_drawer.h +++ b/source/map_drawer.h @@ -180,7 +180,7 @@ class MapDrawer { }; void getColor(Brush* brush, const Position &position, uint8_t &r, uint8_t &g, uint8_t &b); - void glBlitTexture(int x, int y, int textureId, int red, int green, int blue, int alpha, bool adjustZoom = false); + void glBlitTexture(int sx, int sy, int texture_number, int red, int green, int blue, int alpha, bool adjustZoom = false, const Outfit &outfit = {}); void glBlitSquare(int x, int y, int red, int green, int blue, int alpha); void glBlitSquare(int x, int y, const wxColor &color); void glColor(const wxColor &color); diff --git a/source/materials.cpp b/source/materials.cpp index 2b793a12..56f8c767 100644 --- a/source/materials.cpp +++ b/source/materials.cpp @@ -43,26 +43,7 @@ void Materials::clear() { delete iter->second; } - for (MaterialsExtensionList::iterator iter = extensions.begin(); iter != extensions.end(); ++iter) { - delete *iter; - } - tilesets.clear(); - extensions.clear(); -} - -const MaterialsExtensionList &Materials::getExtensions() { - return extensions; -} - -MaterialsExtensionList Materials::getExtensionsByVersion(uint16_t version_id) { - MaterialsExtensionList ret_list; - for (MaterialsExtensionList::iterator iter = extensions.begin(); iter != extensions.end(); ++iter) { - if ((*iter)->isForVersion(version_id)) { - ret_list.push_back(*iter); - } - } - return ret_list; } bool Materials::loadMaterials(const FileName &identifier, wxString &error, wxArrayString &warnings) { @@ -83,114 +64,6 @@ bool Materials::loadMaterials(const FileName &identifier, wxString &error, wxArr return true; } -bool Materials::loadExtensions(FileName directoryName, wxString &error, wxArrayString &warnings) { - directoryName.Mkdir(0755, wxPATH_MKDIR_FULL); // Create if it doesn't exist - - wxDir ext_dir(directoryName.GetPath()); - if (!ext_dir.IsOpened()) { - error = "Could not open extensions directory."; - return false; - } - - wxString filename; - if (!ext_dir.GetFirst(&filename)) { - // No extensions found - return true; - } - - StringVector clientVersions; - do { - FileName fn; - fn.SetPath(directoryName.GetPath()); - fn.SetFullName(filename); - if (fn.GetExt() != "xml") { - continue; - } - - pugi::xml_document doc; - pugi::xml_parse_result result = doc.load_file(fn.GetFullPath().mb_str()); - if (!result) { - warnings.push_back("Could not open " + filename + " (file not found or syntax error)"); - continue; - } - - pugi::xml_node extensionNode = doc.child("materialsextension"); - if (!extensionNode) { - warnings.push_back(filename + ": Invalid rootheader."); - continue; - } - - pugi::xml_attribute attribute; - if (!(attribute = extensionNode.attribute("name"))) { - warnings.push_back(filename + ": Couldn't read extension name."); - continue; - } - - const std::string &extensionName = attribute.as_string(); - if (!(attribute = extensionNode.attribute("author"))) { - warnings.push_back(filename + ": Couldn't read extension name."); - continue; - } - - const std::string &extensionAuthor = attribute.as_string(); - if (!(attribute = extensionNode.attribute("description"))) { - warnings.push_back(filename + ": Couldn't read extension name."); - continue; - } - - const std::string &extensionDescription = attribute.as_string(); - if (extensionName.empty() || extensionAuthor.empty() || extensionDescription.empty()) { - warnings.push_back(filename + ": Couldn't read extension attributes (name, author, description)."); - continue; - } - - std::string extensionUrl = extensionNode.attribute("url").as_string(); - extensionUrl.erase(std::remove(extensionUrl.begin(), extensionUrl.end(), '\'')); - - std::string extensionAuthorLink = extensionNode.attribute("authorurl").as_string(); - extensionAuthorLink.erase(std::remove(extensionAuthorLink.begin(), extensionAuthorLink.end(), '\'')); - - MaterialsExtension* materialExtension = newd MaterialsExtension(extensionName, extensionAuthor, extensionDescription); - materialExtension->url = extensionUrl; - materialExtension->author_url = extensionAuthorLink; - - if ((attribute = extensionNode.attribute("client"))) { - clientVersions.clear(); - const std::string &extensionClientString = attribute.as_string(); - - size_t lastPosition = 0; - size_t position = extensionClientString.find(';'); - while (position != std::string::npos) { - clientVersions.push_back(extensionClientString.substr(lastPosition, position - lastPosition)); - lastPosition = position + 1; - position = extensionClientString.find(';', lastPosition); - } - - clientVersions.push_back(extensionClientString.substr(lastPosition)); - for (const std::string &version : clientVersions) { - materialExtension->addVersion(version); - } - - std::sort(materialExtension->version_list.begin(), materialExtension->version_list.end(), VersionComparisonPredicate); - - auto duplicate = std::unique(materialExtension->version_list.begin(), materialExtension->version_list.end()); - while (duplicate != materialExtension->version_list.end()) { - materialExtension->version_list.erase(duplicate); - duplicate = std::unique(materialExtension->version_list.begin(), materialExtension->version_list.end()); - } - } else { - warnings.push_back(filename + ": Extension is not available for any version."); - } - - extensions.push_back(materialExtension); - if (materialExtension->isForVersion(g_gui.GetCurrentVersionID())) { - unserializeMaterials(filename, extensionNode, error, warnings); - } - } while (ext_dir.GetNext(&filename)); - - return true; -} - bool Materials::unserializeMaterials(const FileName &filename, pugi::xml_node node, wxString &error, wxArrayString &warnings) { wxString warning; pugi::xml_attribute attribute; diff --git a/source/materials.h b/source/materials.h index a8995821..58e81b06 100644 --- a/source/materials.h +++ b/source/materials.h @@ -18,7 +18,7 @@ #ifndef RME_MATERIALS_H_ #define RME_MATERIALS_H_ -#include "extension.h" +using TilesetContainer = std::map; class Materials { public: @@ -27,13 +27,9 @@ class Materials { void clear(); - const MaterialsExtensionList &getExtensions(); - MaterialsExtensionList getExtensionsByVersion(uint16_t version_id); - TilesetContainer tilesets; bool loadMaterials(const FileName &identifier, wxString &error, wxArrayString &warnings); - bool loadExtensions(FileName identifier, wxString &error, wxArrayString &warnings); void createOtherTileset(); void createNpcTileset(); @@ -44,8 +40,6 @@ class Materials { bool unserializeMaterials(const FileName &filename, pugi::xml_node node, wxString &error, wxArrayString &warnings); bool unserializeTileset(pugi::xml_node node, wxArrayString &warnings); - MaterialsExtensionList extensions; - private: Materials(const Materials &); Materials &operator=(const Materials &); diff --git a/source/monsters.cpp b/source/monsters.cpp index 681d8c89..89e1fedf 100644 --- a/source/monsters.cpp +++ b/source/monsters.cpp @@ -67,6 +67,7 @@ MonsterType* MonsterType::loadFromXML(pugi::xml_node node, wxArrayString &warnin MonsterType* ct = newd MonsterType(); ct->name = attribute.as_string(); + ct->outfit.name = ct->name; if ((attribute = node.attribute("looktype"))) { ct->outfit.lookType = attribute.as_int(); @@ -121,6 +122,7 @@ MonsterType* MonsterType::loadFromOTXML(const FileName &filename, pugi::xml_docu MonsterType* ct = newd MonsterType(); ct->name = attribute.as_string(); + ct->outfit.name = ct->name; for (pugi::xml_node optionNode = node.first_child(); optionNode; optionNode = optionNode.next_sibling()) { if (as_lower_str(optionNode.name()) != "look") { @@ -357,3 +359,13 @@ bool MonsterDatabase::saveToXML(const FileName &filename) { } return doc.save_file(filename.GetFullPath().mb_str(), "\t", pugi::format_default, pugi::encoding_utf8); } + +wxArrayString MonsterDatabase::getMissingMonsterNames() const { + wxArrayString missingMonsters; + for (const auto &monsterEntry : monster_map) { + if (monsterEntry.second->missing) { + missingMonsters.Add(monsterEntry.second->name); + } + } + return missingMonsters; +} diff --git a/source/monsters.h b/source/monsters.h index 0be83b96..2b45bc32 100644 --- a/source/monsters.h +++ b/source/monsters.h @@ -57,6 +57,7 @@ class MonsterDatabase { bool importXMLFromOT(const FileName &filename, wxString &error, wxArrayString &warnings); bool saveToXML(const FileName &filename); + wxArrayString getMissingMonsterNames() const; }; class MonsterType { diff --git a/source/npcs.cpp b/source/npcs.cpp index 9e22fed1..c545cba3 100644 --- a/source/npcs.cpp +++ b/source/npcs.cpp @@ -67,6 +67,7 @@ NpcType* NpcType::loadFromXML(pugi::xml_node node, wxArrayString &warnings) { NpcType* npcType = newd NpcType(); npcType->name = attribute.as_string(); + npcType->outfit.name = npcType->name; if ((attribute = node.attribute("looktype"))) { npcType->outfit.lookType = attribute.as_int(); @@ -118,7 +119,6 @@ NpcType* NpcType::loadFromOTXML(const FileName &filename, pugi::xml_document &do NpcType* npcType = newd NpcType(); npcType->name = nstr(filename.GetName()); - npcType->name = nstr(filename.GetName()); for (pugi::xml_node optionNode = node.first_child(); optionNode; optionNode = optionNode.next_sibling()) { if (as_lower_str(optionNode.name()) != "look") { @@ -310,3 +310,13 @@ bool NpcDatabase::saveToXML(const FileName &filename) { } return doc.save_file(filename.GetFullPath().mb_str(), "\t", pugi::format_default, pugi::encoding_utf8); } + +wxArrayString NpcDatabase::getMissingNpcNames() const { + wxArrayString missingNpcs; + for (const auto &ncpEntry : npcMap) { + if (ncpEntry.second->missing) { + missingNpcs.Add(ncpEntry.second->name); + } + } + return missingNpcs; +} diff --git a/source/npcs.h b/source/npcs.h index 48f57dd1..8ad361dd 100644 --- a/source/npcs.h +++ b/source/npcs.h @@ -57,6 +57,7 @@ class NpcDatabase { bool importXMLFromOT(const FileName &filename, wxString &error, wxArrayString &warnings); bool saveToXML(const FileName &filename); + wxArrayString getMissingNpcNames() const; }; class NpcType { diff --git a/source/outfit.h b/source/outfit.h index bf92d689..fd3fc0aa 100644 --- a/source/outfit.h +++ b/source/outfit.h @@ -22,6 +22,7 @@ struct Outfit { Outfit() : lookType(0), lookItem(0), lookMount(0), lookAddon(0), lookHead(0), lookBody(0), lookLegs(0), lookFeet(0) { } ~Outfit() { } + std::string name; int lookType; int lookItem; int lookMount; diff --git a/source/preferences.cpp b/source/preferences.cpp index 98463019..c4a2a2e1 100644 --- a/source/preferences.cpp +++ b/source/preferences.cpp @@ -18,9 +18,8 @@ #include "main.h" #include "settings.h" -#include "client_version.h" #include "editor.h" - +#include "client_assets.h" #include "gui.h" #include "preferences.h" @@ -32,7 +31,7 @@ EVT_BUTTON(wxID_APPLY, PreferencesWindow::OnClickApply) EVT_COLLAPSIBLEPANE_CHANGED(wxID_ANY, PreferencesWindow::OnCollapsiblePane) END_EVENT_TABLE() -PreferencesWindow::PreferencesWindow(wxWindow* parent, bool clientVersionSelected = false) : +PreferencesWindow::PreferencesWindow(wxWindow* parent) : wxDialog(parent, wxID_ANY, "Preferences", wxDefaultPosition, wxSize(400, 400), wxCAPTION | wxCLOSE_BOX) { wxSizer* sizer = newd wxBoxSizer(wxVERTICAL); @@ -43,7 +42,8 @@ PreferencesWindow::PreferencesWindow(wxWindow* parent, bool clientVersionSelecte book->AddPage(CreateEditorPage(), "Editor"); book->AddPage(CreateGraphicsPage(), "Graphics"); book->AddPage(CreateUIPage(), "Interface"); - book->AddPage(CreateClientPage(), "Client Version", clientVersionSelected); + book->AddPage(CreateClientPage(), "Client Folder"); + version_dir_picker->Bind(wxEVT_DIRPICKER_CHANGED, &PreferencesWindow::SelectNewAssetsFolder, this); sizer->Add(book, 1, wxEXPAND | wxALL, 10); @@ -235,12 +235,7 @@ wxNotebookPage* PreferencesWindow::CreateGraphicsPage() { sizer->Add(icon_selection_shadow_chkbox, 0, wxLEFT | wxTOP, 5); SetWindowToolTip(icon_selection_shadow_chkbox, "When this option is checked, selected items in the palette menu will be shaded."); - use_memcached_chkbox = newd wxCheckBox(graphics_page, wxID_ANY, "Use memcached sprites"); - use_memcached_chkbox->SetValue(g_settings.getBoolean(Config::USE_MEMCACHED_SPRITES)); - sizer->Add(use_memcached_chkbox, 0, wxLEFT | wxTOP, 5); - SetWindowToolTip(use_memcached_chkbox, "When this is checked, sprites will be loaded into memory at startup and unpacked at runtime. This is faster but consumes more memory.\nIf it is not checked, the editor will use less memory but there will be a performance decrease due to reading sprites from the disk."); - - sizer->AddSpacer(10); + sizer->AddSpacer(5); auto* subsizer = newd wxFlexGridSizer(2, 10, 10); subsizer->AddGrowableCol(1); @@ -499,62 +494,26 @@ wxNotebookPage* PreferencesWindow::CreateClientPage() { wxNotebookPage* client_page = newd wxPanel(book, wxID_ANY); // Refresh g_settings - ClientVersion::saveVersions(); - ClientVersionList versions = ClientVersion::getAllVisible(); + ClientAssets::save(); wxSizer* topsizer = newd wxBoxSizer(wxVERTICAL); - auto* options_sizer = newd wxFlexGridSizer(2, 10, 10); - options_sizer->AddGrowableCol(1); - - // Default client version choice control - default_version_choice = newd wxChoice(client_page, wxID_ANY); - wxStaticText* default_client_tooltip = newd wxStaticText(client_page, wxID_ANY, "Default client version:"); - options_sizer->Add(default_client_tooltip, 0, wxLEFT | wxTOP, 5); - options_sizer->Add(default_version_choice, 0, wxTOP, 5); - SetWindowToolTip(default_client_tooltip, default_version_choice, "This will decide what client version will be used when new maps are created."); - - // Check file sigs checkbox - check_sigs_chkbox = newd wxCheckBox(client_page, wxID_ANY, "Check file signatures"); - check_sigs_chkbox->SetValue(g_settings.getBoolean(Config::CHECK_SIGNATURES)); - check_sigs_chkbox->SetToolTip("When this option is not checked, the editor will load any OTB/DAT/SPR combination without complaints. This may cause graphics bugs."); - options_sizer->Add(check_sigs_chkbox, 0, wxLEFT | wxRIGHT | wxTOP, 5); - - // Add the grid sizer - topsizer->Add(options_sizer, wxSizerFlags(0).Expand()); - topsizer->AddSpacer(10); - wxScrolledWindow* client_list_window = newd wxScrolledWindow(client_page, wxID_ANY, wxDefaultPosition, wxDefaultSize); client_list_window->SetMinSize(FROM_DIP(this, wxSize(450, 450))); auto* client_list_sizer = newd wxFlexGridSizer(2, 10, 10); client_list_sizer->AddGrowableCol(1); - int version_counter = 0; - for (auto version : versions) { - if (!version->isVisible()) { - continue; - } - - default_version_choice->Append(wxstr(version->getName())); - - wxStaticText* tmp_text = newd wxStaticText(client_list_window, wxID_ANY, wxString(version->getName())); - client_list_sizer->Add(tmp_text, wxSizerFlags(0).Expand()); - - wxDirPickerCtrl* dir_picker = newd wxDirPickerCtrl(client_list_window, wxID_ANY, version->getClientPath().GetFullPath()); - version_dir_pickers.push_back(dir_picker); - client_list_sizer->Add(dir_picker, wxSizerFlags(0).Border(wxRIGHT, 10).Expand()); + wxStaticText* tmp_text = newd wxStaticText(client_list_window, wxID_ANY, wxString("Select path:")); + client_list_sizer->Add(tmp_text, wxSizerFlags(0).Expand()); - wxString tooltip; - tooltip << "The editor will look for " << wxstr(version->getName()) << " DAT & SPR here."; - tmp_text->SetToolTip(tooltip); - dir_picker->SetToolTip(tooltip); + wxDirPickerCtrl* dir_picker = newd wxDirPickerCtrl(client_list_window, wxID_ANY, ClientAssets::getPath()); + version_dir_picker = dir_picker; + client_list_sizer->Add(dir_picker, wxSizerFlags(0).Border(wxRIGHT, 10).Expand()); - if (version->getID() == g_settings.getInteger(Config::DEFAULT_CLIENT_VERSION)) { - default_version_choice->SetSelection(version_counter); - } - - version_counter++; - } + wxString tooltip; + tooltip << "The editor will look for client directory here."; + tmp_text->SetToolTip(tooltip); + dir_picker->SetToolTip(tooltip); // Set the sizers client_list_window->SetSizer(client_list_sizer); @@ -563,6 +522,8 @@ wxNotebookPage* PreferencesWindow::CreateClientPage() { topsizer->Add(client_list_window, 0, wxALL, 5); client_page->SetSizerAndFit(topsizer); + spdlog::warn("text window {}", client_page->GetLabel().ToStdString()); + return client_page; } @@ -581,6 +542,18 @@ void PreferencesWindow::OnClickApply(wxCommandEvent &WXUNUSED(event)) { Apply(); } +void PreferencesWindow::SelectNewAssetsFolder(wxCommandEvent &event) { + wxDirPickerCtrl* dir_picker = static_cast(event.GetEventObject()); + wxString path = dir_picker->GetPath(); + if (!path.IsEmpty()) { + ClientAssets::setPath(path); + // spdlog::info("New directory selected: {}", path.ToStdString()); + } else { + wxMessageDialog dialog(this, "Directory is empty, please, select a valid directory", "Error", wxOK | wxICON_ERROR); + dialog.ShowModal(); + } +} + void PreferencesWindow::OnCollapsiblePane(wxCollapsiblePaneEvent &event) { auto* win = (wxWindow*)event.GetEventObject(); win->GetParent()->Fit(); @@ -589,7 +562,6 @@ void PreferencesWindow::OnCollapsiblePane(wxCollapsiblePaneEvent &event) { // Stuff void PreferencesWindow::Apply() { - bool must_restart = false; // General g_settings.setInteger(Config::WELCOME_DIALOG, show_welcome_dialog_chkbox->GetValue()); g_settings.setInteger(Config::ALWAYS_MAKE_BACKUP, always_make_backup_chkbox->GetValue()); @@ -616,11 +588,6 @@ void PreferencesWindow::Apply() { g_settings.setInteger(Config::MERGE_PASTE, merge_paste_chkbox->GetValue()); // Graphics - g_settings.setInteger(Config::USE_GUI_SELECTION_SHADOW, icon_selection_shadow_chkbox->GetValue()); - if (g_settings.getBoolean(Config::USE_MEMCACHED_SPRITES) != use_memcached_chkbox->GetValue()) { - must_restart = true; - } - g_settings.setInteger(Config::USE_MEMCACHED_SPRITES_TO_SAVE, use_memcached_chkbox->GetValue()); if (icon_background_choice->GetSelection() == 0) { if (g_settings.getInteger(Config::ICON_BACKGROUND) != 0) { g_gui.gfx.cleanSoftwareSprites(); @@ -697,32 +664,9 @@ void PreferencesWindow::Apply() { g_settings.setFloat(Config::SCROLL_SPEED, scroll_mul * scroll_speed_slider->GetValue() / 10.f); g_settings.setFloat(Config::ZOOM_SPEED, zoom_speed_slider->GetValue() / 10.f); - // Client - ClientVersionList versions = ClientVersion::getAllVisible(); - int version_counter = 0; - for (auto version : versions) { - auto dir = version_dir_pickers[version_counter]->GetPath().ToStdString(); - if (dir.size() > 0 && dir.back() != '/' && dir.back() != '\\') { - dir.push_back('/'); - } - version->setClientPath(FileName(dir)); - - if (version->getName() == default_version_choice->GetStringSelection()) { - g_settings.setInteger(Config::DEFAULT_CLIENT_VERSION, version->getID()); - } - - version_counter++; - } - g_settings.setInteger(Config::CHECK_SIGNATURES, check_sigs_chkbox->GetValue()); - - // Make sure to reload client paths - ClientVersion::saveVersions(); - ClientVersion::loadVersions(); + ClientAssets::save(); + ClientAssets::load(); g_settings.save(); - - if (must_restart) { - g_gui.PopupDialog(this, "Notice", "You must restart the editor for the changes to take effect.", wxOK); - } g_gui.RebuildPalettes(); } diff --git a/source/preferences.h b/source/preferences.h index ff6b725e..ff24bc23 100644 --- a/source/preferences.h +++ b/source/preferences.h @@ -22,17 +22,18 @@ class PreferencesWindow : public wxDialog { public: - explicit PreferencesWindow(wxWindow* parent) : - PreferencesWindow(parent, false) {}; - PreferencesWindow(wxWindow* parent, bool clientVersionSelected); + explicit PreferencesWindow(wxWindow* parent); virtual ~PreferencesWindow(); void OnClickDefaults(wxCommandEvent &); void OnClickApply(wxCommandEvent &); void OnClickOK(wxCommandEvent &); void OnClickCancel(wxCommandEvent &); - + void SelectNewAssetsFolder(wxCommandEvent &event); void OnCollapsiblePane(wxCollapsiblePaneEvent &); + wxBookCtrl &getBookCtrl() { + return *book; + } protected: void SetDefaults(); @@ -69,7 +70,6 @@ class PreferencesWindow : public wxDialog { // Graphics wxCheckBox* icon_selection_shadow_chkbox; wxChoice* icon_background_choice; - wxCheckBox* use_memcached_chkbox; wxDirPickerCtrl* screenshot_directory_picker; wxChoice* screenshot_format_choice; wxCheckBox* hide_items_when_zoomed_chkbox; @@ -106,7 +106,7 @@ class PreferencesWindow : public wxDialog { // Client info wxChoice* default_version_choice; - std::vector version_dir_pickers; + wxDirPickerCtrl* version_dir_picker; wxCheckBox* check_sigs_chkbox; // Create controls diff --git a/source/protobuf/CMakeLists.txt b/source/protobuf/CMakeLists.txt new file mode 100644 index 00000000..9b8200a8 --- /dev/null +++ b/source/protobuf/CMakeLists.txt @@ -0,0 +1,27 @@ +# ***************************************************************************** +# Project protobuf +# ***************************************************************************** +project(protobuf) + +find_package(Protobuf REQUIRED) +find_package(Threads) + +include_directories(${PROTOBUF_INCLUDE_DIRS}) + +file(GLOB ProtoFiles + "${CMAKE_CURRENT_SOURCE_DIR}/**/*.proto" + "${CMAKE_CURRENT_SOURCE_DIR}/*.proto" +) + +add_library(${PROJECT_NAME} ${ProtoFiles}) +target_link_libraries(${PROJECT_NAME} + PUBLIC + protobuf::libprotobuf +) +target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) + +if (MSVC AND BUILD_STATIC_LIBRARY) + set_property(TARGET ${PROJECT_NAME} PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") +endif() + +protobuf_generate(TARGET ${PROJECT_NAME} LANGUAGE cpp) diff --git a/source/protobuf/appearances.proto b/source/protobuf/appearances.proto new file mode 100644 index 00000000..c8078a39 --- /dev/null +++ b/source/protobuf/appearances.proto @@ -0,0 +1,263 @@ +syntax = "proto2"; + +package canary.protobuf.appearances; + +message Coordinate { + optional uint32 x = 1; + optional uint32 y = 2; + optional uint32 z = 3; +} + +enum PLAYER_ACTION { + PLAYER_ACTION_NONE = 0; + PLAYER_ACTION_LOOK = 1; + PLAYER_ACTION_USE = 2; + PLAYER_ACTION_OPEN = 3; + PLAYER_ACTION_AUTOWALK_HIGHLIGHT = 4; +} + +enum ITEM_CATEGORY { + ITEM_CATEGORY_ARMORS = 1; + ITEM_CATEGORY_AMULETS = 2; + ITEM_CATEGORY_BOOTS = 3; + ITEM_CATEGORY_CONTAINERS = 4; + ITEM_CATEGORY_DECORATION = 5; + ITEM_CATEGORY_FOOD = 6; + ITEM_CATEGORY_HELMETS_HATS = 7; + ITEM_CATEGORY_LEGS = 8; + ITEM_CATEGORY_OTHERS = 9; + ITEM_CATEGORY_POTIONS = 10; + ITEM_CATEGORY_RINGS = 11; + ITEM_CATEGORY_RUNES = 12; + ITEM_CATEGORY_SHIELDS = 13; + ITEM_CATEGORY_TOOLS = 14; + ITEM_CATEGORY_VALUABLES = 15; + ITEM_CATEGORY_AMMUNITION = 16; + ITEM_CATEGORY_AXES = 17; + ITEM_CATEGORY_CLUBS = 18; + ITEM_CATEGORY_DISTANCE_WEAPONS = 19; + ITEM_CATEGORY_SWORDS = 20; + ITEM_CATEGORY_WANDS_RODS = 21; + ITEM_CATEGORY_PREMIUM_SCROLLS = 22; + ITEM_CATEGORY_TIBIA_COINS = 23; + ITEM_CATEGORY_CREATURE_PRODUCTS = 24; +} + +enum PLAYER_PROFESSION { + PLAYER_PROFESSION_ANY = -1; + PLAYER_PROFESSION_NONE = 0; + PLAYER_PROFESSION_KNIGHT = 1; + PLAYER_PROFESSION_PALADIN = 2; + PLAYER_PROFESSION_SORCERER = 3; + PLAYER_PROFESSION_DRUID = 4; + PLAYER_PROFESSION_PROMOTED = 10; +} + +enum ANIMATION_LOOP_TYPE { + ANIMATION_LOOP_TYPE_PINGPONG = -1; + ANIMATION_LOOP_TYPE_INFINITE = 0; + ANIMATION_LOOP_TYPE_COUNTED = 1; +} + +enum HOOK_TYPE { + HOOK_TYPE_SOUTH = 1; + HOOK_TYPE_EAST = 2; +} + +message Appearances { + repeated Appearance object = 1; + repeated Appearance outfit = 2; + repeated Appearance effect = 3; + repeated Appearance missile = 4; + optional SpecialMeaningAppearanceIds special_meaning_appearance_ids = 5; +} + +message SpritePhase { + optional uint32 duration_min = 1; + optional uint32 duration_max = 2; +} + +message SpriteAnimation { + optional uint32 default_start_phase = 1; + optional bool synchronized = 2; + optional bool random_start_phase = 3; + optional ANIMATION_LOOP_TYPE loop_type = 4; + optional uint32 loop_count = 5; + repeated SpritePhase sprite_phase = 6; +} + +message Box { + optional uint32 x = 1; + optional uint32 y = 2; + optional uint32 width = 3; + optional uint32 height = 4; +} + +message SpriteInfo { + optional uint32 pattern_width = 1; + optional uint32 pattern_height = 2; + optional uint32 pattern_depth = 3; + optional uint32 layers = 4; + repeated uint32 sprite_id = 5; + optional uint32 bounding_square = 7; + optional SpriteAnimation animation = 6; + optional bool is_opaque = 8; + repeated Box bounding_box_per_direction = 9; +} + +message FrameGroup { + optional FIXED_FRAME_GROUP fixed_frame_group = 1; + optional uint32 id = 2; + optional SpriteInfo sprite_info = 3; +} + +message Appearance { + optional uint32 id = 1; + repeated FrameGroup frame_group = 2; + optional AppearanceFlags flags = 3; + optional string name = 4; + optional string description = 5; +} + +message AppearanceFlags { + optional AppearanceFlagBank bank = 1; + optional bool clip = 2; + optional bool bottom = 3; + optional bool top = 4; + optional bool container = 5; + optional bool cumulative = 6; + optional bool usable = 7; + optional bool forceuse = 8; + optional bool multiuse = 9; + optional AppearanceFlagWrite write = 10; + optional AppearanceFlagWriteOnce write_once = 11; + optional bool liquidpool = 12; + optional bool unpass = 13; + optional bool unmove = 14; + optional bool unsight = 15; + optional bool avoid = 16; + optional bool no_movement_animation = 17; + optional bool take = 18; + optional bool liquidcontainer = 19; + optional bool hang = 20; + optional AppearanceFlagHook hook = 21; + optional bool rotate = 22; + optional AppearanceFlagLight light = 23; + optional bool dont_hide = 24; + optional bool translucent = 25; + optional AppearanceFlagShift shift = 26; + optional AppearanceFlagHeight height = 27; + optional bool lying_object = 28; + optional bool animate_always = 29; + optional AppearanceFlagAutomap automap = 30; + optional AppearanceFlagLenshelp lenshelp = 31; + optional bool fullbank = 32; + optional bool ignore_look = 33; + optional AppearanceFlagClothes clothes = 34; + optional AppearanceFlagDefaultAction default_action = 35; + optional AppearanceFlagMarket market = 36; + optional bool wrap = 37; + optional bool unwrap = 38; + optional bool topeffect = 39; + repeated AppearanceFlagNPC npcsaledata = 40; + optional AppearanceFlagChangedToExpire changedtoexpire = 41; + optional bool corpse = 42; + optional bool player_corpse = 43; + optional AppearanceFlagCyclopedia cyclopediaitem = 44; + optional bool ammo = 45; + optional bool show_off_socket = 46; + optional bool reportable = 47; + optional AppearanceFlagUpgradeClassification upgradeclassification = 48; +} + +message AppearanceFlagUpgradeClassification { + optional uint32 upgrade_classification = 1; +} + +message AppearanceFlagBank { + optional uint32 waypoints = 1; +} + +message AppearanceFlagWrite { + optional uint32 max_text_length = 1; +} + +message AppearanceFlagWriteOnce { + optional uint32 max_text_length_once = 1; +} + +message AppearanceFlagLight { + optional uint32 brightness = 1; + optional uint32 color = 2; +} + +message AppearanceFlagHeight { + optional uint32 elevation = 1; +} + +message AppearanceFlagShift { + optional uint32 x = 1; + optional uint32 y = 2; +} + +message AppearanceFlagClothes { + optional uint32 slot = 1; +} + +message AppearanceFlagDefaultAction { + optional PLAYER_ACTION action = 1; +} + +message AppearanceFlagMarket { + optional ITEM_CATEGORY category = 1; + optional uint32 trade_as_object_id = 2; + optional uint32 show_as_object_id = 3; + optional string name = 4; + repeated PLAYER_PROFESSION restrict_to_profession = 5; + optional uint32 minimum_level = 6; +} + +message AppearanceFlagNPC { + optional string name = 1; + optional string location = 2; + optional uint32 sale_price = 3; + optional uint32 buy_price = 4; + optional uint32 currency_object_type_id = 5; + optional string currency_quest_flag_display_name = 6; +} + +message AppearanceFlagAutomap { + optional uint32 color = 1; +} + +message AppearanceFlagHook { + optional HOOK_TYPE south = 1; + optional HOOK_TYPE east = 2; +} + +message AppearanceFlagLenshelp { + optional uint32 id = 1; +} + +message AppearanceFlagChangedToExpire { + optional uint32 former_object_typeid = 1; +} + +message AppearanceFlagCyclopedia { + optional uint32 cyclopedia_type = 1; +} + +message SpecialMeaningAppearanceIds { + optional uint32 gold_coin_id = 1; + optional uint32 platinum_coin_id = 2; + optional uint32 crystal_coin_id = 3; + optional uint32 tibia_coin_id = 4; + optional uint32 stamped_letter_id = 5; + optional uint32 supply_stash_id = 6; +} + +enum FIXED_FRAME_GROUP { + FIXED_FRAME_GROUP_OUTFIT_IDLE = 0; + FIXED_FRAME_GROUP_OUTFIT_MOVING = 1; + FIXED_FRAME_GROUP_OBJECT_INITIAL = 2; +} diff --git a/source/raw_brush.cpp b/source/raw_brush.cpp index efdb7a02..548b07b9 100644 --- a/source/raw_brush.cpp +++ b/source/raw_brush.cpp @@ -36,7 +36,7 @@ RAWBrush::~RAWBrush() { int RAWBrush::getLookID() const { if (itemtype) { - return itemtype->clientID; + return itemtype->id; } return 0; } diff --git a/source/settings.cpp b/source/settings.cpp index b950674f..14f1deb3 100644 --- a/source/settings.cpp +++ b/source/settings.cpp @@ -18,8 +18,9 @@ #include "main.h" #include "settings.h" + #include "gui_ids.h" -#include "client_version.h" +#include "client_assets.h" Settings g_settings; @@ -214,17 +215,15 @@ void Settings::IO(IOMode mode) { Int(SHOW_TOOLTIPS, 1); Int(SHOW_ONLY_TILEFLAGS, 0); Int(SHOW_ONLY_MODIFIED_TILES, 0); - Int(SHOW_PREVIEW, 1); + Int(SHOW_PREVIEW, 0); Int(SHOW_WALL_HOOKS, 0); Int(SHOW_PICKUPABLES, 0); Int(SHOW_MOVEABLES, 0); section("Version"); Int(VERSION_ID, 0); - Int(CHECK_SIGNATURES, 1); Int(USE_CUSTOM_DATA_DIRECTORY, 0); String(DATA_DIRECTORY, ""); - String(EXTENSIONS_DIRECTORY, ""); String(ASSETS_DATA_DIRS, ""); section("Editor"); @@ -263,7 +262,7 @@ void Settings::IO(IOMode mode) { Int(DEFAULT_SPAWN_NPC_TIME, 60); Int(MAX_SPAWN_NPC_RADIUS, 30); Int(CURRENT_SPAWN_NPC_RADIUS, 1); - Int(DEFAULT_CLIENT_VERSION, CLIENT_VERSION_NONE); + Int(DEFAULT_CLIENT_VERSION, std::atoi(ClientAssets::getVersionName().c_str())); Int(RAW_LIKE_SIMONE, 1); Int(ONLY_ONE_INSTANCE, 1); Int(USE_OTBM_4_FOR_ALL_MAPS, 0); @@ -285,7 +284,6 @@ void Settings::IO(IOMode mode) { Int(HIDE_ITEMS_WHEN_ZOOMED, 1); String(SCREENSHOT_DIRECTORY, ""); String(SCREENSHOT_FORMAT, "png"); - IntToSave(USE_MEMCACHED_SPRITES, 0); Int(MINIMAP_UPDATE_DELAY, 333); Int(MINIMAP_VIEW_BOX, 1); String(MINIMAP_EXPORT_DIR, ""); @@ -364,7 +362,7 @@ void Settings::load() { use_file_cfg = true; g_settings.setInteger(Config::INDIRECTORY_INSTALLATION, 1); } else { // Use registry - conf = newd wxConfig("Remere's Map Editor", "Remere", "", "", wxCONFIG_USE_GLOBAL_FILE); + conf = newd wxConfig("Canary Map Editor", "OpenTibiaBR", "", "", wxCONFIG_USE_GLOBAL_FILE); g_settings.setInteger(Config::INDIRECTORY_INSTALLATION, 0); } #else diff --git a/source/settings.h b/source/settings.h index 26f60540..d25bff48 100644 --- a/source/settings.h +++ b/source/settings.h @@ -27,7 +27,6 @@ namespace Config { USE_CUSTOM_DATA_DIRECTORY, DATA_DIRECTORY, - EXTENSIONS_DIRECTORY, MERGE_MOVE, TEXTURE_MANAGEMENT, @@ -35,8 +34,6 @@ namespace Config { TEXTURE_CLEAN_THRESHOLD, TEXTURE_LONGEVITY, HARD_REFRESH_RATE, - USE_MEMCACHED_SPRITES, - USE_MEMCACHED_SPRITES_TO_SAVE, SOFTWARE_CLEAN_THRESHOLD, SOFTWARE_CLEAN_SIZE, TRANSPARENT_FLOORS, @@ -108,7 +105,6 @@ namespace Config { ASSETS_DATA_DIRS, DEFAULT_CLIENT_VERSION, - CHECK_SIGNATURES, CURSOR_RED, CURSOR_GREEN, diff --git a/source/sprite_appearances.cpp b/source/sprite_appearances.cpp new file mode 100644 index 00000000..d9b74618 --- /dev/null +++ b/source/sprite_appearances.cpp @@ -0,0 +1,282 @@ +////////////////////////////////////////////////////////////////////// +// This file is part of Canary Map Editor +////////////////////////////////////////////////////////////////////// +// Canary Map Editor is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Canary Map Editor is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +////////////////////////////////////////////////////////////////////// + +#include "main.h" + +#include "sprite_appearances.h" +#include "settings.h" +#include "filehandle.h" +#include "gui.h" + +#include + +// APPEARANCES +#define BYTES_IN_SPRITE_SHEET 384 * 384 * 4 +#define LZMA_UNCOMPRESSED_SIZE BYTES_IN_SPRITE_SHEET + 122 +#define LZMA_HEADER_SIZE LZMA_PROPS_SIZE + 8 +#define SPRITE_SHEET_WIDTH_BYTES 384 * 4 + +namespace fs = std::filesystem; + +SpriteAppearances g_spriteAppearances; + +void SpriteAppearances::init() { + // in tibia 12.81 there is currently 3482 sheets + sheets.reserve(4000); +} + +void SpriteAppearances::terminate() { + unload(); +} + +bool SpriteAppearances::loadCatalogContent(const std::string &dir, bool loadData /* true*/) { + using json = nlohmann::json; + fs::path catalogPath = fs::path(dir) / fs::path("catalog-content.json"); + if (!fs::exists(catalogPath)) { + spdlog::error("catalog-content.json is not present in given directory. {}", catalogPath.string().c_str()); + throw std::exception(fmt::format("catalog-content.json is not present in given directory: {}", catalogPath.string()).c_str()); + return false; + } + + std::ifstream file(catalogPath, std::ios::in); + if (!file.is_open()) { + spdlog::error("Unable to open catalog-content.json."); + throw std::exception("Unable to open catalog-content.json."); + return false; + } + + json document = json::parse(file, nullptr, false); + + file.close(); + + for (const auto &obj : document) { + const auto &type = obj["type"]; + if (type == "appearances") { + appearanceFile = obj["file"]; + } else if (type == "sprite") { + int lastSpriteId = obj["lastspriteid"].get(); + + SpriteSheetPtr sheet = SpriteSheetPtr(new SpriteSheet(obj["firstspriteid"].get(), lastSpriteId, static_cast(obj["spritetype"].get()), (fs::path(dir) / fs::path(obj["file"].get())).string())); + sheets.push_back(sheet); + + spritesCount = std::max(spritesCount, lastSpriteId); + + if (loadData) { + if (!loadSpriteSheet(sheet)) { + spdlog::error("[SpriteAppearances::loadCatalogContent] - Unable to load sprite sheet"); + return false; + } + } + } + } + return true; +} + +bool SpriteAppearances::loadSpriteSheet(const SpriteSheetPtr &sheet) { + if (sheet->loaded) { + return false; + } + + std::ifstream file(sheet->path, std::ios::binary | std::ios::in); + if (!file.is_open()) { + spdlog::error("[SpriteAppearances::loadSpriteSheet] - Unable to open given sheets files"); + throw std::exception("Unable to open given file."); + } + + std::vector buffer((std::istreambuf_iterator(file)), (std::istreambuf_iterator())); + + int pos = 0; + + file.close(); + + /* + CIP's header, always 32 (0x20) bytes. + Header format: + [0x00, X): A variable number of NULL (0x00) bytes. The amount of pad-bytes can vary depending on how many + bytes the "7-bit integer encoded LZMA file size" take. + [X, X + 0x05): The constant byte sequence [0x70 0x0A 0xFA 0x80 0x24] + [X + 0x05, 0x20]: LZMA file size (Note: excluding the 32 bytes of this header) encoded as a 7-bit integer + */ + + while (buffer[pos++] == 0x00) + ; + pos += 4; + while ((buffer[pos++] & 0x80) == 0x80) + ; + + uint8_t lclppb = buffer[pos++]; + + lzma_options_lzma options {}; + options.lc = lclppb % 9; + + int remainder = lclppb / 9; + options.lp = remainder % 5; + options.pb = remainder / 5; + + uint32_t dictionarySize = 0; + for (uint8_t i = 0; i < 4; ++i) { + dictionarySize += buffer[pos++] << (i * 8); + } + + options.dict_size = dictionarySize; + + pos += 8; // cip compressed size + + lzma_stream stream = LZMA_STREAM_INIT; + + lzma_filter filters[2] = { + lzma_filter { LZMA_FILTER_LZMA1, &options }, + lzma_filter { LZMA_VLI_UNKNOWN, NULL } + }; + + lzma_ret ret = lzma_raw_decoder(&stream, filters); + if (ret != LZMA_OK) { + spdlog::error("Failed to initialize lzma raw decoder result: {}", static_cast(ret)); + throw std::exception(fmt::format("Failed to initialize lzma raw decoder result: {}", static_cast(ret)).c_str()); + } + + std::unique_ptr decompressed = std::make_unique(LZMA_UNCOMPRESSED_SIZE); // uncompressed size, bmp file + 122 bytes header + + stream.next_in = &buffer[pos]; + stream.next_out = decompressed.get(); + stream.avail_in = buffer.size(); + stream.avail_out = LZMA_UNCOMPRESSED_SIZE; + + ret = lzma_code(&stream, LZMA_RUN); + if (ret != LZMA_STREAM_END) { + spdlog::error("Failed to decode lzma buffer result: {}", static_cast(ret)); + throw std::exception(fmt::format("failed to decode lzma buffer result: {}", static_cast(ret)).c_str()); + } + + lzma_end(&stream); // free memory + + // pixel data start (bmp header end offset) + uint32_t data; + std::memcpy(&data, decompressed.get() + 10, sizeof(uint32_t)); + + uint8_t* bufferStart = decompressed.get() + data; + + // Flip vertically + for (int y = 0; y < 192; ++y) { + uint8_t* itr1 = &bufferStart[y * SPRITE_SHEET_WIDTH_BYTES]; + uint8_t* itr2 = &bufferStart[(384 - y - 1) * SPRITE_SHEET_WIDTH_BYTES]; + + for (std::size_t x = 0; x < SPRITE_SHEET_WIDTH_BYTES; ++x) { + std::swap(*(itr1 + x), *(itr2 + x)); + } + } + + // Fix magenta (remove transparency by black collor) + for (int offset = 0; offset < BYTES_IN_SPRITE_SHEET; offset += 4) { + std::memcpy(&data, bufferStart + offset, 4); + if (data == 0xFF00FF) { + std::memset(bufferStart + offset, 0x00, 4); + } + } + + sheet->data = std::make_unique(LZMA_UNCOMPRESSED_SIZE); + std::memcpy(sheet->data.get(), bufferStart, BYTES_IN_SPRITE_SHEET); + + sheet->loaded = true; + return true; +} + +void SpriteAppearances::unload() { + spritesCount = 0; + sheets.clear(); +} + +SpriteSheetPtr SpriteAppearances::getSheetBySpriteId(int id, bool load /* = true */) { + if (id == 0) { + return nullptr; + } + + // find sheet + auto sheetIt = std::find_if(sheets.begin(), sheets.end(), [=](const SpriteSheetPtr &sheet) { + return id >= sheet->firstId && id <= sheet->lastId; + }); + + if (sheetIt == sheets.end()) { + return nullptr; + } + + const SpriteSheetPtr &sheet = *sheetIt; + if (load && !sheet->loaded) { + loadSpriteSheet(sheet); + } + + return sheet; +} + +wxImage SpriteAppearances::getWxImageBySpriteId(int id, bool toSavePng /* = false*/) { + const auto &sprite = getSprite(id); + if (!sprite) { + spdlog::error("[{}] - Unknown sprite id", __func__); + return {}; + } + + const int width = sprite->size.width; + const int height = sprite->size.height; + auto pixels = sprite->pixels.data(); + wxImage image(width, height); + for (int y = 0; y < height; ++y) { + for (int x = 0; x < width; ++x) { + const int index = (y * width + x) * 4; + const uint8_t r = pixels[index + 2]; + const uint8_t g = pixels[index + 1]; + const uint8_t b = pixels[index]; + image.SetRGB(x, y, r, g, b); + } + } + + return image; +} + +SpritePtr SpriteAppearances::getSprite(int spriteId) { + // caching + auto it = sprites.find(spriteId); + if (it != sprites.end()) { + return it->second; + } + + const auto &sheet = getSheetBySpriteId(spriteId); + if (!sheet || !sheet->loaded) { + return nullptr; + } + + auto spriteWidth = sheet->getSpriteSize().width; + auto spriteHeight = sheet->getSpriteSize().height; + + SpritePtr sprite = SpritePtr(new Sprites(spriteWidth, spriteHeight)); + + int spriteOffset = spriteId - sheet->firstId; + int allColumns = spriteWidth == 32 ? 12 : 6; // 64 pixel width == 6 columns each 64x or 32 pixels, 12 columns + int spriteRow = static_cast(std::floor(static_cast(spriteOffset) / static_cast(allColumns))); + int spriteColumn = spriteOffset % allColumns; + + int spriteWidthBytes = spriteWidth * 4; + + for (int height = spriteHeight * spriteRow, offset = 0; height < spriteHeight + (spriteRow * spriteHeight); height++, offset++) { + auto bufferData = &sheet->data[(height * SPRITE_SHEET_WIDTH_BYTES) + (spriteColumn * spriteWidthBytes)]; + std::memcpy(&sprite->pixels[offset * spriteWidthBytes], bufferData, spriteWidthBytes); + } + + // cache it for faster later access + sprites[spriteId] = sprite; + + return sprite; +} diff --git a/source/sprite_appearances.h b/source/sprite_appearances.h new file mode 100644 index 00000000..93bf82c8 --- /dev/null +++ b/source/sprite_appearances.h @@ -0,0 +1,184 @@ +////////////////////////////////////////////////////////////////////// +// This file is part of Canary Map Editor +////////////////////////////////////////////////////////////////////// +// Canary Map Editor is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Canary Map Editor is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +////////////////////////////////////////////////////////////////////// + +#ifndef SPRITEAPPEARANCES_H +#define SPRITEAPPEARANCES_H + +#include "definitions.h" +#include "main.h" +#include "graphics.h" + +class GameSprite; + +enum class SpriteLayout { + ONE_BY_ONE = 0, + ONE_BY_TWO = 1, + TWO_BY_ONE = 2, + TWO_BY_TWO = 3 +}; + +struct SpritesSize { +public: + SpritesSize(int height, int width) : + height(height), width(width) { } + + void resize(int height, int width) { + this->height = height; + this->width = width; + } + void setHeight(int height) { + this->height = height; + } + void setWidth(int width) { + this->width = width; + } + int area() const { + return width * height; + } + + int height = 0; + int width = 0; +}; + +struct Sprites { +public: + Sprites(int32_t width, int32_t height) : + size(width, height) { + pixels.resize(width * height * 4, 0); + } + + bool save(const std::string &file, bool fixMagenta = false) { + wxImage image(size.width, size.height); + + for (int y = 0; y < size.height; ++y) { + for (int x = 0; x < size.width; ++x) { + const int index = (y * size.width + x) * 4; + const uint8_t r = pixels[index + 2]; + const uint8_t g = pixels[index + 1]; + const uint8_t b = pixels[index]; + image.SetRGB(x, y, r, g, b); + } + } + + return image.SaveFile(wxString(file), wxBITMAP_TYPE_PNG); + } + + std::vector pixels; + SpritesSize size; +}; + +class SpriteSheet { +public: + SpriteSheet(int firstId, int lastId, SpriteLayout spriteLayout, const std::string &path) : + firstId(firstId), lastId(lastId), spriteLayout(spriteLayout), path(path) { } + + SpritesSize getSpriteSize() { + SpritesSize size(rme::SpritePixels, rme::SpritePixels); + + switch (spriteLayout) { + case SpriteLayout::ONE_BY_ONE: + break; + case SpriteLayout::ONE_BY_TWO: + size.setHeight(64); + break; + case SpriteLayout::TWO_BY_ONE: + size.setWidth(64); + break; + case SpriteLayout::TWO_BY_TWO: + size.resize(64, 64); + break; + default: + break; + } + return size; + } + + bool exportSheetImage(const std::string &file, bool fixMagenta = false) { + wxImage image(384, 384, data.get(), true); + return image.SaveFile(wxString(file), wxBITMAP_TYPE_PNG); + }; + + int firstId = 0; + int lastId = 0; + SpriteLayout spriteLayout = SpriteLayout::ONE_BY_ONE; + std::unique_ptr data; + std::string path; + bool loaded = false; +}; + +using SpritePtr = std::shared_ptr; +using SpriteSheetPtr = std::shared_ptr; + +//@bindsingleton g_spriteAppearances +class SpriteAppearances { +public: + void init(); + void terminate(); + + void unload(); + + // sprites + void exportSpriteImage(int id, const std::string &path); + SpritePtr getSprite(int spriteId); + + int getSpritesCount() { + return spritesCount; + } + + void setSpritesCount(int count) { + spritesCount = count; + } + int getSpritesCount() const { + return spritesCount; + } + /** + Returns a wxImage object containing the sprite image data for a given sprite ID. + @param id The ID of the sprite to retrieve. + @param toSavePng Determines if the image should be saved as a PNG file. + @return A wxImage object containing the sprite image data. + @remarks The image data is retrieved from a sprite sheet using the provided ID. + If toSavePng is set to true, the black color (0, 0, 0) is set to transparent by default and the image will be saved as a PNG file (with transparent background). + Use to save image: image.SaveFile(g_gui.GetDataDirectory().ToStdString() + "image.png", wxBITMAP_TYPE_PNG); + */ + wxImage getWxImageBySpriteId(int id, bool toSavePng = false); + + const std::string getAppearanceFileName() const { + return appearanceFile; + } + + bool loadCatalogContent(const std::string &dir, bool loadData = true); + bool loadSpriteSheet(const SpriteSheetPtr &sheet); + void saveSheetToFileBySprite(int id, const std::string &file); + void saveSheetToFile(const SpriteSheetPtr &sheet, const std::string &file); + SpriteSheetPtr getSheetBySpriteId(int id, bool load = true); + + void addSpriteSheet(SpriteSheetPtr sheet) { + sheets.push_back(sheet); + } + + void saveSpriteToFile(int id, const std::string &file); + +private: + int spritesCount = 0; + std::vector sheets; + std::map sprites; + std::string appearanceFile; +}; + +extern SpriteAppearances g_spriteAppearances; + +#endif diff --git a/source/tileset.cpp b/source/tileset.cpp index 65581e29..f950a9b9 100644 --- a/source/tileset.cpp +++ b/source/tileset.cpp @@ -244,9 +244,8 @@ void TilesetCategory::loadBrush(pugi::xml_node node, wxArrayString &warnings) { std::vector tempBrushVector; for (uint16_t id = fromId; id <= toId; ++id) { ItemType* type = g_items.getRawItemType(id); - if (!type) { - warnings.push_back(wxString::Format("Brush: %s, From: %d, To: %d", wxstr(brushName), fromId, toId)); - warnings.push_back("Unknown item id #" + std::to_string(id) + "."); + // Ignore item if not exist + if (!type || type->id == 0) { continue; } diff --git a/source/tileset.h b/source/tileset.h index af4e5bc7..60a76f24 100644 --- a/source/tileset.h +++ b/source/tileset.h @@ -92,6 +92,6 @@ class Tileset { friend class TilesetCategory; }; -typedef std::map TilesetContainer; +using TilesetContainer = std::map; #endif diff --git a/source/town.h b/source/town.h index 82c14e76..df41d3a9 100644 --- a/source/town.h +++ b/source/town.h @@ -90,15 +90,9 @@ class Towns { TownMap::iterator find(uint32_t id) { return towns.find(id); } -#ifdef __VISUALC__ // C++0x compliance to some degree :) TownMap::iterator erase(TownMap::iterator iter) noexcept { return towns.erase(iter); } -#else - void erase(TownMap::iterator iter) { - towns.erase(iter); - } -#endif private: TownMap towns; diff --git a/source/welcome_dialog.cpp b/source/welcome_dialog.cpp index b2bd4c0a..b21465e5 100644 --- a/source/welcome_dialog.cpp +++ b/source/welcome_dialog.cpp @@ -35,13 +35,13 @@ void WelcomeDialog::OnButtonClicked(const wxMouseEvent &event) { wxPoint click_point = event.GetPosition(); if (click_point.x > 0 && click_point.x < button_size.x && click_point.y > 0 && click_point.y < button_size.x) { if (button->GetAction() == wxID_PREFERENCES) { - PreferencesWindow preferences_window(m_welcome_dialog_panel, true); + PreferencesWindow preferences_window(m_welcome_dialog_panel); preferences_window.ShowModal(); m_welcome_dialog_panel->updateInputs(); } else { wxCommandEvent action_event(WELCOME_DIALOG_ACTION); if (button->GetAction() == wxID_OPEN) { - wxString wildcard = g_settings.getInteger(Config::USE_OTGZ) != 0 ? "(*.otbm;*.otgz)|*.otbm;*.otgz" : "(*.otbm)|*.otbm|Compressed OpenTibia Binary Map (*.otgz)|*.otgz"; + wxString wildcard = "OpenTibia Binary Map (*.otbm)|*.otbm"; wxFileDialog file_dialog(this, "Open map file", "", "", wildcard, wxFD_OPEN | wxFD_FILE_MUST_EXIST); if (file_dialog.ShowModal() == wxID_OK) { action_event.SetString(file_dialog.GetPath()); diff --git a/vcpkg.json b/vcpkg.json index 5439b620..5890b6b7 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -8,7 +8,10 @@ "nlohmann-json", "fmt", "wxwidgets", - "opengl" + "opengl", + "protobuf", + "liblzma", + "spdlog" ], "builtin-baseline": "4a600e9fea71bd7872080cbb716797e04d30e6d3" } diff --git a/vcproj/Project/RME.vcxproj b/vcproj/Project/RME.vcxproj index de70b1c0..2b41d1d2 100644 --- a/vcproj/Project/RME.vcxproj +++ b/vcproj/Project/RME.vcxproj @@ -260,11 +260,7 @@ - - - - @@ -368,8 +364,8 @@ - - + + @@ -413,6 +409,8 @@ + +