From 49850ab6bbc77103ef0b56343ef7aead4955a9f5 Mon Sep 17 00:00:00 2001 From: Sarah Wesker Date: Wed, 3 Apr 2024 23:45:36 -0400 Subject: [PATCH] update items properties and revert items via lua --- data/items/{items.lua => #items.lua} | 0 data/items/items.xml | 21800 +++++++++++++++++++++++++ src/const.h | 3 + src/creature.cpp | 23 + src/enums.h | 21 +- src/item.cpp | 126 +- src/item.h | 46 +- src/items.cpp | 1948 ++- src/items.h | 189 +- src/luagame.cpp | 16 - src/luaitem.cpp | 58 + src/luaitemtype.cpp | 34 +- src/luamonster.cpp | 4 - src/luanpc.cpp | 4 - src/luaplayer.cpp | 4 - src/luascript.cpp | 27 +- src/luascript.h | 2 + src/movement.cpp | 28 + src/otserv.cpp | 5 + src/player.cpp | 12 + src/player.h | 14 + src/tile.cpp | 4 +- 22 files changed, 23853 insertions(+), 515 deletions(-) rename data/items/{items.lua => #items.lua} (100%) create mode 100644 data/items/items.xml diff --git a/data/items/items.lua b/data/items/#items.lua similarity index 100% rename from data/items/items.lua rename to data/items/#items.lua diff --git a/data/items/items.xml b/data/items/items.xml new file mode 100644 index 0000000..e365891 --- /dev/null +++ b/data/items/items.xml @@ -0,0 +1,21800 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/const.h b/src/const.h index 1e8873e..7d28ff7 100644 --- a/src/const.h +++ b/src/const.h @@ -179,6 +179,7 @@ enum FluidColors_t : uint8_t FLUID_YELLOW, FLUID_WHITE, FLUID_PURPLE, + FLUID_BLACK, }; enum FluidTypes_t : uint8_t @@ -191,6 +192,7 @@ enum FluidTypes_t : uint8_t FLUID_LEMONADE = FLUID_YELLOW, FLUID_MILK = FLUID_WHITE, FLUID_MANA = FLUID_PURPLE, + FLUID_INK = FLUID_BLACK, FLUID_LIFE = FLUID_RED + 8, FLUID_OIL = FLUID_BROWN + 8, @@ -300,6 +302,7 @@ enum WeaponType_t : uint8_t WEAPON_DISTANCE, WEAPON_WAND, WEAPON_AMMO, + WEAPON_QUIVER, }; enum Ammo_t : uint8_t diff --git a/src/creature.cpp b/src/creature.cpp index ce30dfc..e0e2ab9 100644 --- a/src/creature.cpp +++ b/src/creature.cpp @@ -881,6 +881,29 @@ BlockType_t Creature::blockHit(Creature* attacker, CombatType_t combatType, int3 } if (attacker) { + if (Player* attackerPlayer = attacker->getPlayer()) { + for (int32_t slot = CONST_SLOT_FIRST; slot <= CONST_SLOT_LAST; ++slot) { + if (!attackerPlayer->isItemAbilityEnabled(static_cast(slot))) { + continue; + } + + Item* item = attackerPlayer->getInventoryItem(static_cast(slot)); + if (!item) { + continue; + } + + const uint16_t boostPercent = item->getBoostPercent(combatType); + if (boostPercent != 0) { + damage += std::round(damage * (boostPercent / 100.)); + } + } + } + + if (damage <= 0) { + damage = 0; + blockType = BLOCK_ARMOR; + } + if (combatType != COMBAT_HEALING) { attacker->onAttackedCreature(this); attacker->onAttackedCreatureBlockHit(blockType); diff --git a/src/enums.h b/src/enums.h index 5e926e0..072c90b 100644 --- a/src/enums.h +++ b/src/enums.h @@ -82,8 +82,9 @@ enum itemAttrTypes : uint32_t ITEM_ATTRIBUTE_WRAPID = 1 << 24, ITEM_ATTRIBUTE_STOREITEM = 1 << 25, ITEM_ATTRIBUTE_ATTACK_SPEED = 1 << 26, + ITEM_ATTRIBUTE_OPENCONTAINER = 1 << 27, ITEM_ATTRIBUTE_DURATION_MIN = ITEM_ATTRIBUTE_DURATION, - ITEM_ATTRIBUTE_DURATION_MAX = 1 << 27, + ITEM_ATTRIBUTE_DURATION_MAX = 1 << 28, ITEM_ATTRIBUTE_CUSTOM = 1U << 31 }; @@ -174,6 +175,7 @@ enum RaceType_t : uint8_t RACE_UNDEAD, RACE_FIRE, RACE_ENERGY, + RACE_INK, }; enum CombatType_t : uint16_t @@ -604,6 +606,7 @@ enum CombatOrigin ORIGIN_MELEE, ORIGIN_RANGED, ORIGIN_WAND, + ORIGIN_REFLECT, }; struct CombatDamage @@ -632,4 +635,20 @@ enum MonstersEvent_t : uint8_t MONSTERS_EVENT_SAY = 5, }; +struct Reflect +{ + Reflect() = default; + Reflect(uint16_t percent, uint16_t chance) : percent(percent), chance(chance){}; + + Reflect& operator+=(const Reflect& other) + { + percent += other.percent; + chance = std::min(100, chance + other.chance); + return *this; + } + + uint16_t percent = 0; + uint16_t chance = 0; +}; + #endif diff --git a/src/item.cpp b/src/item.cpp index d2b0baa..71b251b 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -405,12 +405,12 @@ Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream& propStream) } case ATTR_DESC: { - auto [desc, ok] = propStream.readString(); + auto [text, ok] = propStream.readString(); if (!ok) { return ATTR_READ_ERROR; } - setSpecialDescription(desc); + setSpecialDescription(text); break; } @@ -566,9 +566,77 @@ Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream& propStream) break; } - // these should be handled through derived classes - // If these are called then something has changed in the items.xml since the map was saved - // just read the values + case ATTR_WRAPID: { + uint16_t wrapId; + if (!propStream.read(wrapId)) { + return ATTR_READ_ERROR; + } + + setIntAttr(ITEM_ATTRIBUTE_WRAPID, wrapId); + break; + } + + case ATTR_STOREITEM: { + uint8_t storeItem; + if (!propStream.read(storeItem)) { + return ATTR_READ_ERROR; + } + + setIntAttr(ITEM_ATTRIBUTE_STOREITEM, storeItem); + break; + } + + case ATTR_OPENCONTAINER: { + uint8_t openContainer; + if (!propStream.read(openContainer)) { + return ATTR_READ_ERROR; + } + + setIntAttr(ITEM_ATTRIBUTE_OPENCONTAINER, openContainer); + break; + } + + case ATTR_REFLECT: { + uint16_t size; + if (!propStream.read(size)) { + return ATTR_READ_ERROR; + } + + for (uint16_t i = 0; i < size; ++i) { + CombatType_t combatType; + Reflect reflect; + + if (!propStream.read(combatType) || !propStream.read(reflect.percent) || + !propStream.read(reflect.chance)) { + return ATTR_READ_ERROR; + } + + getAttributes()->reflect[combatType] = reflect; + } + break; + } + + case ATTR_BOOST: { + uint16_t size; + if (!propStream.read(size)) { + return ATTR_READ_ERROR; + } + + for (uint16_t i = 0; i < size; ++i) { + CombatType_t combatType; + uint16_t percent; + + if (!propStream.read(combatType) || !propStream.read(percent)) { + return ATTR_READ_ERROR; + } + + getAttributes()->boostPercent[combatType] = percent; + } + break; + } + + // these should be handled through derived classes If these are called then something has changed in the + // items.xml since the map was saved just read the values // Depot class case ATTR_DEPOT_ID: { @@ -601,6 +669,14 @@ Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream& propStream) break; } + // Podium class + case ATTR_PODIUMOUTFIT: { + if (!propStream.skip(15)) { + return ATTR_READ_ERROR; + } + break; + } + // Teleport class case ATTR_TELE_DEST: { if (!propStream.skip(5)) { @@ -971,10 +1047,43 @@ LightInfo Item::getLightInfo() const return {it.lightLevel, it.lightColor}; } +Reflect Item::getReflect(CombatType_t combatType, bool total /* = true */) const +{ + const ItemType& it = Item::items[id]; + + Reflect reflect; + if (attributes) { + reflect += attributes->getReflect(combatType); + } + + if (total && it.abilities) { + reflect += it.abilities->reflect[combatTypeToIndex(combatType)]; + } + + return reflect; +} + +uint16_t Item::getBoostPercent(CombatType_t combatType, bool total /* = true */) const +{ + const ItemType& it = Item::items[id]; + + uint16_t boostPercent = 0; + if (attributes) { + boostPercent += attributes->getBoostPercent(combatType); + } + + if (total && it.abilities) { + boostPercent += it.abilities->boostPercent[combatTypeToIndex(combatType)]; + } + + return boostPercent; +} + std::string ItemAttributes::emptyString; int64_t ItemAttributes::emptyInt; double ItemAttributes::emptyDouble; bool ItemAttributes::emptyBool; +Reflect ItemAttributes::emptyReflect; std::string_view ItemAttributes::getStrAttr(itemAttrTypes type) const { @@ -1000,11 +1109,8 @@ void ItemAttributes::setStrAttr(itemAttrTypes type, std::string_view value) } Attribute& attr = getAttr(type); - if (attr.value.string) { - *attr.value.string = std::string{value}; - } else { - attr.value.string = new std::string{value}; - } + delete attr.value.string; + attr.value.string = new std::string(value); } void ItemAttributes::removeAttribute(itemAttrTypes type) diff --git a/src/item.h b/src/item.h index 7216df1..ec8b1f0 100644 --- a/src/item.h +++ b/src/item.h @@ -84,9 +84,14 @@ enum AttrTypes_t ATTR_SHOOTRANGE = 33, ATTR_CUSTOM_ATTRIBUTES = 34, ATTR_DECAYTO = 35, - // ATTR_WRAPID = 36, - // ATTR_STOREITEM = 37, + ATTR_WRAPID = 36, + ATTR_STOREITEM = 37, ATTR_ATTACK_SPEED = 38, + ATTR_OPENCONTAINER = 39, + ATTR_PODIUMOUTFIT = 40, + // ATTR_TIER = 41, // mapeditor + ATTR_REFLECT = 42, + ATTR_BOOST = 43, }; enum Attr_ReadValue @@ -270,6 +275,7 @@ class ItemAttributes static int64_t emptyInt; static double emptyDouble; static bool emptyBool; + static Reflect emptyReflect; using CustomAttributeMap = std::unordered_map; @@ -345,6 +351,20 @@ class ItemAttributes std::vector attributes; uint32_t attributeBits = 0; + std::map reflect; + std::map boostPercent; + + const Reflect& getReflect(CombatType_t combatType) + { + auto it = reflect.find(combatType); + return it != reflect.end() ? it->second : emptyReflect; + } + int16_t getBoostPercent(CombatType_t combatType) + { + auto it = boostPercent.find(combatType); + return it != boostPercent.end() ? it->second : 0; + } + std::string_view getStrAttr(itemAttrTypes type) const; void setStrAttr(itemAttrTypes type, std::string_view value); @@ -440,8 +460,8 @@ class ItemAttributes ITEM_ATTRIBUTE_ATTACK | ITEM_ATTRIBUTE_DEFENSE | ITEM_ATTRIBUTE_EXTRADEFENSE | ITEM_ATTRIBUTE_ARMOR | ITEM_ATTRIBUTE_HITCHANCE | ITEM_ATTRIBUTE_SHOOTRANGE | ITEM_ATTRIBUTE_OWNER | ITEM_ATTRIBUTE_DURATION | ITEM_ATTRIBUTE_DECAYSTATE | ITEM_ATTRIBUTE_CORPSEOWNER | ITEM_ATTRIBUTE_CHARGES | ITEM_ATTRIBUTE_FLUIDTYPE | - ITEM_ATTRIBUTE_DOORID | ITEM_ATTRIBUTE_DECAYTO | ITEM_ATTRIBUTE_ATTACK_SPEED; - + ITEM_ATTRIBUTE_DOORID | ITEM_ATTRIBUTE_DECAYTO | ITEM_ATTRIBUTE_WRAPID | ITEM_ATTRIBUTE_STOREITEM | + ITEM_ATTRIBUTE_ATTACK_SPEED | ITEM_ATTRIBUTE_OPENCONTAINER; const static uint32_t stringAttributeTypes = ITEM_ATTRIBUTE_DESCRIPTION | ITEM_ATTRIBUTE_TEXT | ITEM_ATTRIBUTE_WRITER | ITEM_ATTRIBUTE_NAME | ITEM_ATTRIBUTE_ARTICLE | ITEM_ATTRIBUTE_PLURALNAME; @@ -495,7 +515,7 @@ class Item : virtual public Thing std::string_view getStrAttr(itemAttrTypes type) const { if (!attributes) { - return ""; + return ItemAttributes::emptyString; } return attributes->getStrAttr(type); } @@ -775,6 +795,12 @@ class Item : virtual public Thing uint32_t getWorth() const; LightInfo getLightInfo() const; + void setReflect(CombatType_t combatType, const Reflect& reflect) { getAttributes()->reflect[combatType] = reflect; } + Reflect getReflect(CombatType_t combatType, bool total = true) const; + + void setBoostPercent(CombatType_t combatType, uint16_t value) { getAttributes()->boostPercent[combatType] = value; } + uint16_t getBoostPercent(CombatType_t combatType, bool total = true) const; + bool hasProperty(ITEMPROPERTY prop) const; bool isBlocking() const { return items[id].blockSolid; } bool isStackable() const { return items[id].stackable; } @@ -791,6 +817,16 @@ class Item : virtual public Thing return it.rotatable && it.rotateTo; } bool hasWalkStack() const { return items[id].walkStack; } + bool isSupply() const { return items[id].isSupply(); } + + void setStoreItem(bool storeItem) { setIntAttr(ITEM_ATTRIBUTE_STOREITEM, static_cast(storeItem)); } + bool isStoreItem() const + { + if (hasAttribute(ITEM_ATTRIBUTE_STOREITEM)) { + return getIntAttr(ITEM_ATTRIBUTE_STOREITEM) == 1; + } + return items[id].storeItem; + } std::string_view getName() const { diff --git a/src/items.cpp b/src/items.cpp index f8ef627..ccc18b6 100644 --- a/src/items.cpp +++ b/src/items.cpp @@ -15,6 +15,292 @@ extern MoveEvents* g_moveEvents; extern Weapons* g_weapons; extern Scripts* g_scripts; +namespace { + +const std::unordered_map ItemParseAttributesMap = { + {"type", ITEM_PARSE_TYPE}, + {"description", ITEM_PARSE_DESCRIPTION}, + {"runespellname", ITEM_PARSE_RUNESPELLNAME}, + {"weight", ITEM_PARSE_WEIGHT}, + {"showcount", ITEM_PARSE_SHOWCOUNT}, + {"armor", ITEM_PARSE_ARMOR}, + {"defense", ITEM_PARSE_DEFENSE}, + {"extradef", ITEM_PARSE_EXTRADEF}, + {"attack", ITEM_PARSE_ATTACK}, + {"attackspeed", ITEM_PARSE_ATTACK_SPEED}, + {"rotateto", ITEM_PARSE_ROTATETO}, + {"moveable", ITEM_PARSE_MOVEABLE}, + {"movable", ITEM_PARSE_MOVEABLE}, + {"blockprojectile", ITEM_PARSE_BLOCKPROJECTILE}, + {"allowpickupable", ITEM_PARSE_PICKUPABLE}, + {"pickupable", ITEM_PARSE_PICKUPABLE}, + {"forceserialize", ITEM_PARSE_FORCESERIALIZE}, + {"forcesave", ITEM_PARSE_FORCESERIALIZE}, + {"floorchange", ITEM_PARSE_FLOORCHANGE}, + {"corpsetype", ITEM_PARSE_CORPSETYPE}, + {"containersize", ITEM_PARSE_CONTAINERSIZE}, + {"fluidsource", ITEM_PARSE_FLUIDSOURCE}, + {"readable", ITEM_PARSE_READABLE}, + {"writeable", ITEM_PARSE_WRITEABLE}, + {"maxtextlen", ITEM_PARSE_MAXTEXTLEN}, + {"writeonceitemid", ITEM_PARSE_WRITEONCEITEMID}, + {"weapontype", ITEM_PARSE_WEAPONTYPE}, + {"slottype", ITEM_PARSE_SLOTTYPE}, + {"ammotype", ITEM_PARSE_AMMOTYPE}, + {"shoottype", ITEM_PARSE_SHOOTTYPE}, + {"effect", ITEM_PARSE_EFFECT}, + {"range", ITEM_PARSE_RANGE}, + {"stopduration", ITEM_PARSE_STOPDURATION}, + {"decayto", ITEM_PARSE_DECAYTO}, + {"transformequipto", ITEM_PARSE_TRANSFORMEQUIPTO}, + {"transformdeequipto", ITEM_PARSE_TRANSFORMDEEQUIPTO}, + {"duration", ITEM_PARSE_DURATION}, + {"showduration", ITEM_PARSE_SHOWDURATION}, + {"charges", ITEM_PARSE_CHARGES}, + {"showcharges", ITEM_PARSE_SHOWCHARGES}, + {"showattributes", ITEM_PARSE_SHOWATTRIBUTES}, + {"hitchance", ITEM_PARSE_HITCHANCE}, + {"maxhitchance", ITEM_PARSE_MAXHITCHANCE}, + {"invisible", ITEM_PARSE_INVISIBLE}, + {"speed", ITEM_PARSE_SPEED}, + {"healthgain", ITEM_PARSE_HEALTHGAIN}, + {"healthticks", ITEM_PARSE_HEALTHTICKS}, + {"managain", ITEM_PARSE_MANAGAIN}, + {"manaticks", ITEM_PARSE_MANATICKS}, + {"manashield", ITEM_PARSE_MANASHIELD}, + {"skillsword", ITEM_PARSE_SKILLSWORD}, + {"skillaxe", ITEM_PARSE_SKILLAXE}, + {"skillclub", ITEM_PARSE_SKILLCLUB}, + {"skilldist", ITEM_PARSE_SKILLDIST}, + {"skillfish", ITEM_PARSE_SKILLFISH}, + {"skillshield", ITEM_PARSE_SKILLSHIELD}, + {"skillfist", ITEM_PARSE_SKILLFIST}, + {"maxhitpoints", ITEM_PARSE_MAXHITPOINTS}, + {"maxhitpointspercent", ITEM_PARSE_MAXHITPOINTSPERCENT}, + {"maxmanapoints", ITEM_PARSE_MAXMANAPOINTS}, + {"maxmanapointspercent", ITEM_PARSE_MAXMANAPOINTSPERCENT}, + {"magicpoints", ITEM_PARSE_MAGICPOINTS}, + {"magiclevelpoints", ITEM_PARSE_MAGICPOINTS}, + {"magicpointspercent", ITEM_PARSE_MAGICPOINTSPERCENT}, + {"criticalhitchance", ITEM_PARSE_CRITICALHITCHANCE}, + {"criticalhitamount", ITEM_PARSE_CRITICALHITAMOUNT}, + {"lifeleechchance", ITEM_PARSE_LIFELEECHCHANCE}, + {"lifeleechamount", ITEM_PARSE_LIFELEECHAMOUNT}, + {"manaleechchance", ITEM_PARSE_MANALEECHCHANCE}, + {"manaleechamount", ITEM_PARSE_MANALEECHAMOUNT}, + {"fieldabsorbpercentenergy", ITEM_PARSE_FIELDABSORBPERCENTENERGY}, + {"fieldabsorbpercentfire", ITEM_PARSE_FIELDABSORBPERCENTFIRE}, + {"fieldabsorbpercentpoison", ITEM_PARSE_FIELDABSORBPERCENTPOISON}, + {"fieldabsorbpercentearth", ITEM_PARSE_FIELDABSORBPERCENTPOISON}, + {"absorbpercentall", ITEM_PARSE_ABSORBPERCENTALL}, + {"absorbpercentallelements", ITEM_PARSE_ABSORBPERCENTALL}, + {"absorbpercentelements", ITEM_PARSE_ABSORBPERCENTELEMENTS}, + {"absorbpercentmagic", ITEM_PARSE_ABSORBPERCENTMAGIC}, + {"absorbpercentenergy", ITEM_PARSE_ABSORBPERCENTENERGY}, + {"absorbpercentfire", ITEM_PARSE_ABSORBPERCENTFIRE}, + {"absorbpercentpoison", ITEM_PARSE_ABSORBPERCENTPOISON}, + {"absorbpercentearth", ITEM_PARSE_ABSORBPERCENTPOISON}, + {"absorbpercentice", ITEM_PARSE_ABSORBPERCENTICE}, + {"absorbpercentholy", ITEM_PARSE_ABSORBPERCENTHOLY}, + {"absorbpercentdeath", ITEM_PARSE_ABSORBPERCENTDEATH}, + {"absorbpercentlifedrain", ITEM_PARSE_ABSORBPERCENTLIFEDRAIN}, + {"absorbpercentmanadrain", ITEM_PARSE_ABSORBPERCENTMANADRAIN}, + {"absorbpercentdrown", ITEM_PARSE_ABSORBPERCENTDROWN}, + {"absorbpercentphysical", ITEM_PARSE_ABSORBPERCENTPHYSICAL}, + {"absorbpercenthealing", ITEM_PARSE_ABSORBPERCENTHEALING}, + {"absorbpercentundefined", ITEM_PARSE_ABSORBPERCENTUNDEFINED}, + {"reflectpercentall", ITEM_PARSE_REFLECTPERCENTALL}, + {"reflectpercentallelements", ITEM_PARSE_REFLECTPERCENTALL}, + {"reflectpercentelements", ITEM_PARSE_REFLECTPERCENTELEMENTS}, + {"reflectpercentmagic", ITEM_PARSE_REFLECTPERCENTMAGIC}, + {"reflectpercentenergy", ITEM_PARSE_REFLECTPERCENTENERGY}, + {"reflectpercentfire", ITEM_PARSE_REFLECTPERCENTFIRE}, + {"reflectpercentpoison", ITEM_PARSE_REFLECTPERCENTEARTH}, + {"reflectpercentearth", ITEM_PARSE_REFLECTPERCENTEARTH}, + {"reflectpercentice", ITEM_PARSE_REFLECTPERCENTICE}, + {"reflectpercentholy", ITEM_PARSE_REFLECTPERCENTHOLY}, + {"reflectpercentdeath", ITEM_PARSE_REFLECTPERCENTDEATH}, + {"reflectpercentlifedrain", ITEM_PARSE_REFLECTPERCENTLIFEDRAIN}, + {"reflectpercentmanadrain", ITEM_PARSE_REFLECTPERCENTMANADRAIN}, + {"reflectpercentdrown", ITEM_PARSE_REFLECTPERCENTDROWN}, + {"reflectpercentphysical", ITEM_PARSE_REFLECTPERCENTPHYSICAL}, + {"reflectpercenthealing", ITEM_PARSE_REFLECTPERCENTHEALING}, + {"reflectchanceall", ITEM_PARSE_REFLECTCHANCEALL}, + {"reflectchanceallelements", ITEM_PARSE_REFLECTCHANCEALL}, + {"reflectchanceelements", ITEM_PARSE_REFLECTCHANCEELEMENTS}, + {"reflectchancemagic", ITEM_PARSE_REFLECTCHANCEMAGIC}, + {"reflectchanceenergy", ITEM_PARSE_REFLECTCHANCEENERGY}, + {"reflectchancefire", ITEM_PARSE_REFLECTCHANCEFIRE}, + {"reflectchancepoison", ITEM_PARSE_REFLECTCHANCEEARTH}, + {"reflectchanceearth", ITEM_PARSE_REFLECTCHANCEEARTH}, + {"reflectchanceice", ITEM_PARSE_REFLECTCHANCEICE}, + {"reflectchanceholy", ITEM_PARSE_REFLECTCHANCEHOLY}, + {"reflectchancedeath", ITEM_PARSE_REFLECTCHANCEDEATH}, + {"reflectchancelifedrain", ITEM_PARSE_REFLECTCHANCELIFEDRAIN}, + {"reflectchancemanadrain", ITEM_PARSE_REFLECTCHANCEMANADRAIN}, + {"reflectchancedrown", ITEM_PARSE_REFLECTCHANCEDROWN}, + {"reflectchancephysical", ITEM_PARSE_REFLECTCHANCEPHYSICAL}, + {"reflectchancehealing", ITEM_PARSE_REFLECTCHANCEHEALING}, + {"boostpercentall", ITEM_PARSE_BOOSTPERCENTALL}, + {"boostpercentallelements", ITEM_PARSE_BOOSTPERCENTALL}, + {"boostpercentelements", ITEM_PARSE_BOOSTPERCENTELEMENTS}, + {"boostpercentmagic", ITEM_PARSE_BOOSTPERCENTMAGIC}, + {"boostpercentenergy", ITEM_PARSE_BOOSTPERCENTENERGY}, + {"boostpercentfire", ITEM_PARSE_BOOSTPERCENTFIRE}, + {"boostpercentpoison", ITEM_PARSE_BOOSTPERCENTEARTH}, + {"boostpercentearth", ITEM_PARSE_BOOSTPERCENTEARTH}, + {"boostpercentice", ITEM_PARSE_BOOSTPERCENTICE}, + {"boostpercentholy", ITEM_PARSE_BOOSTPERCENTHOLY}, + {"boostpercentdeath", ITEM_PARSE_BOOSTPERCENTDEATH}, + {"boostpercentlifedrain", ITEM_PARSE_BOOSTPERCENTLIFEDRAIN}, + {"boostpercentmanadrain", ITEM_PARSE_BOOSTPERCENTMANADRAIN}, + {"boostpercentdrown", ITEM_PARSE_BOOSTPERCENTDROWN}, + {"boostpercentphysical", ITEM_PARSE_BOOSTPERCENTPHYSICAL}, + {"boostpercenthealing", ITEM_PARSE_BOOSTPERCENTHEALING}, + {"magiclevelenergy", ITEM_PARSE_MAGICLEVELENERGY}, + {"magiclevelfire", ITEM_PARSE_MAGICLEVELFIRE}, + {"magiclevelpoison", ITEM_PARSE_MAGICLEVELPOISON}, + {"magiclevelearth", ITEM_PARSE_MAGICLEVELPOISON}, + {"magiclevelice", ITEM_PARSE_MAGICLEVELICE}, + {"magiclevelholy", ITEM_PARSE_MAGICLEVELHOLY}, + {"magicleveldeath", ITEM_PARSE_MAGICLEVELDEATH}, + {"magiclevellifedrain", ITEM_PARSE_MAGICLEVELLIFEDRAIN}, + {"magiclevelmanadrain", ITEM_PARSE_MAGICLEVELMANADRAIN}, + {"magicleveldrown", ITEM_PARSE_MAGICLEVELDROWN}, + {"magiclevelphysical", ITEM_PARSE_MAGICLEVELPHYSICAL}, + {"magiclevelhealing", ITEM_PARSE_MAGICLEVELHEALING}, + {"magiclevelundefined", ITEM_PARSE_MAGICLEVELUNDEFINED}, + {"suppressdrunk", ITEM_PARSE_SUPPRESSDRUNK}, + {"suppressenergy", ITEM_PARSE_SUPPRESSENERGY}, + {"suppressfire", ITEM_PARSE_SUPPRESSFIRE}, + {"suppresspoison", ITEM_PARSE_SUPPRESSPOISON}, + {"suppressdrown", ITEM_PARSE_SUPPRESSDROWN}, + {"suppressphysical", ITEM_PARSE_SUPPRESSPHYSICAL}, + {"suppressfreeze", ITEM_PARSE_SUPPRESSFREEZE}, + {"suppressdazzle", ITEM_PARSE_SUPPRESSDAZZLE}, + {"suppresscurse", ITEM_PARSE_SUPPRESSCURSE}, + {"field", ITEM_PARSE_FIELD}, + {"replaceable", ITEM_PARSE_REPLACEABLE}, + {"partnerdirection", ITEM_PARSE_PARTNERDIRECTION}, + {"leveldoor", ITEM_PARSE_LEVELDOOR}, + {"maletransformto", ITEM_PARSE_MALETRANSFORMTO}, + {"malesleeper", ITEM_PARSE_MALETRANSFORMTO}, + {"femaletransformto", ITEM_PARSE_FEMALETRANSFORMTO}, + {"femalesleeper", ITEM_PARSE_FEMALETRANSFORMTO}, + {"transformto", ITEM_PARSE_TRANSFORMTO}, + {"destroyto", ITEM_PARSE_DESTROYTO}, + {"elementice", ITEM_PARSE_ELEMENTICE}, + {"elementearth", ITEM_PARSE_ELEMENTEARTH}, + {"elementfire", ITEM_PARSE_ELEMENTFIRE}, + {"elementenergy", ITEM_PARSE_ELEMENTENERGY}, + {"elementdeath", ITEM_PARSE_ELEMENTDEATH}, + {"elementholy", ITEM_PARSE_ELEMENTHOLY}, + {"walkstack", ITEM_PARSE_WALKSTACK}, + {"blocking", ITEM_PARSE_BLOCKING}, + {"allowdistread", ITEM_PARSE_ALLOWDISTREAD}, + {"storeitem", ITEM_PARSE_STOREITEM}, + {"worth", ITEM_PARSE_WORTH}, + {"supply", ITEM_PARSE_SUPPLY}, +}; + +const std::unordered_map ItemTypesMap = {{"key", ITEM_TYPE_KEY}, + {"magicfield", ITEM_TYPE_MAGICFIELD}, + {"container", ITEM_TYPE_CONTAINER}, + {"depot", ITEM_TYPE_DEPOT}, + {"mailbox", ITEM_TYPE_MAILBOX}, + {"trashholder", ITEM_TYPE_TRASHHOLDER}, + {"teleport", ITEM_TYPE_TELEPORT}, + {"door", ITEM_TYPE_DOOR}, + {"bed", ITEM_TYPE_BED}, + {"rune", ITEM_TYPE_RUNE}, + {"podium", ITEM_TYPE_PODIUM}}; + +const std::unordered_map TileStatesMap = { + {"down", TILESTATE_FLOORCHANGE_DOWN}, {"north", TILESTATE_FLOORCHANGE_NORTH}, + {"south", TILESTATE_FLOORCHANGE_SOUTH}, {"southalt", TILESTATE_FLOORCHANGE_SOUTH_ALT}, + {"west", TILESTATE_FLOORCHANGE_WEST}, {"east", TILESTATE_FLOORCHANGE_EAST}, + {"eastalt", TILESTATE_FLOORCHANGE_EAST_ALT}, +}; + +const std::unordered_map RaceTypesMap = { + {"venom", RACE_VENOM}, {"blood", RACE_BLOOD}, {"undead", RACE_UNDEAD}, + {"fire", RACE_FIRE}, {"energy", RACE_ENERGY}, {"ink", RACE_INK}, +}; + +const std::unordered_map WeaponTypesMap = { + {"sword", WEAPON_SWORD}, {"club", WEAPON_CLUB}, {"axe", WEAPON_AXE}, {"shield", WEAPON_SHIELD}, + {"distance", WEAPON_DISTANCE}, {"wand", WEAPON_WAND}, {"ammunition", WEAPON_AMMO}, {"quiver", WEAPON_QUIVER}, +}; + +const std::unordered_map FluidTypesMap = { + {"water", FLUID_WATER}, + {"blood", FLUID_BLOOD}, + {"beer", FLUID_BEER}, + {"slime", FLUID_SLIME}, + {"lemonade", FLUID_LEMONADE}, + {"milk", FLUID_MILK}, + {"mana", FLUID_MANA}, + {"life", FLUID_LIFE}, + {"oil", FLUID_OIL}, + {"urine", FLUID_URINE}, + {"coconut", FLUID_COCONUTMILK}, + {"wine", FLUID_WINE}, + {"mud", FLUID_MUD}, + {"fruitjuice", FLUID_FRUITJUICE}, + {"lava", FLUID_LAVA}, + {"rum", FLUID_RUM}, + {"swamp", FLUID_SWAMP}, + {"tea", FLUID_TEA}, + {"mead", FLUID_MEAD}, + {"ink", FLUID_INK}, +}; + +const std::unordered_map DirectionsMap = { + {"north", DIRECTION_NORTH}, + {"n", DIRECTION_NORTH}, + {"0", DIRECTION_NORTH}, + {"east", DIRECTION_EAST}, + {"e", DIRECTION_EAST}, + {"1", DIRECTION_EAST}, + {"south", DIRECTION_SOUTH}, + {"s", DIRECTION_SOUTH}, + {"2", DIRECTION_SOUTH}, + {"west", DIRECTION_WEST}, + {"w", DIRECTION_WEST}, + {"3", DIRECTION_WEST}, + {"southwest", DIRECTION_SOUTHWEST}, + {"south west", DIRECTION_SOUTHWEST}, + {"south-west", DIRECTION_SOUTHWEST}, + {"sw", DIRECTION_SOUTHWEST}, + {"4", DIRECTION_SOUTHWEST}, + {"southeast", DIRECTION_SOUTHEAST}, + {"south east", DIRECTION_SOUTHEAST}, + {"south-east", DIRECTION_SOUTHEAST}, + {"se", DIRECTION_SOUTHEAST}, + {"5", DIRECTION_SOUTHEAST}, + {"northwest", DIRECTION_NORTHWEST}, + {"north west", DIRECTION_NORTHWEST}, + {"north-west", DIRECTION_NORTHWEST}, + {"nw", DIRECTION_NORTHWEST}, + {"6", DIRECTION_NORTHWEST}, + {"northeast", DIRECTION_NORTHEAST}, + {"north east", DIRECTION_NORTHEAST}, + {"north-east", DIRECTION_NORTHEAST}, + {"ne", DIRECTION_NORTHEAST}, + {"7", DIRECTION_NORTHEAST}, +}; + +Direction getDirection(std::string_view string) +{ + if (auto it = DirectionsMap.find(string); it != DirectionsMap.end()) { + return it->second; + } + fmt::print("[Warning - getDirection] Invalid direction: {}\n", string); + return DIRECTION_NORTH; +} + +} // namespace + Items::Items() { items.reserve(30000); @@ -35,6 +321,10 @@ bool Items::reload() clear(); loadFromOtb("data/items/items.otb"); + if (!loadFromXml()) { + return false; + } + g_scripts->loadScripts("items", false, true); g_moveEvents->reload(); g_weapons->reload(); @@ -272,98 +562,45 @@ bool Items::loadFromOtb(const std::string& file) return true; } -void Items::buildInventoryList() -{ - inventory.reserve(items.size()); - for (const auto& type : items) { - if (type.weaponType != WEAPON_NONE || type.ammoType != AMMO_NONE || type.attack != 0 || type.defense != 0 || - type.extraDefense != 0 || type.armor != 0 || type.slotPosition & SLOTP_NECKLACE || - type.slotPosition & SLOTP_RING || type.slotPosition & SLOTP_AMMO || type.slotPosition & SLOTP_FEET || - type.slotPosition & SLOTP_HEAD || type.slotPosition & SLOTP_ARMOR || type.slotPosition & SLOTP_LEGS) { - inventory.push_back(type.clientId); - } - } - inventory.shrink_to_fit(); - std::sort(inventory.begin(), inventory.end()); -} - -ItemType& Items::getItemType(size_t id) -{ - if (id < items.size()) { - return items[id]; - } - return items.front(); -} - -const ItemType& Items::getItemType(size_t id) const -{ - if (id < items.size()) { - return items[id]; - } - return items.front(); -} - -const ItemType& Items::getItemIdByClientId(uint16_t spriteId) const -{ - if (spriteId >= 100) { - if (uint16_t serverId = clientIdToServerIdMap.getServerId(spriteId)) { - return getItemType(serverId); - } - } - return items.front(); -} - -uint16_t Items::getItemIdByName(const std::string& name) -{ - if (name.empty()) { - return 0; - } - - auto result = nameToItems.find(boost::algorithm::to_lower_copy(name)); - if (result == nameToItems.end()) return 0; - - return result->second; -} - -void Items::loadFromLua(lua_State* L) +bool Items::loadFromXml() { - auto table = sol::stack::get(L, 1); - if (!table.valid()) { - fmt::print(">>> Items::loadFromLua: Invalid items table\n"); - return; + pugi::xml_document doc; + pugi::xml_parse_result result = doc.load_file("data/items/items.xml"); + if (!result) { + printXMLError("Error - Items::loadFromXml", "data/items/items.xml", result); + return false; } - for (const auto& pair : table) { - auto item = pair.second.as(); - if (!item.valid()) { - fmt::print("[Warning - Items::loadFromLua] Invalid item\n"); + for (auto itemNode : doc.child("items").children()) { + pugi::xml_attribute idAttribute = itemNode.attribute("id"); + if (idAttribute) { + parseItemNode(itemNode, pugi::cast(idAttribute.value())); continue; } - if (auto id = item["id"]; id.valid()) { - parseLuaItem(item, id.get()); + pugi::xml_attribute fromIdAttribute = itemNode.attribute("fromid"); + if (!fromIdAttribute) { + std::cout << "[Warning - Items::loadFromXml] No item id found" << std::endl; continue; } - auto fromIdField = item["fromId"]; - auto toIdField = item["toId"]; - - if (!fromIdField.valid() && !toIdField.valid()) { - fmt::print("[Warning - Items::loadFromLua] Invalid item\n"); + pugi::xml_attribute toIdAttribute = itemNode.attribute("toid"); + if (!toIdAttribute) { + std::cout << "[Warning - Items::loadFromXml] fromid (" << fromIdAttribute.value() << ") without toid" + << std::endl; continue; } - auto id = fromIdField.get(); - auto toId = toIdField.get(); + uint16_t id = pugi::cast(fromIdAttribute.value()); + uint16_t toId = pugi::cast(toIdAttribute.value()); while (id <= toId) { - parseLuaItem(item, id++); + parseItemNode(itemNode, id++); } } - - buildInventoryList(); + return true; } -void Items::parseLuaItem(const sol::table& item, uint16_t id) +void Items::parseItemNode(const pugi::xml_node& itemNode, uint16_t id) { if (id > 0 && id < 100) { ItemType& iType = items[id]; @@ -376,512 +613,1311 @@ void Items::parseLuaItem(const sol::table& item, uint16_t id) } if (!it.name.empty()) { - fmt::print("[Warning - Items::loadFromLua] Duplicated item id: {}\n", it.id); + std::cout << "[Warning - Items::parseItemNode] Duplicate item with id: " << id << std::endl; return; } - it.name = item["name"].get(); + it.name = itemNode.attribute("name").as_string(); + if (!it.name.empty()) { - auto lowerCaseName = boost::algorithm::to_lower_copy(it.name); + std::string lowerCaseName = boost::algorithm::to_lower_copy(it.name); if (nameToItems.find(lowerCaseName) == nameToItems.end()) { nameToItems.emplace(std::move(lowerCaseName), id); } } - if (auto article = item["article"]; article.valid()) { - it.article = article.get(); + pugi::xml_attribute articleAttribute = itemNode.attribute("article"); + if (articleAttribute) { + it.article = articleAttribute.as_string(); } - if (auto plural = item["plural"]; plural.valid()) { - it.pluralName = plural.get(); + pugi::xml_attribute pluralAttribute = itemNode.attribute("plural"); + if (pluralAttribute) { + it.pluralName = pluralAttribute.as_string(); } - if (auto description = item["description"]; description.valid()) { - it.description = description.get(); - } + Abilities& abilities = it.getAbilities(); - if (auto runeSpellName = item["runeSpellName"]; runeSpellName.valid()) { - it.runeSpellName = runeSpellName.get(); - } + for (auto attributeNode : itemNode.children()) { + pugi::xml_attribute keyAttribute = attributeNode.attribute("key"); + if (!keyAttribute) { + continue; + } - if (auto weight = item["weight"]; weight.valid()) { - it.weight = weight.get(); - } + pugi::xml_attribute valueAttribute = attributeNode.attribute("value"); + pugi::xml_attribute maxValueAttr; + if (!valueAttribute) { + valueAttribute = attributeNode.attribute("minvalue"); + if (!valueAttribute) { + continue; + } - if (auto type = item["type"]; type.valid()) { - it.type = type.get(); - if (it.type == ITEM_TYPE_CONTAINER) { - it.group = ITEM_GROUP_CONTAINER; + maxValueAttr = attributeNode.attribute("maxvalue"); + if (!maxValueAttr) { + continue; + } } - } - if (auto showCount = item["showCount"]; showCount.valid()) { - it.showCount = showCount.get(); - } + std::string tmpStrValue = boost::algorithm::to_lower_copy(keyAttribute.as_string()); + auto parseAttribute = ItemParseAttributesMap.find(tmpStrValue); + if (parseAttribute != ItemParseAttributesMap.end()) { + ItemParseAttributes_t parseType = parseAttribute->second; + switch (parseType) { + case ITEM_PARSE_TYPE: { + tmpStrValue = boost::algorithm::to_lower_copy(valueAttribute.as_string()); + auto it2 = ItemTypesMap.find(tmpStrValue); + if (it2 != ItemTypesMap.end()) { + it.type = it2->second; + if (it.type == ITEM_TYPE_CONTAINER) { + it.group = ITEM_GROUP_CONTAINER; + } + } else { + std::cout << "[Warning - Items::parseItemNode] Unknown type: " << valueAttribute.as_string() + << std::endl; + } + break; + } - if (auto armor = item["armor"]; armor.valid()) { - it.armor = armor.get(); - } + case ITEM_PARSE_DESCRIPTION: { + it.description = valueAttribute.as_string(); + break; + } - if (auto defense = item["defense"]; defense.valid()) { - it.defense = defense.get(); - } + case ITEM_PARSE_RUNESPELLNAME: { + it.runeSpellName = valueAttribute.as_string(); + break; + } - if (auto extraDefense = item["extraDefense"]; extraDefense.valid()) { - it.extraDefense = extraDefense.get(); - } + case ITEM_PARSE_WEIGHT: { + it.weight = pugi::cast(valueAttribute.value()); + break; + } - if (auto attack = item["attack"]; attack.valid()) { - it.attack = attack.get(); - } + case ITEM_PARSE_SHOWCOUNT: { + it.showCount = valueAttribute.as_bool(); + break; + } - if (auto attackSpeed = item["attackSpeed"]; attackSpeed.valid()) { - it.attackSpeed = attackSpeed.get(); - } + case ITEM_PARSE_SUPPLY: { + it.supply = valueAttribute.as_bool(); + break; + } - if (auto rotateTo = item["rotateTo"]; rotateTo.valid()) { - it.rotateTo = rotateTo.get(); - } + case ITEM_PARSE_ARMOR: { + it.armor = pugi::cast(valueAttribute.value()); + break; + } - if (auto moveable = item["moveable"]; moveable.valid()) { - it.moveable = moveable.get(); - } + case ITEM_PARSE_DEFENSE: { + it.defense = pugi::cast(valueAttribute.value()); + break; + } - if (auto blockProjectile = item["blockProjectile"]; blockProjectile.valid()) { - it.blockProjectile = blockProjectile.get(); - } + case ITEM_PARSE_EXTRADEF: { + it.extraDefense = pugi::cast(valueAttribute.value()); + break; + } - if (auto ignoreBlocking = item["ignoreBlocking"]; ignoreBlocking.valid()) { - it.ignoreBlocking = ignoreBlocking.get(); - } + case ITEM_PARSE_ATTACK: { + it.attack = pugi::cast(valueAttribute.value()); + break; + } - if (auto pickupable = item["pickupable"]; pickupable.valid()) { - it.pickupable = pickupable.get(); - } + case ITEM_PARSE_ATTACK_SPEED: { + it.attackSpeed = pugi::cast(valueAttribute.value()); + if (it.attackSpeed > 0 && it.attackSpeed < 100) { + std::cout << "[Warning - Items::parseItemNode] AttackSpeed lower than 100 for item: " << it.id + << std::endl; + it.attackSpeed = 100; + } + break; + } - if (auto forceSerialize = item["forceSerialize"]; forceSerialize.valid()) { - it.forceSerialize = forceSerialize.get(); - } + case ITEM_PARSE_ROTATETO: { + it.rotateTo = pugi::cast(valueAttribute.value()); + break; + } - if (auto floorChange = item["floorChange"]; floorChange.valid()) { - it.floorChange = floorChange.get(); - } + case ITEM_PARSE_MOVEABLE: { + it.moveable = valueAttribute.as_bool(); + break; + } - if (auto corpseType = item["corpseType"]; corpseType.valid()) { - it.corpseType = corpseType.get(); - } + case ITEM_PARSE_BLOCKPROJECTILE: { + it.blockProjectile = valueAttribute.as_bool(); + break; + } - if (auto maxItems = item["containerSize"]; maxItems.valid()) { - it.maxItems = maxItems.get(); - } + case ITEM_PARSE_PICKUPABLE: { + it.allowPickupable = valueAttribute.as_bool(); + break; + } - if (auto fluidSource = item["fluidSource"]; fluidSource.valid()) { - it.fluidSource = fluidSource.get(); - } + case ITEM_PARSE_FORCESERIALIZE: { + it.forceSerialize = valueAttribute.as_bool(); + break; + } - if (auto fluidContainer = item["fluidContainer"]; fluidContainer.valid()) { - if (fluidContainer.get()) { - it.group = ITEM_GROUP_FLUID; - } - } + case ITEM_PARSE_FLOORCHANGE: { + tmpStrValue = boost::algorithm::to_lower_copy(valueAttribute.as_string()); + auto it2 = TileStatesMap.find(tmpStrValue); + if (it2 != TileStatesMap.end()) { + it.floorChange |= it2->second; + } else { + std::cout << "[Warning - Items::parseItemNode] Unknown floorChange: " + << valueAttribute.as_string() << std::endl; + } + break; + } - if (auto canReadText = item["readable"]; canReadText.valid()) { - it.canReadText = canReadText.get(); - } + case ITEM_PARSE_CORPSETYPE: { + tmpStrValue = boost::algorithm::to_lower_copy(valueAttribute.as_string()); + auto it2 = RaceTypesMap.find(tmpStrValue); + if (it2 != RaceTypesMap.end()) { + it.corpseType = it2->second; + } else { + std::cout << "[Warning - Items::parseItemNode] Unknown corpseType: " + << valueAttribute.as_string() << std::endl; + } + break; + } - if (auto canWriteText = item["writeable"]; canWriteText.valid()) { - it.canWriteText = canWriteText.get(); - } + case ITEM_PARSE_CONTAINERSIZE: { + it.maxItems = pugi::cast(valueAttribute.value()); + break; + } - if (auto maxTextLen = item["maxTextLen"]; maxTextLen.valid()) { - it.maxTextLen = maxTextLen.get(); - } + case ITEM_PARSE_FLUIDSOURCE: { + tmpStrValue = boost::algorithm::to_lower_copy(valueAttribute.as_string()); + auto it2 = FluidTypesMap.find(tmpStrValue); + if (it2 != FluidTypesMap.end()) { + it.fluidSource = it2->second; + } else { + std::cout << "[Warning - Items::parseItemNode] Unknown fluidSource: " + << valueAttribute.as_string() << std::endl; + } + break; + } - if (auto writeOnceItemId = item["writeOnceItemId"]; writeOnceItemId.valid()) { - it.writeOnceItemId = writeOnceItemId.get(); - } + case ITEM_PARSE_READABLE: { + it.canReadText = valueAttribute.as_bool(); + break; + } - if (auto weaponType = item["weaponType"]; weaponType.valid()) { - it.weaponType = weaponType.get(); - } + case ITEM_PARSE_WRITEABLE: { + it.canWriteText = valueAttribute.as_bool(); + it.canReadText = it.canWriteText; + break; + } - if (auto slotPosition = item["slotPosition"]; slotPosition.valid()) { - it.slotPosition = slotPosition.get(); - } + case ITEM_PARSE_MAXTEXTLEN: { + it.maxTextLen = pugi::cast(valueAttribute.value()); + break; + } - if (auto ammoType = item["ammoType"]; ammoType.valid()) { - it.ammoType = ammoType.get(); - } + case ITEM_PARSE_WRITEONCEITEMID: { + it.writeOnceItemId = pugi::cast(valueAttribute.value()); + break; + } - if (auto shootType = item["shootType"]; shootType.valid()) { - it.shootType = shootType.get(); - } + case ITEM_PARSE_WEAPONTYPE: { + tmpStrValue = boost::algorithm::to_lower_copy(valueAttribute.as_string()); + auto it2 = WeaponTypesMap.find(tmpStrValue); + if (it2 != WeaponTypesMap.end()) { + it.weaponType = it2->second; + } else { + std::cout << "[Warning - Items::parseItemNode] Unknown weaponType: " + << valueAttribute.as_string() << std::endl; + } + break; + } - if (auto magicEffect = item["magicEffect"]; magicEffect.valid()) { - it.magicEffect = magicEffect.get(); - } + case ITEM_PARSE_SLOTTYPE: { + tmpStrValue = boost::algorithm::to_lower_copy(valueAttribute.as_string()); + if (tmpStrValue == "head") { + it.slotPosition |= SLOTP_HEAD; + } else if (tmpStrValue == "body") { + it.slotPosition |= SLOTP_ARMOR; + } else if (tmpStrValue == "legs") { + it.slotPosition |= SLOTP_LEGS; + } else if (tmpStrValue == "feet") { + it.slotPosition |= SLOTP_FEET; + } else if (tmpStrValue == "backpack") { + it.slotPosition |= SLOTP_BACKPACK; + } else if (tmpStrValue == "two-handed") { + it.slotPosition |= SLOTP_TWO_HAND; + } else if (tmpStrValue == "right-hand") { + it.slotPosition &= ~SLOTP_LEFT; + } else if (tmpStrValue == "left-hand") { + it.slotPosition &= ~SLOTP_RIGHT; + } else if (tmpStrValue == "necklace") { + it.slotPosition |= SLOTP_NECKLACE; + } else if (tmpStrValue == "ring") { + it.slotPosition |= SLOTP_RING; + } else if (tmpStrValue == "ammo") { + it.slotPosition |= SLOTP_AMMO; + } else if (tmpStrValue == "hand") { + it.slotPosition |= SLOTP_HAND; + } else { + std::cout << "[Warning - Items::parseItemNode] Unknown slotType: " << valueAttribute.as_string() + << std::endl; + } + break; + } - if (auto shootRange = item["shootRange"]; shootRange.valid()) { - it.shootRange = shootRange.get(); - } + case ITEM_PARSE_AMMOTYPE: { + it.ammoType = getAmmoType(boost::algorithm::to_lower_copy(valueAttribute.as_string())); + if (it.ammoType == AMMO_NONE) { + std::cout << "[Warning - Items::parseItemNode] Unknown ammoType: " << valueAttribute.as_string() + << std::endl; + } + break; + } - if (auto stopTime = item["stopDuration"]; stopTime.valid()) { - it.stopTime = stopTime.get(); - } + case ITEM_PARSE_SHOOTTYPE: { + ShootType_t shoot = + getShootType(boost::algorithm::to_lower_copy(valueAttribute.as_string())); + if (shoot != CONST_ANI_NONE) { + it.shootType = shoot; + } else { + std::cout << "[Warning - Items::parseItemNode] Unknown shootType: " + << valueAttribute.as_string() << std::endl; + } + break; + } - if (auto decayTo = item["decayTo"]; decayTo.valid()) { - it.decayTo = decayTo.get(); - } + case ITEM_PARSE_EFFECT: { + MagicEffectClasses effect = + getMagicEffect(boost::algorithm::to_lower_copy(valueAttribute.as_string())); + if (effect != CONST_ME_NONE) { + it.magicEffect = effect; + } else { + std::cout << "[Warning - Items::parseItemNode] Unknown effect: " << valueAttribute.as_string() + << std::endl; + } + break; + } - if (auto transformEquipTo = item["transformEquipTo"]; transformEquipTo.valid()) { - it.transformEquipTo = transformEquipTo.get(); - } + case ITEM_PARSE_RANGE: { + it.shootRange = pugi::cast(valueAttribute.value()); + break; + } - if (auto transformDeEquipTo = item["transformDeEquipTo"]; transformDeEquipTo.valid()) { - it.transformDeEquipTo = transformDeEquipTo.get(); - } + case ITEM_PARSE_STOPDURATION: { + it.stopTime = valueAttribute.as_bool(); + break; + } - if (auto duration = item["duration"]; duration.valid()) { - it.decayTimeMin = duration.get(); - } else { - if (auto decayTimeMin = item["minDuration"]; decayTimeMin.valid()) { - it.decayTimeMin = decayTimeMin.get(); - } + case ITEM_PARSE_DECAYTO: { + it.decayTo = pugi::cast(valueAttribute.value()); + break; + } - if (auto decayTimeMax = item["maxDuration"]; decayTimeMax.valid()) { - it.decayTimeMax = decayTimeMax.get(); - } - } + case ITEM_PARSE_TRANSFORMEQUIPTO: { + it.transformEquipTo = pugi::cast(valueAttribute.value()); + break; + } - if (auto showDuration = item["showDuration"]; showDuration.valid()) { - it.showDuration = showDuration.get(); - } + case ITEM_PARSE_TRANSFORMDEEQUIPTO: { + it.transformDeEquipTo = pugi::cast(valueAttribute.value()); + break; + } - if (auto charges = item["charges"]; charges.valid()) { - it.charges = charges.get(); - } + case ITEM_PARSE_DURATION: { + it.decayTimeMin = pugi::cast(valueAttribute.value()); - if (auto showCharges = item["showCharges"]; showCharges.valid()) { - it.showCharges = showCharges.get(); - } + if (maxValueAttr) { + it.decayTimeMax = pugi::cast(maxValueAttr.value()); + } + break; + } - if (auto showAttributes = item["showAttributes"]; showAttributes.valid()) { - it.showAttributes = showAttributes.get(); - } + case ITEM_PARSE_SHOWDURATION: { + it.showDuration = valueAttribute.as_bool(); + break; + } - if (auto hitChance = item["hitChance"]; hitChance.valid()) { - it.hitChance = std::min(100, std::max(127, hitChance.get())); - } + case ITEM_PARSE_CHARGES: { + it.charges = pugi::cast(valueAttribute.value()); + break; + } - if (auto field = item["field"]; field.valid()) { - it.group = ITEM_GROUP_MAGICFIELD; - it.type = ITEM_TYPE_MAGICFIELD; + case ITEM_PARSE_SHOWCHARGES: { + it.showCharges = valueAttribute.as_bool(); + break; + } - CombatType_t combatType = COMBAT_NONE; - if (auto combatTypeField = field["combatType"]; combatTypeField.valid()) { - combatType = combatTypeField.get(); - } + case ITEM_PARSE_SHOWATTRIBUTES: { + it.showAttributes = valueAttribute.as_bool(); + break; + } - if (combatType != COMBAT_NONE) { - ConditionDamage* conditionDamage = nullptr; - - if (combatType == COMBAT_PHYSICALDAMAGE) { - conditionDamage = new ConditionDamage(CONDITIONID_COMBAT, CONDITION_BLEEDING); - } else if (combatType == COMBAT_ENERGYDAMAGE) { - conditionDamage = new ConditionDamage(CONDITIONID_COMBAT, CONDITION_ENERGY); - } else if (combatType == COMBAT_EARTHDAMAGE) { - conditionDamage = new ConditionDamage(CONDITIONID_COMBAT, CONDITION_POISON); - } else if (combatType == COMBAT_FIREDAMAGE) { - conditionDamage = new ConditionDamage(CONDITIONID_COMBAT, CONDITION_FIRE); - } else if (combatType == COMBAT_DROWNDAMAGE) { - conditionDamage = new ConditionDamage(CONDITIONID_COMBAT, CONDITION_DROWN); - } else { - fmt::print("[Warning - Items::loadFromLua] Invalid combat type: {}\n", static_cast(combatType)); - } + case ITEM_PARSE_HITCHANCE: { + it.hitChance = + std::min(100, std::max(-100, pugi::cast(valueAttribute.value()))); + break; + } - it.combatType = combatType; - it.conditionDamage.reset(conditionDamage); + case ITEM_PARSE_MAXHITCHANCE: { + it.maxHitChance = std::min(100, pugi::cast(valueAttribute.value())); + break; + } - uint32_t ticks = 0; - int32_t start = 0; - int32_t count = 1; - int32_t initDamage = -1; - int32_t damage = 0; + case ITEM_PARSE_INVISIBLE: { + abilities.invisible = valueAttribute.as_bool(); + break; + } - if (auto ticksField = field["ticks"]; ticksField.valid()) { - ticks = ticksField.get(); - } + case ITEM_PARSE_SPEED: { + abilities.speed = pugi::cast(valueAttribute.value()); + break; + } - if (auto startField = field["start"]; startField.valid()) { - start = std::max(0, startField.get()); - } + case ITEM_PARSE_HEALTHGAIN: { + abilities.regeneration = true; + abilities.healthGain = pugi::cast(valueAttribute.value()); + break; + } - if (auto countField = field["count"]; countField.valid()) { - count = std::max(1, countField.get()); - } + case ITEM_PARSE_HEALTHTICKS: { + abilities.regeneration = true; + abilities.healthTicks = pugi::cast(valueAttribute.value()); + break; + } - if (auto initDamageField = field["initDamage"]; initDamageField.valid()) { - initDamage = initDamageField.get(); - } + case ITEM_PARSE_MANAGAIN: { + abilities.regeneration = true; + abilities.manaGain = pugi::cast(valueAttribute.value()); + break; + } - if (auto damageField = field["damage"]; damageField.valid()) { - damage = -damageField.get(); - if (start > 0) { - std::list damageList; - ConditionDamage::generateDamageList(damage, start, damageList); - for (int32_t damageValue : damageList) { - conditionDamage->addDamage(1, ticks, -damageValue); - } + case ITEM_PARSE_MANATICKS: { + abilities.regeneration = true; + abilities.manaTicks = pugi::cast(valueAttribute.value()); + break; + } - start = 0; - } else { - conditionDamage->addDamage(count, ticks, damage); + case ITEM_PARSE_MANASHIELD: { + abilities.manaShield = valueAttribute.as_bool(); + break; } - } - // datapack compatibility, presume damage to be initialdamage if initialdamage is not declared. - // initDamage = 0 (don't override initDamage with damage, don't set any initDamage) - // initDamage = -1 (undefined, override initDamage with damage) - if (initDamage > 0 || initDamage < -1) { - conditionDamage->setInitDamage(-initDamage); - } else if (initDamage == -1 && start != 0) { - conditionDamage->setInitDamage(start); - } + case ITEM_PARSE_SKILLSWORD: { + abilities.skills[SKILL_SWORD] = pugi::cast(valueAttribute.value()); + break; + } - conditionDamage->setParam(CONDITION_PARAM_FIELD, 1); + case ITEM_PARSE_SKILLAXE: { + abilities.skills[SKILL_AXE] = pugi::cast(valueAttribute.value()); + break; + } - if (conditionDamage->getTotalDamage() > 0) { - conditionDamage->setParam(CONDITION_PARAM_FORCEUPDATE, 1); - } - } - } + case ITEM_PARSE_SKILLCLUB: { + abilities.skills[SKILL_CLUB] = pugi::cast(valueAttribute.value()); + break; + } - if (auto maxHitChance = item["maxHitChance"]; maxHitChance.valid()) { - it.maxHitChance = std::min(100, std::max(127, maxHitChance.get())); - } + case ITEM_PARSE_SKILLDIST: { + abilities.skills[SKILL_DISTANCE] = pugi::cast(valueAttribute.value()); + break; + } - if (auto replaceable = item["replaceable"]; replaceable.valid()) { - it.replaceable = replaceable.get(); - } + case ITEM_PARSE_SKILLFISH: { + abilities.skills[SKILL_FISHING] = pugi::cast(valueAttribute.value()); + break; + } - if (auto partnerDirection = item["partnerDirection"]; partnerDirection.valid()) { - it.bedPartnerDir = partnerDirection.get(); - } + case ITEM_PARSE_SKILLSHIELD: { + abilities.skills[SKILL_SHIELD] = pugi::cast(valueAttribute.value()); + break; + } - if (auto levelDoor = item["levelDoor"]; levelDoor.valid()) { - it.levelDoor = levelDoor.get(); - } + case ITEM_PARSE_SKILLFIST: { + abilities.skills[SKILL_FIST] = pugi::cast(valueAttribute.value()); + break; + } - if (auto maleSleeper = item["maleSleeper"]; maleSleeper.valid()) { - auto value = maleSleeper.get(); - it.transformToOnUse[PLAYERSEX_MALE] = value; - ItemType& other = getItemType(value); - if (other.transformToFree == 0) { - other.transformToFree = it.id; - } + case ITEM_PARSE_CRITICALHITAMOUNT: { + abilities.specialSkills[SPECIALSKILL_CRITICALHITAMOUNT] = + pugi::cast(valueAttribute.value()); + break; + } - if (it.transformToOnUse[PLAYERSEX_FEMALE] == 0) { - it.transformToOnUse[PLAYERSEX_FEMALE] = value; - } - } + case ITEM_PARSE_CRITICALHITCHANCE: { + abilities.specialSkills[SPECIALSKILL_CRITICALHITCHANCE] = + pugi::cast(valueAttribute.value()); + break; + } - if (auto femaleSleeper = item["femaleSleeper"]; femaleSleeper.valid()) { - auto value = femaleSleeper.get(); - it.transformToOnUse[PLAYERSEX_FEMALE] = value; + case ITEM_PARSE_MANALEECHAMOUNT: { + abilities.specialSkills[SPECIALSKILL_MANALEECHAMOUNT] = pugi::cast(valueAttribute.value()); + break; + } - ItemType& other = getItemType(value); - if (other.transformToFree == 0) { - other.transformToFree = it.id; - } + case ITEM_PARSE_MANALEECHCHANCE: { + abilities.specialSkills[SPECIALSKILL_MANALEECHCHANCE] = pugi::cast(valueAttribute.value()); + break; + } - if (it.transformToOnUse[PLAYERSEX_MALE] == 0) { - it.transformToOnUse[PLAYERSEX_MALE] = value; - } - } + case ITEM_PARSE_LIFELEECHAMOUNT: { + abilities.specialSkills[SPECIALSKILL_LIFELEECHAMOUNT] = pugi::cast(valueAttribute.value()); + break; + } - if (auto transformTo = item["transformTo"]; transformTo.valid()) { - it.transformToFree = transformTo.get(); - } + case ITEM_PARSE_LIFELEECHCHANCE: { + abilities.specialSkills[SPECIALSKILL_LIFELEECHCHANCE] = pugi::cast(valueAttribute.value()); + break; + } - if (auto destroyTo = item["destroyTo"]; destroyTo.valid()) { - it.destroyTo = destroyTo.get(); - } + case ITEM_PARSE_MAXHITPOINTS: { + abilities.stats[STAT_MAXHITPOINTS] = pugi::cast(valueAttribute.value()); + break; + } - if (auto walkStack = item["walkStack"]; walkStack.valid()) { - it.walkStack = walkStack.get(); - } + case ITEM_PARSE_MAXHITPOINTSPERCENT: { + abilities.statsPercent[STAT_MAXHITPOINTS] = pugi::cast(valueAttribute.value()); + break; + } - if (auto blocking = item["blocking"]; blocking.valid()) { - it.blockSolid = blocking.get(); - } + case ITEM_PARSE_MAXMANAPOINTS: { + abilities.stats[STAT_MAXMANAPOINTS] = pugi::cast(valueAttribute.value()); + break; + } - if (auto allowDistRead = item["allowDistRead"]; allowDistRead.valid()) { - it.allowDistRead = allowDistRead.get(); - } + case ITEM_PARSE_MAXMANAPOINTSPERCENT: { + abilities.statsPercent[STAT_MAXMANAPOINTS] = pugi::cast(valueAttribute.value()); + break; + } - if (auto worth = item["worth"]; worth.valid()) { - auto value = worth.get(); - if (currencyItems.find(value) != currencyItems.end()) { - fmt::print("[Warning - Items::loadFromLua] Duplicated currency worth. Item {} redefine worth./n", it.id); - } else { - currencyItems.insert(CurrencyMap::value_type(worth, id)); - it.worth = value; - } - } + case ITEM_PARSE_MAGICPOINTS: { + abilities.stats[STAT_MAGICPOINTS] = pugi::cast(valueAttribute.value()); + break; + } - if (auto stackSize = item["stackSize"]; stackSize.valid()) { - auto value = stackSize.get(); - if (value == 0 || value >= 255) { - fmt::print("[Warning - Items::loadFromLua] Invalid stack size. Item {} has stack size {}.\n", it.id, value); - } else { - it.stackSize = static_cast(value); - } - } + case ITEM_PARSE_MAGICPOINTSPERCENT: { + abilities.statsPercent[STAT_MAGICPOINTS] = pugi::cast(valueAttribute.value()); + break; + } - auto& abilities = it.getAbilities(); + case ITEM_PARSE_FIELDABSORBPERCENTENERGY: { + abilities.fieldAbsorbPercent[combatTypeToIndex(COMBAT_ENERGYDAMAGE)] += + pugi::cast(valueAttribute.value()); + break; + } - if (auto conditionSuppressions = item["conditionSuppressions"]; conditionSuppressions.valid()) { - abilities.conditionSuppressions = conditionSuppressions.get(); - } + case ITEM_PARSE_FIELDABSORBPERCENTFIRE: { + abilities.fieldAbsorbPercent[combatTypeToIndex(COMBAT_FIREDAMAGE)] += + pugi::cast(valueAttribute.value()); + break; + } - if (auto elementType = item["elementType"]; elementType.valid()) { - abilities.elementType = elementType.get(); - } + case ITEM_PARSE_FIELDABSORBPERCENTPOISON: { + abilities.fieldAbsorbPercent[combatTypeToIndex(COMBAT_EARTHDAMAGE)] += + pugi::cast(valueAttribute.value()); + break; + } - if (auto elementDamage = item["elementDamage"]; elementDamage.valid()) { - abilities.elementDamage = elementDamage.get(); - } + case ITEM_PARSE_ABSORBPERCENTALL: { + int16_t value = pugi::cast(valueAttribute.value()); + for (auto& i : abilities.absorbPercent) { + i += value; + } + break; + } - if (auto invisible = item["invisible"]; invisible.valid()) { - abilities.invisible = invisible.get(); - } + case ITEM_PARSE_ABSORBPERCENTELEMENTS: { + int16_t value = pugi::cast(valueAttribute.value()); + abilities.absorbPercent[combatTypeToIndex(COMBAT_ENERGYDAMAGE)] += value; + abilities.absorbPercent[combatTypeToIndex(COMBAT_FIREDAMAGE)] += value; + abilities.absorbPercent[combatTypeToIndex(COMBAT_EARTHDAMAGE)] += value; + abilities.absorbPercent[combatTypeToIndex(COMBAT_ICEDAMAGE)] += value; + break; + } - if (auto speed = item["speed"]; speed.valid()) { - abilities.speed = speed.get(); - } + case ITEM_PARSE_ABSORBPERCENTMAGIC: { + int16_t value = pugi::cast(valueAttribute.value()); + abilities.absorbPercent[combatTypeToIndex(COMBAT_ENERGYDAMAGE)] += value; + abilities.absorbPercent[combatTypeToIndex(COMBAT_FIREDAMAGE)] += value; + abilities.absorbPercent[combatTypeToIndex(COMBAT_EARTHDAMAGE)] += value; + abilities.absorbPercent[combatTypeToIndex(COMBAT_ICEDAMAGE)] += value; + abilities.absorbPercent[combatTypeToIndex(COMBAT_HOLYDAMAGE)] += value; + abilities.absorbPercent[combatTypeToIndex(COMBAT_DEATHDAMAGE)] += value; + break; + } - if (auto healthGain = item["healthGain"]; healthGain.valid()) { - abilities.healthGain = healthGain.get(); - abilities.regeneration = true; - } + case ITEM_PARSE_ABSORBPERCENTENERGY: { + abilities.absorbPercent[combatTypeToIndex(COMBAT_ENERGYDAMAGE)] += + pugi::cast(valueAttribute.value()); + break; + } - if (auto healthTicks = item["healthTicks"]; healthTicks.valid()) { - abilities.healthTicks = healthTicks.get(); - abilities.regeneration = true; - } + case ITEM_PARSE_ABSORBPERCENTFIRE: { + abilities.absorbPercent[combatTypeToIndex(COMBAT_FIREDAMAGE)] += + pugi::cast(valueAttribute.value()); + break; + } - if (auto manaGain = item["manaGain"]; manaGain.valid()) { - abilities.manaGain = manaGain.get(); - abilities.regeneration = true; - } + case ITEM_PARSE_ABSORBPERCENTPOISON: { + abilities.absorbPercent[combatTypeToIndex(COMBAT_EARTHDAMAGE)] += + pugi::cast(valueAttribute.value()); + break; + } - if (auto manaTicks = item["manaTicks"]; manaTicks.valid()) { - abilities.manaTicks = manaTicks.get(); - abilities.regeneration = true; - } + case ITEM_PARSE_ABSORBPERCENTICE: { + abilities.absorbPercent[combatTypeToIndex(COMBAT_ICEDAMAGE)] += + pugi::cast(valueAttribute.value()); + break; + } - if (auto manaShield = item["manaShield"]; manaShield.valid()) { - abilities.manaShield = manaShield.get(); - } + case ITEM_PARSE_ABSORBPERCENTHOLY: { + abilities.absorbPercent[combatTypeToIndex(COMBAT_HOLYDAMAGE)] += + pugi::cast(valueAttribute.value()); + break; + } - if (auto skillFist = item["skillFist"]; skillFist.valid()) { - abilities.skills[SKILL_FIST] = skillFist.get(); - } + case ITEM_PARSE_ABSORBPERCENTDEATH: { + abilities.absorbPercent[combatTypeToIndex(COMBAT_DEATHDAMAGE)] += + pugi::cast(valueAttribute.value()); + break; + } - if (auto skillClub = item["skillClub"]; skillClub.valid()) { - abilities.skills[SKILL_CLUB] = skillClub.get(); - } + case ITEM_PARSE_ABSORBPERCENTLIFEDRAIN: { + abilities.absorbPercent[combatTypeToIndex(COMBAT_LIFEDRAIN)] += + pugi::cast(valueAttribute.value()); + break; + } - if (auto skillSword = item["skillSword"]; skillSword.valid()) { - abilities.skills[SKILL_SWORD] = skillSword.get(); - } + case ITEM_PARSE_ABSORBPERCENTMANADRAIN: { + abilities.absorbPercent[combatTypeToIndex(COMBAT_MANADRAIN)] += + pugi::cast(valueAttribute.value()); + break; + } - if (auto skillAxe = item["skillAxe"]; skillAxe.valid()) { - abilities.skills[SKILL_AXE] = skillAxe.get(); - } + case ITEM_PARSE_ABSORBPERCENTDROWN: { + abilities.absorbPercent[combatTypeToIndex(COMBAT_DROWNDAMAGE)] += + pugi::cast(valueAttribute.value()); + break; + } - if (auto skillDistance = item["skillDist"]; skillDistance.valid()) { - abilities.skills[SKILL_DISTANCE] = skillDistance.get(); - } + case ITEM_PARSE_ABSORBPERCENTPHYSICAL: { + abilities.absorbPercent[combatTypeToIndex(COMBAT_PHYSICALDAMAGE)] += + pugi::cast(valueAttribute.value()); + break; + } - if (auto skillShield = item["skillShield"]; skillShield.valid()) { - abilities.skills[SKILL_SHIELD] = skillShield.get(); - } + case ITEM_PARSE_ABSORBPERCENTHEALING: { + abilities.absorbPercent[combatTypeToIndex(COMBAT_HEALING)] += + pugi::cast(valueAttribute.value()); + break; + } - if (auto skillFishing = item["skillFishing"]; skillFishing.valid()) { - abilities.skills[SKILL_FISHING] = skillFishing.get(); - } + case ITEM_PARSE_ABSORBPERCENTUNDEFINED: { + abilities.absorbPercent[combatTypeToIndex(COMBAT_UNDEFINEDDAMAGE)] += + pugi::cast(valueAttribute.value()); + break; + } - if (auto criticalHitChance = item["criticalHitChance"]; criticalHitChance.valid()) { - abilities.specialSkills[SPECIALSKILL_CRITICALHITCHANCE] = criticalHitChance.get(); - } + case ITEM_PARSE_REFLECTPERCENTALL: { + int16_t value = pugi::cast(valueAttribute.value()); + for (auto& i : abilities.reflect) { + i.percent += value; + } + break; + } - if (auto criticalHitAmount = item["criticalHitAmount"]; criticalHitAmount.valid()) { - abilities.specialSkills[SPECIALSKILL_CRITICALHITAMOUNT] = criticalHitAmount.get(); - } + case ITEM_PARSE_REFLECTPERCENTELEMENTS: { + int16_t value = pugi::cast(valueAttribute.value()); + abilities.reflect[combatTypeToIndex(COMBAT_ENERGYDAMAGE)].percent += value; + abilities.reflect[combatTypeToIndex(COMBAT_FIREDAMAGE)].percent += value; + abilities.reflect[combatTypeToIndex(COMBAT_EARTHDAMAGE)].percent += value; + abilities.reflect[combatTypeToIndex(COMBAT_ICEDAMAGE)].percent += value; + break; + } - if (auto lifeLeechChance = item["lifeLeechChance"]; lifeLeechChance.valid()) { - abilities.specialSkills[SPECIALSKILL_LIFELEECHCHANCE] = lifeLeechChance.get(); - } + case ITEM_PARSE_REFLECTPERCENTMAGIC: { + int16_t value = pugi::cast(valueAttribute.value()); + abilities.reflect[combatTypeToIndex(COMBAT_ENERGYDAMAGE)].percent += value; + abilities.reflect[combatTypeToIndex(COMBAT_FIREDAMAGE)].percent += value; + abilities.reflect[combatTypeToIndex(COMBAT_EARTHDAMAGE)].percent += value; + abilities.reflect[combatTypeToIndex(COMBAT_ICEDAMAGE)].percent += value; + abilities.reflect[combatTypeToIndex(COMBAT_HOLYDAMAGE)].percent += value; + abilities.reflect[combatTypeToIndex(COMBAT_DEATHDAMAGE)].percent += value; + break; + } - if (auto lifeLeechAmount = item["lifeLeechAmount"]; lifeLeechAmount.valid()) { - abilities.specialSkills[SPECIALSKILL_LIFELEECHAMOUNT] = lifeLeechAmount.get(); - } + case ITEM_PARSE_REFLECTPERCENTENERGY: { + abilities.reflect[combatTypeToIndex(COMBAT_ENERGYDAMAGE)].percent += + pugi::cast(valueAttribute.value()); + break; + } - if (auto manaLeechChance = item["manaLeechChance"]; manaLeechChance.valid()) { - abilities.specialSkills[SPECIALSKILL_MANALEECHCHANCE] = manaLeechChance.get(); - } + case ITEM_PARSE_REFLECTPERCENTFIRE: { + abilities.reflect[combatTypeToIndex(COMBAT_FIREDAMAGE)].percent += + pugi::cast(valueAttribute.value()); + break; + } - if (auto manaLeechAmount = item["manaLeechAmount"]; manaLeechAmount.valid()) { - abilities.specialSkills[SPECIALSKILL_MANALEECHAMOUNT] = manaLeechAmount.get(); - } + case ITEM_PARSE_REFLECTPERCENTEARTH: { + abilities.reflect[combatTypeToIndex(COMBAT_EARTHDAMAGE)].percent += + pugi::cast(valueAttribute.value()); + break; + } - if (auto maxHealth = item["maxHealth"]; maxHealth.valid()) { - abilities.stats[STAT_MAXHITPOINTS] = maxHealth.get(); - } + case ITEM_PARSE_REFLECTPERCENTICE: { + abilities.reflect[combatTypeToIndex(COMBAT_ICEDAMAGE)].percent += + pugi::cast(valueAttribute.value()); + break; + } - if (auto maxHealthPercent = item["maxHealthPercent"]; maxHealthPercent.valid()) { - abilities.statsPercent[STAT_MAXHITPOINTS] = maxHealthPercent.get(); - } + case ITEM_PARSE_REFLECTPERCENTHOLY: { + abilities.reflect[combatTypeToIndex(COMBAT_HOLYDAMAGE)].percent += + pugi::cast(valueAttribute.value()); + break; + } + + case ITEM_PARSE_REFLECTPERCENTDEATH: { + abilities.reflect[combatTypeToIndex(COMBAT_DEATHDAMAGE)].percent += + pugi::cast(valueAttribute.value()); + break; + } + + case ITEM_PARSE_REFLECTPERCENTLIFEDRAIN: { + abilities.reflect[combatTypeToIndex(COMBAT_LIFEDRAIN)].percent += + pugi::cast(valueAttribute.value()); + break; + } + + case ITEM_PARSE_REFLECTPERCENTMANADRAIN: { + abilities.reflect[combatTypeToIndex(COMBAT_MANADRAIN)].percent += + pugi::cast(valueAttribute.value()); + break; + } + + case ITEM_PARSE_REFLECTPERCENTDROWN: { + abilities.reflect[combatTypeToIndex(COMBAT_DROWNDAMAGE)].percent += + pugi::cast(valueAttribute.value()); + break; + } + + case ITEM_PARSE_REFLECTPERCENTPHYSICAL: { + abilities.reflect[combatTypeToIndex(COMBAT_PHYSICALDAMAGE)].percent += + pugi::cast(valueAttribute.value()); + break; + } + + case ITEM_PARSE_REFLECTPERCENTHEALING: { + abilities.reflect[combatTypeToIndex(COMBAT_HEALING)].percent += + pugi::cast(valueAttribute.value()); + break; + } + + case ITEM_PARSE_REFLECTCHANCEALL: { + int16_t value = pugi::cast(valueAttribute.value()); + for (auto& i : abilities.reflect) { + i.chance += value; + } + break; + } + + case ITEM_PARSE_REFLECTCHANCEELEMENTS: { + int16_t value = pugi::cast(valueAttribute.value()); + abilities.reflect[combatTypeToIndex(COMBAT_ENERGYDAMAGE)].chance += value; + abilities.reflect[combatTypeToIndex(COMBAT_FIREDAMAGE)].chance += value; + abilities.reflect[combatTypeToIndex(COMBAT_EARTHDAMAGE)].chance += value; + abilities.reflect[combatTypeToIndex(COMBAT_ICEDAMAGE)].chance += value; + break; + } + + case ITEM_PARSE_REFLECTCHANCEMAGIC: { + int16_t value = pugi::cast(valueAttribute.value()); + abilities.reflect[combatTypeToIndex(COMBAT_ENERGYDAMAGE)].chance += value; + abilities.reflect[combatTypeToIndex(COMBAT_FIREDAMAGE)].chance += value; + abilities.reflect[combatTypeToIndex(COMBAT_EARTHDAMAGE)].chance += value; + abilities.reflect[combatTypeToIndex(COMBAT_ICEDAMAGE)].chance += value; + abilities.reflect[combatTypeToIndex(COMBAT_HOLYDAMAGE)].chance += value; + abilities.reflect[combatTypeToIndex(COMBAT_DEATHDAMAGE)].chance += value; + break; + } + + case ITEM_PARSE_REFLECTCHANCEENERGY: { + abilities.reflect[combatTypeToIndex(COMBAT_ENERGYDAMAGE)].chance += + pugi::cast(valueAttribute.value()); + break; + } + + case ITEM_PARSE_REFLECTCHANCEFIRE: { + abilities.reflect[combatTypeToIndex(COMBAT_FIREDAMAGE)].chance += + pugi::cast(valueAttribute.value()); + break; + } + + case ITEM_PARSE_REFLECTCHANCEEARTH: { + abilities.reflect[combatTypeToIndex(COMBAT_EARTHDAMAGE)].chance += + pugi::cast(valueAttribute.value()); + break; + } + + case ITEM_PARSE_REFLECTCHANCEICE: { + abilities.reflect[combatTypeToIndex(COMBAT_ICEDAMAGE)].chance += + pugi::cast(valueAttribute.value()); + break; + } + + case ITEM_PARSE_REFLECTCHANCEHOLY: { + abilities.reflect[combatTypeToIndex(COMBAT_HOLYDAMAGE)].chance += + pugi::cast(valueAttribute.value()); + break; + } + + case ITEM_PARSE_REFLECTCHANCEDEATH: { + abilities.reflect[combatTypeToIndex(COMBAT_DEATHDAMAGE)].chance += + pugi::cast(valueAttribute.value()); + break; + } + + case ITEM_PARSE_REFLECTCHANCELIFEDRAIN: { + abilities.reflect[combatTypeToIndex(COMBAT_LIFEDRAIN)].chance += + pugi::cast(valueAttribute.value()); + break; + } + + case ITEM_PARSE_REFLECTCHANCEMANADRAIN: { + abilities.reflect[combatTypeToIndex(COMBAT_MANADRAIN)].chance += + pugi::cast(valueAttribute.value()); + break; + } + + case ITEM_PARSE_REFLECTCHANCEDROWN: { + abilities.reflect[combatTypeToIndex(COMBAT_DROWNDAMAGE)].chance += + pugi::cast(valueAttribute.value()); + break; + } + + case ITEM_PARSE_REFLECTCHANCEPHYSICAL: { + abilities.reflect[combatTypeToIndex(COMBAT_PHYSICALDAMAGE)].chance += + pugi::cast(valueAttribute.value()); + break; + } + + case ITEM_PARSE_REFLECTCHANCEHEALING: { + abilities.reflect[combatTypeToIndex(COMBAT_HEALING)].chance += + pugi::cast(valueAttribute.value()); + break; + } + + case ITEM_PARSE_BOOSTPERCENTALL: { + int16_t value = pugi::cast(valueAttribute.value()); + for (auto& i : abilities.boostPercent) { + i += value; + } + break; + } + + case ITEM_PARSE_BOOSTPERCENTELEMENTS: { + int16_t value = pugi::cast(valueAttribute.value()); + abilities.boostPercent[combatTypeToIndex(COMBAT_ENERGYDAMAGE)] += value; + abilities.boostPercent[combatTypeToIndex(COMBAT_FIREDAMAGE)] += value; + abilities.boostPercent[combatTypeToIndex(COMBAT_EARTHDAMAGE)] += value; + abilities.boostPercent[combatTypeToIndex(COMBAT_ICEDAMAGE)] += value; + break; + } + + case ITEM_PARSE_BOOSTPERCENTMAGIC: { + int16_t value = pugi::cast(valueAttribute.value()); + abilities.boostPercent[combatTypeToIndex(COMBAT_ENERGYDAMAGE)] += value; + abilities.boostPercent[combatTypeToIndex(COMBAT_FIREDAMAGE)] += value; + abilities.boostPercent[combatTypeToIndex(COMBAT_EARTHDAMAGE)] += value; + abilities.boostPercent[combatTypeToIndex(COMBAT_ICEDAMAGE)] += value; + abilities.boostPercent[combatTypeToIndex(COMBAT_HOLYDAMAGE)] += value; + abilities.boostPercent[combatTypeToIndex(COMBAT_DEATHDAMAGE)] += value; + break; + } + + case ITEM_PARSE_BOOSTPERCENTENERGY: { + abilities.boostPercent[combatTypeToIndex(COMBAT_ENERGYDAMAGE)] += + pugi::cast(valueAttribute.value()); + break; + } + + case ITEM_PARSE_BOOSTPERCENTFIRE: { + abilities.boostPercent[combatTypeToIndex(COMBAT_FIREDAMAGE)] += + pugi::cast(valueAttribute.value()); + break; + } + + case ITEM_PARSE_BOOSTPERCENTEARTH: { + abilities.boostPercent[combatTypeToIndex(COMBAT_EARTHDAMAGE)] += + pugi::cast(valueAttribute.value()); + break; + } + + case ITEM_PARSE_BOOSTPERCENTICE: { + abilities.boostPercent[combatTypeToIndex(COMBAT_ICEDAMAGE)] += + pugi::cast(valueAttribute.value()); + break; + } + + case ITEM_PARSE_BOOSTPERCENTHOLY: { + abilities.boostPercent[combatTypeToIndex(COMBAT_HOLYDAMAGE)] += + pugi::cast(valueAttribute.value()); + break; + } + + case ITEM_PARSE_BOOSTPERCENTDEATH: { + abilities.boostPercent[combatTypeToIndex(COMBAT_DEATHDAMAGE)] += + pugi::cast(valueAttribute.value()); + break; + } + + case ITEM_PARSE_BOOSTPERCENTLIFEDRAIN: { + abilities.boostPercent[combatTypeToIndex(COMBAT_LIFEDRAIN)] += + pugi::cast(valueAttribute.value()); + break; + } + + case ITEM_PARSE_BOOSTPERCENTMANADRAIN: { + abilities.boostPercent[combatTypeToIndex(COMBAT_MANADRAIN)] += + pugi::cast(valueAttribute.value()); + break; + } + + case ITEM_PARSE_BOOSTPERCENTDROWN: { + abilities.boostPercent[combatTypeToIndex(COMBAT_DROWNDAMAGE)] += + pugi::cast(valueAttribute.value()); + break; + } + + case ITEM_PARSE_BOOSTPERCENTPHYSICAL: { + abilities.boostPercent[combatTypeToIndex(COMBAT_PHYSICALDAMAGE)] += + pugi::cast(valueAttribute.value()); + break; + } + + case ITEM_PARSE_BOOSTPERCENTHEALING: { + abilities.boostPercent[combatTypeToIndex(COMBAT_HEALING)] += + pugi::cast(valueAttribute.value()); + break; + } + + case ITEM_PARSE_MAGICLEVELENERGY: { + abilities.specialMagicLevelSkill[combatTypeToIndex(COMBAT_ENERGYDAMAGE)] += + pugi::cast(valueAttribute.value()); + break; + } + + case ITEM_PARSE_MAGICLEVELFIRE: { + abilities.specialMagicLevelSkill[combatTypeToIndex(COMBAT_FIREDAMAGE)] += + pugi::cast(valueAttribute.value()); + break; + } + + case ITEM_PARSE_MAGICLEVELPOISON: { + abilities.specialMagicLevelSkill[combatTypeToIndex(COMBAT_EARTHDAMAGE)] += + pugi::cast(valueAttribute.value()); + break; + } + + case ITEM_PARSE_MAGICLEVELICE: { + abilities.specialMagicLevelSkill[combatTypeToIndex(COMBAT_ICEDAMAGE)] += + pugi::cast(valueAttribute.value()); + break; + } + + case ITEM_PARSE_MAGICLEVELHOLY: { + abilities.specialMagicLevelSkill[combatTypeToIndex(COMBAT_HOLYDAMAGE)] += + pugi::cast(valueAttribute.value()); + break; + } + + case ITEM_PARSE_MAGICLEVELDEATH: { + abilities.specialMagicLevelSkill[combatTypeToIndex(COMBAT_DEATHDAMAGE)] += + pugi::cast(valueAttribute.value()); + break; + } + + case ITEM_PARSE_MAGICLEVELLIFEDRAIN: { + abilities.specialMagicLevelSkill[combatTypeToIndex(COMBAT_LIFEDRAIN)] += + pugi::cast(valueAttribute.value()); + break; + } + + case ITEM_PARSE_MAGICLEVELMANADRAIN: { + abilities.specialMagicLevelSkill[combatTypeToIndex(COMBAT_MANADRAIN)] += + pugi::cast(valueAttribute.value()); + break; + } + + case ITEM_PARSE_MAGICLEVELDROWN: { + abilities.specialMagicLevelSkill[combatTypeToIndex(COMBAT_DROWNDAMAGE)] += + pugi::cast(valueAttribute.value()); + break; + } + + case ITEM_PARSE_MAGICLEVELPHYSICAL: { + abilities.specialMagicLevelSkill[combatTypeToIndex(COMBAT_PHYSICALDAMAGE)] += + pugi::cast(valueAttribute.value()); + break; + } + + case ITEM_PARSE_MAGICLEVELHEALING: { + abilities.specialMagicLevelSkill[combatTypeToIndex(COMBAT_HEALING)] += + pugi::cast(valueAttribute.value()); + break; + } + + case ITEM_PARSE_MAGICLEVELUNDEFINED: { + abilities.specialMagicLevelSkill[combatTypeToIndex(COMBAT_UNDEFINEDDAMAGE)] += + pugi::cast(valueAttribute.value()); + break; + } + + case ITEM_PARSE_SUPPRESSDRUNK: { + if (valueAttribute.as_bool()) { + abilities.conditionSuppressions |= CONDITION_DRUNK; + } + break; + } + + case ITEM_PARSE_SUPPRESSENERGY: { + if (valueAttribute.as_bool()) { + abilities.conditionSuppressions |= CONDITION_ENERGY; + } + break; + } + + case ITEM_PARSE_SUPPRESSFIRE: { + if (valueAttribute.as_bool()) { + abilities.conditionSuppressions |= CONDITION_FIRE; + } + break; + } + + case ITEM_PARSE_SUPPRESSPOISON: { + if (valueAttribute.as_bool()) { + abilities.conditionSuppressions |= CONDITION_POISON; + } + break; + } + + case ITEM_PARSE_SUPPRESSDROWN: { + if (valueAttribute.as_bool()) { + abilities.conditionSuppressions |= CONDITION_DROWN; + } + break; + } + + case ITEM_PARSE_SUPPRESSPHYSICAL: { + if (valueAttribute.as_bool()) { + abilities.conditionSuppressions |= CONDITION_BLEEDING; + } + break; + } + + case ITEM_PARSE_SUPPRESSFREEZE: { + if (valueAttribute.as_bool()) { + abilities.conditionSuppressions |= CONDITION_FREEZING; + } + break; + } + + case ITEM_PARSE_SUPPRESSDAZZLE: { + if (valueAttribute.as_bool()) { + abilities.conditionSuppressions |= CONDITION_DAZZLED; + } + break; + } + + case ITEM_PARSE_SUPPRESSCURSE: { + if (valueAttribute.as_bool()) { + abilities.conditionSuppressions |= CONDITION_CURSED; + } + break; + } + + case ITEM_PARSE_FIELD: { + it.group = ITEM_GROUP_MAGICFIELD; + it.type = ITEM_TYPE_MAGICFIELD; + + CombatType_t combatType = COMBAT_NONE; + ConditionDamage* conditionDamage = nullptr; + + tmpStrValue = boost::algorithm::to_lower_copy(valueAttribute.as_string()); + if (tmpStrValue == "fire") { + conditionDamage = new ConditionDamage(CONDITIONID_COMBAT, CONDITION_FIRE); + combatType = COMBAT_FIREDAMAGE; + } else if (tmpStrValue == "energy") { + conditionDamage = new ConditionDamage(CONDITIONID_COMBAT, CONDITION_ENERGY); + combatType = COMBAT_ENERGYDAMAGE; + } else if (tmpStrValue == "poison") { + conditionDamage = new ConditionDamage(CONDITIONID_COMBAT, CONDITION_POISON); + combatType = COMBAT_EARTHDAMAGE; + } else if (tmpStrValue == "drown") { + conditionDamage = new ConditionDamage(CONDITIONID_COMBAT, CONDITION_DROWN); + combatType = COMBAT_DROWNDAMAGE; + } else if (tmpStrValue == "physical") { + conditionDamage = new ConditionDamage(CONDITIONID_COMBAT, CONDITION_BLEEDING); + combatType = COMBAT_PHYSICALDAMAGE; + } else { + std::cout << "[Warning - Items::parseItemNode] Unknown field value: " + << valueAttribute.as_string() << std::endl; + } + + if (combatType != COMBAT_NONE) { + it.combatType = combatType; + it.conditionDamage.reset(conditionDamage); + + uint32_t ticks = 0; + int32_t start = 0; + int32_t count = 1; + int32_t initDamage = -1; + int32_t damage = 0; + for (auto subAttributeNode : attributeNode.children()) { + pugi::xml_attribute subKeyAttribute = subAttributeNode.attribute("key"); + if (!subKeyAttribute) { + continue; + } + + pugi::xml_attribute subValueAttribute = subAttributeNode.attribute("value"); + if (!subValueAttribute) { + continue; + } + + tmpStrValue = boost::algorithm::to_lower_copy(subKeyAttribute.as_string()); + if (tmpStrValue == "initdamage") { + initDamage = pugi::cast(subValueAttribute.value()); + } else if (tmpStrValue == "ticks") { + ticks = pugi::cast(subValueAttribute.value()); + } else if (tmpStrValue == "count") { + count = std::max(1, pugi::cast(subValueAttribute.value())); + } else if (tmpStrValue == "start") { + start = std::max(0, pugi::cast(subValueAttribute.value())); + } else if (tmpStrValue == "damage") { + damage = -pugi::cast(subValueAttribute.value()); + if (start > 0) { + std::list damageList; + ConditionDamage::generateDamageList(damage, start, damageList); + for (int32_t damageValue : damageList) { + conditionDamage->addDamage(1, ticks, -damageValue); + } + + start = 0; + } else { + conditionDamage->addDamage(count, ticks, damage); + } + } + } + + // datapack compatibility, presume damage to be initialdamage if initialdamage is not declared. + // initDamage = 0 (don't override initDamage with damage, don't set any initDamage) + // initDamage = -1 (undefined, override initDamage with damage) + if (initDamage > 0 || initDamage < -1) { + conditionDamage->setInitDamage(-initDamage); + } else if (initDamage == -1 && start != 0) { + conditionDamage->setInitDamage(start); + } + + conditionDamage->setParam(CONDITION_PARAM_FIELD, 1); + + if (conditionDamage->getTotalDamage() > 0) { + conditionDamage->setParam(CONDITION_PARAM_FORCEUPDATE, 1); + } + } + break; + } + + case ITEM_PARSE_REPLACEABLE: { + it.replaceable = valueAttribute.as_bool(); + break; + } + + case ITEM_PARSE_PARTNERDIRECTION: { + it.bedPartnerDir = getDirection(valueAttribute.as_string()); + break; + } + + case ITEM_PARSE_LEVELDOOR: { + it.levelDoor = pugi::cast(valueAttribute.value()); + break; + } + + case ITEM_PARSE_MALETRANSFORMTO: { + uint16_t value = pugi::cast(valueAttribute.value()); + it.transformToOnUse[PLAYERSEX_MALE] = value; + ItemType& other = getItemType(value); + if (other.transformToFree == 0) { + other.transformToFree = it.id; + } + + if (it.transformToOnUse[PLAYERSEX_FEMALE] == 0) { + it.transformToOnUse[PLAYERSEX_FEMALE] = value; + } + break; + } - if (auto maxMana = item["maxMana"]; maxMana.valid()) { - abilities.stats[STAT_MAXMANAPOINTS] = maxMana.get(); + case ITEM_PARSE_FEMALETRANSFORMTO: { + uint16_t value = pugi::cast(valueAttribute.value()); + it.transformToOnUse[PLAYERSEX_FEMALE] = value; + + ItemType& other = getItemType(value); + if (other.transformToFree == 0) { + other.transformToFree = it.id; + } + + if (it.transformToOnUse[PLAYERSEX_MALE] == 0) { + it.transformToOnUse[PLAYERSEX_MALE] = value; + } + break; + } + + case ITEM_PARSE_TRANSFORMTO: { + it.transformToFree = pugi::cast(valueAttribute.value()); + break; + } + + case ITEM_PARSE_DESTROYTO: { + it.destroyTo = pugi::cast(valueAttribute.value()); + break; + } + + case ITEM_PARSE_ELEMENTICE: { + abilities.elementDamage = pugi::cast(valueAttribute.value()); + abilities.elementType = COMBAT_ICEDAMAGE; + break; + } + + case ITEM_PARSE_ELEMENTEARTH: { + abilities.elementDamage = pugi::cast(valueAttribute.value()); + abilities.elementType = COMBAT_EARTHDAMAGE; + break; + } + + case ITEM_PARSE_ELEMENTFIRE: { + abilities.elementDamage = pugi::cast(valueAttribute.value()); + abilities.elementType = COMBAT_FIREDAMAGE; + break; + } + + case ITEM_PARSE_ELEMENTENERGY: { + abilities.elementDamage = pugi::cast(valueAttribute.value()); + abilities.elementType = COMBAT_ENERGYDAMAGE; + break; + } + + case ITEM_PARSE_ELEMENTDEATH: { + abilities.elementDamage = pugi::cast(valueAttribute.value()); + abilities.elementType = COMBAT_DEATHDAMAGE; + break; + } + + case ITEM_PARSE_ELEMENTHOLY: { + abilities.elementDamage = pugi::cast(valueAttribute.value()); + abilities.elementType = COMBAT_HOLYDAMAGE; + break; + } + + case ITEM_PARSE_WALKSTACK: { + it.walkStack = valueAttribute.as_bool(); + break; + } + + case ITEM_PARSE_BLOCKING: { + it.blockSolid = valueAttribute.as_bool(); + break; + } + + case ITEM_PARSE_ALLOWDISTREAD: { + it.allowDistRead = booleanString(valueAttribute.as_string()); + break; + } + + case ITEM_PARSE_STOREITEM: { + it.storeItem = booleanString(valueAttribute.as_string()); + break; + } + + case ITEM_PARSE_WORTH: { + uint64_t worth = pugi::cast(valueAttribute.value()); + if (currencyItems.find(worth) != currencyItems.end()) { + std::cout << "[Warning - Items::parseItemNode] Duplicated currency worth. Item " << id + << " redefines worth " << worth << std::endl; + } else { + currencyItems.insert(CurrencyMap::value_type(worth, id)); + it.worth = worth; + } + break; + } + + default: { + // It should not ever get to here, only if you add a new key to the map and don't configure a case + // for it. + std::cout << "[Warning - Items::parseItemNode] Not configured key value: " + << keyAttribute.as_string() << std::endl; + break; + } + } + } else { + std::cout << "[Warning - Items::parseItemNode] Unknown key value: " << keyAttribute.as_string() + << std::endl; + } } - if (auto maxManaPercent = item["maxManaPercent"]; maxManaPercent.valid()) { - abilities.statsPercent[STAT_MAXMANAPOINTS] = maxManaPercent.get(); + // check bed items + if ((it.transformToFree != 0 || it.transformToOnUse[PLAYERSEX_FEMALE] != 0 || + it.transformToOnUse[PLAYERSEX_MALE] != 0) && + it.type != ITEM_TYPE_BED) { + std::cout << "[Warning - Items::parseItemNode] Item " << it.id << " is not set as a bed-type" << std::endl; } +} - if (auto magicLevelPoints = item["magicLevelPoints"]; magicLevelPoints.valid()) { - abilities.stats[STAT_MAGICPOINTS] = magicLevelPoints.get(); +void Items::buildInventoryList() +{ + inventory.reserve(items.size()); + for (const auto& type : items) { + if (type.weaponType != WEAPON_NONE || type.ammoType != AMMO_NONE || type.attack != 0 || type.defense != 0 || + type.extraDefense != 0 || type.armor != 0 || type.slotPosition & SLOTP_NECKLACE || + type.slotPosition & SLOTP_RING || type.slotPosition & SLOTP_AMMO || type.slotPosition & SLOTP_FEET || + type.slotPosition & SLOTP_HEAD || type.slotPosition & SLOTP_ARMOR || type.slotPosition & SLOTP_LEGS) { + inventory.push_back(type.clientId); + } } + inventory.shrink_to_fit(); + std::sort(inventory.begin(), inventory.end()); +} - if (auto magicLevelPointsPercent = item["magicLevelPointsPercent"]; magicLevelPointsPercent.valid()) { - abilities.statsPercent[STAT_MAGICPOINTS] = magicLevelPointsPercent.get(); +ItemType& Items::getItemType(size_t id) +{ + if (id < items.size()) { + return items[id]; } + return items.front(); +} - if (auto absorbPercent = item["absorbPercent"]; absorbPercent.valid()) { - const auto& absorbInfo = absorbPercent.get(); - for (const auto& pair : absorbInfo) { - const auto& combatType = pair.first.as(); - const auto& value = pair.second.as(); - abilities.absorbPercent[combatTypeToIndex(combatType)] += value; - } +const ItemType& Items::getItemType(size_t id) const +{ + if (id < items.size()) { + return items[id]; } + return items.front(); +} - if (auto fieldAbsorbPercent = item["fieldAbsorbPercent"]; fieldAbsorbPercent.valid()) { - const auto& absorbInfo = fieldAbsorbPercent.get(); - for (const auto& pair : absorbInfo) { - const auto& combatType = pair.first.as(); - const auto& value = pair.second.as(); - abilities.fieldAbsorbPercent[combatTypeToIndex(combatType)] += value; +const ItemType& Items::getItemIdByClientId(uint16_t spriteId) const +{ + if (spriteId >= 100) { + if (uint16_t serverId = clientIdToServerIdMap.getServerId(spriteId)) { + return getItemType(serverId); } } + return items.front(); +} - // check bed items - if ((it.transformToFree != 0 || it.transformToOnUse[PLAYERSEX_FEMALE] != 0 || - it.transformToOnUse[PLAYERSEX_MALE] != 0) && - it.type != ITEM_TYPE_BED) { - fmt::print("[Warning - Items::parseLuaItem] Item {} is not set as a bed-type\n", it.id); +uint16_t Items::getItemIdByName(const std::string& name) +{ + if (name.empty()) { + return 0; } + + auto result = nameToItems.find(boost::algorithm::to_lower_copy(name)); + if (result == nameToItems.end()) return 0; + + return result->second; } diff --git a/src/items.h b/src/items.h index 1034675..5053f61 100644 --- a/src/items.h +++ b/src/items.h @@ -9,8 +9,6 @@ #include "itemloader.h" #include "position.h" -#include - enum SlotPositionBits : uint32_t { SLOTP_WHEREEVER = 0xFFFFFFFF, @@ -42,9 +40,181 @@ enum ItemTypes_t ITEM_TYPE_BED, ITEM_TYPE_KEY, ITEM_TYPE_RUNE, + ITEM_TYPE_PODIUM, ITEM_TYPE_LAST }; +enum ItemParseAttributes_t +{ + ITEM_PARSE_TYPE, + ITEM_PARSE_DESCRIPTION, + ITEM_PARSE_RUNESPELLNAME, + ITEM_PARSE_WEIGHT, + ITEM_PARSE_SHOWCOUNT, + ITEM_PARSE_ARMOR, + ITEM_PARSE_DEFENSE, + ITEM_PARSE_EXTRADEF, + ITEM_PARSE_ATTACK, + ITEM_PARSE_ATTACK_SPEED, + ITEM_PARSE_ROTATETO, + ITEM_PARSE_MOVEABLE, + ITEM_PARSE_BLOCKPROJECTILE, + ITEM_PARSE_PICKUPABLE, + ITEM_PARSE_FORCESERIALIZE, + ITEM_PARSE_FLOORCHANGE, + ITEM_PARSE_CORPSETYPE, + ITEM_PARSE_CONTAINERSIZE, + ITEM_PARSE_FLUIDSOURCE, + ITEM_PARSE_READABLE, + ITEM_PARSE_WRITEABLE, + ITEM_PARSE_MAXTEXTLEN, + ITEM_PARSE_WRITEONCEITEMID, + ITEM_PARSE_WEAPONTYPE, + ITEM_PARSE_SLOTTYPE, + ITEM_PARSE_AMMOTYPE, + ITEM_PARSE_SHOOTTYPE, + ITEM_PARSE_EFFECT, + ITEM_PARSE_RANGE, + ITEM_PARSE_STOPDURATION, + ITEM_PARSE_DECAYTO, + ITEM_PARSE_TRANSFORMEQUIPTO, + ITEM_PARSE_TRANSFORMDEEQUIPTO, + ITEM_PARSE_DURATION, + ITEM_PARSE_SHOWDURATION, + ITEM_PARSE_CHARGES, + ITEM_PARSE_SHOWCHARGES, + ITEM_PARSE_SHOWATTRIBUTES, + ITEM_PARSE_HITCHANCE, + ITEM_PARSE_MAXHITCHANCE, + ITEM_PARSE_INVISIBLE, + ITEM_PARSE_SPEED, + ITEM_PARSE_HEALTHGAIN, + ITEM_PARSE_HEALTHTICKS, + ITEM_PARSE_MANAGAIN, + ITEM_PARSE_MANATICKS, + ITEM_PARSE_MANASHIELD, + ITEM_PARSE_SKILLSWORD, + ITEM_PARSE_SKILLAXE, + ITEM_PARSE_SKILLCLUB, + ITEM_PARSE_SKILLDIST, + ITEM_PARSE_SKILLFISH, + ITEM_PARSE_SKILLSHIELD, + ITEM_PARSE_SKILLFIST, + ITEM_PARSE_MAXHITPOINTS, + ITEM_PARSE_MAXHITPOINTSPERCENT, + ITEM_PARSE_MAXMANAPOINTS, + ITEM_PARSE_MAXMANAPOINTSPERCENT, + ITEM_PARSE_MAGICPOINTS, + ITEM_PARSE_MAGICPOINTSPERCENT, + ITEM_PARSE_CRITICALHITCHANCE, + ITEM_PARSE_CRITICALHITAMOUNT, + ITEM_PARSE_LIFELEECHCHANCE, + ITEM_PARSE_LIFELEECHAMOUNT, + ITEM_PARSE_MANALEECHCHANCE, + ITEM_PARSE_MANALEECHAMOUNT, + ITEM_PARSE_FIELDABSORBPERCENTENERGY, + ITEM_PARSE_FIELDABSORBPERCENTFIRE, + ITEM_PARSE_FIELDABSORBPERCENTPOISON, + ITEM_PARSE_ABSORBPERCENTALL, + ITEM_PARSE_ABSORBPERCENTELEMENTS, + ITEM_PARSE_ABSORBPERCENTMAGIC, + ITEM_PARSE_ABSORBPERCENTENERGY, + ITEM_PARSE_ABSORBPERCENTFIRE, + ITEM_PARSE_ABSORBPERCENTPOISON, + ITEM_PARSE_ABSORBPERCENTICE, + ITEM_PARSE_ABSORBPERCENTHOLY, + ITEM_PARSE_ABSORBPERCENTDEATH, + ITEM_PARSE_ABSORBPERCENTLIFEDRAIN, + ITEM_PARSE_ABSORBPERCENTMANADRAIN, + ITEM_PARSE_ABSORBPERCENTDROWN, + ITEM_PARSE_ABSORBPERCENTPHYSICAL, + ITEM_PARSE_ABSORBPERCENTHEALING, + ITEM_PARSE_ABSORBPERCENTUNDEFINED, + ITEM_PARSE_MAGICLEVELENERGY, + ITEM_PARSE_MAGICLEVELFIRE, + ITEM_PARSE_MAGICLEVELPOISON, + ITEM_PARSE_MAGICLEVELICE, + ITEM_PARSE_MAGICLEVELHOLY, + ITEM_PARSE_MAGICLEVELDEATH, + ITEM_PARSE_MAGICLEVELLIFEDRAIN, + ITEM_PARSE_MAGICLEVELMANADRAIN, + ITEM_PARSE_MAGICLEVELDROWN, + ITEM_PARSE_MAGICLEVELPHYSICAL, + ITEM_PARSE_MAGICLEVELHEALING, + ITEM_PARSE_MAGICLEVELUNDEFINED, + ITEM_PARSE_SUPPRESSDRUNK, + ITEM_PARSE_SUPPRESSENERGY, + ITEM_PARSE_SUPPRESSFIRE, + ITEM_PARSE_SUPPRESSPOISON, + ITEM_PARSE_SUPPRESSDROWN, + ITEM_PARSE_SUPPRESSPHYSICAL, + ITEM_PARSE_SUPPRESSFREEZE, + ITEM_PARSE_SUPPRESSDAZZLE, + ITEM_PARSE_SUPPRESSCURSE, + ITEM_PARSE_FIELD, + ITEM_PARSE_REPLACEABLE, + ITEM_PARSE_PARTNERDIRECTION, + ITEM_PARSE_LEVELDOOR, + ITEM_PARSE_MALETRANSFORMTO, + ITEM_PARSE_FEMALETRANSFORMTO, + ITEM_PARSE_TRANSFORMTO, + ITEM_PARSE_DESTROYTO, + ITEM_PARSE_ELEMENTICE, + ITEM_PARSE_ELEMENTEARTH, + ITEM_PARSE_ELEMENTFIRE, + ITEM_PARSE_ELEMENTENERGY, + ITEM_PARSE_ELEMENTDEATH, + ITEM_PARSE_ELEMENTHOLY, + ITEM_PARSE_WALKSTACK, + ITEM_PARSE_BLOCKING, + ITEM_PARSE_ALLOWDISTREAD, + ITEM_PARSE_STOREITEM, + ITEM_PARSE_WORTH, + ITEM_PARSE_REFLECTPERCENTALL, + ITEM_PARSE_REFLECTPERCENTELEMENTS, + ITEM_PARSE_REFLECTPERCENTMAGIC, + ITEM_PARSE_REFLECTPERCENTENERGY, + ITEM_PARSE_REFLECTPERCENTFIRE, + ITEM_PARSE_REFLECTPERCENTEARTH, + ITEM_PARSE_REFLECTPERCENTICE, + ITEM_PARSE_REFLECTPERCENTHOLY, + ITEM_PARSE_REFLECTPERCENTDEATH, + ITEM_PARSE_REFLECTPERCENTLIFEDRAIN, + ITEM_PARSE_REFLECTPERCENTMANADRAIN, + ITEM_PARSE_REFLECTPERCENTDROWN, + ITEM_PARSE_REFLECTPERCENTPHYSICAL, + ITEM_PARSE_REFLECTPERCENTHEALING, + ITEM_PARSE_REFLECTCHANCEALL, + ITEM_PARSE_REFLECTCHANCEELEMENTS, + ITEM_PARSE_REFLECTCHANCEMAGIC, + ITEM_PARSE_REFLECTCHANCEENERGY, + ITEM_PARSE_REFLECTCHANCEFIRE, + ITEM_PARSE_REFLECTCHANCEEARTH, + ITEM_PARSE_REFLECTCHANCEICE, + ITEM_PARSE_REFLECTCHANCEHOLY, + ITEM_PARSE_REFLECTCHANCEDEATH, + ITEM_PARSE_REFLECTCHANCELIFEDRAIN, + ITEM_PARSE_REFLECTCHANCEMANADRAIN, + ITEM_PARSE_REFLECTCHANCEDROWN, + ITEM_PARSE_REFLECTCHANCEPHYSICAL, + ITEM_PARSE_REFLECTCHANCEHEALING, + ITEM_PARSE_BOOSTPERCENTALL, + ITEM_PARSE_BOOSTPERCENTELEMENTS, + ITEM_PARSE_BOOSTPERCENTMAGIC, + ITEM_PARSE_BOOSTPERCENTENERGY, + ITEM_PARSE_BOOSTPERCENTFIRE, + ITEM_PARSE_BOOSTPERCENTEARTH, + ITEM_PARSE_BOOSTPERCENTICE, + ITEM_PARSE_BOOSTPERCENTHOLY, + ITEM_PARSE_BOOSTPERCENTDEATH, + ITEM_PARSE_BOOSTPERCENTLIFEDRAIN, + ITEM_PARSE_BOOSTPERCENTMANADRAIN, + ITEM_PARSE_BOOSTPERCENTDROWN, + ITEM_PARSE_BOOSTPERCENTPHYSICAL, + ITEM_PARSE_BOOSTPERCENTHEALING, + ITEM_PARSE_SUPPLY, +}; + struct Abilities { uint32_t healthGain = 0; @@ -62,6 +232,7 @@ struct Abilities // extra skill modifiers std::array skills = {0}; std::array specialSkills = {0}; + std::array specialMagicLevelSkill = {0}; int32_t speed = 0; @@ -71,6 +242,10 @@ struct Abilities // damage abilities modifiers std::array absorbPercent = {0}; + std::array reflect; + + int16_t boostPercent[COMBAT_COUNT] = {0}; + // elemental damage uint16_t elementDamage = 0; CombatType_t elementType = COMBAT_NONE; @@ -108,9 +283,10 @@ class ItemType bool isTrashHolder() const { return (type == ITEM_TYPE_TRASHHOLDER); } bool isBed() const { return (type == ITEM_TYPE_BED); } bool isRune() const { return (type == ITEM_TYPE_RUNE); } - bool isPickupable() const { return pickupable; } + bool isPickupable() const { return (allowPickupable || pickupable); } bool isUseable() const { return (useable); } bool hasSubType() const { return (isFluidContainer() || isSplash() || stackable || charges != 0); } + bool isSupply() const { return supply; } Abilities& getAbilities() { @@ -204,7 +380,7 @@ class ItemType bool blockPickupable = false; bool blockProjectile = false; bool blockPathFind = false; - bool ignoreBlocking = false; + bool allowPickupable = false; bool showDuration = false; bool showCharges = false; bool showAttributes = false; @@ -223,6 +399,7 @@ class ItemType bool lookThrough = false; bool stopTime = false; bool showCount = true; + bool supply = false; }; class Items @@ -255,8 +432,8 @@ class Items uint32_t minorVersion = 0; uint32_t buildNumber = 0; - void loadFromLua(lua_State* L); - void parseLuaItem(const sol::table& itemTable, uint16_t id); + bool loadFromXml(); + void parseItemNode(const pugi::xml_node& itemNode, uint16_t id); void buildInventoryList(); const InventoryVector& getInventory() const { return inventory; } diff --git a/src/luagame.cpp b/src/luagame.cpp index 1ec3029..b075e91 100644 --- a/src/luagame.cpp +++ b/src/luagame.cpp @@ -637,20 +637,6 @@ int luaGameSaveAccountStorageValues(lua_State* L) return 1; } - -int luaGameRegisterItemTypes(lua_State* L) -{ - // Game.registerItemTypes(items) - if (LuaScriptInterface::getScriptEnv()->getScriptInterface() != &g_scripts->getScriptInterface()) { - reportErrorFunc(L, "ItemTypes can only be registered in the Scripts interface."); - lua_pushnil(L); - return 1; - } - - Item::items.loadFromLua(L); - pushBoolean(L, true); - return 1; -} } // namespace void LuaScriptInterface::registerGame() @@ -704,6 +690,4 @@ void LuaScriptInterface::registerGame() registerMethod("Game", "getAccountStorageValue", luaGameGetAccountStorageValue); registerMethod("Game", "setAccountStorageValue", luaGameSetAccountStorageValue); registerMethod("Game", "saveAccountStorageValues", luaGameSaveAccountStorageValues); - - registerMethod("Game", "registerItemTypes", luaGameRegisterItemTypes); } diff --git a/src/luaitem.cpp b/src/luaitem.cpp index 924c957..76c140c 100644 --- a/src/luaitem.cpp +++ b/src/luaitem.cpp @@ -721,6 +721,58 @@ int luaItemIsLoadedFromMap(lua_State* L) } return 1; } + +int luaItemSetReflect(lua_State* L) +{ + // item:setReflect(combatType, reflect) + Item* item = getUserdata(L, 1); + if (!item) { + lua_pushnil(L); + return 1; + } + + item->setReflect(getInteger(L, 2), getReflect(L, 3)); + pushBoolean(L, true); + return 1; +} + +int luaItemGetReflect(lua_State* L) +{ + // item:getReflect(combatType[, total = true]) + const Item* item = getUserdata(L, 1); + if (item) { + pushReflect(L, item->getReflect(getInteger(L, 2), getBoolean(L, 3, true))); + } else { + lua_pushnil(L); + } + return 1; +} + +int luaItemSetBoostPercent(lua_State* L) +{ + // item:setBoostPercent(combatType, percent) + Item* item = getUserdata(L, 1); + if (!item) { + lua_pushnil(L); + return 1; + } + + item->setBoostPercent(getInteger(L, 2), getInteger(L, 3)); + pushBoolean(L, true); + return 1; +} + +int luaItemGetBoostPercent(lua_State* L) +{ + // item:getBoostPercent(combatType[, total = true]) + const Item* item = getUserdata(L, 1); + if (item) { + lua_pushinteger(L, item->getBoostPercent(getInteger(L, 2), getBoolean(L, 3, true))); + } else { + lua_pushnil(L); + } + return 1; +} } // namespace void LuaScriptInterface::registerItem() @@ -775,4 +827,10 @@ void LuaScriptInterface::registerItem() registerMethod("Item", "hasProperty", luaItemHasProperty); registerMethod("Item", "isLoadedFromMap", luaItemIsLoadedFromMap); + + registerMethod("Item", "setReflect", luaItemSetReflect); + registerMethod("Item", "getReflect", luaItemGetReflect); + + registerMethod("Item", "setBoostPercent", luaItemSetBoostPercent); + registerMethod("Item", "getBoostPercent", luaItemGetBoostPercent); } diff --git a/src/luaitemtype.cpp b/src/luaitemtype.cpp index b9106e6..25ef03e 100644 --- a/src/luaitemtype.cpp +++ b/src/luaitemtype.cpp @@ -522,7 +522,7 @@ int luaItemTypeGetAbilities(lua_State* L) ItemType* itemType = getUserdata(L, 1); if (itemType) { Abilities& abilities = itemType->getAbilities(); - lua_createtable(L, 6, 12); + lua_createtable(L, 10, 12); setField(L, "healthGain", abilities.healthGain); setField(L, "healthTicks", abilities.healthTicks); setField(L, "manaGain", abilities.manaGain); @@ -587,6 +587,38 @@ int luaItemTypeGetAbilities(lua_State* L) lua_rawseti(L, -2, i + 1); } lua_setfield(L, -2, "absorbPercent"); + + // special magic level + lua_createtable(L, 0, COMBAT_COUNT); + for (int32_t i = 0; i < COMBAT_COUNT; i++) { + lua_pushinteger(L, abilities.specialMagicLevelSkill[i]); + lua_rawseti(L, -2, i + 1); + } + lua_setfield(L, -2, "specialMagicLevel"); + + // Damage boost percent + lua_createtable(L, 0, COMBAT_COUNT); + for (int32_t i = 0; i < COMBAT_COUNT; i++) { + lua_pushinteger(L, abilities.boostPercent[i]); + lua_rawseti(L, -2, i + 1); + } + lua_setfield(L, -2, "boostPercent"); + + // Reflect chance + lua_createtable(L, 0, COMBAT_COUNT); + for (int32_t i = 0; i < COMBAT_COUNT; i++) { + lua_pushinteger(L, abilities.reflect[i].chance); + lua_rawseti(L, -2, i + 1); + } + lua_setfield(L, -2, "reflectChance"); + + // Reflect percent + lua_createtable(L, 0, COMBAT_COUNT); + for (int32_t i = 0; i < COMBAT_COUNT; i++) { + lua_pushinteger(L, abilities.reflect[i].percent); + lua_rawseti(L, -2, i + 1); + } + lua_setfield(L, -2, "reflectPercent"); } return 1; } diff --git a/src/luamonster.cpp b/src/luamonster.cpp index 8bf8d92..8528817 100644 --- a/src/luamonster.cpp +++ b/src/luamonster.cpp @@ -22,10 +22,6 @@ int luaMonsterCreate(lua_State* L) if (isInteger(L, 2)) { monster = g_game.getMonsterByID(getInteger(L, 2)); } else if (isUserdata(L, 2)) { - if (getUserdataType(L, 2) != LuaData_Monster) { - lua_pushnil(L); - return 1; - } monster = getUserdata(L, 2); } else { monster = nullptr; diff --git a/src/luanpc.cpp b/src/luanpc.cpp index e0ec4e8..caa2903 100644 --- a/src/luanpc.cpp +++ b/src/luanpc.cpp @@ -23,10 +23,6 @@ int luaNpcCreate(lua_State* L) } else if (isString(L, 2)) { npc = g_game.getNpcByName(getString(L, 2)); } else if (isUserdata(L, 2)) { - if (getUserdataType(L, 2) != LuaData_Npc) { - lua_pushnil(L); - return 1; - } npc = getUserdata(L, 2); } else { npc = nullptr; diff --git a/src/luaplayer.cpp b/src/luaplayer.cpp index 51d4315..1be24a1 100644 --- a/src/luaplayer.cpp +++ b/src/luaplayer.cpp @@ -40,10 +40,6 @@ int luaPlayerCreate(lua_State* L) return 2; } } else if (isUserdata(L, 2)) { - if (getUserdataType(L, 2) != LuaData_Player) { - lua_pushnil(L); - return 1; - } player = getUserdata(L, 2); } else { player = nullptr; diff --git a/src/luascript.cpp b/src/luascript.cpp index 6daf9ca..98c4559 100644 --- a/src/luascript.cpp +++ b/src/luascript.cpp @@ -856,6 +856,14 @@ InstantSpell* Lua::getInstantSpell(lua_State* L, int32_t arg) return spell; } +Reflect Lua::getReflect(lua_State* L, int32_t arg) +{ + uint16_t percent = getField(L, arg, "percent"); + uint16_t chance = getField(L, arg, "chance"); + lua_pop(L, 2); + return Reflect(percent, chance); +} + Thing* Lua::getThing(lua_State* L, int32_t arg) { Thing* thing; @@ -1046,6 +1054,13 @@ void Lua::pushLoot(lua_State* L, const std::vector& lootList) } } +void Lua::pushReflect(lua_State* L, const Reflect& reflect) +{ + lua_createtable(L, 0, 2); + setField(L, "percent", reflect.percent); + setField(L, "chance", reflect.chance); +} + #define registerEnum(value) \ { \ std::string enumName = #value; \ @@ -2322,7 +2337,7 @@ int LuaScriptInterface::luaTransformToSHA1(lua_State* L) int LuaScriptInterface::luaDebugPrint(lua_State* L) { // debugPrint(text) - reportErrorFunc(L, Lua::getString(L, -1)); + reportErrorFunc(L, Lua::getString(L, 1)); return 0; } @@ -2507,14 +2522,14 @@ int LuaScriptInterface::luaDoChallengeCreature(lua_State* L) int LuaScriptInterface::luaIsValidUID(lua_State* L) { // isValidUID(uid) - Lua::pushBoolean(L, getScriptEnv()->getThingByUID(Lua::getInteger(L, -1)) != nullptr); + Lua::pushBoolean(L, getScriptEnv()->getThingByUID(Lua::getInteger(L, 1)) != nullptr); return 1; } int LuaScriptInterface::luaIsDepot(lua_State* L) { // isDepot(uid) - Container* container = getScriptEnv()->getContainerByUID(Lua::getInteger(L, -1)); + Container* container = getScriptEnv()->getContainerByUID(Lua::getInteger(L, 1)); Lua::pushBoolean(L, container && container->getDepotLocker()); return 1; } @@ -2523,7 +2538,7 @@ int LuaScriptInterface::luaIsMoveable(lua_State* L) { // isMoveable(uid) // isMovable(uid) - Thing* thing = getScriptEnv()->getThingByUID(Lua::getInteger(L, -1)); + Thing* thing = getScriptEnv()->getThingByUID(Lua::getInteger(L, 1)); Lua::pushBoolean(L, thing && thing->isPushable()); return 1; } @@ -2596,7 +2611,7 @@ int LuaScriptInterface::luaDoAddContainerItem(lua_State* L) int LuaScriptInterface::luaGetDepotId(lua_State* L) { // getDepotId(uid) - uint32_t uid = Lua::getInteger(L, -1); + uint32_t uid = Lua::getInteger(L, 1); Container* container = getScriptEnv()->getContainerByUID(uid); if (!container) { @@ -3065,7 +3080,7 @@ int LuaScriptInterface::luaResultGetStream(lua_State* L) auto stream = res->getString(Lua::getString(L, 2)); lua_pushlstring(L, stream.data(), stream.size()); - lua_pushnumber(L, stream.size()); + lua_pushinteger(L, stream.size()); return 2; } diff --git a/src/luascript.h b/src/luascript.h index 68618d5..44b6235 100644 --- a/src/luascript.h +++ b/src/luascript.h @@ -711,6 +711,7 @@ Position getPosition(lua_State* L, int32_t arg); Outfit_t getOutfit(lua_State* L, int32_t arg); Outfit getOutfitClass(lua_State* L, int32_t arg); InstantSpell* getInstantSpell(lua_State* L, int32_t arg); +Reflect getReflect(lua_State* L, int32_t arg); Thing* getThing(lua_State* L, int32_t arg); Creature* getCreature(lua_State* L, int32_t arg); @@ -819,6 +820,7 @@ void pushOutfit(lua_State* L, const Outfit_t& outfit); void pushOutfit(lua_State* L, const Outfit* outfit); void pushMount(lua_State* L, const Mount* mount); void pushLoot(lua_State* L, const std::vector& lootList); +void pushReflect(lua_State* L, const Reflect& reflect); // Userdata template diff --git a/src/movement.cpp b/src/movement.cpp index 8ec33ac..b1c8cdd 100644 --- a/src/movement.cpp +++ b/src/movement.cpp @@ -770,6 +770,19 @@ ReturnValue MoveEvent::EquipItem(MoveEvent* moveEvent, Player* player, Item* ite } } + for (int32_t i = 0; i < COMBAT_COUNT; ++i) { + if (it.abilities->specialMagicLevelSkill[i]) { + player->setSpecialMagicLevelSkill(indexToCombatType(i), it.abilities->specialMagicLevelSkill[i]); + } + } + + for (int32_t i = SPECIALSKILL_FIRST; i <= SPECIALSKILL_LAST; ++i) { + if (it.abilities->specialSkills[i]) { + needUpdateSkills = true; + player->setVarSpecialSkill(static_cast(i), it.abilities->specialSkills[i]); + } + } + if (needUpdateSkills) { player->sendSkills(); } @@ -793,6 +806,7 @@ ReturnValue MoveEvent::EquipItem(MoveEvent* moveEvent, Player* player, Item* ite if (needUpdateStats) { player->sendStats(); + player->sendSkills(); } return RETURNVALUE_NOERROR; @@ -847,6 +861,19 @@ ReturnValue MoveEvent::DeEquipItem(MoveEvent*, Player* player, Item* item, slots } } + for (int32_t i = 0; i < COMBAT_COUNT; ++i) { + if (it.abilities->specialMagicLevelSkill[i] != 0) { + player->setSpecialMagicLevelSkill(indexToCombatType(i), -it.abilities->specialMagicLevelSkill[i]); + } + } + + for (int32_t i = SPECIALSKILL_FIRST; i <= SPECIALSKILL_LAST; ++i) { + if (it.abilities->specialSkills[i] != 0) { + needUpdateSkills = true; + player->setVarSpecialSkill(static_cast(i), -it.abilities->specialSkills[i]); + } + } + if (needUpdateSkills) { player->sendSkills(); } @@ -870,6 +897,7 @@ ReturnValue MoveEvent::DeEquipItem(MoveEvent*, Player* player, Item* item, slots if (needUpdateStats) { player->sendStats(); + player->sendSkills(); } return RETURNVALUE_NOERROR; diff --git a/src/otserv.cpp b/src/otserv.cpp index e0b84ca..349b090 100644 --- a/src/otserv.cpp +++ b/src/otserv.cpp @@ -148,6 +148,11 @@ void mainLoader(ServiceManager* services) Item::items.buildNumber) << std::endl; + if (!Item::items.loadFromXml()) { + startupErrorMessage("Unable to load items (XML)!"); + return; + } + std::cout << ">> Loading script systems" << std::endl; if (!ScriptingManager::getInstance().loadScriptSystems()) { startupErrorMessage("Failed to load script systems"); diff --git a/src/player.cpp b/src/player.cpp index 178eeb5..051d502 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -1796,6 +1796,8 @@ BlockType_t Player::blockHit(Creature* attacker, CombatType_t combatType, int32_ } if (!ignoreResistances) { + Reflect reflect; + for (int32_t slot = CONST_SLOT_FIRST; slot <= CONST_SLOT_AMMO; ++slot) { if (!isItemAbilityEnabled(static_cast(slot))) { continue; @@ -1826,6 +1828,8 @@ BlockType_t Player::blockHit(Creature* attacker, CombatType_t combatType, int32_ } } + reflect += item->getReflect(combatType); + if (field) { const int16_t& fieldAbsorbPercent = it.abilities->fieldAbsorbPercent[combatTypeToIndex(combatType)]; if (fieldAbsorbPercent != 0) { @@ -1838,6 +1842,14 @@ BlockType_t Player::blockHit(Creature* attacker, CombatType_t combatType, int32_ } } } + + if (attacker && reflect.chance > 0 && reflect.percent != 0 && uniform_random(1, 100) <= reflect.chance) { + CombatDamage reflectDamage; + reflectDamage.primary.type = combatType; + reflectDamage.primary.value = -std::round(damage * (reflect.percent / 100.)); + reflectDamage.origin = ORIGIN_REFLECT; + g_game.combatChangeHealth(this, attacker, reflectDamage); + } } if (damage <= 0) { diff --git a/src/player.h b/src/player.h index ad631b9..8dd5501 100644 --- a/src/player.h +++ b/src/player.h @@ -254,6 +254,10 @@ class Player final : public Creature, public Cylinder uint32_t getLevel() const { return level; } uint8_t getLevelPercent() const { return levelPercent; } uint32_t getMagicLevel() const { return std::max(0, magLevel + varStats[STAT_MAGICPOINTS]); } + uint32_t getSpecialMagicLevel(CombatType_t type) const + { + return std::max(0, specialMagicLevelSkill[combatTypeToIndex(type)]); + } uint32_t getBaseMagicLevel() const { return magLevel; } uint16_t getMagicLevelPercent() const { return magLevelPercent; } uint8_t getSoul() const { return soul; } @@ -321,6 +325,11 @@ class Player final : public Creature, public Cylinder void setVarSpecialSkill(SpecialSkills_t skill, int32_t modifier) { varSpecialSkills[skill] += modifier; } + void setSpecialMagicLevelSkill(CombatType_t type, int16_t modifier) + { + specialMagicLevelSkill[combatTypeToIndex(type)] += modifier; + } + void setVarStats(stats_t stat, int32_t modifier); int32_t getDefaultStats(stats_t stat) const; @@ -424,6 +433,10 @@ class Player final : public Creature, public Cylinder { return static_cast(std::max(0, skills[skill].level + varSkills[skill])); } + uint16_t getSpecialMagicLevelSkill(CombatType_t type) const + { + return static_cast(std::max(0, specialMagicLevelSkill[combatTypeToIndex(type)])); + } uint16_t getBaseSkill(uint8_t skill) const { return skills[skill].level; } uint16_t getSkillPercent(uint8_t skill) const { return skills[skill].percent; } uint64_t getSkillTries(uint8_t skill) const { return skills[skill].tries; } @@ -1082,6 +1095,7 @@ class Player final : public Creature, public Cylinder int32_t varSpecialSkills[SPECIALSKILL_LAST + 1] = {}; int32_t varSkills[SKILL_LAST + 1] = {}; int32_t varStats[STAT_LAST + 1] = {}; + std::array specialMagicLevelSkill = {0}; int32_t purchaseCallback = -1; int32_t saleCallback = -1; int32_t MessageBufferCount = 0; diff --git a/src/tile.cpp b/src/tile.cpp index d93b432..10c1685 100644 --- a/src/tile.cpp +++ b/src/tile.cpp @@ -637,7 +637,7 @@ ReturnValue Tile::queryAdd(int32_t, const Thing& thing, uint32_t, uint32_t flags if (ground) { const ItemType& iiType = Item::items[ground->getID()]; if (iiType.blockSolid) { - if (!iiType.ignoreBlocking || item->isMagicField() || item->isBlocking()) { + if (!iiType.allowPickupable || item->isMagicField() || item->isBlocking()) { if (!item->isPickupable()) { return RETURNVALUE_NOTENOUGHROOM; } @@ -656,7 +656,7 @@ ReturnValue Tile::queryAdd(int32_t, const Thing& thing, uint32_t, uint32_t flags continue; } - if (iiType.ignoreBlocking && !item->isMagicField() && !item->isBlocking()) { + if (iiType.allowPickupable && !item->isMagicField() && !item->isBlocking()) { continue; }