mirror of
https://github.com/ErikasKontenis/SabrehavenServer.git
synced 2025-04-29 17:19:20 +02:00
added mounts, auras, wings and shop. Packet compression does not work extendedOpCodes does not work but shop is because of extra configuration
This commit is contained in:
parent
b130e66149
commit
1c35d04337
1
.gitignore
vendored
1
.gitignore
vendored
@ -25,3 +25,4 @@ vc14/.vs/
|
|||||||
/SabrehavenOTClient/tibianus.log
|
/SabrehavenOTClient/tibianus.log
|
||||||
/800OTClient/otclientv8.log
|
/800OTClient/otclientv8.log
|
||||||
/800OTClient/exports
|
/800OTClient/exports
|
||||||
|
/otclientv8-master
|
||||||
|
Binary file not shown.
Binary file not shown.
@ -13,30 +13,28 @@ function updateFeatures(version)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- you can add custom features here, list of them is in the modules\gamelib\const.lua
|
-- you can add custom features here, list of them is in the modules\gamelib\const.lua
|
||||||
|
g_game.enableFeature(GameLooktypeU16)
|
||||||
|
g_game.enableFeature(GameMessageStatements)
|
||||||
|
g_game.enableFeature(GameLoginPacketEncryption)
|
||||||
|
g_game.enableFeature(GamePlayerAddons)
|
||||||
|
g_game.enableFeature(GamePlayerStamina)
|
||||||
|
g_game.enableFeature(GameNewFluids)
|
||||||
|
g_game.enableFeature(GameMessageLevel)
|
||||||
|
g_game.enableFeature(GamePlayerStateU16)
|
||||||
|
g_game.enableFeature(GameNewOutfitProtocol)
|
||||||
|
g_game.enableFeature(GameWritableDate)
|
||||||
|
|
||||||
|
-- customs
|
||||||
|
--g_game.enableFeature(GameIngameStore)
|
||||||
g_game.enableFeature(GamePlayerMarket)
|
g_game.enableFeature(GamePlayerMarket)
|
||||||
--g_game.enableFeature(GameClientPing)
|
|
||||||
--g_game.enableFeature(GameExtendedOpcode)
|
g_game.enableFeature(GamePlayerMounts)
|
||||||
--g_game.enableFeature(GameMinimapLimitedToSingleFloor) -- it will generate minimap only for current floor
|
g_game.enableFeature(GameWingsAndAura)
|
||||||
--g_game.enableFeature(GameSpritesAlphaChannel)
|
g_game.enableFeature(GameOutfitShaders)
|
||||||
|
--g_game.enableFeature(GameExtendedOpcode)
|
||||||
--if(version >= 770) then
|
--g_game.enableFeature(GamePacketCompression)
|
||||||
g_game.enableFeature(GameLooktypeU16)
|
g_game.enableFeature(GameClientPing)
|
||||||
g_game.enableFeature(GameMessageStatements)
|
g_game.enableFeature(GameExtendedClientPing)
|
||||||
g_game.enableFeature(GameLoginPacketEncryption)
|
|
||||||
--end
|
|
||||||
|
|
||||||
--if(version >= 780) then
|
|
||||||
g_game.enableFeature(GamePlayerAddons)
|
|
||||||
g_game.enableFeature(GamePlayerStamina)
|
|
||||||
g_game.enableFeature(GameNewFluids)
|
|
||||||
g_game.enableFeature(GameMessageLevel)
|
|
||||||
g_game.enableFeature(GamePlayerStateU16)
|
|
||||||
g_game.enableFeature(GameNewOutfitProtocol)
|
|
||||||
--end
|
|
||||||
|
|
||||||
--if(version >= 790) then
|
|
||||||
g_game.enableFeature(GameWritableDate)
|
|
||||||
--end
|
|
||||||
|
|
||||||
if(version >= 840) then
|
if(version >= 840) then
|
||||||
g_game.enableFeature(GameProtocolChecksum)
|
g_game.enableFeature(GameProtocolChecksum)
|
||||||
|
@ -12,8 +12,8 @@ function init()
|
|||||||
-- outfit shaders
|
-- outfit shaders
|
||||||
g_shaders.createOutfitShader("outfit_default", "/shaders/outfit_default_vertex", "/shaders/outfit_default_fragment")
|
g_shaders.createOutfitShader("outfit_default", "/shaders/outfit_default_vertex", "/shaders/outfit_default_fragment")
|
||||||
|
|
||||||
g_shaders.createOutfitShader("outfit_rainbow", "/shaders/outfit_rainbow_vertex", "/shaders/outfit_rainbow_fragment")
|
g_shaders.createOutfitShader("Rainbow Outfit", "/shaders/outfit_rainbow_vertex", "/shaders/outfit_rainbow_fragment")
|
||||||
g_shaders.addTexture("outfit_rainbow", "/images/shaders/rainbow.png")
|
g_shaders.addTexture("Rainbow Outfit", "/images/shaders/rainbow.png")
|
||||||
|
|
||||||
-- you can use creature:setOutfitShader("outfit_rainbow") to set shader
|
-- you can use creature:setOutfitShader("outfit_rainbow") to set shader
|
||||||
|
|
||||||
|
@ -21,9 +21,9 @@ local AD = {}
|
|||||||
local selectedOffer = {}
|
local selectedOffer = {}
|
||||||
|
|
||||||
local function sendAction(action, data)
|
local function sendAction(action, data)
|
||||||
if not g_game.getFeature(GameExtendedOpcode) then
|
--if not g_game.getFeature(GameExtendedOpcode) then
|
||||||
return
|
-- return
|
||||||
end
|
--end
|
||||||
|
|
||||||
local protocolGame = g_game.getProtocolGame()
|
local protocolGame = g_game.getProtocolGame()
|
||||||
if data == nil then
|
if data == nil then
|
||||||
|
File diff suppressed because one or more lines are too long
@ -25,6 +25,7 @@ find_package(GMP REQUIRED)
|
|||||||
find_package(PugiXML REQUIRED)
|
find_package(PugiXML REQUIRED)
|
||||||
find_package(MySQL)
|
find_package(MySQL)
|
||||||
find_package(Threads)
|
find_package(Threads)
|
||||||
|
find_package(ZLIB REQUIRED)
|
||||||
|
|
||||||
# Selects LuaJIT if user defines or auto-detected
|
# Selects LuaJIT if user defines or auto-detected
|
||||||
if(DEFINED USE_LUAJIT AND NOT USE_LUAJIT)
|
if(DEFINED USE_LUAJIT AND NOT USE_LUAJIT)
|
||||||
@ -49,7 +50,7 @@ add_subdirectory(src)
|
|||||||
add_executable(tfs ${tfs_SRC})
|
add_executable(tfs ${tfs_SRC})
|
||||||
|
|
||||||
include_directories(${MYSQL_INCLUDE_DIR} ${LUA_INCLUDE_DIR} ${Boost_INCLUDE_DIRS} ${PUGIXML_INCLUDE_DIR} ${GMP_INCLUDE_DIR} ${Crypto++_INCLUDE_DIR})
|
include_directories(${MYSQL_INCLUDE_DIR} ${LUA_INCLUDE_DIR} ${Boost_INCLUDE_DIRS} ${PUGIXML_INCLUDE_DIR} ${GMP_INCLUDE_DIR} ${Crypto++_INCLUDE_DIR})
|
||||||
target_link_libraries(tfs ${MYSQL_CLIENT_LIBS} ${LUA_LIBRARIES} ${Boost_LIBRARIES} ${Boost_FILESYSTEM_LIBRARY} ${PUGIXML_LIBRARIES} ${GMP_LIBRARIES} ${Crypto++_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
|
target_link_libraries(tfs ${MYSQL_CLIENT_LIBS} ${LUA_LIBRARIES} ${Boost_LIBRARIES} ${Boost_FILESYSTEM_LIBRARY} ${PUGIXML_LIBRARIES} ${ZLIB_LIBRARY} ${GMP_LIBRARIES} ${Crypto++_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
|
||||||
|
|
||||||
set_target_properties(tfs PROPERTIES COTIRE_CXX_PREFIX_HEADER_INIT "src/otpch.h")
|
set_target_properties(tfs PROPERTIES COTIRE_CXX_PREFIX_HEADER_INIT "src/otpch.h")
|
||||||
set_target_properties(tfs PROPERTIES COTIRE_ADD_UNITY_BUILD FALSE)
|
set_target_properties(tfs PROPERTIES COTIRE_ADD_UNITY_BUILD FALSE)
|
||||||
|
16
cmake/FindZLIB.cmake
Normal file
16
cmake/FindZLIB.cmake
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# Try to find the ZLIB library
|
||||||
|
# ZLIB_FOUND - system has ZLIB
|
||||||
|
# ZLIB_INCLUDE_DIR - the ZLIB include directory
|
||||||
|
# ZLIB_LIBRARY - the ZLIB library
|
||||||
|
|
||||||
|
FIND_PATH(ZLIB_INCLUDE_DIR NAMES zlib.h)
|
||||||
|
SET(_ZLIB_STATIC_LIBS libz.a libzlib.a zlib1.a)
|
||||||
|
SET(_ZLIB_SHARED_LIBS libz.dll.a zdll zlib zlib1 z)
|
||||||
|
IF(USE_STATIC_LIBS)
|
||||||
|
FIND_LIBRARY(ZLIB_LIBRARY NAMES ${_ZLIB_STATIC_LIBS} ${_ZLIB_SHARED_LIBS})
|
||||||
|
ELSE()
|
||||||
|
FIND_LIBRARY(ZLIB_LIBRARY NAMES ${_ZLIB_SHARED_LIBS} ${_ZLIB_STATIC_LIBS})
|
||||||
|
ENDIF()
|
||||||
|
INCLUDE(FindPackageHandleStandardArgs)
|
||||||
|
FIND_PACKAGE_HANDLE_STANDARD_ARGS(ZLIB DEFAULT_MSG ZLIB_LIBRARY ZLIB_INCLUDE_DIR)
|
||||||
|
MARK_AS_ADVANCED(ZLIB_LIBRARY ZLIB_INCLUDE_DIR)
|
@ -51,7 +51,8 @@ allowClones = false
|
|||||||
serverName = "Tibianus"
|
serverName = "Tibianus"
|
||||||
statusTimeout = 5000
|
statusTimeout = 5000
|
||||||
replaceKickOnLogin = true
|
replaceKickOnLogin = true
|
||||||
maxPacketsPerSecond = -1
|
maxPacketsPerSecond = 50
|
||||||
|
packetCompression = true
|
||||||
autoStackCumulatives = true
|
autoStackCumulatives = true
|
||||||
moneyRate = 1
|
moneyRate = 1
|
||||||
|
|
||||||
|
6
data/XML/auras.xml
Normal file
6
data/XML/auras.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<auras>
|
||||||
|
<!-- <aura id="1" clientid="368" name="Widow Queen" speed="20" />
|
||||||
|
<aura id="2" clientid="369" name="Racing Bird" speed="20" />
|
||||||
|
<aura id="3" clientid="370" name="War Bear" speed="20" /> -->
|
||||||
|
</auras>
|
106
data/XML/mounts.xml
Normal file
106
data/XML/mounts.xml
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<mounts>
|
||||||
|
<mount id="1" clientid="368" name="Widow Queen" speed="20" premium="no" />
|
||||||
|
<!--
|
||||||
|
<mount id="2" clientid="369" name="Racing Bird" speed="20" premium="yes" />
|
||||||
|
<mount id="3" clientid="370" name="War Bear" speed="20" premium="yes" />
|
||||||
|
<mount id="4" clientid="371" name="Black Sheep" speed="20" premium="yes" />
|
||||||
|
<mount id="5" clientid="372" name="Midnight Panther" speed="20" premium="yes" />
|
||||||
|
<mount id="6" clientid="373" name="Draptor" speed="20" premium="yes" />
|
||||||
|
<mount id="7" clientid="374" name="Titanica" speed="20" premium="yes" />
|
||||||
|
<mount id="8" clientid="375" name="Tin Lizzard" speed="20" premium="yes" />
|
||||||
|
<mount id="9" clientid="376" name="Blazebringer" speed="20" premium="yes" />
|
||||||
|
<mount id="10" clientid="377" name="Rapid Boar" speed="20" premium="yes" />
|
||||||
|
<mount id="11" clientid="378" name="Stampor" speed="20" premium="yes" />
|
||||||
|
<mount id="12" clientid="379" name="Undead Cavebear" speed="20" premium="yes" />
|
||||||
|
<mount id="13" clientid="387" name="Donkey" speed="20" premium="yes" />
|
||||||
|
<mount id="14" clientid="388" name="Tiger Slug" speed="20" premium="yes" />
|
||||||
|
<mount id="15" clientid="389" name="Uniwheel" speed="20" premium="yes" />
|
||||||
|
<mount id="16" clientid="390" name="Crystal Wolf" speed="20" premium="yes" />
|
||||||
|
<mount id="17" clientid="392" name="War Horse" speed="20" premium="yes" />
|
||||||
|
<mount id="18" clientid="401" name="Kingly Deer" speed="20" premium="yes" />
|
||||||
|
<mount id="19" clientid="402" name="Tamed Panda" speed="20" premium="yes" />
|
||||||
|
<mount id="20" clientid="405" name="Dromedary" speed="20" premium="yes" />
|
||||||
|
<mount id="21" clientid="406" name="Scorpion King" speed="20" premium="yes" />
|
||||||
|
<mount id="22" clientid="421" name="Rented Horse" speed="20" premium="no" />
|
||||||
|
<mount id="23" clientid="426" name="Armoured War Horse" speed="20" premium="no" />
|
||||||
|
<mount id="24" clientid="427" name="Shadow Draptor" speed="20" premium="yes" />
|
||||||
|
<mount id="25" clientid="437" name="Rented Horse" speed="20" premium="no" />
|
||||||
|
<mount id="26" clientid="438" name="Rented Horse" speed="20" premium="no" />
|
||||||
|
<mount id="27" clientid="447" name="Lady Bug" speed="20" premium="yes" />
|
||||||
|
<mount id="28" clientid="450" name="Manta Ray" speed="20" premium="yes" />
|
||||||
|
<mount id="29" clientid="502" name="Ironblight" speed="20" premium="yes" />
|
||||||
|
<mount id="30" clientid="503" name="Magma Crawler" speed="20" premium="yes" />
|
||||||
|
<mount id="31" clientid="506" name="Dragonling" speed="20" premium="yes" />
|
||||||
|
<mount id="32" clientid="515" name="Gnarlhound" speed="20" premium="yes" />
|
||||||
|
<mount id="33" clientid="521" name="Crimson Ray" speed="20" premium="no" />
|
||||||
|
<mount id="34" clientid="522" name="Steelbeak" speed="20" premium="no" />
|
||||||
|
<mount id="35" clientid="526" name="Water Buffalo" speed="20" premium="yes" />
|
||||||
|
<mount id="36" clientid="546" name="Tombstinger" speed="20" premium="no" />
|
||||||
|
<mount id="37" clientid="547" name="Platesaurian" speed="20" premium="no" />
|
||||||
|
<mount id="38" clientid="548" name="Ursagrodon" speed="20" premium="yes" />
|
||||||
|
<mount id="39" clientid="559" name="The Hellgrip" speed="20" premium="yes" />
|
||||||
|
<mount id="40" clientid="571" name="Noble Lion" speed="20" premium="yes" />
|
||||||
|
<mount id="41" clientid="572" name="Desert King" speed="20" premium="no" />
|
||||||
|
<mount id="42" clientid="580" name="Shock Head" speed="20" premium="yes" />
|
||||||
|
<mount id="43" clientid="606" name="Walker" speed="20" premium="yes" />
|
||||||
|
<mount id="44" clientid="621" name="Azudocus" speed="20" premium="no" />
|
||||||
|
<mount id="45" clientid="622" name="Carpacosaurus" speed="20" premium="no" />
|
||||||
|
<mount id="46" clientid="624" name="Death Crawler" speed="20" premium="no" />
|
||||||
|
<mount id="47" clientid="626" name="Flamesteed" speed="20" premium="no" />
|
||||||
|
<mount id="48" clientid="627" name="Jade Lion" speed="20" premium="no" />
|
||||||
|
<mount id="49" clientid="628" name="Jade Pincer" speed="20" premium="no" />
|
||||||
|
<mount id="50" clientid="629" name="Nethersteed" speed="20" premium="no" />
|
||||||
|
<mount id="51" clientid="630" name="Tempest" speed="20" premium="no" />
|
||||||
|
<mount id="52" clientid="631" name="Winter King" speed="20" premium="no" />
|
||||||
|
<mount id="53" clientid="644" name="Doombringer" speed="20" premium="no" />
|
||||||
|
<mount id="54" clientid="647" name="Woodland Prince" speed="20" premium="no" />
|
||||||
|
<mount id="55" clientid="648" name="Hailstorm Fury" speed="20" premium="no" />
|
||||||
|
<mount id="56" clientid="649" name="Siegebreaker" speed="20" premium="no" />
|
||||||
|
<mount id="57" clientid="650" name="Poisonbane" speed="20" premium="no" />
|
||||||
|
<mount id="58" clientid="651" name="Blackpelt" speed="20" premium="no" />
|
||||||
|
<mount id="59" clientid="669" name="Golden Dragonfly" speed="20" premium="no" />
|
||||||
|
<mount id="60" clientid="670" name="Steel Bee" speed="20" premium="no" />
|
||||||
|
<mount id="61" clientid="671" name="Copper Fly" speed="20" premium="no" />
|
||||||
|
<mount id="62" clientid="672" name="Tundra Rambler" speed="20" premium="no" />
|
||||||
|
<mount id="63" clientid="673" name="Highland Yak" speed="20" premium="no" />
|
||||||
|
<mount id="64" clientid="674" name="Glacier Vagabond" speed="20" premium="no" />
|
||||||
|
<mount id="65" clientid="688" name="Flying Divan" speed="20" premium="no" />
|
||||||
|
<mount id="66" clientid="689" name="Magic Carpet" speed="20" premium="no" />
|
||||||
|
<mount id="67" clientid="690" name="Floating Kashmir" speed="20" premium="no" />
|
||||||
|
<mount id="68" clientid="691" name="Ringtail Waccoon" speed="20" premium="no" />
|
||||||
|
<mount id="69" clientid="692" name="Night Waccoon" speed="20" premium="no" />
|
||||||
|
<mount id="70" clientid="693" name="Emerald Waccoon" speed="20" premium="no" />
|
||||||
|
<mount id="71" clientid="682" name="Glooth Glider" speed="20" premium="yes" />
|
||||||
|
<mount id="72" clientid="685" name="Shadow Hart" speed="20" premium="no" />
|
||||||
|
<mount id="73" clientid="686" name="Black Stag" speed="20" premium="no" />
|
||||||
|
<mount id="74" clientid="687" name="Emperor Deer" speed="20" premium="no" />
|
||||||
|
<mount id="75" clientid="726" name="Flitterkatzen" speed="20" premium="no" />
|
||||||
|
<mount id="76" clientid="727" name="Venompaw" speed="20" premium="no" />
|
||||||
|
<mount id="77" clientid="728" name="Batcat" speed="20" premium="no" />
|
||||||
|
<mount id="78" clientid="734" name="Sea Devil" speed="20" premium="no" />
|
||||||
|
<mount id="79" clientid="735" name="Coralripper" speed="20" premium="no" />
|
||||||
|
<mount id="80" clientid="736" name="Plumfish" speed="20" premium="no" />
|
||||||
|
<mount id="81" clientid="738" name="Gorongra" speed="20" premium="no" />
|
||||||
|
<mount id="82" clientid="739" name="Noctungra" speed="20" premium="no" />
|
||||||
|
<mount id="83" clientid="740" name="Silverneck" speed="20" premium="no" />
|
||||||
|
<mount id="84" clientid="761" name="Slagsnare" speed="20" premium="no" />
|
||||||
|
<mount id="85" clientid="762" name="Nightstinger" speed="20" premium="no" />
|
||||||
|
<mount id="86" clientid="763" name="Razorcreep" speed="20" premium="no" />
|
||||||
|
<mount id="87" clientid="848" name="Rift Runner" speed="20" premium="yes" />
|
||||||
|
<mount id="88" clientid="849" name="Nightdweller" speed="20" premium="no" />
|
||||||
|
<mount id="89" clientid="850" name="Frostflare" speed="20" premium="no" />
|
||||||
|
<mount id="90" clientid="851" name="Cinderhoof" speed="20" premium="no" />
|
||||||
|
<mount id="91" clientid="868" name="Mouldpincer" speed="20" premium="no" />
|
||||||
|
<mount id="92" clientid="869" name="Bloodcurl" speed="20" premium="no" />
|
||||||
|
<mount id="93" clientid="870" name="Leafscuttler" speed="20" premium="no" />
|
||||||
|
<mount id="94" clientid="883" name="Sparkion" speed="20" premium="yes" />
|
||||||
|
<mount id="95" clientid="886" name="Swamp Snapper" speed="20" premium="no" />
|
||||||
|
<mount id="96" clientid="887" name="Mould Shell" speed="20" premium="no" />
|
||||||
|
<mount id="97" clientid="888" name="Reed Lurker" speed="20" premium="no" />
|
||||||
|
<mount id="98" clientid="889" name="Neon Sparkid" speed="20" premium="yes" />
|
||||||
|
<mount id="99" clientid="890" name="Vortexion" speed="20" premium="yes" />
|
||||||
|
<mount id="100" clientid="901" name="Ivory Fang" speed="20" premium="no" />
|
||||||
|
<mount id="101" clientid="902" name="Shadow Claw" speed="20" premium="no" />
|
||||||
|
<mount id="102" clientid="903" name="Snow Pelt" speed="20" premium="no" /> -->
|
||||||
|
</mounts>
|
4
data/XML/shaders.xml
Normal file
4
data/XML/shaders.xml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<shaders>
|
||||||
|
<shader id="1" name="Rainbow Outfit" premium="no" />
|
||||||
|
</shaders>
|
6
data/XML/wings.xml
Normal file
6
data/XML/wings.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<wings>
|
||||||
|
<!-- <wing id="1" clientid="368" name="Widow Queen" speed="20" />
|
||||||
|
<wing id="2" clientid="369" name="Racing Bird" speed="20" />
|
||||||
|
<wing id="3" clientid="370" name="War Bear" speed="20" /> -->
|
||||||
|
</wings>
|
@ -16,4 +16,6 @@
|
|||||||
|
|
||||||
<!-- Svargrond Arena: Killing a boss -->
|
<!-- Svargrond Arena: Killing a boss -->
|
||||||
<event type="kill" name="SvargrondArenaKill" script="arena_kill.lua" />
|
<event type="kill" name="SvargrondArenaKill" script="arena_kill.lua" />
|
||||||
|
|
||||||
|
<event type="extendedopcode" name="Shop" script="shop.lua" />
|
||||||
</creaturescripts>
|
</creaturescripts>
|
||||||
|
@ -324,6 +324,7 @@ function onLogin(player)
|
|||||||
player:registerEvent("InquisitionUngreez")
|
player:registerEvent("InquisitionUngreez")
|
||||||
player:registerEvent("InquisitionBosses")
|
player:registerEvent("InquisitionBosses")
|
||||||
player:registerEvent("SvargrondArenaKill")
|
player:registerEvent("SvargrondArenaKill")
|
||||||
|
player:registerEvent("Shop")
|
||||||
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
362
data/creaturescripts/scripts/shop.lua
Normal file
362
data/creaturescripts/scripts/shop.lua
Normal file
@ -0,0 +1,362 @@
|
|||||||
|
-- BETA VERSION, net tested yet
|
||||||
|
-- Instruction:
|
||||||
|
-- creaturescripts.xml <event type="extendedopcode" name="Shop" script="shop.lua" />
|
||||||
|
-- and in login.lua player:registerEvent("Shop")
|
||||||
|
-- create sql table shop_history
|
||||||
|
-- set variables
|
||||||
|
-- set up function init(), add there items and categories, follow examples
|
||||||
|
-- set up callbacks at the bottom to add player item/outfit/whatever you want
|
||||||
|
|
||||||
|
local SHOP_EXTENDED_OPCODE = 201
|
||||||
|
local SHOP_OFFERS = {}
|
||||||
|
local SHOP_CALLBACKS = {}
|
||||||
|
local SHOP_CATEGORIES = nil
|
||||||
|
local SHOP_BUY_URL = "http://otland.net" -- can be empty
|
||||||
|
local SHOP_AD = { -- can be nil
|
||||||
|
image = "https://s3.envato.com/files/62273611/PNG%20Blue/Banner%20blue%20468x60.png",
|
||||||
|
url = "http://otclient.ovh",
|
||||||
|
text = ""
|
||||||
|
}
|
||||||
|
local MAX_PACKET_SIZE = 50000
|
||||||
|
|
||||||
|
--[[ SQL TABLE
|
||||||
|
CREATE TABLE `shop_history` (
|
||||||
|
`id` int(11) NOT NULL,
|
||||||
|
`account` int(11) NOT NULL,
|
||||||
|
`player` int(11) NOT NULL,
|
||||||
|
`date` datetime NOT NULL,
|
||||||
|
`title` varchar(100) NOT NULL,
|
||||||
|
`cost` int(11) NOT NULL,
|
||||||
|
`details` varchar(500) NOT NULL
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
ALTER TABLE `shop_history`
|
||||||
|
ADD PRIMARY KEY (`id`);
|
||||||
|
ALTER TABLE `shop_history`
|
||||||
|
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
|
||||||
|
]]--
|
||||||
|
|
||||||
|
function init()
|
||||||
|
-- print(json.encode(g_game.getLocalPlayer():getOutfit())) -- in console in otclient, will print current outfit and mount
|
||||||
|
|
||||||
|
SHOP_CATEGORIES = {}
|
||||||
|
|
||||||
|
local category1 = addCategory({
|
||||||
|
type="item",
|
||||||
|
item=ItemType(6561):getId(),
|
||||||
|
count=1,
|
||||||
|
name="Items"
|
||||||
|
})
|
||||||
|
local category2 = addCategory({
|
||||||
|
type="outfit",
|
||||||
|
name="Outfits",
|
||||||
|
outfit={
|
||||||
|
mount=0,
|
||||||
|
feet=0,
|
||||||
|
legs=0,
|
||||||
|
body=0,
|
||||||
|
type=136,
|
||||||
|
auxType=0,
|
||||||
|
addons=0,
|
||||||
|
head=0,
|
||||||
|
rotating=false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
local category3 = addCategory({
|
||||||
|
type="outfit",
|
||||||
|
name="Mounts",
|
||||||
|
outfit={
|
||||||
|
mount=0,
|
||||||
|
feet=0,
|
||||||
|
legs=0,
|
||||||
|
body=0,
|
||||||
|
type=368,
|
||||||
|
auxType=0,
|
||||||
|
addons=0,
|
||||||
|
head=0,
|
||||||
|
rotating=false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
local category4 = addCategory({
|
||||||
|
type="item",
|
||||||
|
item=ItemType(5919):getId(),
|
||||||
|
count=1,
|
||||||
|
name="Addon Items"
|
||||||
|
})
|
||||||
|
local category5 = addCategory({
|
||||||
|
type="item",
|
||||||
|
item=ItemType(4835):getId(),
|
||||||
|
count=1,
|
||||||
|
name="Quest Items"
|
||||||
|
})
|
||||||
|
local category6 = addCategory({
|
||||||
|
type="item",
|
||||||
|
item=ItemType(3734):getId(),
|
||||||
|
count=1,
|
||||||
|
name="Decorations"
|
||||||
|
})
|
||||||
|
-- local category4 = addCategory({
|
||||||
|
-- type="image",
|
||||||
|
-- image="/data/images/game/states/electrified.png",
|
||||||
|
-- name="Category with local image"
|
||||||
|
-- })
|
||||||
|
|
||||||
|
|
||||||
|
category1.addItem(50, 6561, 1, "Ceremonial Ankh", "gives you all blessings")
|
||||||
|
category1.addItem(20, 5908, 1, "Obsidian Knife", "")
|
||||||
|
category1.addItem(200, 5797, 1, "Dice", "")
|
||||||
|
category1.addItem(100, 6549, 1, "Green Djinn Access", "The magical powder will bless you with the power to convince the green djinns")
|
||||||
|
category1.addItem(100, 6551, 1, "Blue Djinn Access", "The magical powder will bless you with the power to convince the blue djinns")
|
||||||
|
category1.addItem(100, 3252, 1, "Postman Access", "The magical horn will grant you the trustworthy postman rank")
|
||||||
|
|
||||||
|
category2.addOutfit(150, {
|
||||||
|
mount=0,
|
||||||
|
feet=0,
|
||||||
|
legs=0,
|
||||||
|
body=0,
|
||||||
|
type=162,
|
||||||
|
auxType=0,
|
||||||
|
addons=0,
|
||||||
|
head=0,
|
||||||
|
rotating=true
|
||||||
|
}, "Monk", "")
|
||||||
|
|
||||||
|
category3.addOutfit(75, {
|
||||||
|
mount=1,
|
||||||
|
feet=0,
|
||||||
|
legs=0,
|
||||||
|
body=0,
|
||||||
|
type=368,
|
||||||
|
auxType=0,
|
||||||
|
addons=0,
|
||||||
|
head=0,
|
||||||
|
rotating=true
|
||||||
|
}, "Widow Queen", "")
|
||||||
|
|
||||||
|
category4.addItem(150, 5919, 1, "Dragon Claw", "It is the claw of Demodras")
|
||||||
|
|
||||||
|
category5.addItem(30, 4835, 1, "Snake Destroyer", "")
|
||||||
|
|
||||||
|
category6.addItem(10, 3734, 10, "10x Blood Herb", "")
|
||||||
|
|
||||||
|
--category4.addImage(10000, "/data/images/game/states/haste.png", "Offer with local image", "another local image\n/data/images/game/states/haste.png")
|
||||||
|
--category4.addImage(10000, "http://otclient.ovh/images/freezing.png", "Offer with remote image and custom buy action", "blalasdasd image\nhttp://otclient.ovh/images/freezing.png", customImageBuyAction)
|
||||||
|
end
|
||||||
|
|
||||||
|
function addCategory(data)
|
||||||
|
data['offers'] = {}
|
||||||
|
table.insert(SHOP_CATEGORIES, data)
|
||||||
|
table.insert(SHOP_CALLBACKS, {})
|
||||||
|
local index = #SHOP_CATEGORIES
|
||||||
|
return {
|
||||||
|
addItem = function(cost, itemId, count, title, description, callback)
|
||||||
|
if not callback then
|
||||||
|
callback = defaultItemBuyAction
|
||||||
|
end
|
||||||
|
table.insert(SHOP_CATEGORIES[index]['offers'], {
|
||||||
|
cost=cost,
|
||||||
|
type="item",
|
||||||
|
item=ItemType(itemId):getId(), -- displayed
|
||||||
|
itemId=itemId,
|
||||||
|
count=count,
|
||||||
|
title=title,
|
||||||
|
description=description
|
||||||
|
})
|
||||||
|
table.insert(SHOP_CALLBACKS[index], callback)
|
||||||
|
end,
|
||||||
|
addOutfit = function(cost, outfit, title, description, callback)
|
||||||
|
if not callback then
|
||||||
|
callback = defaultOutfitBuyAction
|
||||||
|
end
|
||||||
|
table.insert(SHOP_CATEGORIES[index]['offers'], {
|
||||||
|
cost=cost,
|
||||||
|
type="outfit",
|
||||||
|
outfit=outfit,
|
||||||
|
title=title,
|
||||||
|
description=description
|
||||||
|
})
|
||||||
|
table.insert(SHOP_CALLBACKS[index], callback)
|
||||||
|
end,
|
||||||
|
addImage = function(cost, image, title, description, callback)
|
||||||
|
if not callback then
|
||||||
|
callback = defaultImageBuyAction
|
||||||
|
end
|
||||||
|
table.insert(SHOP_CATEGORIES[index]['offers'], {
|
||||||
|
cost=cost,
|
||||||
|
type="image",
|
||||||
|
image=image,
|
||||||
|
title=title,
|
||||||
|
description=description
|
||||||
|
})
|
||||||
|
table.insert(SHOP_CALLBACKS[index], callback)
|
||||||
|
end
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function getPoints(player)
|
||||||
|
local points = 0
|
||||||
|
local resultId = db.storeQuery("SELECT `points` FROM `znote_accounts` WHERE `id` = " .. player:getAccountId())
|
||||||
|
if resultId ~= false then
|
||||||
|
points = result.getDataInt(resultId, "points")
|
||||||
|
result.free(resultId)
|
||||||
|
end
|
||||||
|
return points
|
||||||
|
end
|
||||||
|
|
||||||
|
function getStatus(player)
|
||||||
|
local status = {
|
||||||
|
ad = SHOP_AD,
|
||||||
|
points = getPoints(player),
|
||||||
|
buyUrl = SHOP_BUY_URL
|
||||||
|
}
|
||||||
|
return status
|
||||||
|
end
|
||||||
|
|
||||||
|
function sendJSON(player, action, data, forceStatus)
|
||||||
|
local status = nil
|
||||||
|
if not player:getStorageValue(1150001) or player:getStorageValue(1150001) + 10 < os.time() or forceStatus then
|
||||||
|
status = getStatus(player)
|
||||||
|
end
|
||||||
|
player:setStorageValue(1150001, os.time())
|
||||||
|
|
||||||
|
|
||||||
|
local buffer = json.encode({action = action, data = data, status = status})
|
||||||
|
local s = {}
|
||||||
|
for i=1, #buffer, MAX_PACKET_SIZE do
|
||||||
|
s[#s+1] = buffer:sub(i,i+MAX_PACKET_SIZE - 1)
|
||||||
|
end
|
||||||
|
local msg = NetworkMessage()
|
||||||
|
if #s == 1 then
|
||||||
|
msg:addByte(50)
|
||||||
|
msg:addByte(SHOP_EXTENDED_OPCODE)
|
||||||
|
msg:addString(s[1])
|
||||||
|
msg:sendToPlayer(player)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
-- split message if too big
|
||||||
|
msg:addByte(50)
|
||||||
|
msg:addByte(SHOP_EXTENDED_OPCODE)
|
||||||
|
msg:addString("S" .. s[1])
|
||||||
|
msg:sendToPlayer(player)
|
||||||
|
for i=2,#s - 1 do
|
||||||
|
msg = NetworkMessage()
|
||||||
|
msg:addByte(50)
|
||||||
|
msg:addByte(SHOP_EXTENDED_OPCODE)
|
||||||
|
msg:addString("P" .. s[i])
|
||||||
|
msg:sendToPlayer(player)
|
||||||
|
end
|
||||||
|
msg = NetworkMessage()
|
||||||
|
msg:addByte(50)
|
||||||
|
msg:addByte(SHOP_EXTENDED_OPCODE)
|
||||||
|
msg:addString("E" .. s[#s])
|
||||||
|
msg:sendToPlayer(player)
|
||||||
|
end
|
||||||
|
|
||||||
|
function sendMessage(player, title, msg, forceStatus)
|
||||||
|
sendJSON(player, "message", {title=title, msg=msg}, forceStatus)
|
||||||
|
end
|
||||||
|
|
||||||
|
function onExtendedOpcode(player, opcode, buffer)
|
||||||
|
if opcode ~= SHOP_EXTENDED_OPCODE then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
local status, json_data = pcall(function() return json.decode(buffer) end)
|
||||||
|
if not status then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local action = json_data['action']
|
||||||
|
local data = json_data['data']
|
||||||
|
if not action or not data then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
if SHOP_CATEGORIES == nil then
|
||||||
|
init()
|
||||||
|
end
|
||||||
|
|
||||||
|
if action == 'init' then
|
||||||
|
sendJSON(player, "categories", SHOP_CATEGORIES)
|
||||||
|
elseif action == 'buy' then
|
||||||
|
processBuy(player, data)
|
||||||
|
elseif action == "history" then
|
||||||
|
sendHistory(player)
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function processBuy(player, data)
|
||||||
|
local categoryId = tonumber(data["category"])
|
||||||
|
local offerId = tonumber(data["offer"])
|
||||||
|
local offer = SHOP_CATEGORIES[categoryId]['offers'][offerId]
|
||||||
|
local callback = SHOP_CALLBACKS[categoryId][offerId]
|
||||||
|
if not offer or not callback or data["title"] ~= offer["title"] or data["cost"] ~= offer["cost"] then
|
||||||
|
sendJSON(player, "categories", SHOP_CATEGORIES) -- refresh categories, maybe invalid
|
||||||
|
return sendMessage(player, "Error!", "Invalid offer")
|
||||||
|
end
|
||||||
|
local points = getPoints(player)
|
||||||
|
if not offer['cost'] or offer['cost'] > points or points < 1 then
|
||||||
|
return sendMessage(player, "Error!", "You don't have enough points to buy " .. offer['title'] .."!", true)
|
||||||
|
end
|
||||||
|
local status = callback(player, offer)
|
||||||
|
if status == true then
|
||||||
|
db.query("UPDATE `znote_accounts` set `points` = `points` - " .. offer['cost'] .. " WHERE `id` = " .. player:getAccountId())
|
||||||
|
db.asyncQuery("INSERT INTO `shop_history` (`account`, `player`, `date`, `title`, `cost`, `details`) VALUES ('" .. player:getAccountId() .. "', '" .. player:getGuid() .. "', NOW(), " .. db.escapeString(offer['title']) .. ", " .. db.escapeString(offer['cost']) .. ", " .. db.escapeString(json.encode(offer)) .. ")")
|
||||||
|
return sendMessage(player, "Success!", "You bought " .. offer['title'] .."!", true)
|
||||||
|
end
|
||||||
|
if status == nil or status == false then
|
||||||
|
status = "Unknown error while buying " .. offer['title']
|
||||||
|
end
|
||||||
|
sendMessage(player, "Error!", status)
|
||||||
|
end
|
||||||
|
|
||||||
|
function sendHistory(player)
|
||||||
|
if player:getStorageValue(1150002) and player:getStorageValue(1150002) + 10 > os.time() then
|
||||||
|
return -- min 10s delay
|
||||||
|
end
|
||||||
|
player:setStorageValue(1150002, os.time())
|
||||||
|
|
||||||
|
local history = {}
|
||||||
|
local resultId = db.storeQuery("SELECT * FROM `shop_history` WHERE `account` = " .. player:getAccountId() .. " order by `id` DESC")
|
||||||
|
|
||||||
|
if resultId ~= false then
|
||||||
|
repeat
|
||||||
|
local details = result.getDataString(resultId, "details")
|
||||||
|
local status, json_data = pcall(function() return json.decode(details) end)
|
||||||
|
if not status then
|
||||||
|
json_data = {
|
||||||
|
type = "image",
|
||||||
|
title = result.getDataString(resultId, "title"),
|
||||||
|
cost = result.getDataInt(resultId, "cost")
|
||||||
|
}
|
||||||
|
end
|
||||||
|
table.insert(history, json_data)
|
||||||
|
history[#history]["description"] = "Bought on " .. result.getDataString(resultId, "date") .. " for " .. result.getDataInt(resultId, "cost") .. " points."
|
||||||
|
until not result.next(resultId)
|
||||||
|
result.free(resultId)
|
||||||
|
end
|
||||||
|
|
||||||
|
sendJSON(player, "history", history)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- BUY CALLBACKS
|
||||||
|
-- May be useful: print(json.encode(offer))
|
||||||
|
|
||||||
|
function defaultItemBuyAction(player, offer)
|
||||||
|
-- todo: check if has capacity
|
||||||
|
if player:addItem(offer["itemId"], offer["count"], false) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
return "Can't add item! Do you have enough space?"
|
||||||
|
end
|
||||||
|
|
||||||
|
function defaultOutfitBuyAction(player, offer)
|
||||||
|
return "default outfit buy action is not implemented"
|
||||||
|
end
|
||||||
|
|
||||||
|
function defaultImageBuyAction(player, offer)
|
||||||
|
return "default image buy action is not implemented"
|
||||||
|
end
|
||||||
|
|
||||||
|
function customImageBuyAction(player, offer)
|
||||||
|
return "custom image buy action is not implemented. Offer: " .. offer['title']
|
||||||
|
end
|
@ -185,7 +185,8 @@ function Player:onLook(thing, position, distance)
|
|||||||
|
|
||||||
if thing:isCreature() then
|
if thing:isCreature() then
|
||||||
if thing:isPlayer() then
|
if thing:isPlayer() then
|
||||||
description = string.format("%s\nIP: %s.", description, Game.convertIpToString(thing:getIp()))
|
local client = thing:getClient()
|
||||||
|
description = string.format("%s\nIP: %s PING: %i FPS: %i.", description, Game.convertIpToString(thing:getIp()), client.ping, client.fps)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -208,7 +209,8 @@ function Player:onLookInBattleList(creature, distance)
|
|||||||
)
|
)
|
||||||
|
|
||||||
if creature:isPlayer() then
|
if creature:isPlayer() then
|
||||||
description = string.format("%s\nIP: %s", description, Game.convertIpToString(creature:getIp()))
|
local client = thing:getClient()
|
||||||
|
description = string.format("%s\nIP: %s PING: %i FPS: %i.", description, Game.convertIpToString(thing:getIp()), client.ping, client.fps)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
self:sendTextMessage(MESSAGE_INFO_DESCR, description)
|
self:sendTextMessage(MESSAGE_INFO_DESCR, description)
|
||||||
|
@ -11,4 +11,5 @@ dofile('data/lib/core/teleport.lua')
|
|||||||
dofile('data/lib/core/tile.lua')
|
dofile('data/lib/core/tile.lua')
|
||||||
dofile('data/lib/core/vocation.lua')
|
dofile('data/lib/core/vocation.lua')
|
||||||
dofile('data/lib/core/guildwars.lua')
|
dofile('data/lib/core/guildwars.lua')
|
||||||
dofile('data/lib/core/svargrondArenaQuest.lua')
|
dofile('data/lib/core/svargrondArenaQuest.lua')
|
||||||
|
dofile('data/lib/core/json.lua')
|
399
data/lib/core/json.lua
Normal file
399
data/lib/core/json.lua
Normal file
@ -0,0 +1,399 @@
|
|||||||
|
-- add to lib/core, later add dofile in lib/core/core.lua
|
||||||
|
|
||||||
|
--
|
||||||
|
-- json.lua
|
||||||
|
--
|
||||||
|
-- Copyright (c) 2018 rxi
|
||||||
|
--
|
||||||
|
-- Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
-- this software and associated documentation files (the "Software"), to deal in
|
||||||
|
-- the Software without restriction, including without limitation the rights to
|
||||||
|
-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
-- of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
-- so, subject to the following conditions:
|
||||||
|
--
|
||||||
|
-- The above copyright notice and this permission notice shall be included in all
|
||||||
|
-- copies or substantial portions of the Software.
|
||||||
|
--
|
||||||
|
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
-- SOFTWARE.
|
||||||
|
--
|
||||||
|
|
||||||
|
json = { _version = "0.1.1" }
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-- Encode
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local encode
|
||||||
|
|
||||||
|
local escape_char_map = {
|
||||||
|
[ "\\" ] = "\\\\",
|
||||||
|
[ "\"" ] = "\\\"",
|
||||||
|
[ "\b" ] = "\\b",
|
||||||
|
[ "\f" ] = "\\f",
|
||||||
|
[ "\n" ] = "\\n",
|
||||||
|
[ "\r" ] = "\\r",
|
||||||
|
[ "\t" ] = "\\t",
|
||||||
|
}
|
||||||
|
|
||||||
|
local escape_char_map_inv = { [ "\\/" ] = "/" }
|
||||||
|
for k, v in pairs(escape_char_map) do
|
||||||
|
escape_char_map_inv[v] = k
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function escape_char(c)
|
||||||
|
return escape_char_map[c] or string.format("\\u%04x", c:byte())
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function encode_nil(val)
|
||||||
|
return "null"
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function encode_table(val, stack)
|
||||||
|
local res = {}
|
||||||
|
stack = stack or {}
|
||||||
|
|
||||||
|
-- Circular reference?
|
||||||
|
if stack[val] then error("circular reference") end
|
||||||
|
|
||||||
|
stack[val] = true
|
||||||
|
|
||||||
|
if val[1] ~= nil or next(val) == nil then
|
||||||
|
-- Treat as array -- check keys are valid and it is not sparse
|
||||||
|
local n = 0
|
||||||
|
for k in pairs(val) do
|
||||||
|
if type(k) ~= "number" then
|
||||||
|
error("invalid table: mixed or invalid key types")
|
||||||
|
end
|
||||||
|
n = n + 1
|
||||||
|
end
|
||||||
|
if n ~= #val then
|
||||||
|
error("invalid table: sparse array")
|
||||||
|
end
|
||||||
|
-- Encode
|
||||||
|
for i, v in ipairs(val) do
|
||||||
|
table.insert(res, encode(v, stack))
|
||||||
|
end
|
||||||
|
stack[val] = nil
|
||||||
|
return "[" .. table.concat(res, ",") .. "]"
|
||||||
|
|
||||||
|
else
|
||||||
|
-- Treat as an object
|
||||||
|
for k, v in pairs(val) do
|
||||||
|
if type(k) ~= "string" then
|
||||||
|
error("invalid table: mixed or invalid key types")
|
||||||
|
end
|
||||||
|
table.insert(res, encode(k, stack) .. ":" .. encode(v, stack))
|
||||||
|
end
|
||||||
|
stack[val] = nil
|
||||||
|
return "{" .. table.concat(res, ",") .. "}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function encode_string(val)
|
||||||
|
return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"'
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function encode_number(val)
|
||||||
|
-- Check for NaN, -inf and inf
|
||||||
|
if val ~= val or val <= -math.huge or val >= math.huge then
|
||||||
|
error("unexpected number value '" .. tostring(val) .. "'")
|
||||||
|
end
|
||||||
|
return string.format("%.14g", val)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local type_func_map = {
|
||||||
|
[ "nil" ] = encode_nil,
|
||||||
|
[ "table" ] = encode_table,
|
||||||
|
[ "string" ] = encode_string,
|
||||||
|
[ "number" ] = encode_number,
|
||||||
|
[ "boolean" ] = tostring,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
encode = function(val, stack)
|
||||||
|
local t = type(val)
|
||||||
|
local f = type_func_map[t]
|
||||||
|
if f then
|
||||||
|
return f(val, stack)
|
||||||
|
end
|
||||||
|
error("unexpected type '" .. t .. "'")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function json.encode(val)
|
||||||
|
return ( encode(val) )
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-- Decode
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local parse
|
||||||
|
|
||||||
|
local function create_set(...)
|
||||||
|
local res = {}
|
||||||
|
for i = 1, select("#", ...) do
|
||||||
|
res[ select(i, ...) ] = true
|
||||||
|
end
|
||||||
|
return res
|
||||||
|
end
|
||||||
|
|
||||||
|
local space_chars = create_set(" ", "\t", "\r", "\n")
|
||||||
|
local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",")
|
||||||
|
local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u")
|
||||||
|
local literals = create_set("true", "false", "null")
|
||||||
|
|
||||||
|
local literal_map = {
|
||||||
|
[ "true" ] = true,
|
||||||
|
[ "false" ] = false,
|
||||||
|
[ "null" ] = nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
local function next_char(str, idx, set, negate)
|
||||||
|
for i = idx, #str do
|
||||||
|
if set[str:sub(i, i)] ~= negate then
|
||||||
|
return i
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return #str + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function decode_error(str, idx, msg)
|
||||||
|
local line_count = 1
|
||||||
|
local col_count = 1
|
||||||
|
for i = 1, idx - 1 do
|
||||||
|
col_count = col_count + 1
|
||||||
|
if str:sub(i, i) == "\n" then
|
||||||
|
line_count = line_count + 1
|
||||||
|
col_count = 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
error( string.format("%s at line %d col %d", msg, line_count, col_count) )
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function codepoint_to_utf8(n)
|
||||||
|
-- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa
|
||||||
|
local f = math.floor
|
||||||
|
if n <= 0x7f then
|
||||||
|
return string.char(n)
|
||||||
|
elseif n <= 0x7ff then
|
||||||
|
return string.char(f(n / 64) + 192, n % 64 + 128)
|
||||||
|
elseif n <= 0xffff then
|
||||||
|
return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128)
|
||||||
|
elseif n <= 0x10ffff then
|
||||||
|
return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,
|
||||||
|
f(n % 4096 / 64) + 128, n % 64 + 128)
|
||||||
|
end
|
||||||
|
error( string.format("invalid unicode codepoint '%x'", n) )
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function parse_unicode_escape(s)
|
||||||
|
local n1 = tonumber( s:sub(3, 6), 16 )
|
||||||
|
local n2 = tonumber( s:sub(9, 12), 16 )
|
||||||
|
-- Surrogate pair?
|
||||||
|
if n2 then
|
||||||
|
return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)
|
||||||
|
else
|
||||||
|
return codepoint_to_utf8(n1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function parse_string(str, i)
|
||||||
|
local has_unicode_escape = false
|
||||||
|
local has_surrogate_escape = false
|
||||||
|
local has_escape = false
|
||||||
|
local last
|
||||||
|
for j = i + 1, #str do
|
||||||
|
local x = str:byte(j)
|
||||||
|
|
||||||
|
if x < 32 then
|
||||||
|
decode_error(str, j, "control character in string")
|
||||||
|
end
|
||||||
|
|
||||||
|
if last == 92 then -- "\\" (escape char)
|
||||||
|
if x == 117 then -- "u" (unicode escape sequence)
|
||||||
|
local hex = str:sub(j + 1, j + 5)
|
||||||
|
if not hex:find("%x%x%x%x") then
|
||||||
|
decode_error(str, j, "invalid unicode escape in string")
|
||||||
|
end
|
||||||
|
if hex:find("^[dD][89aAbB]") then
|
||||||
|
has_surrogate_escape = true
|
||||||
|
else
|
||||||
|
has_unicode_escape = true
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local c = string.char(x)
|
||||||
|
if not escape_chars[c] then
|
||||||
|
decode_error(str, j, "invalid escape char '" .. c .. "' in string")
|
||||||
|
end
|
||||||
|
has_escape = true
|
||||||
|
end
|
||||||
|
last = nil
|
||||||
|
|
||||||
|
elseif x == 34 then -- '"' (end of string)
|
||||||
|
local s = str:sub(i + 1, j - 1)
|
||||||
|
if has_surrogate_escape then
|
||||||
|
s = s:gsub("\\u[dD][89aAbB]..\\u....", parse_unicode_escape)
|
||||||
|
end
|
||||||
|
if has_unicode_escape then
|
||||||
|
s = s:gsub("\\u....", parse_unicode_escape)
|
||||||
|
end
|
||||||
|
if has_escape then
|
||||||
|
s = s:gsub("\\.", escape_char_map_inv)
|
||||||
|
end
|
||||||
|
return s, j + 1
|
||||||
|
|
||||||
|
else
|
||||||
|
last = x
|
||||||
|
end
|
||||||
|
end
|
||||||
|
decode_error(str, i, "expected closing quote for string")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function parse_number(str, i)
|
||||||
|
local x = next_char(str, i, delim_chars)
|
||||||
|
local s = str:sub(i, x - 1)
|
||||||
|
local n = tonumber(s)
|
||||||
|
if not n then
|
||||||
|
decode_error(str, i, "invalid number '" .. s .. "'")
|
||||||
|
end
|
||||||
|
return n, x
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function parse_literal(str, i)
|
||||||
|
local x = next_char(str, i, delim_chars)
|
||||||
|
local word = str:sub(i, x - 1)
|
||||||
|
if not literals[word] then
|
||||||
|
decode_error(str, i, "invalid literal '" .. word .. "'")
|
||||||
|
end
|
||||||
|
return literal_map[word], x
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function parse_array(str, i)
|
||||||
|
local res = {}
|
||||||
|
local n = 1
|
||||||
|
i = i + 1
|
||||||
|
while 1 do
|
||||||
|
local x
|
||||||
|
i = next_char(str, i, space_chars, true)
|
||||||
|
-- Empty / end of array?
|
||||||
|
if str:sub(i, i) == "]" then
|
||||||
|
i = i + 1
|
||||||
|
break
|
||||||
|
end
|
||||||
|
-- Read token
|
||||||
|
x, i = parse(str, i)
|
||||||
|
res[n] = x
|
||||||
|
n = n + 1
|
||||||
|
-- Next token
|
||||||
|
i = next_char(str, i, space_chars, true)
|
||||||
|
local chr = str:sub(i, i)
|
||||||
|
i = i + 1
|
||||||
|
if chr == "]" then break end
|
||||||
|
if chr ~= "," then decode_error(str, i, "expected ']' or ','") end
|
||||||
|
end
|
||||||
|
return res, i
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function parse_object(str, i)
|
||||||
|
local res = {}
|
||||||
|
i = i + 1
|
||||||
|
while 1 do
|
||||||
|
local key, val
|
||||||
|
i = next_char(str, i, space_chars, true)
|
||||||
|
-- Empty / end of object?
|
||||||
|
if str:sub(i, i) == "}" then
|
||||||
|
i = i + 1
|
||||||
|
break
|
||||||
|
end
|
||||||
|
-- Read key
|
||||||
|
if str:sub(i, i) ~= '"' then
|
||||||
|
decode_error(str, i, "expected string for key")
|
||||||
|
end
|
||||||
|
key, i = parse(str, i)
|
||||||
|
-- Read ':' delimiter
|
||||||
|
i = next_char(str, i, space_chars, true)
|
||||||
|
if str:sub(i, i) ~= ":" then
|
||||||
|
decode_error(str, i, "expected ':' after key")
|
||||||
|
end
|
||||||
|
i = next_char(str, i + 1, space_chars, true)
|
||||||
|
-- Read value
|
||||||
|
val, i = parse(str, i)
|
||||||
|
-- Set
|
||||||
|
res[key] = val
|
||||||
|
-- Next token
|
||||||
|
i = next_char(str, i, space_chars, true)
|
||||||
|
local chr = str:sub(i, i)
|
||||||
|
i = i + 1
|
||||||
|
if chr == "}" then break end
|
||||||
|
if chr ~= "," then decode_error(str, i, "expected '}' or ','") end
|
||||||
|
end
|
||||||
|
return res, i
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local char_func_map = {
|
||||||
|
[ '"' ] = parse_string,
|
||||||
|
[ "0" ] = parse_number,
|
||||||
|
[ "1" ] = parse_number,
|
||||||
|
[ "2" ] = parse_number,
|
||||||
|
[ "3" ] = parse_number,
|
||||||
|
[ "4" ] = parse_number,
|
||||||
|
[ "5" ] = parse_number,
|
||||||
|
[ "6" ] = parse_number,
|
||||||
|
[ "7" ] = parse_number,
|
||||||
|
[ "8" ] = parse_number,
|
||||||
|
[ "9" ] = parse_number,
|
||||||
|
[ "-" ] = parse_number,
|
||||||
|
[ "t" ] = parse_literal,
|
||||||
|
[ "f" ] = parse_literal,
|
||||||
|
[ "n" ] = parse_literal,
|
||||||
|
[ "[" ] = parse_array,
|
||||||
|
[ "{" ] = parse_object,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
parse = function(str, idx)
|
||||||
|
local chr = str:sub(idx, idx)
|
||||||
|
local f = char_func_map[chr]
|
||||||
|
if f then
|
||||||
|
return f(str, idx)
|
||||||
|
end
|
||||||
|
decode_error(str, idx, "unexpected character '" .. chr .. "'")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function json.decode(str)
|
||||||
|
if type(str) ~= "string" then
|
||||||
|
error("expected argument of type string, got " .. type(str))
|
||||||
|
end
|
||||||
|
local res, idx = parse(str, next_char(str, 1, space_chars, true))
|
||||||
|
idx = next_char(str, idx, space_chars, true)
|
||||||
|
if idx <= #str then
|
||||||
|
decode_error(str, idx, "trailing garbage")
|
||||||
|
end
|
||||||
|
return res
|
||||||
|
end
|
@ -25,7 +25,17 @@ local reloadTypes = {
|
|||||||
|
|
||||||
["monster"] = { targetType = RELOAD_TYPE_MONSTERS, name = "monsters" },
|
["monster"] = { targetType = RELOAD_TYPE_MONSTERS, name = "monsters" },
|
||||||
["monsters"] = { targetType = RELOAD_TYPE_MONSTERS, name = "monsters" },
|
["monsters"] = { targetType = RELOAD_TYPE_MONSTERS, name = "monsters" },
|
||||||
|
["mount"] = RELOAD_TYPE_MOUNTS,
|
||||||
|
|
||||||
|
["aura"] = RELOAD_TYPE_AURAS,
|
||||||
|
["auras"] = RELOAD_TYPE_AURAS,
|
||||||
|
|
||||||
|
["wing"] = RELOAD_TYPE_WINGS,
|
||||||
|
["wings"] = RELOAD_TYPE_WINGS,
|
||||||
|
|
||||||
|
["shader"] = RELOAD_TYPE_SHADERS,
|
||||||
|
["shaders"] = RELOAD_TYPE_SHADERS,
|
||||||
|
|
||||||
["move"] = { targetType = RELOAD_TYPE_MOVEMENTS, name = "movements" },
|
["move"] = { targetType = RELOAD_TYPE_MOVEMENTS, name = "movements" },
|
||||||
["movement"] = { targetType = RELOAD_TYPE_MOVEMENTS, name = "movements" },
|
["movement"] = { targetType = RELOAD_TYPE_MOVEMENTS, name = "movements" },
|
||||||
["movements"] = { targetType = RELOAD_TYPE_MOVEMENTS, name = "movements" },
|
["movements"] = { targetType = RELOAD_TYPE_MOVEMENTS, name = "movements" },
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
set(tfs_SRC
|
set(tfs_SRC
|
||||||
${CMAKE_CURRENT_LIST_DIR}/otpch.cpp
|
${CMAKE_CURRENT_LIST_DIR}/otpch.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/actions.cpp
|
${CMAKE_CURRENT_LIST_DIR}/actions.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/auras.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/ban.cpp
|
${CMAKE_CURRENT_LIST_DIR}/ban.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/baseevents.cpp
|
${CMAKE_CURRENT_LIST_DIR}/baseevents.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/bed.cpp
|
${CMAKE_CURRENT_LIST_DIR}/bed.cpp
|
||||||
@ -38,6 +39,7 @@ set(tfs_SRC
|
|||||||
${CMAKE_CURRENT_LIST_DIR}/map.cpp
|
${CMAKE_CURRENT_LIST_DIR}/map.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/monster.cpp
|
${CMAKE_CURRENT_LIST_DIR}/monster.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/monsters.cpp
|
${CMAKE_CURRENT_LIST_DIR}/monsters.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/mounts.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/movement.cpp
|
${CMAKE_CURRENT_LIST_DIR}/movement.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/networkmessage.cpp
|
${CMAKE_CURRENT_LIST_DIR}/networkmessage.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/npc.cpp
|
${CMAKE_CURRENT_LIST_DIR}/npc.cpp
|
||||||
@ -56,6 +58,7 @@ set(tfs_SRC
|
|||||||
${CMAKE_CURRENT_LIST_DIR}/scheduler.cpp
|
${CMAKE_CURRENT_LIST_DIR}/scheduler.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/scriptmanager.cpp
|
${CMAKE_CURRENT_LIST_DIR}/scriptmanager.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/server.cpp
|
${CMAKE_CURRENT_LIST_DIR}/server.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/shaders.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/spawn.cpp
|
${CMAKE_CURRENT_LIST_DIR}/spawn.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/spells.cpp
|
${CMAKE_CURRENT_LIST_DIR}/spells.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/script.cpp
|
${CMAKE_CURRENT_LIST_DIR}/script.cpp
|
||||||
@ -68,6 +71,7 @@ set(tfs_SRC
|
|||||||
${CMAKE_CURRENT_LIST_DIR}/vocation.cpp
|
${CMAKE_CURRENT_LIST_DIR}/vocation.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/waitlist.cpp
|
${CMAKE_CURRENT_LIST_DIR}/waitlist.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/wildcardtree.cpp
|
${CMAKE_CURRENT_LIST_DIR}/wildcardtree.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/wings.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/xtea.cpp
|
${CMAKE_CURRENT_LIST_DIR}/xtea.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/quests.cpp
|
${CMAKE_CURRENT_LIST_DIR}/quests.cpp
|
||||||
PARENT_SCOPE)
|
PARENT_SCOPE)
|
||||||
|
@ -324,7 +324,6 @@ ReturnValue Actions::internalUseItem(Player* player, const Position& pos, uint8_
|
|||||||
bool Actions::useItem(Player* player, const Position& pos, uint8_t index, Item* item, bool isHotkey)
|
bool Actions::useItem(Player* player, const Position& pos, uint8_t index, Item* item, bool isHotkey)
|
||||||
{
|
{
|
||||||
player->setNextAction(OTSYS_TIME() + g_config.getNumber(ConfigManager::ACTIONS_DELAY_INTERVAL));
|
player->setNextAction(OTSYS_TIME() + g_config.getNumber(ConfigManager::ACTIONS_DELAY_INTERVAL));
|
||||||
player->stopWalk();
|
|
||||||
|
|
||||||
if (item->getID() == ITEM_MARKET)
|
if (item->getID() == ITEM_MARKET)
|
||||||
{
|
{
|
||||||
@ -360,7 +359,6 @@ bool Actions::useItemEx(Player* player, const Position& fromPos, const Position&
|
|||||||
uint8_t toStackPos, Item* item, bool isHotkey, Creature* creature/* = nullptr*/)
|
uint8_t toStackPos, Item* item, bool isHotkey, Creature* creature/* = nullptr*/)
|
||||||
{
|
{
|
||||||
player->setNextAction(OTSYS_TIME() + g_config.getNumber(ConfigManager::EX_ACTIONS_DELAY_INTERVAL));
|
player->setNextAction(OTSYS_TIME() + g_config.getNumber(ConfigManager::EX_ACTIONS_DELAY_INTERVAL));
|
||||||
player->stopWalk();
|
|
||||||
|
|
||||||
Action* action = getAction(item);
|
Action* action = getAction(item);
|
||||||
if (!action) {
|
if (!action) {
|
||||||
|
63
src/auras.cpp
Normal file
63
src/auras.cpp
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
#include "otpch.h"
|
||||||
|
|
||||||
|
#include "auras.h"
|
||||||
|
|
||||||
|
#include "pugicast.h"
|
||||||
|
#include "tools.h"
|
||||||
|
|
||||||
|
bool Auras::reload()
|
||||||
|
{
|
||||||
|
auras.clear();
|
||||||
|
return loadFromXml();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Auras::loadFromXml()
|
||||||
|
{
|
||||||
|
pugi::xml_document doc;
|
||||||
|
pugi::xml_parse_result result = doc.load_file("data/XML/auras.xml");
|
||||||
|
if (!result) {
|
||||||
|
printXMLError("Error - Auras::loadFromXml", "data/XML/auras.xml", result);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto auraNode : doc.child("auras").children()) {
|
||||||
|
auras.emplace_back(
|
||||||
|
static_cast<uint8_t>(pugi::cast<uint16_t>(auraNode.attribute("id").value())),
|
||||||
|
pugi::cast<uint16_t>(auraNode.attribute("clientid").value()),
|
||||||
|
auraNode.attribute("name").as_string(),
|
||||||
|
pugi::cast<int32_t>(auraNode.attribute("speed").value()),
|
||||||
|
auraNode.attribute("premium").as_bool()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
auras.shrink_to_fit();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Aura* Auras::getAuraByID(uint8_t id)
|
||||||
|
{
|
||||||
|
auto it = std::find_if(auras.begin(), auras.end(), [id](const Aura& aura) {
|
||||||
|
return aura.id == id;
|
||||||
|
});
|
||||||
|
|
||||||
|
return it != auras.end() ? &*it : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Aura* Auras::getAuraByName(const std::string& name) {
|
||||||
|
auto auraName = name.c_str();
|
||||||
|
for (auto& it : auras) {
|
||||||
|
if (strcasecmp(auraName, it.name.c_str()) == 0) {
|
||||||
|
return ⁢
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Aura* Auras::getAuraByClientID(uint16_t clientId)
|
||||||
|
{
|
||||||
|
auto it = std::find_if(auras.begin(), auras.end(), [clientId](const Aura& aura) {
|
||||||
|
return aura.clientId == clientId;
|
||||||
|
});
|
||||||
|
|
||||||
|
return it != auras.end() ? &*it : nullptr;
|
||||||
|
}
|
33
src/auras.h
Normal file
33
src/auras.h
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#ifndef FS_AURAS_H
|
||||||
|
#define FS_AURAS_H
|
||||||
|
|
||||||
|
struct Aura
|
||||||
|
{
|
||||||
|
Aura(uint8_t id, uint16_t clientId, std::string name, int32_t speed, bool premium) :
|
||||||
|
name(std::move(name)), speed(speed), clientId(clientId), id(id), premium(premium) {}
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
int32_t speed;
|
||||||
|
uint16_t clientId;
|
||||||
|
uint8_t id;
|
||||||
|
bool premium;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Auras
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bool reload();
|
||||||
|
bool loadFromXml();
|
||||||
|
Aura* getAuraByID(uint8_t id);
|
||||||
|
Aura* getAuraByName(const std::string& name);
|
||||||
|
Aura* getAuraByClientID(uint16_t clientId);
|
||||||
|
|
||||||
|
const std::vector<Aura>& getAuras() const {
|
||||||
|
return auras;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<Aura> auras;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
@ -87,6 +87,7 @@ bool ConfigManager::load()
|
|||||||
boolean[ROPE_SPOT_BLOCK] = getGlobalBoolean(L, "ropeSpotBlock", false);
|
boolean[ROPE_SPOT_BLOCK] = getGlobalBoolean(L, "ropeSpotBlock", false);
|
||||||
boolean[DROP_ITEMS] = getGlobalBoolean(L, "dropItems", false);
|
boolean[DROP_ITEMS] = getGlobalBoolean(L, "dropItems", false);
|
||||||
boolean[DISTANCE_WEAPONS_DROP_ON_GROUND] = getGlobalBoolean(L, "distanceWeaponsDropOnGround", true);
|
boolean[DISTANCE_WEAPONS_DROP_ON_GROUND] = getGlobalBoolean(L, "distanceWeaponsDropOnGround", true);
|
||||||
|
boolean[PACKET_COMPRESSION] = getGlobalBoolean(L, "packetCompression", true);
|
||||||
|
|
||||||
string[DEFAULT_PRIORITY] = getGlobalString(L, "defaultPriority", "high");
|
string[DEFAULT_PRIORITY] = getGlobalString(L, "defaultPriority", "high");
|
||||||
string[SERVER_NAME] = getGlobalString(L, "serverName", "");
|
string[SERVER_NAME] = getGlobalString(L, "serverName", "");
|
||||||
|
@ -53,6 +53,7 @@ class ConfigManager
|
|||||||
DISTANCE_WEAPONS_DROP_ON_GROUND,
|
DISTANCE_WEAPONS_DROP_ON_GROUND,
|
||||||
CORPSE_OWNER_ENABLED,
|
CORPSE_OWNER_ENABLED,
|
||||||
ROPE_SPOT_BLOCK,
|
ROPE_SPOT_BLOCK,
|
||||||
|
PACKET_COMPRESSION,
|
||||||
LAST_BOOLEAN_CONFIG /* this must be the last one */
|
LAST_BOOLEAN_CONFIG /* this must be the last one */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
140
src/const.h
140
src/const.h
@ -20,7 +20,7 @@
|
|||||||
#ifndef FS_CONST_H_0A49B5996F074465BF44B90F4F780E8B
|
#ifndef FS_CONST_H_0A49B5996F074465BF44B90F4F780E8B
|
||||||
#define FS_CONST_H_0A49B5996F074465BF44B90F4F780E8B
|
#define FS_CONST_H_0A49B5996F074465BF44B90F4F780E8B
|
||||||
|
|
||||||
static constexpr int32_t NETWORKMESSAGE_MAXSIZE = 24590;
|
static constexpr int32_t NETWORKMESSAGE_MAXSIZE = 65500;
|
||||||
static constexpr int32_t MIN_MARKET_FEE = 20;
|
static constexpr int32_t MIN_MARKET_FEE = 20;
|
||||||
static constexpr int32_t MAX_MARKET_FEE = 100000;
|
static constexpr int32_t MAX_MARKET_FEE = 100000;
|
||||||
|
|
||||||
@ -378,6 +378,7 @@ enum PlayerFlags : uint64_t {
|
|||||||
enum ReloadTypes_t : uint8_t {
|
enum ReloadTypes_t : uint8_t {
|
||||||
RELOAD_TYPE_ALL,
|
RELOAD_TYPE_ALL,
|
||||||
RELOAD_TYPE_ACTIONS,
|
RELOAD_TYPE_ACTIONS,
|
||||||
|
RELOAD_TYPE_AURAS,
|
||||||
RELOAD_TYPE_CHAT,
|
RELOAD_TYPE_CHAT,
|
||||||
RELOAD_TYPE_COMMANDS,
|
RELOAD_TYPE_COMMANDS,
|
||||||
RELOAD_TYPE_CONFIG,
|
RELOAD_TYPE_CONFIG,
|
||||||
@ -393,8 +394,129 @@ enum ReloadTypes_t : uint8_t {
|
|||||||
RELOAD_TYPE_QUESTS,
|
RELOAD_TYPE_QUESTS,
|
||||||
RELOAD_TYPE_RAIDS,
|
RELOAD_TYPE_RAIDS,
|
||||||
RELOAD_TYPE_SPELLS,
|
RELOAD_TYPE_SPELLS,
|
||||||
|
RELOAD_TYPE_SHADERS,
|
||||||
RELOAD_TYPE_TALKACTIONS,
|
RELOAD_TYPE_TALKACTIONS,
|
||||||
RELOAD_TYPE_WEAPONS,
|
RELOAD_TYPE_WEAPONS,
|
||||||
|
RELOAD_TYPE_WINGS,
|
||||||
|
};
|
||||||
|
|
||||||
|
// OTCv8 features (from src/client/const.h)
|
||||||
|
enum GameFeature {
|
||||||
|
GameProtocolChecksum = 1,
|
||||||
|
GameAccountNames = 2,
|
||||||
|
GameChallengeOnLogin = 3,
|
||||||
|
GamePenalityOnDeath = 4,
|
||||||
|
GameNameOnNpcTrade = 5,
|
||||||
|
GameDoubleFreeCapacity = 6,
|
||||||
|
GameDoubleExperience = 7,
|
||||||
|
GameTotalCapacity = 8,
|
||||||
|
GameSkillsBase = 9,
|
||||||
|
GamePlayerRegenerationTime = 10,
|
||||||
|
GameChannelPlayerList = 11,
|
||||||
|
GamePlayerMounts = 12,
|
||||||
|
GameEnvironmentEffect = 13,
|
||||||
|
GameCreatureEmblems = 14,
|
||||||
|
GameItemAnimationPhase = 15,
|
||||||
|
GameMagicEffectU16 = 16,
|
||||||
|
GamePlayerMarket = 17,
|
||||||
|
GameSpritesU32 = 18,
|
||||||
|
GameTileAddThingWithStackpos = 19,
|
||||||
|
GameOfflineTrainingTime = 20,
|
||||||
|
GamePurseSlot = 21,
|
||||||
|
GameFormatCreatureName = 22,
|
||||||
|
GameSpellList = 23,
|
||||||
|
GameClientPing = 24,
|
||||||
|
GameExtendedClientPing = 25,
|
||||||
|
GameDoubleHealth = 28,
|
||||||
|
GameDoubleSkills = 29,
|
||||||
|
GameChangeMapAwareRange = 30,
|
||||||
|
GameMapMovePosition = 31,
|
||||||
|
GameAttackSeq = 32,
|
||||||
|
GameBlueNpcNameColor = 33,
|
||||||
|
GameDiagonalAnimatedText = 34,
|
||||||
|
GameLoginPending = 35,
|
||||||
|
GameNewSpeedLaw = 36,
|
||||||
|
GameForceFirstAutoWalkStep = 37,
|
||||||
|
GameMinimapRemove = 38,
|
||||||
|
GameDoubleShopSellAmount = 39,
|
||||||
|
GameContainerPagination = 40,
|
||||||
|
GameThingMarks = 41,
|
||||||
|
GameLooktypeU16 = 42,
|
||||||
|
GamePlayerStamina = 43,
|
||||||
|
GamePlayerAddons = 44,
|
||||||
|
GameMessageStatements = 45,
|
||||||
|
GameMessageLevel = 46,
|
||||||
|
GameNewFluids = 47,
|
||||||
|
GamePlayerStateU16 = 48,
|
||||||
|
GameNewOutfitProtocol = 49,
|
||||||
|
GamePVPMode = 50,
|
||||||
|
GameWritableDate = 51,
|
||||||
|
GameAdditionalVipInfo = 52,
|
||||||
|
GameBaseSkillU16 = 53,
|
||||||
|
GameCreatureIcons = 54,
|
||||||
|
GameHideNpcNames = 55,
|
||||||
|
GameSpritesAlphaChannel = 56,
|
||||||
|
GamePremiumExpiration = 57,
|
||||||
|
GameBrowseField = 58,
|
||||||
|
GameEnhancedAnimations = 59,
|
||||||
|
GameOGLInformation = 60,
|
||||||
|
GameMessageSizeCheck = 61,
|
||||||
|
GamePreviewState = 62,
|
||||||
|
GameLoginPacketEncryption = 63,
|
||||||
|
GameClientVersion = 64,
|
||||||
|
GameContentRevision = 65,
|
||||||
|
GameExperienceBonus = 66,
|
||||||
|
GameAuthenticator = 67,
|
||||||
|
GameUnjustifiedPoints = 68,
|
||||||
|
GameSessionKey = 69,
|
||||||
|
GameDeathType = 70,
|
||||||
|
GameIdleAnimations = 71,
|
||||||
|
GameKeepUnawareTiles = 72,
|
||||||
|
GameIngameStore = 73,
|
||||||
|
GameIngameStoreHighlights = 74,
|
||||||
|
GameIngameStoreServiceType = 75,
|
||||||
|
GameAdditionalSkills = 76,
|
||||||
|
GameDistanceEffectU16 = 77,
|
||||||
|
GamePrey = 78,
|
||||||
|
GameDoubleMagicLevel = 79,
|
||||||
|
|
||||||
|
GameExtendedOpcode = 80,
|
||||||
|
GameMinimapLimitedToSingleFloor = 81,
|
||||||
|
GameSendWorldName = 82,
|
||||||
|
|
||||||
|
GameDoubleLevel = 83,
|
||||||
|
GameDoubleSoul = 84,
|
||||||
|
GameDoublePlayerGoodsMoney = 85,
|
||||||
|
GameCreatureWalkthrough = 86,
|
||||||
|
GameDoubleTradeMoney = 87,
|
||||||
|
GameSequencedPackets = 88,
|
||||||
|
GameTibia12Protocol = 89,
|
||||||
|
|
||||||
|
// 90-99 otclientv8 features
|
||||||
|
GameNewWalking = 90,
|
||||||
|
GameSlowerManualWalking = 91,
|
||||||
|
|
||||||
|
GameItemTooltip = 93,
|
||||||
|
|
||||||
|
GameBot = 95,
|
||||||
|
GameBiggerMapCache = 96,
|
||||||
|
GameForceLight = 97,
|
||||||
|
GameNoDebug = 98,
|
||||||
|
GameBotProtection = 99,
|
||||||
|
|
||||||
|
// Custom features for customer
|
||||||
|
GameFasterAnimations = 101,
|
||||||
|
GameCenteredOutfits = 102,
|
||||||
|
GameSendIdentifiers = 103,
|
||||||
|
GameWingsAndAura = 104,
|
||||||
|
GamePlayerStateU32 = 105,
|
||||||
|
GameOutfitShaders = 106,
|
||||||
|
|
||||||
|
// advanced features
|
||||||
|
GamePacketSizeU32 = 110,
|
||||||
|
GamePacketCompression = 111,
|
||||||
|
|
||||||
|
LastGameFeature = 120
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ClientVersion_t : uint16_t {
|
enum ClientVersion_t : uint16_t {
|
||||||
@ -417,6 +539,22 @@ static constexpr int32_t PSTRG_RESERVED_RANGE_SIZE = 10000000;
|
|||||||
//[1000 - 1500];
|
//[1000 - 1500];
|
||||||
static constexpr int32_t PSTRG_OUTFITS_RANGE_START = (PSTRG_RESERVED_RANGE_START + 1000);
|
static constexpr int32_t PSTRG_OUTFITS_RANGE_START = (PSTRG_RESERVED_RANGE_START + 1000);
|
||||||
static constexpr int32_t PSTRG_OUTFITS_RANGE_SIZE = 500;
|
static constexpr int32_t PSTRG_OUTFITS_RANGE_SIZE = 500;
|
||||||
|
//[2001 - 2011];
|
||||||
|
static constexpr int32_t PSTRG_MOUNTS_RANGE_START = (PSTRG_RESERVED_RANGE_START + 2001);
|
||||||
|
static constexpr int32_t PSTRG_MOUNTS_RANGE_SIZE = 10;
|
||||||
|
static constexpr int32_t PSTRG_MOUNTS_CURRENTMOUNT = (PSTRG_MOUNTS_RANGE_START + 10);
|
||||||
|
//[2012 - 2022];
|
||||||
|
static constexpr int32_t PSTRG_WINGS_RANGE_START = (PSTRG_RESERVED_RANGE_START + 2012);
|
||||||
|
static constexpr int32_t PSTRG_WINGS_RANGE_SIZE = 10;
|
||||||
|
static constexpr int32_t PSTRG_WINGS_CURRENTWINGS = (PSTRG_WINGS_RANGE_START + 10);
|
||||||
|
//[2023 - 2033];
|
||||||
|
static constexpr int32_t PSTRG_AURAS_RANGE_START = (PSTRG_RESERVED_RANGE_START + 2023);
|
||||||
|
static constexpr int32_t PSTRG_AURAS_RANGE_SIZE = 10;
|
||||||
|
static constexpr int32_t PSTRG_AURAS_CURRENTAURA = (PSTRG_AURAS_RANGE_START + 10);
|
||||||
|
//[2034 - 2044];
|
||||||
|
static constexpr int32_t PSTRG_SHADERS_RANGE_START = (PSTRG_RESERVED_RANGE_START + 2034);
|
||||||
|
static constexpr int32_t PSTRG_SHADERS_RANGE_SIZE = 10;
|
||||||
|
static constexpr int32_t PSTRG_SHADERS_CURRENTSHADER = (PSTRG_SHADERS_RANGE_START + 10);
|
||||||
|
|
||||||
#define IS_IN_KEYRANGE(key, range) (key >= PSTRG_##range##_START && ((key - PSTRG_##range##_START) <= PSTRG_##range##_SIZE))
|
#define IS_IN_KEYRANGE(key, range) (key >= PSTRG_##range##_START && ((key - PSTRG_##range##_START) <= PSTRG_##range##_SIZE))
|
||||||
|
|
||||||
|
15
src/enums.h
15
src/enums.h
@ -56,6 +56,7 @@ enum itemAttrTypes : uint32_t {
|
|||||||
ITEM_ATTRIBUTE_DOORQUESTVALUE = 1 << 24,
|
ITEM_ATTRIBUTE_DOORQUESTVALUE = 1 << 24,
|
||||||
ITEM_ATTRIBUTE_DOORLEVEL = 1 << 25,
|
ITEM_ATTRIBUTE_DOORLEVEL = 1 << 25,
|
||||||
ITEM_ATTRIBUTE_CHESTQUESTNUMBER = 1 << 26,
|
ITEM_ATTRIBUTE_CHESTQUESTNUMBER = 1 << 26,
|
||||||
|
ITEM_ATTRIBUTE_AUTOOPEN = 1 << 27,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum VipStatus_t : uint8_t {
|
enum VipStatus_t : uint8_t {
|
||||||
@ -93,6 +94,16 @@ enum OperatingSystem_t : uint8_t {
|
|||||||
CLIENTOS_OTCLIENT_LINUX = 10,
|
CLIENTOS_OTCLIENT_LINUX = 10,
|
||||||
CLIENTOS_OTCLIENT_WINDOWS = 11,
|
CLIENTOS_OTCLIENT_WINDOWS = 11,
|
||||||
CLIENTOS_OTCLIENT_MAC = 12,
|
CLIENTOS_OTCLIENT_MAC = 12,
|
||||||
|
|
||||||
|
// by default OTCv8 uses CLIENTOS_WINDOWS for backward compatibility
|
||||||
|
// for correct value enable g_game.enableFeature(GameExtendedOpcode)
|
||||||
|
// in modules/game_features/features.lua
|
||||||
|
CLIENTOS_OTCLIENTV8_LINUX = 20,
|
||||||
|
CLIENTOS_OTCLIENTV8_WINDOWS = 21,
|
||||||
|
CLIENTOS_OTCLIENTV8_MAC = 22,
|
||||||
|
CLIENTOS_OTCLIENTV8_ANDROID = 23,
|
||||||
|
CLIENTOS_OTCLIENTV8_IOS = 24,
|
||||||
|
CLIENTOS_OTCLIENTV8_WEB = 25
|
||||||
};
|
};
|
||||||
|
|
||||||
enum AccountType_t : uint8_t {
|
enum AccountType_t : uint8_t {
|
||||||
@ -379,6 +390,10 @@ enum ReturnValue {
|
|||||||
struct Outfit_t {
|
struct Outfit_t {
|
||||||
uint16_t lookType = 0;
|
uint16_t lookType = 0;
|
||||||
uint16_t lookTypeEx = 0;
|
uint16_t lookTypeEx = 0;
|
||||||
|
uint16_t lookMount = 0;
|
||||||
|
uint16_t lookWings = 0;
|
||||||
|
uint16_t lookAura = 0;
|
||||||
|
uint16_t lookShader = 0;
|
||||||
uint8_t lookHead = 0;
|
uint8_t lookHead = 0;
|
||||||
uint8_t lookBody = 0;
|
uint8_t lookBody = 0;
|
||||||
uint8_t lookLegs = 0;
|
uint8_t lookLegs = 0;
|
||||||
|
115
src/game.cpp
115
src/game.cpp
@ -106,6 +106,10 @@ void Game::setGameState(GameState_t newState)
|
|||||||
raids.startup();
|
raids.startup();
|
||||||
|
|
||||||
quests.loadFromXml();
|
quests.loadFromXml();
|
||||||
|
mounts.loadFromXml();
|
||||||
|
auras.loadFromXml();
|
||||||
|
wings.loadFromXml();
|
||||||
|
shaders.loadFromXml();
|
||||||
|
|
||||||
loadMotdNum();
|
loadMotdNum();
|
||||||
loadPlayersRecord();
|
loadPlayersRecord();
|
||||||
@ -1866,6 +1870,18 @@ void Game::playerReceivePingBack(uint32_t playerId)
|
|||||||
player->sendPingBack();
|
player->sendPingBack();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Game::playerReceiveNewPing(uint32_t playerId, uint16_t ping, uint16_t fps)
|
||||||
|
{
|
||||||
|
Player* player = getPlayerByID(playerId);
|
||||||
|
if (!player) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
player->receivePing();
|
||||||
|
player->setLocalPing(ping);
|
||||||
|
player->setFPS(fps);
|
||||||
|
}
|
||||||
|
|
||||||
void Game::playerAutoWalk(uint32_t playerId, const std::forward_list<Direction>& listDir)
|
void Game::playerAutoWalk(uint32_t playerId, const std::forward_list<Direction>& listDir)
|
||||||
{
|
{
|
||||||
Player* player = getPlayerByID(playerId);
|
Player* player = getPlayerByID(playerId);
|
||||||
@ -2906,6 +2922,17 @@ void Game::playerRequestOutfit(uint32_t playerId)
|
|||||||
player->sendOutfitWindow();
|
player->sendOutfitWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Game::playerToggleOutfitExtension(uint32_t playerId, int mount, int wings, int aura, int shader)
|
||||||
|
{
|
||||||
|
Player* player = getPlayerByID(playerId);
|
||||||
|
if (!player) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mount != -1)
|
||||||
|
player->toggleMount(mount == 1);
|
||||||
|
}
|
||||||
|
|
||||||
void Game::playerChangeOutfit(uint32_t playerId, Outfit_t outfit)
|
void Game::playerChangeOutfit(uint32_t playerId, Outfit_t outfit)
|
||||||
{
|
{
|
||||||
if (!g_config.getBoolean(ConfigManager::ALLOW_CHANGEOUTFIT)) {
|
if (!g_config.getBoolean(ConfigManager::ALLOW_CHANGEOUTFIT)) {
|
||||||
@ -2917,6 +2944,74 @@ void Game::playerChangeOutfit(uint32_t playerId, Outfit_t outfit)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Outfit* playerOutfit = Outfits::getInstance().getOutfitByLookType(player->getSex(), outfit.lookType);
|
||||||
|
if (!playerOutfit) {
|
||||||
|
outfit.lookMount = 0;
|
||||||
|
outfit.lookWings = 0;
|
||||||
|
outfit.lookAura = 0;
|
||||||
|
outfit.lookShader = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outfit.lookMount != 0) {
|
||||||
|
Mount* mount = mounts.getMountByClientID(outfit.lookMount);
|
||||||
|
if (!mount) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!player->hasMount(mount)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (player->isMounted()) {
|
||||||
|
Mount* prevMount = mounts.getMountByID(player->getCurrentMount());
|
||||||
|
if (prevMount) {
|
||||||
|
changeSpeed(player, mount->speed - prevMount->speed);
|
||||||
|
}
|
||||||
|
|
||||||
|
player->setCurrentMount(mount->id);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
player->setCurrentMount(mount->id);
|
||||||
|
outfit.lookMount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (player->isMounted()) {
|
||||||
|
player->dismount();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outfit.lookWings != 0) {
|
||||||
|
Wing* wing = wings.getWingByID(outfit.lookWings);
|
||||||
|
if (!wing) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!player->hasWing(wing)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outfit.lookAura != 0) {
|
||||||
|
Aura* aura = auras.getAuraByID(outfit.lookAura);
|
||||||
|
if (!aura) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!player->hasAura(aura)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outfit.lookShader) {
|
||||||
|
Shader* shader = shaders.getShaderByID(outfit.lookShader);
|
||||||
|
if (!shader) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!player->hasShader(shader)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (player->canWear(outfit.lookType, outfit.lookAddons)) {
|
if (player->canWear(outfit.lookType, outfit.lookAddons)) {
|
||||||
player->defaultOutfit = outfit;
|
player->defaultOutfit = outfit;
|
||||||
|
|
||||||
@ -5099,6 +5194,7 @@ bool Game::reload(ReloadTypes_t reloadType)
|
|||||||
{
|
{
|
||||||
switch (reloadType) {
|
switch (reloadType) {
|
||||||
case RELOAD_TYPE_ACTIONS: return g_actions->reload();
|
case RELOAD_TYPE_ACTIONS: return g_actions->reload();
|
||||||
|
case RELOAD_TYPE_AURAS: return auras.reload();
|
||||||
case RELOAD_TYPE_CHAT: return g_chat->load();
|
case RELOAD_TYPE_CHAT: return g_chat->load();
|
||||||
case RELOAD_TYPE_CONFIG: return g_config.reload();
|
case RELOAD_TYPE_CONFIG: return g_config.reload();
|
||||||
case RELOAD_TYPE_CREATURESCRIPTS: return g_creatureEvents->reload();
|
case RELOAD_TYPE_CREATURESCRIPTS: return g_creatureEvents->reload();
|
||||||
@ -5106,6 +5202,7 @@ bool Game::reload(ReloadTypes_t reloadType)
|
|||||||
case RELOAD_TYPE_GLOBALEVENTS: return g_globalEvents->reload();
|
case RELOAD_TYPE_GLOBALEVENTS: return g_globalEvents->reload();
|
||||||
case RELOAD_TYPE_ITEMS: return Item::items.reload();
|
case RELOAD_TYPE_ITEMS: return Item::items.reload();
|
||||||
case RELOAD_TYPE_MONSTERS: return g_monsters.reload();
|
case RELOAD_TYPE_MONSTERS: return g_monsters.reload();
|
||||||
|
case RELOAD_TYPE_MOUNTS: return mounts.reload();
|
||||||
case RELOAD_TYPE_MOVEMENTS: return g_moveEvents->reload();
|
case RELOAD_TYPE_MOVEMENTS: return g_moveEvents->reload();
|
||||||
case RELOAD_TYPE_NPCS: {
|
case RELOAD_TYPE_NPCS: {
|
||||||
Npcs::reload();
|
Npcs::reload();
|
||||||
@ -5114,7 +5211,7 @@ bool Game::reload(ReloadTypes_t reloadType)
|
|||||||
|
|
||||||
case RELOAD_TYPE_QUESTS: return quests.reload();
|
case RELOAD_TYPE_QUESTS: return quests.reload();
|
||||||
case RELOAD_TYPE_RAIDS: return raids.reload() && raids.startup();
|
case RELOAD_TYPE_RAIDS: return raids.reload() && raids.startup();
|
||||||
|
case RELOAD_TYPE_SHADERS: return shaders.reload();
|
||||||
case RELOAD_TYPE_SPELLS: {
|
case RELOAD_TYPE_SPELLS: {
|
||||||
if (!g_spells->reload()) {
|
if (!g_spells->reload()) {
|
||||||
std::cout << "[Error - Game::reload] Failed to reload spells." << std::endl;
|
std::cout << "[Error - Game::reload] Failed to reload spells." << std::endl;
|
||||||
@ -5128,6 +5225,7 @@ bool Game::reload(ReloadTypes_t reloadType)
|
|||||||
}
|
}
|
||||||
|
|
||||||
case RELOAD_TYPE_TALKACTIONS: return g_talkActions->reload();
|
case RELOAD_TYPE_TALKACTIONS: return g_talkActions->reload();
|
||||||
|
case RELOAD_TYPE_WINGS: return wings.reload();
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
if (!g_spells->reload()) {
|
if (!g_spells->reload()) {
|
||||||
@ -5151,6 +5249,10 @@ bool Game::reload(ReloadTypes_t reloadType)
|
|||||||
g_talkActions->reload();
|
g_talkActions->reload();
|
||||||
Item::items.reload();
|
Item::items.reload();
|
||||||
quests.reload();
|
quests.reload();
|
||||||
|
auras.reload();
|
||||||
|
mounts.reload();
|
||||||
|
wings.reload();
|
||||||
|
shaders.reload();
|
||||||
g_globalEvents->reload();
|
g_globalEvents->reload();
|
||||||
g_events->load();
|
g_events->load();
|
||||||
g_chat->load();
|
g_chat->load();
|
||||||
@ -5158,3 +5260,14 @@ bool Game::reload(ReloadTypes_t reloadType)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Game::startProgressbar(Creature* creature, uint32_t duration, bool ltr)
|
||||||
|
{
|
||||||
|
SpectatorVec spectators;
|
||||||
|
map.getSpectators(spectators, creature->getPosition(), false, true);
|
||||||
|
for (Creature* spectator : spectators) {
|
||||||
|
if (Player* tmpPlayer = spectator->getPlayer()) {
|
||||||
|
tmpPlayer->sendProgressbar(creature->getID(), duration, ltr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -32,6 +32,7 @@
|
|||||||
#include "npc.h"
|
#include "npc.h"
|
||||||
#include "wildcardtree.h"
|
#include "wildcardtree.h"
|
||||||
#include "quests.h"
|
#include "quests.h"
|
||||||
|
#include "shaders.h"
|
||||||
|
|
||||||
class ServiceManager;
|
class ServiceManager;
|
||||||
class Creature;
|
class Creature;
|
||||||
@ -360,6 +361,7 @@ class Game
|
|||||||
void playerOpenPrivateChannel(uint32_t playerId, std::string& receiver);
|
void playerOpenPrivateChannel(uint32_t playerId, std::string& receiver);
|
||||||
void playerReceivePing(uint32_t playerId);
|
void playerReceivePing(uint32_t playerId);
|
||||||
void playerReceivePingBack(uint32_t playerId);
|
void playerReceivePingBack(uint32_t playerId);
|
||||||
|
void playerReceiveNewPing(uint32_t playerId, uint16_t ping, uint16_t fps);
|
||||||
void playerAutoWalk(uint32_t playerId, const std::forward_list<Direction>& listDir);
|
void playerAutoWalk(uint32_t playerId, const std::forward_list<Direction>& listDir);
|
||||||
void playerStopAutoWalk(uint32_t playerId);
|
void playerStopAutoWalk(uint32_t playerId);
|
||||||
void playerUseItemEx(uint32_t playerId, const Position& fromPos, uint8_t fromStackPos,
|
void playerUseItemEx(uint32_t playerId, const Position& fromPos, uint8_t fromStackPos,
|
||||||
@ -399,6 +401,7 @@ class Game
|
|||||||
void playerPassPartyLeadership(uint32_t playerId, uint32_t newLeaderId);
|
void playerPassPartyLeadership(uint32_t playerId, uint32_t newLeaderId);
|
||||||
void playerLeaveParty(uint32_t playerId);
|
void playerLeaveParty(uint32_t playerId);
|
||||||
void playerEnableSharedPartyExperience(uint32_t playerId, bool sharedExpActive);
|
void playerEnableSharedPartyExperience(uint32_t playerId, bool sharedExpActive);
|
||||||
|
void playerToggleOutfitExtension(uint32_t playerId, int mount, int wings, int aura, int shader);
|
||||||
void playerProcessRuleViolationReport(uint32_t playerId, const std::string& name);
|
void playerProcessRuleViolationReport(uint32_t playerId, const std::string& name);
|
||||||
void playerCloseRuleViolationReport(uint32_t playerId, const std::string& name);
|
void playerCloseRuleViolationReport(uint32_t playerId, const std::string& name);
|
||||||
void playerCancelRuleViolationReport(uint32_t playerId);
|
void playerCancelRuleViolationReport(uint32_t playerId);
|
||||||
@ -503,10 +506,16 @@ class Game
|
|||||||
void setBedSleeper(BedItem* bed, uint32_t guid);
|
void setBedSleeper(BedItem* bed, uint32_t guid);
|
||||||
void removeBedSleeper(uint32_t guid);
|
void removeBedSleeper(uint32_t guid);
|
||||||
bool reload(ReloadTypes_t reloadType);
|
bool reload(ReloadTypes_t reloadType);
|
||||||
|
void startProgressbar(Creature* creature, uint32_t duration, bool ltr = true);
|
||||||
|
|
||||||
|
Auras auras;
|
||||||
Groups groups;
|
Groups groups;
|
||||||
Map map;
|
Map map;
|
||||||
|
Mounts mounts;
|
||||||
Raids raids;
|
Raids raids;
|
||||||
Quests quests;
|
Quests quests;
|
||||||
|
Wings wings;
|
||||||
|
Shaders shaders;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool playerSaySpell(Player* player, SpeakClasses type, const std::string& text);
|
bool playerSaySpell(Player* player, SpeakClasses type, const std::string& text);
|
||||||
|
@ -525,7 +525,7 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IOLoginData::saveItems(const Player* player, const ItemBlockList& itemList, DBInsert& query_insert, PropWriteStream& propWriteStream)
|
bool IOLoginData::saveItems(const Player* player, const ItemBlockList& itemList, DBInsert& query_insert, PropWriteStream& propWriteStream, std::map<Container*, int>& openContainers)
|
||||||
{
|
{
|
||||||
std::ostringstream ss;
|
std::ostringstream ss;
|
||||||
|
|
||||||
@ -540,6 +540,17 @@ bool IOLoginData::saveItems(const Player* player, const ItemBlockList& itemList,
|
|||||||
Item* item = it.second;
|
Item* item = it.second;
|
||||||
++runningId;
|
++runningId;
|
||||||
|
|
||||||
|
if (Container* container = item->getContainer()) {
|
||||||
|
auto it = openContainers.find(container);
|
||||||
|
if (it == openContainers.end()) {
|
||||||
|
container->resetAutoOpen();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
container->setAutoOpen(it->second);
|
||||||
|
}
|
||||||
|
queue.emplace_back(container, runningId);
|
||||||
|
}
|
||||||
|
|
||||||
propWriteStream.clear();
|
propWriteStream.clear();
|
||||||
item->serializeAttr(propWriteStream);
|
item->serializeAttr(propWriteStream);
|
||||||
|
|
||||||
@ -550,10 +561,6 @@ bool IOLoginData::saveItems(const Player* player, const ItemBlockList& itemList,
|
|||||||
if (!query_insert.addRow(ss)) {
|
if (!query_insert.addRow(ss)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Container* container = item->getContainer()) {
|
|
||||||
queue.emplace_back(container, runningId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
while (!queue.empty()) {
|
while (!queue.empty()) {
|
||||||
@ -567,6 +574,14 @@ bool IOLoginData::saveItems(const Player* player, const ItemBlockList& itemList,
|
|||||||
|
|
||||||
Container* subContainer = item->getContainer();
|
Container* subContainer = item->getContainer();
|
||||||
if (subContainer) {
|
if (subContainer) {
|
||||||
|
auto it = openContainers.find(subContainer);
|
||||||
|
if (it == openContainers.end()) {
|
||||||
|
subContainer->resetAutoOpen();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
subContainer->setAutoOpen(it->second);
|
||||||
|
}
|
||||||
|
|
||||||
queue.emplace_back(subContainer, runningId);
|
queue.emplace_back(subContainer, runningId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -749,6 +764,11 @@ bool IOLoginData::savePlayer(Player* player)
|
|||||||
|
|
||||||
//item saving
|
//item saving
|
||||||
query.str(std::string());
|
query.str(std::string());
|
||||||
|
std::map<Container*, int> openContainers;
|
||||||
|
for (auto container : player->getOpenContainers()) {
|
||||||
|
if (!container.second.container) continue;
|
||||||
|
openContainers[container.second.container] = container.first;
|
||||||
|
}
|
||||||
query << "DELETE FROM `player_items` WHERE `player_id` = " << player->getGUID();
|
query << "DELETE FROM `player_items` WHERE `player_id` = " << player->getGUID();
|
||||||
if (!db->executeQuery(query.str())) {
|
if (!db->executeQuery(query.str())) {
|
||||||
return false;
|
return false;
|
||||||
@ -764,7 +784,7 @@ bool IOLoginData::savePlayer(Player* player)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!saveItems(player, itemList, itemsQuery, propWriteStream)) {
|
if (!saveItems(player, itemList, itemsQuery, propWriteStream, openContainers)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -783,7 +803,7 @@ bool IOLoginData::savePlayer(Player* player)
|
|||||||
itemList.emplace_back(it.first, it.second);
|
itemList.emplace_back(it.first, it.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!saveItems(player, itemList, depotQuery, propWriteStream)) {
|
if (!saveItems(player, itemList, depotQuery, propWriteStream, openContainers)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ class IOLoginData
|
|||||||
typedef std::map<uint32_t, std::pair<Item*, uint32_t>> ItemMap;
|
typedef std::map<uint32_t, std::pair<Item*, uint32_t>> ItemMap;
|
||||||
|
|
||||||
static void loadItems(ItemMap& itemMap, DBResult_ptr result);
|
static void loadItems(ItemMap& itemMap, DBResult_ptr result);
|
||||||
static bool saveItems(const Player* player, const ItemBlockList& itemList, DBInsert& query_insert, PropWriteStream& stream);
|
static bool saveItems(const Player* player, const ItemBlockList& itemList, DBInsert& query_insert, PropWriteStream& stream, std::map<Container*, int>& openContainers);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
16
src/item.cpp
16
src/item.cpp
@ -551,6 +551,17 @@ Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream& propStream)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case ATTR_AUTOOPEN:
|
||||||
|
{
|
||||||
|
int8_t autoOpen;
|
||||||
|
if (!propStream.read<int8_t>(autoOpen)) {
|
||||||
|
return ATTR_READ_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
setIntAttr(ITEM_ATTRIBUTE_AUTOOPEN, autoOpen);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case ATTR_KEYNUMBER: {
|
case ATTR_KEYNUMBER: {
|
||||||
uint16_t keyNumber;
|
uint16_t keyNumber;
|
||||||
if (!propStream.read<uint16_t>(keyNumber)) {
|
if (!propStream.read<uint16_t>(keyNumber)) {
|
||||||
@ -788,6 +799,11 @@ void Item::serializeAttr(PropWriteStream& propWriteStream) const
|
|||||||
propWriteStream.write<uint8_t>(getIntAttr(ITEM_ATTRIBUTE_SHOOTRANGE));
|
propWriteStream.write<uint8_t>(getIntAttr(ITEM_ATTRIBUTE_SHOOTRANGE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hasAttribute(ITEM_ATTRIBUTE_AUTOOPEN)) {
|
||||||
|
propWriteStream.write<uint8_t>(ATTR_AUTOOPEN);
|
||||||
|
propWriteStream.write<int8_t>(getIntAttr(ITEM_ATTRIBUTE_AUTOOPEN));
|
||||||
|
}
|
||||||
|
|
||||||
if (hasAttribute(ITEM_ATTRIBUTE_KEYNUMBER)) {
|
if (hasAttribute(ITEM_ATTRIBUTE_KEYNUMBER)) {
|
||||||
propWriteStream.write<uint8_t>(ATTR_KEYNUMBER);
|
propWriteStream.write<uint8_t>(ATTR_KEYNUMBER);
|
||||||
propWriteStream.write<uint16_t>(getIntAttr(ITEM_ATTRIBUTE_KEYNUMBER));
|
propWriteStream.write<uint16_t>(getIntAttr(ITEM_ATTRIBUTE_KEYNUMBER));
|
||||||
|
17
src/item.h
17
src/item.h
@ -103,6 +103,7 @@ enum AttrTypes_t {
|
|||||||
ATTR_DEFENSE = 35,
|
ATTR_DEFENSE = 35,
|
||||||
ATTR_ARMOR = 36,
|
ATTR_ARMOR = 36,
|
||||||
ATTR_SHOOTRANGE = 37,
|
ATTR_SHOOTRANGE = 37,
|
||||||
|
ATTR_AUTOOPEN = 38
|
||||||
};
|
};
|
||||||
|
|
||||||
enum Attr_ReadValue {
|
enum Attr_ReadValue {
|
||||||
@ -818,6 +819,22 @@ class Item : virtual public Thing
|
|||||||
return !parent || parent->isRemoved();
|
return !parent || parent->isRemoved();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int8_t getAutoOpen()
|
||||||
|
{
|
||||||
|
if (hasAttribute(ITEM_ATTRIBUTE_AUTOOPEN)) {
|
||||||
|
return getIntAttr(ITEM_ATTRIBUTE_AUTOOPEN);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
void setAutoOpen(int8_t value)
|
||||||
|
{
|
||||||
|
setIntAttr(ITEM_ATTRIBUTE_AUTOOPEN, value);
|
||||||
|
}
|
||||||
|
void resetAutoOpen()
|
||||||
|
{
|
||||||
|
removeAttribute(ITEM_ATTRIBUTE_AUTOOPEN);
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::string getWeightDescription(uint32_t weight) const;
|
std::string getWeightDescription(uint32_t weight) const;
|
||||||
|
|
||||||
|
@ -731,6 +731,9 @@ Position LuaScriptInterface::getPosition(lua_State* L, int32_t arg)
|
|||||||
Outfit_t LuaScriptInterface::getOutfit(lua_State* L, int32_t arg)
|
Outfit_t LuaScriptInterface::getOutfit(lua_State* L, int32_t arg)
|
||||||
{
|
{
|
||||||
Outfit_t outfit;
|
Outfit_t outfit;
|
||||||
|
outfit.lookMount = getField<uint16_t>(L, arg, "lookMount");
|
||||||
|
outfit.lookWings = getField<uint16_t>(L, arg, "lookWings");
|
||||||
|
outfit.lookAura = getField<uint16_t>(L, arg, "lookAura");
|
||||||
outfit.lookAddons = getField<uint8_t>(L, arg, "lookAddons");
|
outfit.lookAddons = getField<uint8_t>(L, arg, "lookAddons");
|
||||||
|
|
||||||
outfit.lookFeet = getField<uint8_t>(L, arg, "lookFeet");
|
outfit.lookFeet = getField<uint8_t>(L, arg, "lookFeet");
|
||||||
@ -741,7 +744,9 @@ Outfit_t LuaScriptInterface::getOutfit(lua_State* L, int32_t arg)
|
|||||||
outfit.lookTypeEx = getField<uint8_t>(L, arg, "lookTypeEx");
|
outfit.lookTypeEx = getField<uint8_t>(L, arg, "lookTypeEx");
|
||||||
outfit.lookType = getField<uint8_t>(L, arg, "lookType");
|
outfit.lookType = getField<uint8_t>(L, arg, "lookType");
|
||||||
|
|
||||||
lua_pop(L, 6);
|
outfit.lookShader = getField<uint16_t>(L, arg, "lookShader");
|
||||||
|
|
||||||
|
lua_pop(L, 8);
|
||||||
return outfit;
|
return outfit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -878,7 +883,7 @@ void LuaScriptInterface::pushPosition(lua_State* L, const Position& position, in
|
|||||||
|
|
||||||
void LuaScriptInterface::pushOutfit(lua_State* L, const Outfit_t& outfit)
|
void LuaScriptInterface::pushOutfit(lua_State* L, const Outfit_t& outfit)
|
||||||
{
|
{
|
||||||
lua_createtable(L, 0, 6);
|
lua_createtable(L, 0, 8);
|
||||||
setField(L, "lookType", outfit.lookType);
|
setField(L, "lookType", outfit.lookType);
|
||||||
setField(L, "lookTypeEx", outfit.lookTypeEx);
|
setField(L, "lookTypeEx", outfit.lookTypeEx);
|
||||||
setField(L, "lookHead", outfit.lookHead);
|
setField(L, "lookHead", outfit.lookHead);
|
||||||
@ -886,6 +891,10 @@ void LuaScriptInterface::pushOutfit(lua_State* L, const Outfit_t& outfit)
|
|||||||
setField(L, "lookLegs", outfit.lookLegs);
|
setField(L, "lookLegs", outfit.lookLegs);
|
||||||
setField(L, "lookFeet", outfit.lookFeet);
|
setField(L, "lookFeet", outfit.lookFeet);
|
||||||
setField(L, "lookAddons", outfit.lookAddons);
|
setField(L, "lookAddons", outfit.lookAddons);
|
||||||
|
setField(L, "lookMount", outfit.lookMount);
|
||||||
|
setField(L, "lookWings", outfit.lookWings);
|
||||||
|
setField(L, "lookAura", outfit.lookAura);
|
||||||
|
setField(L, "lookShader", outfit.lookShader);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define registerEnum(value) { std::string enumName = #value; registerGlobalVariable(enumName.substr(enumName.find_last_of(':') + 1), value); }
|
#define registerEnum(value) { std::string enumName = #value; registerGlobalVariable(enumName.substr(enumName.find_last_of(':') + 1), value); }
|
||||||
@ -1636,6 +1645,7 @@ void LuaScriptInterface::registerFunctions()
|
|||||||
|
|
||||||
registerEnum(RELOAD_TYPE_ALL)
|
registerEnum(RELOAD_TYPE_ALL)
|
||||||
registerEnum(RELOAD_TYPE_ACTIONS)
|
registerEnum(RELOAD_TYPE_ACTIONS)
|
||||||
|
registerEnum(RELOAD_TYPE_AURAS)
|
||||||
registerEnum(RELOAD_TYPE_CHAT)
|
registerEnum(RELOAD_TYPE_CHAT)
|
||||||
registerEnum(RELOAD_TYPE_COMMANDS)
|
registerEnum(RELOAD_TYPE_COMMANDS)
|
||||||
registerEnum(RELOAD_TYPE_CONFIG)
|
registerEnum(RELOAD_TYPE_CONFIG)
|
||||||
@ -1651,8 +1661,10 @@ void LuaScriptInterface::registerFunctions()
|
|||||||
registerEnum(RELOAD_TYPE_QUESTS)
|
registerEnum(RELOAD_TYPE_QUESTS)
|
||||||
registerEnum(RELOAD_TYPE_RAIDS)
|
registerEnum(RELOAD_TYPE_RAIDS)
|
||||||
registerEnum(RELOAD_TYPE_SPELLS)
|
registerEnum(RELOAD_TYPE_SPELLS)
|
||||||
|
registerEnum(RELOAD_TYPE_SHADERS)
|
||||||
registerEnum(RELOAD_TYPE_TALKACTIONS)
|
registerEnum(RELOAD_TYPE_TALKACTIONS)
|
||||||
registerEnum(RELOAD_TYPE_WEAPONS)
|
registerEnum(RELOAD_TYPE_WEAPONS)
|
||||||
|
registerEnum(RELOAD_TYPE_WINGS)
|
||||||
|
|
||||||
// _G
|
// _G
|
||||||
registerGlobalVariable("INDEX_WHEREEVER", INDEX_WHEREEVER);
|
registerGlobalVariable("INDEX_WHEREEVER", INDEX_WHEREEVER);
|
||||||
@ -2015,6 +2027,8 @@ void LuaScriptInterface::registerFunctions()
|
|||||||
|
|
||||||
registerMethod("Creature", "getPathTo", LuaScriptInterface::luaCreatureGetPathTo);
|
registerMethod("Creature", "getPathTo", LuaScriptInterface::luaCreatureGetPathTo);
|
||||||
|
|
||||||
|
registerMethod("Creature", "sendProgressbar", LuaScriptInterface::luaCreatureSetProgressbar);
|
||||||
|
|
||||||
// Player
|
// Player
|
||||||
registerClass("Player", "Creature", LuaScriptInterface::luaPlayerCreate);
|
registerClass("Player", "Creature", LuaScriptInterface::luaPlayerCreate);
|
||||||
registerMetaMethod("Player", "__eq", LuaScriptInterface::luaUserdataCompare);
|
registerMetaMethod("Player", "__eq", LuaScriptInterface::luaUserdataCompare);
|
||||||
@ -2138,6 +2152,10 @@ void LuaScriptInterface::registerFunctions()
|
|||||||
registerMethod("Player", "hasOutfit", LuaScriptInterface::luaPlayerHasOutfit);
|
registerMethod("Player", "hasOutfit", LuaScriptInterface::luaPlayerHasOutfit);
|
||||||
registerMethod("Player", "sendOutfitWindow", LuaScriptInterface::luaPlayerSendOutfitWindow);
|
registerMethod("Player", "sendOutfitWindow", LuaScriptInterface::luaPlayerSendOutfitWindow);
|
||||||
|
|
||||||
|
registerMethod("Player", "addMount", LuaScriptInterface::luaPlayerAddMount);
|
||||||
|
registerMethod("Player", "removeMount", LuaScriptInterface::luaPlayerRemoveMount);
|
||||||
|
registerMethod("Player", "hasMount", LuaScriptInterface::luaPlayerHasMount);
|
||||||
|
|
||||||
registerMethod("Player", "getPremiumDays", LuaScriptInterface::luaPlayerGetPremiumDays);
|
registerMethod("Player", "getPremiumDays", LuaScriptInterface::luaPlayerGetPremiumDays);
|
||||||
registerMethod("Player", "addPremiumDays", LuaScriptInterface::luaPlayerAddPremiumDays);
|
registerMethod("Player", "addPremiumDays", LuaScriptInterface::luaPlayerAddPremiumDays);
|
||||||
registerMethod("Player", "removePremiumDays", LuaScriptInterface::luaPlayerRemovePremiumDays);
|
registerMethod("Player", "removePremiumDays", LuaScriptInterface::luaPlayerRemovePremiumDays);
|
||||||
@ -8597,6 +8615,79 @@ int LuaScriptInterface::luaPlayerSendOutfitWindow(lua_State* L)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int LuaScriptInterface::luaPlayerAddMount(lua_State* L) {
|
||||||
|
// player:addMount(mountId or mountName)
|
||||||
|
Player* player = getUserdata<Player>(L, 1);
|
||||||
|
if (!player) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t mountId;
|
||||||
|
if (isNumber(L, 2)) {
|
||||||
|
mountId = getNumber<uint8_t>(L, 2);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Mount* mount = g_game.mounts.getMountByName(getString(L, 2));
|
||||||
|
if (!mount) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
mountId = mount->id;
|
||||||
|
}
|
||||||
|
pushBoolean(L, player->tameMount(mountId));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int LuaScriptInterface::luaPlayerRemoveMount(lua_State* L) {
|
||||||
|
// player:removeMount(mountId or mountName)
|
||||||
|
Player* player = getUserdata<Player>(L, 1);
|
||||||
|
if (!player) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t mountId;
|
||||||
|
if (isNumber(L, 2)) {
|
||||||
|
mountId = getNumber<uint8_t>(L, 2);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Mount* mount = g_game.mounts.getMountByName(getString(L, 2));
|
||||||
|
if (!mount) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
mountId = mount->id;
|
||||||
|
}
|
||||||
|
pushBoolean(L, player->untameMount(mountId));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int LuaScriptInterface::luaPlayerHasMount(lua_State* L) {
|
||||||
|
// player:hasMount(mountId or mountName)
|
||||||
|
const Player* player = getUserdata<const Player>(L, 1);
|
||||||
|
if (!player) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Mount* mount = nullptr;
|
||||||
|
if (isNumber(L, 2)) {
|
||||||
|
mount = g_game.mounts.getMountByID(getNumber<uint8_t>(L, 2));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
mount = g_game.mounts.getMountByName(getString(L, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mount) {
|
||||||
|
pushBoolean(L, player->hasMount(mount));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
lua_pushnil(L);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
int LuaScriptInterface::luaPlayerGetPremiumDays(lua_State* L)
|
int LuaScriptInterface::luaPlayerGetPremiumDays(lua_State* L)
|
||||||
{
|
{
|
||||||
// player:getPremiumDays()
|
// player:getPremiumDays()
|
||||||
@ -8823,10 +8914,14 @@ int LuaScriptInterface::luaPlayerGetClient(lua_State* L)
|
|||||||
// player:getClient()
|
// player:getClient()
|
||||||
Player* player = getUserdata<Player>(L, 1);
|
Player* player = getUserdata<Player>(L, 1);
|
||||||
if (player) {
|
if (player) {
|
||||||
lua_createtable(L, 0, 2);
|
lua_createtable(L, 0, 5);
|
||||||
setField(L, "version", player->getProtocolVersion());
|
setField(L, "version", player->getProtocolVersion());
|
||||||
setField(L, "os", player->getOperatingSystem());
|
setField(L, "os", player->getOperatingSystem());
|
||||||
} else {
|
setField(L, "otcv8", player->getOTCv8Version());
|
||||||
|
setField(L, "ping", player->getLocalPing());
|
||||||
|
setField(L, "fps", player->getFPS());
|
||||||
|
}
|
||||||
|
else {
|
||||||
lua_pushnil(L);
|
lua_pushnil(L);
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
@ -11294,11 +11389,15 @@ int LuaScriptInterface::luaConditionSetSpeedDelta(lua_State* L)
|
|||||||
int LuaScriptInterface::luaConditionSetOutfit(lua_State* L)
|
int LuaScriptInterface::luaConditionSetOutfit(lua_State* L)
|
||||||
{
|
{
|
||||||
// condition:setOutfit(outfit)
|
// condition:setOutfit(outfit)
|
||||||
// condition:setOutfit(lookTypeEx, lookType, lookHead, lookBody, lookLegs, lookFeet[, lookAddons])
|
// condition:setOutfit(lookTypeEx, lookType, lookHead, lookBody, lookLegs, lookFeet[, lookAddons[, lookMount, lookWings, lookAura, lookShader]])
|
||||||
Outfit_t outfit;
|
Outfit_t outfit;
|
||||||
if (isTable(L, 2)) {
|
if (isTable(L, 2)) {
|
||||||
outfit = getOutfit(L, 2);
|
outfit = getOutfit(L, 2);
|
||||||
} else {
|
} else {
|
||||||
|
outfit.lookShader = getNumber<uint16_t>(L, 12, outfit.lookShader);
|
||||||
|
outfit.lookAura = getNumber<uint16_t>(L, 11, outfit.lookAura);
|
||||||
|
outfit.lookWings = getNumber<uint16_t>(L, 10, outfit.lookWings);
|
||||||
|
outfit.lookMount = getNumber<uint16_t>(L, 9, outfit.lookMount);
|
||||||
outfit.lookAddons = getNumber<uint8_t>(L, 8, outfit.lookAddons);
|
outfit.lookAddons = getNumber<uint8_t>(L, 8, outfit.lookAddons);
|
||||||
outfit.lookFeet = getNumber<uint8_t>(L, 7);
|
outfit.lookFeet = getNumber<uint8_t>(L, 7);
|
||||||
outfit.lookLegs = getNumber<uint8_t>(L, 6);
|
outfit.lookLegs = getNumber<uint8_t>(L, 6);
|
||||||
@ -12277,3 +12376,20 @@ void LuaEnvironment::executeTimerEvent(uint32_t eventIndex)
|
|||||||
luaL_unref(luaState, LUA_REGISTRYINDEX, parameter);
|
luaL_unref(luaState, LUA_REGISTRYINDEX, parameter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int LuaScriptInterface::luaCreatureSetProgressbar(lua_State* L)
|
||||||
|
{
|
||||||
|
// creature:sendProgressbar(duration, leftToRight)
|
||||||
|
Creature* creature = getUserdata<Creature>(L, 1);
|
||||||
|
uint32_t duration = getNumber<uint32_t>(L, 2);
|
||||||
|
bool ltr = getBoolean(L, 3);
|
||||||
|
if (creature) {
|
||||||
|
g_game.startProgressbar(creature, duration, ltr);
|
||||||
|
pushBoolean(L, true);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
lua_pushnil(L);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
@ -786,6 +786,8 @@ class LuaScriptInterface
|
|||||||
|
|
||||||
static int luaCreatureGetPathTo(lua_State* L);
|
static int luaCreatureGetPathTo(lua_State* L);
|
||||||
|
|
||||||
|
static int luaCreatureSetProgressbar(lua_State* L);
|
||||||
|
|
||||||
// Player
|
// Player
|
||||||
static int luaPlayerCreate(lua_State* L);
|
static int luaPlayerCreate(lua_State* L);
|
||||||
|
|
||||||
@ -909,6 +911,10 @@ class LuaScriptInterface
|
|||||||
static int luaPlayerHasOutfit(lua_State* L);
|
static int luaPlayerHasOutfit(lua_State* L);
|
||||||
static int luaPlayerSendOutfitWindow(lua_State* L);
|
static int luaPlayerSendOutfitWindow(lua_State* L);
|
||||||
|
|
||||||
|
static int luaPlayerAddMount(lua_State* L);
|
||||||
|
static int luaPlayerRemoveMount(lua_State* L);
|
||||||
|
static int luaPlayerHasMount(lua_State* L);
|
||||||
|
|
||||||
static int luaPlayerGetPremiumDays(lua_State* L);
|
static int luaPlayerGetPremiumDays(lua_State* L);
|
||||||
static int luaPlayerAddPremiumDays(lua_State* L);
|
static int luaPlayerAddPremiumDays(lua_State* L);
|
||||||
static int luaPlayerRemovePremiumDays(lua_State* L);
|
static int luaPlayerRemovePremiumDays(lua_State* L);
|
||||||
|
@ -775,6 +775,23 @@ bool Monsters::loadMonster(const std::string& file, const std::string& monsterNa
|
|||||||
std::cout << "[Warning - Monsters::loadMonster] Missing look type/typeex. " << file << std::endl;
|
std::cout << "[Warning - Monsters::loadMonster] Missing look type/typeex. " << file << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((attr = node.attribute("mount"))) {
|
||||||
|
mType->info.outfit.lookMount = pugi::cast<uint16_t>(attr.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((attr = node.attribute("aura"))) {
|
||||||
|
mType->info.outfit.lookAura = pugi::cast<uint16_t>(attr.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((attr = node.attribute("wings"))) {
|
||||||
|
mType->info.outfit.lookWings = pugi::cast<uint16_t>(attr.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((attr = node.attribute("shader"))) {
|
||||||
|
Shader* shader = g_game.shaders.getShaderByName(attr.as_string());
|
||||||
|
mType->info.outfit.lookShader = shader ? shader->id : 0;
|
||||||
|
}
|
||||||
|
|
||||||
if ((attr = node.attribute("corpse"))) {
|
if ((attr = node.attribute("corpse"))) {
|
||||||
mType->info.lookcorpse = pugi::cast<uint16_t>(attr.value());
|
mType->info.lookcorpse = pugi::cast<uint16_t>(attr.value());
|
||||||
}
|
}
|
||||||
|
82
src/mounts.cpp
Normal file
82
src/mounts.cpp
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
/**
|
||||||
|
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||||
|
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "otpch.h"
|
||||||
|
|
||||||
|
#include "mounts.h"
|
||||||
|
|
||||||
|
#include "pugicast.h"
|
||||||
|
#include "tools.h"
|
||||||
|
|
||||||
|
bool Mounts::reload()
|
||||||
|
{
|
||||||
|
mounts.clear();
|
||||||
|
return loadFromXml();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Mounts::loadFromXml()
|
||||||
|
{
|
||||||
|
pugi::xml_document doc;
|
||||||
|
pugi::xml_parse_result result = doc.load_file("data/XML/mounts.xml");
|
||||||
|
if (!result) {
|
||||||
|
printXMLError("Error - Mounts::loadFromXml", "data/XML/mounts.xml", result);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto mountNode : doc.child("mounts").children()) {
|
||||||
|
mounts.emplace_back(
|
||||||
|
static_cast<uint8_t>(pugi::cast<uint16_t>(mountNode.attribute("id").value())),
|
||||||
|
pugi::cast<uint16_t>(mountNode.attribute("clientid").value()),
|
||||||
|
mountNode.attribute("name").as_string(),
|
||||||
|
pugi::cast<int32_t>(mountNode.attribute("speed").value()),
|
||||||
|
mountNode.attribute("premium").as_bool()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
mounts.shrink_to_fit();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Mount* Mounts::getMountByID(uint8_t id)
|
||||||
|
{
|
||||||
|
auto it = std::find_if(mounts.begin(), mounts.end(), [id](const Mount& mount) {
|
||||||
|
return mount.id == id;
|
||||||
|
});
|
||||||
|
|
||||||
|
return it != mounts.end() ? &*it : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Mount* Mounts::getMountByName(const std::string& name) {
|
||||||
|
auto mountName = name.c_str();
|
||||||
|
for (auto& it : mounts) {
|
||||||
|
if (strcasecmp(mountName, it.name.c_str()) == 0) {
|
||||||
|
return ⁢
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Mount* Mounts::getMountByClientID(uint16_t clientId)
|
||||||
|
{
|
||||||
|
auto it = std::find_if(mounts.begin(), mounts.end(), [clientId](const Mount& mount) {
|
||||||
|
return mount.clientId == clientId;
|
||||||
|
});
|
||||||
|
|
||||||
|
return it != mounts.end() ? &*it : nullptr;
|
||||||
|
}
|
52
src/mounts.h
Normal file
52
src/mounts.h
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
/**
|
||||||
|
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||||
|
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FS_MOUNTS_H_73716D11906A4C5C9F4A7B68D34C9BA6
|
||||||
|
#define FS_MOUNTS_H_73716D11906A4C5C9F4A7B68D34C9BA6
|
||||||
|
|
||||||
|
struct Mount
|
||||||
|
{
|
||||||
|
Mount(uint8_t id, uint16_t clientId, std::string name, int32_t speed, bool premium) :
|
||||||
|
name(std::move(name)), speed(speed), clientId(clientId), id(id), premium(premium) {}
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
int32_t speed;
|
||||||
|
uint16_t clientId;
|
||||||
|
uint8_t id;
|
||||||
|
bool premium;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Mounts
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bool reload();
|
||||||
|
bool loadFromXml();
|
||||||
|
Mount* getMountByID(uint8_t id);
|
||||||
|
Mount* getMountByName(const std::string& name);
|
||||||
|
Mount* getMountByClientID(uint16_t clientId);
|
||||||
|
|
||||||
|
const std::vector<Mount>& getMounts() const {
|
||||||
|
return mounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<Mount> mounts;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
@ -51,7 +51,7 @@ Position NetworkMessage::getPosition()
|
|||||||
void NetworkMessage::addString(const std::string& value)
|
void NetworkMessage::addString(const std::string& value)
|
||||||
{
|
{
|
||||||
size_t stringLen = value.length();
|
size_t stringLen = value.length();
|
||||||
if (!canAdd(stringLen + 2) || stringLen > 8192) {
|
if (!canAdd(stringLen + 2)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,7 +69,7 @@ void NetworkMessage::addDouble(double value, uint8_t precision/* = 2*/)
|
|||||||
|
|
||||||
void NetworkMessage::addBytes(const char* bytes, size_t size)
|
void NetworkMessage::addBytes(const char* bytes, size_t size)
|
||||||
{
|
{
|
||||||
if (!canAdd(size) || size > 8192) {
|
if (!canAdd(size)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,7 +43,14 @@ public:
|
|||||||
add_header(info.length);
|
add_header(info.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
void addCryptoHeader() {
|
void addCryptoHeader(bool addChecksum, bool compression = false) {
|
||||||
|
if (compression) {
|
||||||
|
add_header<uint32_t>(0);
|
||||||
|
}
|
||||||
|
else if (addChecksum) {
|
||||||
|
//add_header(adlerChecksum(buffer + outputBufferStart, info.length));
|
||||||
|
}
|
||||||
|
|
||||||
writeMessageLength();
|
writeMessageLength();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
283
src/player.cpp
283
src/player.cpp
@ -528,6 +528,9 @@ void Player::addStorageValue(const uint32_t key, const int32_t value, const bool
|
|||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
else if (IS_IN_KEYRANGE(key, MOUNTS_RANGE) || IS_IN_KEYRANGE(key, WINGS_RANGE) || IS_IN_KEYRANGE(key, AURAS_RANGE)) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
std::cout << "Warning: unknown reserved key: " << key << " player: " << getName() << std::endl;
|
std::cout << "Warning: unknown reserved key: " << key << " player: " << getName() << std::endl;
|
||||||
return;
|
return;
|
||||||
@ -684,6 +687,31 @@ void Player::sendPing()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Player::autoOpenContainers()
|
||||||
|
{
|
||||||
|
for (int32_t i = CONST_SLOT_FIRST; i <= CONST_SLOT_LAST; i++) {
|
||||||
|
Item* item = inventory[i];
|
||||||
|
if (!item) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Container* container = item->getContainer()) {
|
||||||
|
if (container->getAutoOpen() >= 0) {
|
||||||
|
addContainer(container->getAutoOpen(), container);
|
||||||
|
onSendContainer(container);
|
||||||
|
}
|
||||||
|
for (ContainerIterator it = container->iterator(); it.hasNext(); it.advance()) {
|
||||||
|
if (Container* subContainer = (*it)->getContainer()) {
|
||||||
|
if (subContainer->getAutoOpen() >= 0) {
|
||||||
|
addContainer(subContainer->getAutoOpen(), subContainer);
|
||||||
|
onSendContainer(subContainer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Item* Player::getWriteItem(uint32_t& windowTextId, uint16_t& maxWriteLen)
|
Item* Player::getWriteItem(uint32_t& windowTextId, uint16_t& maxWriteLen)
|
||||||
{
|
{
|
||||||
windowTextId = this->windowTextId;
|
windowTextId = this->windowTextId;
|
||||||
@ -3034,6 +3062,11 @@ void Player::updateItemsLight(bool internal /*=false*/)
|
|||||||
void Player::onAddCondition(ConditionType_t type)
|
void Player::onAddCondition(ConditionType_t type)
|
||||||
{
|
{
|
||||||
Creature::onAddCondition(type);
|
Creature::onAddCondition(type);
|
||||||
|
|
||||||
|
if (type == CONDITION_OUTFIT) {
|
||||||
|
dismount();
|
||||||
|
}
|
||||||
|
|
||||||
sendIcons();
|
sendIcons();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3825,6 +3858,256 @@ void Player::sendClosePrivate(uint16_t channelId)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8_t Player::getCurrentMount() const
|
||||||
|
{
|
||||||
|
int32_t value;
|
||||||
|
if (getStorageValue(PSTRG_MOUNTS_CURRENTMOUNT, value)) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::setCurrentMount(uint8_t mountId)
|
||||||
|
{
|
||||||
|
addStorageValue(PSTRG_MOUNTS_CURRENTMOUNT, mountId);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Player::toggleMount(bool mount)
|
||||||
|
{
|
||||||
|
if ((OTSYS_TIME() - lastToggleMount) < 3000 && !wasMounted) {
|
||||||
|
sendCancelMessage(RETURNVALUE_YOUAREEXHAUSTED);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mount) {
|
||||||
|
if (isMounted()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!group->access && tile->hasFlag(TILESTATE_PROTECTIONZONE)) {
|
||||||
|
sendCancelMessage(RETURNVALUE_ACTIONNOTPERMITTEDINPROTECTIONZONE);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Outfit* playerOutfit = Outfits::getInstance().getOutfitByLookType(getSex(), defaultOutfit.lookType);
|
||||||
|
if (!playerOutfit) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t currentMountId = getCurrentMount();
|
||||||
|
if (currentMountId == 0) {
|
||||||
|
sendOutfitWindow();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Mount* currentMount = g_game.mounts.getMountByID(currentMountId);
|
||||||
|
if (!currentMount) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasMount(currentMount)) {
|
||||||
|
setCurrentMount(0);
|
||||||
|
sendOutfitWindow();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentMount->premium && !isPremium()) {
|
||||||
|
sendCancelMessage(RETURNVALUE_YOUNEEDPREMIUMACCOUNT);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasCondition(CONDITION_OUTFIT)) {
|
||||||
|
sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultOutfit.lookMount = currentMount->clientId;
|
||||||
|
|
||||||
|
if (currentMount->speed != 0) {
|
||||||
|
g_game.changeSpeed(this, currentMount->speed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (!isMounted()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
dismount();
|
||||||
|
}
|
||||||
|
|
||||||
|
g_game.internalCreatureChangeOutfit(this, defaultOutfit);
|
||||||
|
lastToggleMount = OTSYS_TIME();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Player::tameMount(uint8_t mountId)
|
||||||
|
{
|
||||||
|
if (!g_game.mounts.getMountByID(mountId)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t tmpMountId = mountId - 1;
|
||||||
|
const uint32_t key = PSTRG_MOUNTS_RANGE_START + (tmpMountId / 31);
|
||||||
|
|
||||||
|
int32_t value;
|
||||||
|
if (getStorageValue(key, value)) {
|
||||||
|
value |= (1 << (tmpMountId % 31));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
value = (1 << (tmpMountId % 31));
|
||||||
|
}
|
||||||
|
|
||||||
|
addStorageValue(key, value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Player::untameMount(uint8_t mountId)
|
||||||
|
{
|
||||||
|
if (!g_game.mounts.getMountByID(mountId)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t tmpMountId = mountId - 1;
|
||||||
|
const uint32_t key = PSTRG_MOUNTS_RANGE_START + (tmpMountId / 31);
|
||||||
|
|
||||||
|
int32_t value;
|
||||||
|
if (!getStorageValue(key, value)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
value &= ~(1 << (tmpMountId % 31));
|
||||||
|
addStorageValue(key, value);
|
||||||
|
|
||||||
|
if (getCurrentMount() == mountId) {
|
||||||
|
if (isMounted()) {
|
||||||
|
dismount();
|
||||||
|
g_game.internalCreatureChangeOutfit(this, defaultOutfit);
|
||||||
|
}
|
||||||
|
|
||||||
|
setCurrentMount(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Player::hasMount(const Mount* mount) const
|
||||||
|
{
|
||||||
|
if (isAccessPlayer()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mount->premium && !isPremium()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t tmpMountId = mount->id - 1;
|
||||||
|
|
||||||
|
int32_t value;
|
||||||
|
if (!getStorageValue(PSTRG_MOUNTS_RANGE_START + (tmpMountId / 31), value)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ((1 << (tmpMountId % 31)) & value) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::dismount()
|
||||||
|
{
|
||||||
|
Mount* mount = g_game.mounts.getMountByID(getCurrentMount());
|
||||||
|
if (mount && mount->speed > 0) {
|
||||||
|
g_game.changeSpeed(this, -mount->speed);
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultOutfit.lookMount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Player::hasWing(const Wing* wing) const
|
||||||
|
{
|
||||||
|
if (isAccessPlayer()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wing->premium && !isPremium()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t tmpWingId = wing->id - 1;
|
||||||
|
|
||||||
|
int32_t value;
|
||||||
|
if (!getStorageValue(PSTRG_WINGS_RANGE_START + (tmpWingId / 31), value)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ((1 << (tmpWingId % 31)) & value) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t Player::getCurrentWing() const
|
||||||
|
{
|
||||||
|
int32_t value;
|
||||||
|
if (getStorageValue(PSTRG_WINGS_CURRENTWINGS, value)) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::setCurrentWing(uint8_t wingId)
|
||||||
|
{
|
||||||
|
addStorageValue(PSTRG_WINGS_CURRENTWINGS, wingId);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Player::hasAura(const Aura* aura) const
|
||||||
|
{
|
||||||
|
if (isAccessPlayer()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aura->premium && !isPremium()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t tmpAuraId = aura->id - 1;
|
||||||
|
|
||||||
|
int32_t value;
|
||||||
|
if (!getStorageValue(PSTRG_AURAS_RANGE_START + (tmpAuraId / 31), value)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ((1 << (tmpAuraId % 31)) & value) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t Player::getCurrentAura() const
|
||||||
|
{
|
||||||
|
int32_t value;
|
||||||
|
if (getStorageValue(PSTRG_AURAS_CURRENTAURA, value)) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::setCurrentAura(uint8_t auraId)
|
||||||
|
{
|
||||||
|
addStorageValue(PSTRG_AURAS_CURRENTAURA, auraId);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Player::hasShader(const Shader* shader) const
|
||||||
|
{
|
||||||
|
if (isAccessPlayer()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shader->premium && !isPremium()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t tmpShaderId = shader->id - 1;
|
||||||
|
|
||||||
|
int32_t value;
|
||||||
|
if (!getStorageValue(PSTRG_SHADERS_RANGE_START + (tmpShaderId / 31), value)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ((1 << (tmpShaderId % 31)) & value) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
bool Player::addOfflineTrainingTries(skills_t skill, uint64_t tries)
|
bool Player::addOfflineTrainingTries(skills_t skill, uint64_t tries)
|
||||||
{
|
{
|
||||||
if (tries == 0 || skill == SKILL_LEVEL) {
|
if (tries == 0 || skill == SKILL_LEVEL) {
|
||||||
|
78
src/player.h
78
src/player.h
@ -33,6 +33,10 @@
|
|||||||
#include "guild.h"
|
#include "guild.h"
|
||||||
#include "groups.h"
|
#include "groups.h"
|
||||||
#include "town.h"
|
#include "town.h"
|
||||||
|
#include "mounts.h"
|
||||||
|
#include "auras.h"
|
||||||
|
#include "wings.h"
|
||||||
|
#include "shaders.h"
|
||||||
|
|
||||||
class BehaviourDatabase;
|
class BehaviourDatabase;
|
||||||
class House;
|
class House;
|
||||||
@ -131,6 +135,42 @@ class Player final : public Creature, public Cylinder
|
|||||||
}
|
}
|
||||||
std::string getDescription(int32_t lookDistance) const final;
|
std::string getDescription(int32_t lookDistance) const final;
|
||||||
|
|
||||||
|
uint8_t getCurrentMount() const;
|
||||||
|
void setCurrentMount(uint8_t mountId);
|
||||||
|
bool isMounted() const
|
||||||
|
{
|
||||||
|
return defaultOutfit.lookMount != 0;
|
||||||
|
}
|
||||||
|
bool hasMount() const
|
||||||
|
{
|
||||||
|
return defaultOutfit.lookMount != 0;
|
||||||
|
}
|
||||||
|
bool hasAura() const
|
||||||
|
{
|
||||||
|
return defaultOutfit.lookAura != 0;
|
||||||
|
}
|
||||||
|
bool hasWings() const
|
||||||
|
{
|
||||||
|
return defaultOutfit.lookWings != 0;
|
||||||
|
}
|
||||||
|
bool hasShader() const
|
||||||
|
{
|
||||||
|
return defaultOutfit.lookShader != 0;
|
||||||
|
}
|
||||||
|
bool toggleMount(bool mount);
|
||||||
|
bool tameMount(uint8_t mountId);
|
||||||
|
bool untameMount(uint8_t mountId);
|
||||||
|
bool hasMount(const Mount* mount) const;
|
||||||
|
void dismount();
|
||||||
|
|
||||||
|
bool hasWing(const Wing* wing) const;
|
||||||
|
uint8_t getCurrentAura() const;
|
||||||
|
void setCurrentAura(uint8_t auraId);
|
||||||
|
bool hasAura(const Aura* aura) const;
|
||||||
|
uint8_t getCurrentWing() const;
|
||||||
|
void setCurrentWing(uint8_t wingId);
|
||||||
|
bool hasShader(const Shader* shader) const;
|
||||||
|
|
||||||
void setGUID(uint32_t guid) {
|
void setGUID(uint32_t guid) {
|
||||||
this->guid = guid;
|
this->guid = guid;
|
||||||
}
|
}
|
||||||
@ -761,6 +801,7 @@ class Player final : public Creature, public Cylinder
|
|||||||
client->sendInventoryItem(slot, item);
|
client->sendInventoryItem(slot, item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
void autoOpenContainers();
|
||||||
|
|
||||||
//event methods
|
//event methods
|
||||||
void onUpdateTileItem(const Tile* tile, const Position& pos, const Item* oldItem,
|
void onUpdateTileItem(const Tile* tile, const Position& pos, const Item* oldItem,
|
||||||
@ -997,10 +1038,39 @@ class Player final : public Creature, public Cylinder
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void sendProgressbar(uint32_t id, uint32_t duration, bool ltr = true) {
|
||||||
|
if (client) {
|
||||||
|
client->sendProgressbar(id, duration, ltr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void receivePing() {
|
void receivePing() {
|
||||||
lastPong = OTSYS_TIME();
|
lastPong = OTSYS_TIME();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setFPS(uint16_t value)
|
||||||
|
{
|
||||||
|
fps = value;
|
||||||
|
}
|
||||||
|
void setLocalPing(uint16_t value)
|
||||||
|
{
|
||||||
|
localPing = value;
|
||||||
|
}
|
||||||
|
uint16_t getFPS() const
|
||||||
|
{
|
||||||
|
return fps;
|
||||||
|
}
|
||||||
|
uint16_t getLocalPing() const
|
||||||
|
{
|
||||||
|
return localPing;
|
||||||
|
}
|
||||||
|
uint16_t getOTCv8Version() const
|
||||||
|
{
|
||||||
|
if (client)
|
||||||
|
return client->otclientV8;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void onThink(uint32_t interval) final;
|
void onThink(uint32_t interval) final;
|
||||||
|
|
||||||
void postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t link = LINK_OWNER) final;
|
void postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t link = LINK_OWNER) final;
|
||||||
@ -1025,6 +1095,10 @@ class Player final : public Creature, public Cylinder
|
|||||||
void learnInstantSpell(const std::string& spellName);
|
void learnInstantSpell(const std::string& spellName);
|
||||||
void forgetInstantSpell(const std::string& spellName);
|
void forgetInstantSpell(const std::string& spellName);
|
||||||
bool hasLearnedInstantSpell(const std::string& spellName) const;
|
bool hasLearnedInstantSpell(const std::string& spellName) const;
|
||||||
|
const std::map<uint8_t, OpenContainer>& getOpenContainers() const
|
||||||
|
{
|
||||||
|
return openContainers;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::forward_list<Condition*> getMuteConditions() const;
|
std::forward_list<Condition*> getMuteConditions() const;
|
||||||
@ -1115,6 +1189,7 @@ class Player final : public Creature, public Cylinder
|
|||||||
int64_t lastPong;
|
int64_t lastPong;
|
||||||
int64_t nextAction = 0;
|
int64_t nextAction = 0;
|
||||||
int64_t earliestAttackTime = 0;
|
int64_t earliestAttackTime = 0;
|
||||||
|
int64_t lastToggleMount = 0;
|
||||||
|
|
||||||
BedItem* bedItem = nullptr;
|
BedItem* bedItem = nullptr;
|
||||||
Guild* guild = nullptr;
|
Guild* guild = nullptr;
|
||||||
@ -1162,6 +1237,8 @@ class Player final : public Creature, public Cylinder
|
|||||||
|
|
||||||
uint16_t staminaMinutes = 3360;
|
uint16_t staminaMinutes = 3360;
|
||||||
uint16_t maxWriteLen = 0;
|
uint16_t maxWriteLen = 0;
|
||||||
|
uint16_t localPing = 0;
|
||||||
|
uint16_t fps = 0;
|
||||||
int16_t lastDepotId = -1;
|
int16_t lastDepotId = -1;
|
||||||
|
|
||||||
uint8_t soul = 0;
|
uint8_t soul = 0;
|
||||||
@ -1179,6 +1256,7 @@ class Player final : public Creature, public Cylinder
|
|||||||
|
|
||||||
bool secureMode = false;
|
bool secureMode = false;
|
||||||
bool inMarket = false;
|
bool inMarket = false;
|
||||||
|
bool wasMounted = false;
|
||||||
bool ghostMode = false;
|
bool ghostMode = false;
|
||||||
bool pzLocked = false;
|
bool pzLocked = false;
|
||||||
bool isConnecting = false;
|
bool isConnecting = false;
|
||||||
|
@ -25,14 +25,27 @@
|
|||||||
|
|
||||||
extern RSA g_RSA;
|
extern RSA g_RSA;
|
||||||
|
|
||||||
|
Protocol::~Protocol()
|
||||||
|
{
|
||||||
|
if (compression) {
|
||||||
|
deflateEnd(&zstream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Protocol::onSendMessage(const OutputMessage_ptr& msg) const
|
void Protocol::onSendMessage(const OutputMessage_ptr& msg) const
|
||||||
{
|
{
|
||||||
if (!rawMessages) {
|
if (!rawMessages) {
|
||||||
|
bool compressed = false;
|
||||||
|
if (compression && msg->getLength() > 64) {
|
||||||
|
compress(*msg);
|
||||||
|
compressed = true;
|
||||||
|
}
|
||||||
|
|
||||||
msg->writeMessageLength();
|
msg->writeMessageLength();
|
||||||
|
|
||||||
if (encryptionEnabled) {
|
if (encryptionEnabled) {
|
||||||
XTEA_encrypt(*msg);
|
XTEA_encrypt(*msg);
|
||||||
msg->addCryptoHeader();
|
msg->addCryptoHeader(checksumEnabled, compressed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -153,3 +166,34 @@ uint32_t Protocol::getIP() const
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Protocol::enableCompression()
|
||||||
|
{
|
||||||
|
if (compression)
|
||||||
|
return;
|
||||||
|
if (deflateInit2(&zstream, 6, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY) != Z_OK) {
|
||||||
|
std::cerr << "ZLIB initialization error: " << (zstream.msg ? zstream.msg : "unknown") << std::endl;
|
||||||
|
}
|
||||||
|
compression = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Protocol::compress(OutputMessage& msg) const
|
||||||
|
{
|
||||||
|
static thread_local std::vector<uint8_t> buffer(NETWORKMESSAGE_MAXSIZE);
|
||||||
|
zstream.next_in = msg.getOutputBuffer();
|
||||||
|
zstream.avail_in = msg.getLength();
|
||||||
|
zstream.next_out = buffer.data();
|
||||||
|
zstream.avail_out = buffer.size();
|
||||||
|
if (deflate(&zstream, Z_SYNC_FLUSH) != Z_OK) {
|
||||||
|
std::cerr << "ZLIB deflate error: " << (zstream.msg ? zstream.msg : "unknown") << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int finalSize = buffer.size() - zstream.avail_out - 4;
|
||||||
|
if (finalSize < 0) {
|
||||||
|
std::cerr << "Packet compression error: " << (zstream.msg ? zstream.msg : "unknown") << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.reset();
|
||||||
|
msg.addBytes((const char*)buffer.data(), finalSize);
|
||||||
|
}
|
@ -21,12 +21,13 @@
|
|||||||
#define FS_PROTOCOL_H_D71405071ACF4137A4B1203899DE80E1
|
#define FS_PROTOCOL_H_D71405071ACF4137A4B1203899DE80E1
|
||||||
|
|
||||||
#include "connection.h"
|
#include "connection.h"
|
||||||
|
#include <zlib.h>
|
||||||
|
|
||||||
class Protocol : public std::enable_shared_from_this<Protocol>
|
class Protocol : public std::enable_shared_from_this<Protocol>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit Protocol(Connection_ptr connection) : connection(connection), key(), encryptionEnabled(false), rawMessages(false) {}
|
explicit Protocol(Connection_ptr connection) : connection(connection), key(), encryptionEnabled(false), rawMessages(false) {}
|
||||||
virtual ~Protocol() = default;
|
virtual ~Protocol();
|
||||||
|
|
||||||
// non-copyable
|
// non-copyable
|
||||||
Protocol(const Protocol&) = delete;
|
Protocol(const Protocol&) = delete;
|
||||||
@ -74,6 +75,9 @@ protected:
|
|||||||
void setXTEAKey(const uint32_t* key) {
|
void setXTEAKey(const uint32_t* key) {
|
||||||
memcpy(this->key, key, sizeof(*key) * 4);
|
memcpy(this->key, key, sizeof(*key) * 4);
|
||||||
}
|
}
|
||||||
|
void disableChecksum() {
|
||||||
|
checksumEnabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
void XTEA_encrypt(OutputMessage& msg) const;
|
void XTEA_encrypt(OutputMessage& msg) const;
|
||||||
bool XTEA_decrypt(NetworkMessage& msg) const;
|
bool XTEA_decrypt(NetworkMessage& msg) const;
|
||||||
@ -82,16 +86,20 @@ protected:
|
|||||||
void setRawMessages(bool value) {
|
void setRawMessages(bool value) {
|
||||||
rawMessages = value;
|
rawMessages = value;
|
||||||
}
|
}
|
||||||
|
void enableCompression();
|
||||||
virtual void release() {}
|
virtual void release() {}
|
||||||
friend class Connection;
|
friend class Connection;
|
||||||
|
|
||||||
OutputMessage_ptr outputBuffer;
|
OutputMessage_ptr outputBuffer;
|
||||||
private:
|
private:
|
||||||
|
void compress(OutputMessage& msg) const;
|
||||||
const ConnectionWeak_ptr connection;
|
const ConnectionWeak_ptr connection;
|
||||||
uint32_t key[4];
|
uint32_t key[4];
|
||||||
bool encryptionEnabled;
|
bool encryptionEnabled = false;
|
||||||
bool rawMessages;
|
bool checksumEnabled = true;
|
||||||
|
bool rawMessages = false;
|
||||||
|
bool compression = false;
|
||||||
|
mutable z_stream zstream = { 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -56,6 +56,17 @@ void ProtocolGame::release()
|
|||||||
|
|
||||||
void ProtocolGame::login(const std::string& name, uint32_t accountId, OperatingSystem_t operatingSystem, bool isFake)
|
void ProtocolGame::login(const std::string& name, uint32_t accountId, OperatingSystem_t operatingSystem, bool isFake)
|
||||||
{
|
{
|
||||||
|
// OTCv8 features and extended opcodes
|
||||||
|
if (otclientV8 || operatingSystem >= CLIENTOS_OTCLIENT_LINUX) {
|
||||||
|
if (otclientV8)
|
||||||
|
sendFeatures();
|
||||||
|
NetworkMessage opcodeMessage;
|
||||||
|
opcodeMessage.addByte(0x32);
|
||||||
|
opcodeMessage.addByte(0x00);
|
||||||
|
opcodeMessage.add<uint16_t>(0x00);
|
||||||
|
writeToOutputBuffer(opcodeMessage);
|
||||||
|
}
|
||||||
|
|
||||||
//dispatcher thread
|
//dispatcher thread
|
||||||
Player* foundPlayer = g_game.getPlayerByName(name);
|
Player* foundPlayer = g_game.getPlayerByName(name);
|
||||||
if (!foundPlayer || g_config.getBoolean(ConfigManager::ALLOW_CLONES)) {
|
if (!foundPlayer || g_config.getBoolean(ConfigManager::ALLOW_CLONES)) {
|
||||||
@ -145,6 +156,8 @@ void ProtocolGame::login(const std::string& name, uint32_t accountId, OperatingS
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
player->autoOpenContainers();
|
||||||
|
|
||||||
if (operatingSystem >= CLIENTOS_OTCLIENT_LINUX) {
|
if (operatingSystem >= CLIENTOS_OTCLIENT_LINUX) {
|
||||||
player->registerCreatureEvent("ExtendedOpcode");
|
player->registerCreatureEvent("ExtendedOpcode");
|
||||||
}
|
}
|
||||||
@ -197,6 +210,7 @@ void ProtocolGame::connect(uint32_t playerId, OperatingSystem_t operatingSystem)
|
|||||||
|
|
||||||
player->client = getThis();
|
player->client = getThis();
|
||||||
sendAddCreature(player, player->getPosition(), 0, false);
|
sendAddCreature(player, player->getPosition(), 0, false);
|
||||||
|
player->autoOpenContainers();
|
||||||
player->lastIP = player->getIP();
|
player->lastIP = player->getIP();
|
||||||
player->lastLoginSaved = std::max<time_t>(time(nullptr), player->lastLoginSaved + 1);
|
player->lastLoginSaved = std::max<time_t>(time(nullptr), player->lastLoginSaved + 1);
|
||||||
acceptPackets = true;
|
acceptPackets = true;
|
||||||
@ -263,20 +277,18 @@ void ProtocolGame::onRecvFirstMessage(NetworkMessage& msg)
|
|||||||
enableXTEAEncryption();
|
enableXTEAEncryption();
|
||||||
setXTEAKey(key);
|
setXTEAKey(key);
|
||||||
|
|
||||||
if (operatingSystem >= CLIENTOS_OTCLIENT_LINUX) {
|
|
||||||
NetworkMessage opcodeMessage;
|
|
||||||
opcodeMessage.addByte(0x32);
|
|
||||||
opcodeMessage.addByte(0x00);
|
|
||||||
opcodeMessage.add<uint16_t>(0x00);
|
|
||||||
writeToOutputBuffer(opcodeMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
msg.skipBytes(1); // gamemaster flag
|
msg.skipBytes(1); // gamemaster flag
|
||||||
|
|
||||||
uint32_t accountNumber = msg.get<uint32_t>();
|
uint32_t accountNumber = msg.get<uint32_t>();
|
||||||
std::string characterName = msg.getString();
|
std::string characterName = msg.getString();
|
||||||
std::string password = msg.getString();
|
std::string password = msg.getString();
|
||||||
|
|
||||||
|
// OTCv8 version detection
|
||||||
|
uint16_t otcV8StringLength = msg.get<uint16_t>();
|
||||||
|
if (otcV8StringLength == 5 && msg.getString(5) == "OTCv8") {
|
||||||
|
otclientV8 = msg.get<uint16_t>(); // 253, 260, 261, ...
|
||||||
|
}
|
||||||
|
|
||||||
/*if (version < CLIENT_VERSION_MIN || version > CLIENT_VERSION_MAX) {
|
/*if (version < CLIENT_VERSION_MIN || version > CLIENT_VERSION_MAX) {
|
||||||
//sendUpdateRequest();
|
//sendUpdateRequest();
|
||||||
disconnectClient("Use Tibia 7.72 to login!");
|
disconnectClient("Use Tibia 7.72 to login!");
|
||||||
@ -338,8 +350,31 @@ void ProtocolGame::onRecvFirstMessage(NetworkMessage& msg)
|
|||||||
|
|
||||||
void ProtocolGame::onConnect()
|
void ProtocolGame::onConnect()
|
||||||
{
|
{
|
||||||
|
// Checksum missimplementation
|
||||||
|
/* auto output = OutputMessagePool::getOutputMessage();
|
||||||
|
static std::random_device rd;
|
||||||
|
static std::ranlux24 generator(rd());
|
||||||
|
static std::uniform_int_distribution<uint16_t> randNumber(0x00, 0xFF);
|
||||||
|
|
||||||
|
// Skip checksum
|
||||||
|
output->skipBytes(sizeof(uint32_t));
|
||||||
|
|
||||||
|
// Packet length & type
|
||||||
|
output->add<uint16_t>(0x0006);
|
||||||
|
output->addByte(0x1F);
|
||||||
|
|
||||||
|
// Add timestamp & random number
|
||||||
|
challengeTimestamp = static_cast<uint32_t>(time(nullptr));
|
||||||
|
output->add<uint32_t>(challengeTimestamp);
|
||||||
|
|
||||||
|
challengeRandom = randNumber(generator);
|
||||||
|
output->addByte(challengeRandom);
|
||||||
|
|
||||||
|
// Go back and write checksum
|
||||||
|
output->skipBytes(-12);
|
||||||
|
output->add<uint32_t>(adlerChecksum(output->getOutputBuffer() + sizeof(uint32_t), 8));
|
||||||
|
|
||||||
|
send(output); */
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProtocolGame::disconnectClient(const std::string& message) const
|
void ProtocolGame::disconnectClient(const std::string& message) const
|
||||||
@ -390,6 +425,7 @@ void ProtocolGame::parsePacket(NetworkMessage& msg)
|
|||||||
case 0x1D: addGameTask(&Game::playerReceivePingBack, player->getID()); break;
|
case 0x1D: addGameTask(&Game::playerReceivePingBack, player->getID()); break;
|
||||||
case 0x1E: addGameTask(&Game::playerReceivePing, player->getID()); break;
|
case 0x1E: addGameTask(&Game::playerReceivePing, player->getID()); break;
|
||||||
case 0x32: parseExtendedOpcode(msg); break; //otclient extended opcode
|
case 0x32: parseExtendedOpcode(msg); break; //otclient extended opcode
|
||||||
|
case 0x40: parseNewPing(msg); break;
|
||||||
case 0x64: parseAutoWalk(msg); break;
|
case 0x64: parseAutoWalk(msg); break;
|
||||||
case 0x65: addGameTask(&Game::playerMove, player->getID(), DIRECTION_NORTH); break;
|
case 0x65: addGameTask(&Game::playerMove, player->getID(), DIRECTION_NORTH); break;
|
||||||
case 0x66: addGameTask(&Game::playerMove, player->getID(), DIRECTION_EAST); break;
|
case 0x66: addGameTask(&Game::playerMove, player->getID(), DIRECTION_EAST); break;
|
||||||
@ -445,6 +481,7 @@ void ProtocolGame::parsePacket(NetworkMessage& msg)
|
|||||||
case 0xCC: parseSeekInContainer(msg); break;
|
case 0xCC: parseSeekInContainer(msg); break;
|
||||||
case 0xD2: addGameTask(&Game::playerRequestOutfit, player->getID()); break;
|
case 0xD2: addGameTask(&Game::playerRequestOutfit, player->getID()); break;
|
||||||
case 0xD3: parseSetOutfit(msg); break;
|
case 0xD3: parseSetOutfit(msg); break;
|
||||||
|
case 0xD4: parseToggleMount(msg); break;
|
||||||
case 0xDC: parseAddVip(msg); break;
|
case 0xDC: parseAddVip(msg); break;
|
||||||
case 0xDD: parseRemoveVip(msg); break;
|
case 0xDD: parseRemoveVip(msg); break;
|
||||||
case 0xE6: parseBugReport(msg); break;
|
case 0xE6: parseBugReport(msg); break;
|
||||||
@ -735,9 +772,27 @@ void ProtocolGame::parseSetOutfit(NetworkMessage& msg)
|
|||||||
newOutfit.lookLegs = msg.getByte();
|
newOutfit.lookLegs = msg.getByte();
|
||||||
newOutfit.lookFeet = msg.getByte();
|
newOutfit.lookFeet = msg.getByte();
|
||||||
newOutfit.lookAddons = msg.getByte();
|
newOutfit.lookAddons = msg.getByte();
|
||||||
|
newOutfit.lookMount = msg.get<uint16_t>();
|
||||||
|
newOutfit.lookWings = otclientV8 ? msg.get<uint16_t>() : 0;
|
||||||
|
newOutfit.lookAura = otclientV8 ? msg.get<uint16_t>() : 0;
|
||||||
|
std::string shaderName = otclientV8 ? msg.getString() : "";
|
||||||
|
Shader* shader = g_game.shaders.getShaderByName(shaderName);
|
||||||
|
newOutfit.lookShader = shader ? shader->id : 0;
|
||||||
addGameTask(&Game::playerChangeOutfit, player->getID(), newOutfit);
|
addGameTask(&Game::playerChangeOutfit, player->getID(), newOutfit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ProtocolGame::parseToggleMount(NetworkMessage& msg)
|
||||||
|
{
|
||||||
|
int mount = msg.get<int8_t>();
|
||||||
|
int wings = -1, aura = -1, shader = -1;
|
||||||
|
if (otclientV8 >= 254) {
|
||||||
|
wings = msg.get<int8_t>();
|
||||||
|
aura = msg.get<int8_t>();
|
||||||
|
shader = msg.get<int8_t>();
|
||||||
|
}
|
||||||
|
addGameTask(&Game::playerToggleOutfitExtension, player->getID(), mount, wings, aura, shader);
|
||||||
|
}
|
||||||
|
|
||||||
void ProtocolGame::parseUseItem(NetworkMessage& msg)
|
void ProtocolGame::parseUseItem(NetworkMessage& msg)
|
||||||
{
|
{
|
||||||
Position pos = msg.getPosition();
|
Position pos = msg.getPosition();
|
||||||
@ -2127,9 +2182,13 @@ void ProtocolGame::sendOutfitWindow()
|
|||||||
msg.addByte(0xC8);
|
msg.addByte(0xC8);
|
||||||
|
|
||||||
Outfit_t currentOutfit = player->getDefaultOutfit();
|
Outfit_t currentOutfit = player->getDefaultOutfit();
|
||||||
|
Mount* currentMount = g_game.mounts.getMountByID(player->getCurrentMount());
|
||||||
|
if (currentMount) {
|
||||||
|
currentOutfit.lookMount = currentMount->clientId;
|
||||||
|
}
|
||||||
|
|
||||||
AddOutfit(msg, currentOutfit);
|
AddOutfit(msg, currentOutfit);
|
||||||
|
|
||||||
const ClientVersion_t clientVersion = g_game.getClientVersion();
|
|
||||||
std::vector<ProtocolOutfit> protocolOutfits;
|
std::vector<ProtocolOutfit> protocolOutfits;
|
||||||
if (player->isAccessPlayer()) {
|
if (player->isAccessPlayer()) {
|
||||||
static const std::string gamemasterOutfitName = "Gamemaster";
|
static const std::string gamemasterOutfitName = "Gamemaster";
|
||||||
@ -2145,22 +2204,72 @@ void ProtocolGame::sendOutfitWindow()
|
|||||||
}
|
}
|
||||||
|
|
||||||
protocolOutfits.emplace_back(outfit.name, outfit.lookType, addons);
|
protocolOutfits.emplace_back(outfit.name, outfit.lookType, addons);
|
||||||
if (CLIENT_VERSION_780 <= clientVersion && clientVersion <= CLIENT_VERSION_792) {
|
if (protocolOutfits.size() == 100) { // Game client doesn't allow more than 100 outfits
|
||||||
if (protocolOutfits.size() == 20) { // Game client doesn't allow more than 15 outfits in 780-792
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
msg.addByte(protocolOutfits.size());
|
msg.addByte(protocolOutfits.size());
|
||||||
for (const ProtocolOutfit& outfit : protocolOutfits) {
|
for (const ProtocolOutfit& outfit : protocolOutfits) {
|
||||||
msg.add<uint16_t>(outfit.lookType);
|
msg.add<uint16_t>(outfit.lookType);
|
||||||
if (clientVersion > CLIENT_VERSION_781) {
|
msg.addString(outfit.name);
|
||||||
msg.addString(outfit.name);
|
|
||||||
}
|
|
||||||
msg.addByte(outfit.addons);
|
msg.addByte(outfit.addons);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<const Mount*> mounts;
|
||||||
|
for (const Mount& mount : g_game.mounts.getMounts()) {
|
||||||
|
if (player->hasMount(&mount)) {
|
||||||
|
mounts.push_back(&mount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.addByte(mounts.size());
|
||||||
|
for (const Mount* mount : mounts) {
|
||||||
|
msg.add<uint16_t>(mount->clientId);
|
||||||
|
msg.addString(mount->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (otclientV8) {
|
||||||
|
std::vector<const Wing*> wings;
|
||||||
|
for (const Wing& wing : g_game.wings.getWings()) {
|
||||||
|
if (player->hasWing(&wing)) {
|
||||||
|
wings.push_back(&wing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.addByte(wings.size());
|
||||||
|
for (const Wing* wing : wings) {
|
||||||
|
msg.add<uint16_t>(wing->clientId);
|
||||||
|
msg.addString(wing->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<const Aura*> auras;
|
||||||
|
for (const Aura& aura : g_game.auras.getAuras()) {
|
||||||
|
if (player->hasAura(&aura)) {
|
||||||
|
auras.push_back(&aura);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.addByte(auras.size());
|
||||||
|
for (const Aura* aura : auras) {
|
||||||
|
msg.add<uint16_t>(aura->clientId);
|
||||||
|
msg.addString(aura->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<const Shader*> shaders;
|
||||||
|
for (const Shader& shader : g_game.shaders.getShaders()) {
|
||||||
|
if (player->hasShader(&shader)) {
|
||||||
|
shaders.push_back(&shader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.addByte(shaders.size());
|
||||||
|
for (const Shader* shader : shaders) {
|
||||||
|
msg.add<uint16_t>(shader->id);
|
||||||
|
msg.addString(shader->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
writeToOutputBuffer(msg);
|
writeToOutputBuffer(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2284,9 +2393,18 @@ void ProtocolGame::AddOutfit(NetworkMessage& msg, const Outfit_t& outfit)
|
|||||||
msg.addByte(outfit.lookLegs);
|
msg.addByte(outfit.lookLegs);
|
||||||
msg.addByte(outfit.lookFeet);
|
msg.addByte(outfit.lookFeet);
|
||||||
msg.addByte(outfit.lookAddons);
|
msg.addByte(outfit.lookAddons);
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
msg.addItemId(outfit.lookTypeEx);
|
msg.addItemId(outfit.lookTypeEx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
msg.add<uint16_t>(outfit.lookMount);
|
||||||
|
if (otclientV8) {
|
||||||
|
msg.add<uint16_t>(outfit.lookWings);
|
||||||
|
msg.add<uint16_t>(outfit.lookAura);
|
||||||
|
Shader* shader = g_game.shaders.getShaderByID(outfit.lookShader);
|
||||||
|
msg.addString(shader ? shader->name : "");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProtocolGame::AddWorldLight(NetworkMessage& msg, const LightInfo& lightInfo)
|
void ProtocolGame::AddWorldLight(NetworkMessage& msg, const LightInfo& lightInfo)
|
||||||
@ -2413,4 +2531,76 @@ void ProtocolGame::parseExtendedOpcode(NetworkMessage& msg)
|
|||||||
|
|
||||||
// process additional opcodes via lua script event
|
// process additional opcodes via lua script event
|
||||||
addGameTask(&Game::parsePlayerExtendedOpcode, player->getID(), opcode, buffer);
|
addGameTask(&Game::parsePlayerExtendedOpcode, player->getID(), opcode, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// OTCv8
|
||||||
|
void ProtocolGame::sendFeatures()
|
||||||
|
{
|
||||||
|
if (!otclientV8)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::map<GameFeature, bool> features;
|
||||||
|
// place for non-standard OTCv8 features
|
||||||
|
//features[GameExtendedOpcode] = true;
|
||||||
|
//features[GameChangeMapAwareRange] = true;
|
||||||
|
//features[GameNewWalking] = true;
|
||||||
|
//features[GameEnvironmentEffect] = false; // disable it, useless 2 byte with every tile
|
||||||
|
//features[GameExtendedClientPing] = true;
|
||||||
|
//features[GameItemTooltip] = true; // fully available from version 2.6
|
||||||
|
//features[GameWingsAndAura] = true;
|
||||||
|
//features[GameOutfitShaders] = true;
|
||||||
|
|
||||||
|
// packet compression
|
||||||
|
// we don't send feature, because feature assumes all packets are compressed
|
||||||
|
// if adler32 is enabled then compression can be detected automaticly, just adlre32 must be 0
|
||||||
|
if (g_config.getBoolean(ConfigManager::PACKET_COMPRESSION)) {
|
||||||
|
enableCompression();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (features.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto msg = getOutputBuffer(1024);
|
||||||
|
msg->addByte(0x43);
|
||||||
|
msg->add<uint16_t>(features.size());
|
||||||
|
for (auto& feature : features) {
|
||||||
|
msg->addByte((uint8_t)feature.first);
|
||||||
|
msg->addByte(feature.second ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
send(std::move(getCurrentBuffer())); // send this packet immediately
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProtocolGame::parseNewPing(NetworkMessage& msg)
|
||||||
|
{
|
||||||
|
uint32_t pingId = msg.get<uint32_t>();
|
||||||
|
uint16_t localPing = msg.get<uint16_t>();
|
||||||
|
uint16_t fps = msg.get<uint16_t>();
|
||||||
|
|
||||||
|
addGameTask(&Game::playerReceiveNewPing, player->getID(), localPing, fps);
|
||||||
|
g_dispatcher.addTask(createTask(std::bind(&ProtocolGame::sendNewPing, getThis(), pingId)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProtocolGame::sendNewPing(uint32_t pingId)
|
||||||
|
{
|
||||||
|
if (!otclientV8)
|
||||||
|
return;
|
||||||
|
|
||||||
|
NetworkMessage msg;
|
||||||
|
msg.addByte(0x40);
|
||||||
|
msg.add<uint32_t>(pingId);
|
||||||
|
writeToOutputBuffer(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProtocolGame::sendProgressbar(uint32_t id, uint32_t duration, bool ltr)
|
||||||
|
{
|
||||||
|
if (!otclientV8 || otclientV8 < 260)
|
||||||
|
return;
|
||||||
|
|
||||||
|
NetworkMessage msg;
|
||||||
|
msg.addByte(0x3b);
|
||||||
|
msg.add<uint32_t>(id);
|
||||||
|
msg.add<uint32_t>(duration);
|
||||||
|
msg.addByte(ltr);
|
||||||
|
writeToOutputBuffer(msg);
|
||||||
}
|
}
|
@ -52,7 +52,8 @@ class ProtocolGame final : public Protocol
|
|||||||
// static protocol information
|
// static protocol information
|
||||||
enum { server_sends_first = true };
|
enum { server_sends_first = true };
|
||||||
enum { protocol_identifier = 0 }; // Not required as we send first
|
enum { protocol_identifier = 0 }; // Not required as we send first
|
||||||
|
enum { use_checksum = true };
|
||||||
|
|
||||||
static const char* protocol_name() {
|
static const char* protocol_name() {
|
||||||
return "gameworld protocol";
|
return "gameworld protocol";
|
||||||
}
|
}
|
||||||
@ -90,6 +91,7 @@ class ProtocolGame final : public Protocol
|
|||||||
//Parse methods
|
//Parse methods
|
||||||
void parseAutoWalk(NetworkMessage& msg);
|
void parseAutoWalk(NetworkMessage& msg);
|
||||||
void parseSetOutfit(NetworkMessage& msg);
|
void parseSetOutfit(NetworkMessage& msg);
|
||||||
|
void parseToggleMount(NetworkMessage& msg);
|
||||||
void parseSay(NetworkMessage& msg);
|
void parseSay(NetworkMessage& msg);
|
||||||
void parseLookAt(NetworkMessage& msg);
|
void parseLookAt(NetworkMessage& msg);
|
||||||
void parseLookInBattleList(NetworkMessage& msg);
|
void parseLookInBattleList(NetworkMessage& msg);
|
||||||
@ -265,6 +267,12 @@ class ProtocolGame final : public Protocol
|
|||||||
//otclient
|
//otclient
|
||||||
void parseExtendedOpcode(NetworkMessage& msg);
|
void parseExtendedOpcode(NetworkMessage& msg);
|
||||||
|
|
||||||
|
//OTCv8
|
||||||
|
void sendFeatures();
|
||||||
|
void sendProgressbar(uint32_t id, uint32_t duration, bool ltr = true);
|
||||||
|
void parseNewPing(NetworkMessage& msg);
|
||||||
|
void sendNewPing(uint32_t pingId);
|
||||||
|
|
||||||
friend class Player;
|
friend class Player;
|
||||||
|
|
||||||
// Helpers so we don't need to bind every time
|
// Helpers so we don't need to bind every time
|
||||||
@ -289,6 +297,7 @@ class ProtocolGame final : public Protocol
|
|||||||
|
|
||||||
bool debugAssertSent = false;
|
bool debugAssertSent = false;
|
||||||
bool acceptPackets = false;
|
bool acceptPackets = false;
|
||||||
|
uint16_t otclientV8 = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -193,6 +193,13 @@ void ProtocolLogin::onRecvFirstMessage(NetworkMessage& msg)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OTCv8 version detection
|
||||||
|
uint16_t otclientV8 = 0;
|
||||||
|
uint16_t otcV8StringLength = msg.get<uint16_t>();
|
||||||
|
if (otcV8StringLength == 5 && msg.getString(5) == "OTCv8") {
|
||||||
|
otclientV8 = msg.get<uint16_t>(); // 253, 260, 261, ...
|
||||||
|
}
|
||||||
|
|
||||||
auto thisPtr = std::static_pointer_cast<ProtocolLogin>(shared_from_this());
|
auto thisPtr = std::static_pointer_cast<ProtocolLogin>(shared_from_this());
|
||||||
g_dispatcher.addTask(createTask(std::bind(&ProtocolLogin::getCharacterList, thisPtr, accountNumber, password, version)));
|
g_dispatcher.addTask(createTask(std::bind(&ProtocolLogin::getCharacterList, thisPtr, accountNumber, password, version)));
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,7 @@ class ProtocolLogin : public Protocol
|
|||||||
// static protocol information
|
// static protocol information
|
||||||
enum {server_sends_first = false};
|
enum {server_sends_first = false};
|
||||||
enum {protocol_identifier = 0x01};
|
enum {protocol_identifier = 0x01};
|
||||||
|
enum { use_checksum = true };
|
||||||
static const char* protocol_name() {
|
static const char* protocol_name() {
|
||||||
return "login protocol";
|
return "login protocol";
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@ class ProtocolStatus final : public Protocol
|
|||||||
// static protocol information
|
// static protocol information
|
||||||
enum {server_sends_first = false};
|
enum {server_sends_first = false};
|
||||||
enum {protocol_identifier = 0xFF};
|
enum {protocol_identifier = 0xFF};
|
||||||
|
enum { use_checksum = false };
|
||||||
static const char* protocol_name() {
|
static const char* protocol_name() {
|
||||||
return "status protocol";
|
return "status protocol";
|
||||||
}
|
}
|
||||||
|
52
src/shaders.cpp
Normal file
52
src/shaders.cpp
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
#include "otpch.h"
|
||||||
|
|
||||||
|
#include "shaders.h"
|
||||||
|
|
||||||
|
#include "pugicast.h"
|
||||||
|
#include "tools.h"
|
||||||
|
|
||||||
|
bool Shaders::reload()
|
||||||
|
{
|
||||||
|
shaders.clear();
|
||||||
|
return loadFromXml();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Shaders::loadFromXml()
|
||||||
|
{
|
||||||
|
pugi::xml_document doc;
|
||||||
|
pugi::xml_parse_result result = doc.load_file("data/XML/shaders.xml");
|
||||||
|
if (!result) {
|
||||||
|
printXMLError("Error - Shaders::loadFromXml", "data/XML/shaders.xml", result);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto shaderNode : doc.child("shaders").children()) {
|
||||||
|
shaders.emplace_back(
|
||||||
|
static_cast<uint8_t>(pugi::cast<uint16_t>(shaderNode.attribute("id").value())),
|
||||||
|
shaderNode.attribute("name").as_string(),
|
||||||
|
shaderNode.attribute("premium").as_bool()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
shaders.shrink_to_fit();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Shader* Shaders::getShaderByID(uint8_t id)
|
||||||
|
{
|
||||||
|
auto it = std::find_if(shaders.begin(), shaders.end(), [id](const Shader& shader) {
|
||||||
|
return shader.id == id;
|
||||||
|
});
|
||||||
|
|
||||||
|
return it != shaders.end() ? &*it : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Shader* Shaders::getShaderByName(const std::string& name) {
|
||||||
|
auto shaderName = name.c_str();
|
||||||
|
for (auto& it : shaders) {
|
||||||
|
if (strcasecmp(shaderName, it.name.c_str()) == 0) {
|
||||||
|
return ⁢
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
30
src/shaders.h
Normal file
30
src/shaders.h
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#ifndef FS_SHADERS_H
|
||||||
|
#define FS_SHADERS_H
|
||||||
|
|
||||||
|
struct Shader
|
||||||
|
{
|
||||||
|
Shader(uint8_t id, std::string name, bool premium) :
|
||||||
|
name(std::move(name)), id(id), premium(premium) {}
|
||||||
|
|
||||||
|
uint8_t id;
|
||||||
|
std::string name;
|
||||||
|
bool premium;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Shaders
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bool reload();
|
||||||
|
bool loadFromXml();
|
||||||
|
Shader* getShaderByID(uint8_t id);
|
||||||
|
Shader* getShaderByName(const std::string& name);
|
||||||
|
|
||||||
|
const std::vector<Shader>& getShaders() const {
|
||||||
|
return shaders;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<Shader> shaders;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
@ -890,6 +890,31 @@ std::string getSkillName(uint8_t skillid)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t adlerChecksum(const uint8_t* data, size_t length)
|
||||||
|
{
|
||||||
|
if (length > NETWORKMESSAGE_MAXSIZE) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint16_t adler = 65521;
|
||||||
|
|
||||||
|
uint32_t a = 1, b = 0;
|
||||||
|
|
||||||
|
while (length > 0) {
|
||||||
|
size_t tmp = length > 5552 ? 5552 : length;
|
||||||
|
length -= tmp;
|
||||||
|
|
||||||
|
do {
|
||||||
|
a += *data++;
|
||||||
|
b += a;
|
||||||
|
} while (--tmp);
|
||||||
|
|
||||||
|
a %= adler;
|
||||||
|
b %= adler;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (b << 16) | a;
|
||||||
|
}
|
||||||
|
|
||||||
std::string ucfirst(std::string str)
|
std::string ucfirst(std::string str)
|
||||||
{
|
{
|
||||||
|
@ -85,6 +85,8 @@ std::string getCombatName(CombatType_t combatType);
|
|||||||
|
|
||||||
std::string getSkillName(uint8_t skillid);
|
std::string getSkillName(uint8_t skillid);
|
||||||
|
|
||||||
|
uint32_t adlerChecksum(const uint8_t* data, size_t length);
|
||||||
|
|
||||||
std::string ucfirst(std::string str);
|
std::string ucfirst(std::string str);
|
||||||
std::string ucwords(std::string str);
|
std::string ucwords(std::string str);
|
||||||
bool booleanString(const std::string& str);
|
bool booleanString(const std::string& str);
|
||||||
|
63
src/wings.cpp
Normal file
63
src/wings.cpp
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
#include "otpch.h"
|
||||||
|
|
||||||
|
#include "wings.h"
|
||||||
|
|
||||||
|
#include "pugicast.h"
|
||||||
|
#include "tools.h"
|
||||||
|
|
||||||
|
bool Wings::reload()
|
||||||
|
{
|
||||||
|
wings.clear();
|
||||||
|
return loadFromXml();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Wings::loadFromXml()
|
||||||
|
{
|
||||||
|
pugi::xml_document doc;
|
||||||
|
pugi::xml_parse_result result = doc.load_file("data/XML/wings.xml");
|
||||||
|
if (!result) {
|
||||||
|
printXMLError("Error - Wings::loadFromXml", "data/XML/wings.xml", result);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto wingNode : doc.child("wings").children()) {
|
||||||
|
wings.emplace_back(
|
||||||
|
static_cast<uint8_t>(pugi::cast<uint16_t>(wingNode.attribute("id").value())),
|
||||||
|
pugi::cast<uint16_t>(wingNode.attribute("clientid").value()),
|
||||||
|
wingNode.attribute("name").as_string(),
|
||||||
|
pugi::cast<int32_t>(wingNode.attribute("speed").value()),
|
||||||
|
wingNode.attribute("premium").as_bool()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
wings.shrink_to_fit();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Wing* Wings::getWingByID(uint8_t id)
|
||||||
|
{
|
||||||
|
auto it = std::find_if(wings.begin(), wings.end(), [id](const Wing& wing) {
|
||||||
|
return wing.id == id;
|
||||||
|
});
|
||||||
|
|
||||||
|
return it != wings.end() ? &*it : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Wing* Wings::getWingByName(const std::string& name) {
|
||||||
|
auto wingName = name.c_str();
|
||||||
|
for (auto& it : wings) {
|
||||||
|
if (strcasecmp(wingName, it.name.c_str()) == 0) {
|
||||||
|
return ⁢
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Wing* Wings::getWingByClientID(uint16_t clientId)
|
||||||
|
{
|
||||||
|
auto it = std::find_if(wings.begin(), wings.end(), [clientId](const Wing& wing) {
|
||||||
|
return wing.clientId == clientId;
|
||||||
|
});
|
||||||
|
|
||||||
|
return it != wings.end() ? &*it : nullptr;
|
||||||
|
}
|
33
src/wings.h
Normal file
33
src/wings.h
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#ifndef FS_WINGS_H
|
||||||
|
#define FS_WINGS_H
|
||||||
|
|
||||||
|
struct Wing
|
||||||
|
{
|
||||||
|
Wing(uint8_t id, uint16_t clientId, std::string name, int32_t speed, bool premium) :
|
||||||
|
name(std::move(name)), speed(speed), clientId(clientId), id(id), premium(premium) {}
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
int32_t speed;
|
||||||
|
uint16_t clientId;
|
||||||
|
uint8_t id;
|
||||||
|
bool premium;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Wings
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bool reload();
|
||||||
|
bool loadFromXml();
|
||||||
|
Wing* getWingByID(uint8_t id);
|
||||||
|
Wing* getWingByName(const std::string& name);
|
||||||
|
Wing* getWingByClientID(uint16_t clientId);
|
||||||
|
|
||||||
|
const std::vector<Wing>& getWings() const {
|
||||||
|
return wings;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<Wing> wings;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
@ -143,6 +143,7 @@
|
|||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="..\src\actions.cpp" />
|
<ClCompile Include="..\src\actions.cpp" />
|
||||||
|
<ClCompile Include="..\src\auras.cpp" />
|
||||||
<ClCompile Include="..\src\ban.cpp" />
|
<ClCompile Include="..\src\ban.cpp" />
|
||||||
<ClCompile Include="..\src\baseevents.cpp" />
|
<ClCompile Include="..\src\baseevents.cpp" />
|
||||||
<ClCompile Include="..\src\bed.cpp" />
|
<ClCompile Include="..\src\bed.cpp" />
|
||||||
@ -179,6 +180,7 @@
|
|||||||
<ClCompile Include="..\src\map.cpp" />
|
<ClCompile Include="..\src\map.cpp" />
|
||||||
<ClCompile Include="..\src\monster.cpp" />
|
<ClCompile Include="..\src\monster.cpp" />
|
||||||
<ClCompile Include="..\src\monsters.cpp" />
|
<ClCompile Include="..\src\monsters.cpp" />
|
||||||
|
<ClCompile Include="..\src\mounts.cpp" />
|
||||||
<ClCompile Include="..\src\movement.cpp" />
|
<ClCompile Include="..\src\movement.cpp" />
|
||||||
<ClCompile Include="..\src\networkmessage.cpp" />
|
<ClCompile Include="..\src\networkmessage.cpp" />
|
||||||
<ClCompile Include="..\src\npc.cpp" />
|
<ClCompile Include="..\src\npc.cpp" />
|
||||||
@ -204,6 +206,7 @@
|
|||||||
<ClCompile Include="..\src\script.cpp" />
|
<ClCompile Include="..\src\script.cpp" />
|
||||||
<ClCompile Include="..\src\scriptmanager.cpp" />
|
<ClCompile Include="..\src\scriptmanager.cpp" />
|
||||||
<ClCompile Include="..\src\server.cpp" />
|
<ClCompile Include="..\src\server.cpp" />
|
||||||
|
<ClCompile Include="..\src\shaders.cpp" />
|
||||||
<ClCompile Include="..\src\spawn.cpp" />
|
<ClCompile Include="..\src\spawn.cpp" />
|
||||||
<ClCompile Include="..\src\spells.cpp" />
|
<ClCompile Include="..\src\spells.cpp" />
|
||||||
<ClCompile Include="..\src\protocolstatus.cpp" />
|
<ClCompile Include="..\src\protocolstatus.cpp" />
|
||||||
@ -216,6 +219,7 @@
|
|||||||
<ClCompile Include="..\src\vocation.cpp" />
|
<ClCompile Include="..\src\vocation.cpp" />
|
||||||
<ClCompile Include="..\src\waitlist.cpp" />
|
<ClCompile Include="..\src\waitlist.cpp" />
|
||||||
<ClCompile Include="..\src\wildcardtree.cpp" />
|
<ClCompile Include="..\src\wildcardtree.cpp" />
|
||||||
|
<ClCompile Include="..\src\wings.cpp" />
|
||||||
<ClCompile Include="..\src\xtea.cpp" />
|
<ClCompile Include="..\src\xtea.cpp" />
|
||||||
<ClCompile Include="..\src\quests.cpp" />
|
<ClCompile Include="..\src\quests.cpp" />
|
||||||
<ClCompile Include="..\src\iomarket.cpp" />
|
<ClCompile Include="..\src\iomarket.cpp" />
|
||||||
@ -223,6 +227,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="..\src\account.h" />
|
<ClInclude Include="..\src\account.h" />
|
||||||
<ClInclude Include="..\src\actions.h" />
|
<ClInclude Include="..\src\actions.h" />
|
||||||
|
<ClInclude Include="..\src\auras.h" />
|
||||||
<ClInclude Include="..\src\ban.h" />
|
<ClInclude Include="..\src\ban.h" />
|
||||||
<ClInclude Include="..\src\baseevents.h" />
|
<ClInclude Include="..\src\baseevents.h" />
|
||||||
<ClInclude Include="..\src\bed.h" />
|
<ClInclude Include="..\src\bed.h" />
|
||||||
@ -263,6 +268,7 @@
|
|||||||
<ClInclude Include="..\src\map.h" />
|
<ClInclude Include="..\src\map.h" />
|
||||||
<ClInclude Include="..\src\monster.h" />
|
<ClInclude Include="..\src\monster.h" />
|
||||||
<ClInclude Include="..\src\monsters.h" />
|
<ClInclude Include="..\src\monsters.h" />
|
||||||
|
<ClInclude Include="..\src\mounts.h" />
|
||||||
<ClInclude Include="..\src\movement.h" />
|
<ClInclude Include="..\src\movement.h" />
|
||||||
<ClInclude Include="..\src\networkmessage.h" />
|
<ClInclude Include="..\src\networkmessage.h" />
|
||||||
<ClInclude Include="..\src\npc.h" />
|
<ClInclude Include="..\src\npc.h" />
|
||||||
@ -282,6 +288,7 @@
|
|||||||
<ClInclude Include="..\src\script.h" />
|
<ClInclude Include="..\src\script.h" />
|
||||||
<ClInclude Include="..\src\scriptmanager.h" />
|
<ClInclude Include="..\src\scriptmanager.h" />
|
||||||
<ClInclude Include="..\src\server.h" />
|
<ClInclude Include="..\src\server.h" />
|
||||||
|
<ClInclude Include="..\src\shaders.h" />
|
||||||
<ClInclude Include="..\src\spawn.h" />
|
<ClInclude Include="..\src\spawn.h" />
|
||||||
<ClInclude Include="..\src\spells.h" />
|
<ClInclude Include="..\src\spells.h" />
|
||||||
<ClInclude Include="..\src\protocolstatus.h" />
|
<ClInclude Include="..\src\protocolstatus.h" />
|
||||||
@ -296,6 +303,7 @@
|
|||||||
<ClInclude Include="..\src\vocation.h" />
|
<ClInclude Include="..\src\vocation.h" />
|
||||||
<ClInclude Include="..\src\waitlist.h" />
|
<ClInclude Include="..\src\waitlist.h" />
|
||||||
<ClInclude Include="..\src\wildcardtree.h" />
|
<ClInclude Include="..\src\wildcardtree.h" />
|
||||||
|
<ClInclude Include="..\src\wings.h" />
|
||||||
<ClInclude Include="..\src\xtea.h" />
|
<ClInclude Include="..\src\xtea.h" />
|
||||||
<ClInclude Include="..\src\quests.h" />
|
<ClInclude Include="..\src\quests.h" />
|
||||||
<ClInclude Include="..\src\iomarket.h" />
|
<ClInclude Include="..\src\iomarket.h" />
|
||||||
|
Loading…
x
Reference in New Issue
Block a user