diff --git a/.gitignore b/.gitignore index 00f3f1c8..400a9aba 100644 --- a/.gitignore +++ b/.gitignore @@ -113,4 +113,8 @@ src/common/Version\.cpp .mtime_cache # generated script loader files -src/servers/sapphire_zone/Script/Scripts/*/ScriptLoader.cpp \ No newline at end of file +src/servers/sapphire_zone/Script/Scripts/*/ScriptLoader.cpp + +# cotire generated files/folders +cotire/ +*_cotire.cmake \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index ca7af689..64baed19 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,6 +31,7 @@ set(LIBRARY_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src/libraries") include( "cmake/boost.cmake" ) include( "cmake/mysql.cmake" ) include( "cmake/compiler.cmake" ) +include( "cmake/cotire.cmake" ) ############################## # Git # @@ -68,3 +69,4 @@ add_subdirectory("src/tools/exd_struct_gen") add_subdirectory("src/tools/exd_struct_test") add_subdirectory("src/tools/quest_parser") #add_subdirectory("src/tools/pcb_reader") +#add_subdirectory("src/tools/event_object_parser") diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9f3f22ab..da0c7441 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -33,10 +33,10 @@ switch( commandId ) case 0x01: // Toggle sheathe { if ( param11 == 1 ) - pPlayer->setStance( Entity::Actor::Stance::Active ); + pPlayer->setStance( Entity::Chara::Stance::Active ); else { - pPlayer->setStance( Entity::Actor::Stance::Passive ); + pPlayer->setStance( Entity::Chara::Stance::Passive ); pPlayer->setAutoattack( false ); } diff --git a/cmake/boost.cmake b/cmake/boost.cmake index 4f3dda80..3e1c37ef 100644 --- a/cmake/boost.cmake +++ b/cmake/boost.cmake @@ -14,10 +14,10 @@ if(UNIX) endif() else() - if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/../src/libraries/external/${SAPPHIRE_BOOST_FOLDER_NAME}) + if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/src/libraries/external/${SAPPHIRE_BOOST_FOLDER_NAME}) message(STATUS "Using boost in /libraries/external") - set(Boost_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../src/libraries/external/${SAPPHIRE_BOOST_FOLDER_NAME}) - set(BOOST_LIBRARYDIR ${CMAKE_CURRENT_SOURCE_DIR}/../src/libraries/external/${SAPPHIRE_BOOST_FOLDER_NAME}/lib32-msvc-14.0) + set(Boost_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src/libraries/external/${SAPPHIRE_BOOST_FOLDER_NAME}) + set(BOOST_LIBRARYDIR ${CMAKE_CURRENT_SOURCE_DIR}/src/libraries/external/${SAPPHIRE_BOOST_FOLDER_NAME}/lib32-msvc-14.0) else() find_package(Boost ${SAPPHIRE_BOOST_VER} COMPONENTS system) if(Boost_FOUND) diff --git a/cmake/compiler.cmake b/cmake/compiler.cmake index ebf42d84..20bab671 100644 --- a/cmake/compiler.cmake +++ b/cmake/compiler.cmake @@ -21,6 +21,10 @@ else() # incremental linking message(STATUS "Enabling Incremental Linking..") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /INCREMENTAL") + + # enable building with multiple processes + message(STATUS "Enabling Build with Multiple Processes..") + add_definitions(/MP) endif() endif() diff --git a/cmake/cotire.cmake b/cmake/cotire.cmake new file mode 100644 index 00000000..46ef8c42 --- /dev/null +++ b/cmake/cotire.cmake @@ -0,0 +1,4055 @@ +# - cotire (compile time reducer) +# +# See the cotire manual for usage hints. +# +#============================================================================= +# Copyright 2012-2017 Sascha Kratky +# +# 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. +#============================================================================= + +if(__COTIRE_INCLUDED) + return() +endif() +set(__COTIRE_INCLUDED TRUE) + +# call cmake_minimum_required, but prevent modification of the CMake policy stack in include mode +# cmake_minimum_required also sets the policy version as a side effect, which we have to avoid +if (NOT CMAKE_SCRIPT_MODE_FILE) + cmake_policy(PUSH) +endif() +cmake_minimum_required(VERSION 2.8.12) +if (NOT CMAKE_SCRIPT_MODE_FILE) + cmake_policy(POP) +endif() + +set (COTIRE_CMAKE_MODULE_FILE "${CMAKE_CURRENT_LIST_FILE}") +set (COTIRE_CMAKE_MODULE_VERSION "1.7.10") + +# activate select policies +if (POLICY CMP0025) + # Compiler id for Apple Clang is now AppleClang + cmake_policy(SET CMP0025 NEW) +endif() + +if (POLICY CMP0026) + # disallow use of the LOCATION target property + cmake_policy(SET CMP0026 NEW) +endif() + +if (POLICY CMP0038) + # targets may not link directly to themselves + cmake_policy(SET CMP0038 NEW) +endif() + +if (POLICY CMP0039) + # utility targets may not have link dependencies + cmake_policy(SET CMP0039 NEW) +endif() + +if (POLICY CMP0040) + # target in the TARGET signature of add_custom_command() must exist + cmake_policy(SET CMP0040 NEW) +endif() + +if (POLICY CMP0045) + # error on non-existent target in get_target_property + cmake_policy(SET CMP0045 NEW) +endif() + +if (POLICY CMP0046) + # error on non-existent dependency in add_dependencies + cmake_policy(SET CMP0046 NEW) +endif() + +if (POLICY CMP0049) + # do not expand variables in target source entries + cmake_policy(SET CMP0049 NEW) +endif() + +if (POLICY CMP0050) + # disallow add_custom_command SOURCE signatures + cmake_policy(SET CMP0050 NEW) +endif() + +if (POLICY CMP0051) + # include TARGET_OBJECTS expressions in a target's SOURCES property + cmake_policy(SET CMP0051 NEW) +endif() + +if (POLICY CMP0053) + # simplify variable reference and escape sequence evaluation + cmake_policy(SET CMP0053 NEW) +endif() + +if (POLICY CMP0054) + # only interpret if() arguments as variables or keywords when unquoted + cmake_policy(SET CMP0054 NEW) +endif() + +if (POLICY CMP0055) + # strict checking for break() command + cmake_policy(SET CMP0055 NEW) +endif() + +include(CMakeParseArguments) +include(ProcessorCount) + +function (cotire_get_configuration_types _configsVar) + set (_configs "") + if (CMAKE_CONFIGURATION_TYPES) + list (APPEND _configs ${CMAKE_CONFIGURATION_TYPES}) + endif() + if (CMAKE_BUILD_TYPE) + list (APPEND _configs "${CMAKE_BUILD_TYPE}") + endif() + if (_configs) + list (REMOVE_DUPLICATES _configs) + set (${_configsVar} ${_configs} PARENT_SCOPE) + else() + set (${_configsVar} "None" PARENT_SCOPE) + endif() +endfunction() + +function (cotire_get_source_file_extension _sourceFile _extVar) + # get_filename_component returns extension from first occurrence of . in file name + # this function computes the extension from last occurrence of . in file name + string (FIND "${_sourceFile}" "." _index REVERSE) + if (_index GREATER -1) + math (EXPR _index "${_index} + 1") + string (SUBSTRING "${_sourceFile}" ${_index} -1 _sourceExt) + else() + set (_sourceExt "") + endif() + set (${_extVar} "${_sourceExt}" PARENT_SCOPE) +endfunction() + +macro (cotire_check_is_path_relative_to _path _isRelativeVar) + set (${_isRelativeVar} FALSE) + if (IS_ABSOLUTE "${_path}") + foreach (_dir ${ARGN}) + file (RELATIVE_PATH _relPath "${_dir}" "${_path}") + if (NOT _relPath OR (NOT IS_ABSOLUTE "${_relPath}" AND NOT "${_relPath}" MATCHES "^\\.\\.")) + set (${_isRelativeVar} TRUE) + break() + endif() + endforeach() + endif() +endmacro() + +function (cotire_filter_language_source_files _language _target _sourceFilesVar _excludedSourceFilesVar _cotiredSourceFilesVar) + if (CMAKE_${_language}_SOURCE_FILE_EXTENSIONS) + set (_languageExtensions "${CMAKE_${_language}_SOURCE_FILE_EXTENSIONS}") + else() + set (_languageExtensions "") + endif() + if (CMAKE_${_language}_IGNORE_EXTENSIONS) + set (_ignoreExtensions "${CMAKE_${_language}_IGNORE_EXTENSIONS}") + else() + set (_ignoreExtensions "") + endif() + if (COTIRE_UNITY_SOURCE_EXCLUDE_EXTENSIONS) + set (_excludeExtensions "${COTIRE_UNITY_SOURCE_EXCLUDE_EXTENSIONS}") + else() + set (_excludeExtensions "") + endif() + if (COTIRE_DEBUG AND _languageExtensions) + message (STATUS "${_language} source file extensions: ${_languageExtensions}") + endif() + if (COTIRE_DEBUG AND _ignoreExtensions) + message (STATUS "${_language} ignore extensions: ${_ignoreExtensions}") + endif() + if (COTIRE_DEBUG AND _excludeExtensions) + message (STATUS "${_language} exclude extensions: ${_excludeExtensions}") + endif() + if (CMAKE_VERSION VERSION_LESS "3.1.0") + set (_allSourceFiles ${ARGN}) + else() + # as of CMake 3.1 target sources may contain generator expressions + # since we cannot obtain required property information about source files added + # through generator expressions at configure time, we filter them out + string (GENEX_STRIP "${ARGN}" _allSourceFiles) + endif() + set (_filteredSourceFiles "") + set (_excludedSourceFiles "") + foreach (_sourceFile ${_allSourceFiles}) + get_source_file_property(_sourceIsHeaderOnly "${_sourceFile}" HEADER_FILE_ONLY) + get_source_file_property(_sourceIsExternal "${_sourceFile}" EXTERNAL_OBJECT) + get_source_file_property(_sourceIsSymbolic "${_sourceFile}" SYMBOLIC) + if (NOT _sourceIsHeaderOnly AND NOT _sourceIsExternal AND NOT _sourceIsSymbolic) + cotire_get_source_file_extension("${_sourceFile}" _sourceExt) + if (_sourceExt) + list (FIND _ignoreExtensions "${_sourceExt}" _ignoreIndex) + if (_ignoreIndex LESS 0) + list (FIND _excludeExtensions "${_sourceExt}" _excludeIndex) + if (_excludeIndex GREATER -1) + list (APPEND _excludedSourceFiles "${_sourceFile}") + else() + list (FIND _languageExtensions "${_sourceExt}" _sourceIndex) + if (_sourceIndex GREATER -1) + # consider source file unless it is excluded explicitly + get_source_file_property(_sourceIsExcluded "${_sourceFile}" COTIRE_EXCLUDED) + if (_sourceIsExcluded) + list (APPEND _excludedSourceFiles "${_sourceFile}") + else() + list (APPEND _filteredSourceFiles "${_sourceFile}") + endif() + else() + get_source_file_property(_sourceLanguage "${_sourceFile}" LANGUAGE) + if ("${_sourceLanguage}" STREQUAL "${_language}") + # add to excluded sources, if file is not ignored and has correct language without having the correct extension + list (APPEND _excludedSourceFiles "${_sourceFile}") + endif() + endif() + endif() + endif() + endif() + endif() + endforeach() + # separate filtered source files from already cotired ones + # the COTIRE_TARGET property of a source file may be set while a target is being processed by cotire + set (_sourceFiles "") + set (_cotiredSourceFiles "") + foreach (_sourceFile ${_filteredSourceFiles}) + get_source_file_property(_sourceIsCotired "${_sourceFile}" COTIRE_TARGET) + if (_sourceIsCotired) + list (APPEND _cotiredSourceFiles "${_sourceFile}") + else() + get_source_file_property(_sourceCompileFlags "${_sourceFile}" COMPILE_FLAGS) + if (_sourceCompileFlags) + # add to excluded sources, if file has custom compile flags + list (APPEND _excludedSourceFiles "${_sourceFile}") + else() + list (APPEND _sourceFiles "${_sourceFile}") + endif() + endif() + endforeach() + if (COTIRE_DEBUG) + if (_sourceFiles) + message (STATUS "Filtered ${_target} ${_language} sources: ${_sourceFiles}") + endif() + if (_excludedSourceFiles) + message (STATUS "Excluded ${_target} ${_language} sources: ${_excludedSourceFiles}") + endif() + if (_cotiredSourceFiles) + message (STATUS "Cotired ${_target} ${_language} sources: ${_cotiredSourceFiles}") + endif() + endif() + set (${_sourceFilesVar} ${_sourceFiles} PARENT_SCOPE) + set (${_excludedSourceFilesVar} ${_excludedSourceFiles} PARENT_SCOPE) + set (${_cotiredSourceFilesVar} ${_cotiredSourceFiles} PARENT_SCOPE) +endfunction() + +function (cotire_get_objects_with_property_on _filteredObjectsVar _property _type) + set (_filteredObjects "") + foreach (_object ${ARGN}) + get_property(_isSet ${_type} "${_object}" PROPERTY ${_property} SET) + if (_isSet) + get_property(_propertyValue ${_type} "${_object}" PROPERTY ${_property}) + if (_propertyValue) + list (APPEND _filteredObjects "${_object}") + endif() + endif() + endforeach() + set (${_filteredObjectsVar} ${_filteredObjects} PARENT_SCOPE) +endfunction() + +function (cotire_get_objects_with_property_off _filteredObjectsVar _property _type) + set (_filteredObjects "") + foreach (_object ${ARGN}) + get_property(_isSet ${_type} "${_object}" PROPERTY ${_property} SET) + if (_isSet) + get_property(_propertyValue ${_type} "${_object}" PROPERTY ${_property}) + if (NOT _propertyValue) + list (APPEND _filteredObjects "${_object}") + endif() + endif() + endforeach() + set (${_filteredObjectsVar} ${_filteredObjects} PARENT_SCOPE) +endfunction() + +function (cotire_get_source_file_property_values _valuesVar _property) + set (_values "") + foreach (_sourceFile ${ARGN}) + get_source_file_property(_propertyValue "${_sourceFile}" ${_property}) + if (_propertyValue) + list (APPEND _values "${_propertyValue}") + endif() + endforeach() + set (${_valuesVar} ${_values} PARENT_SCOPE) +endfunction() + +function (cotire_resolve_config_properties _configurations _propertiesVar) + set (_properties "") + foreach (_property ${ARGN}) + if ("${_property}" MATCHES "") + foreach (_config ${_configurations}) + string (TOUPPER "${_config}" _upperConfig) + string (REPLACE "" "${_upperConfig}" _configProperty "${_property}") + list (APPEND _properties ${_configProperty}) + endforeach() + else() + list (APPEND _properties ${_property}) + endif() + endforeach() + set (${_propertiesVar} ${_properties} PARENT_SCOPE) +endfunction() + +function (cotire_copy_set_properties _configurations _type _source _target) + cotire_resolve_config_properties("${_configurations}" _properties ${ARGN}) + foreach (_property ${_properties}) + get_property(_isSet ${_type} ${_source} PROPERTY ${_property} SET) + if (_isSet) + get_property(_propertyValue ${_type} ${_source} PROPERTY ${_property}) + set_property(${_type} ${_target} PROPERTY ${_property} "${_propertyValue}") + endif() + endforeach() +endfunction() + +function (cotire_get_target_usage_requirements _target _config _targetRequirementsVar) + set (_targetRequirements "") + get_target_property(_librariesToProcess ${_target} LINK_LIBRARIES) + while (_librariesToProcess) + # remove from head + list (GET _librariesToProcess 0 _library) + list (REMOVE_AT _librariesToProcess 0) + if (_library MATCHES "^\\$<\\$:([A-Za-z0-9_:-]+)>$") + set (_library "${CMAKE_MATCH_1}") + elseif (_config STREQUAL "None" AND _library MATCHES "^\\$<\\$:([A-Za-z0-9_:-]+)>$") + set (_library "${CMAKE_MATCH_1}") + endif() + if (TARGET ${_library}) + list (FIND _targetRequirements ${_library} _index) + if (_index LESS 0) + list (APPEND _targetRequirements ${_library}) + # BFS traversal of transitive libraries + get_target_property(_libraries ${_library} INTERFACE_LINK_LIBRARIES) + if (_libraries) + list (APPEND _librariesToProcess ${_libraries}) + list (REMOVE_DUPLICATES _librariesToProcess) + endif() + endif() + endif() + endwhile() + set (${_targetRequirementsVar} ${_targetRequirements} PARENT_SCOPE) +endfunction() + +function (cotire_filter_compile_flags _language _flagFilter _matchedOptionsVar _unmatchedOptionsVar) + if (WIN32 AND CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel") + set (_flagPrefix "[/-]") + else() + set (_flagPrefix "--?") + endif() + set (_optionFlag "") + set (_matchedOptions "") + set (_unmatchedOptions "") + foreach (_compileFlag ${ARGN}) + if (_compileFlag) + if (_optionFlag AND NOT "${_compileFlag}" MATCHES "^${_flagPrefix}") + # option with separate argument + list (APPEND _matchedOptions "${_compileFlag}") + set (_optionFlag "") + elseif ("${_compileFlag}" MATCHES "^(${_flagPrefix})(${_flagFilter})$") + # remember option + set (_optionFlag "${CMAKE_MATCH_2}") + elseif ("${_compileFlag}" MATCHES "^(${_flagPrefix})(${_flagFilter})(.+)$") + # option with joined argument + list (APPEND _matchedOptions "${CMAKE_MATCH_3}") + set (_optionFlag "") + else() + # flush remembered option + if (_optionFlag) + list (APPEND _matchedOptions "${_optionFlag}") + set (_optionFlag "") + endif() + # add to unfiltered options + list (APPEND _unmatchedOptions "${_compileFlag}") + endif() + endif() + endforeach() + if (_optionFlag) + list (APPEND _matchedOptions "${_optionFlag}") + endif() + if (COTIRE_DEBUG AND _matchedOptions) + message (STATUS "Filter ${_flagFilter} matched: ${_matchedOptions}") + endif() + if (COTIRE_DEBUG AND _unmatchedOptions) + message (STATUS "Filter ${_flagFilter} unmatched: ${_unmatchedOptions}") + endif() + set (${_matchedOptionsVar} ${_matchedOptions} PARENT_SCOPE) + set (${_unmatchedOptionsVar} ${_unmatchedOptions} PARENT_SCOPE) +endfunction() + +function (cotire_is_target_supported _target _isSupportedVar) + if (NOT TARGET "${_target}") + set (${_isSupportedVar} FALSE PARENT_SCOPE) + return() + endif() + get_target_property(_imported ${_target} IMPORTED) + if (_imported) + set (${_isSupportedVar} FALSE PARENT_SCOPE) + return() + endif() + get_target_property(_targetType ${_target} TYPE) + if (NOT _targetType MATCHES "EXECUTABLE|(STATIC|SHARED|MODULE|OBJECT)_LIBRARY") + set (${_isSupportedVar} FALSE PARENT_SCOPE) + return() + endif() + set (${_isSupportedVar} TRUE PARENT_SCOPE) +endfunction() + +function (cotire_get_target_compile_flags _config _language _target _flagsVar) + string (TOUPPER "${_config}" _upperConfig) + # collect options from CMake language variables + set (_compileFlags "") + if (CMAKE_${_language}_FLAGS) + set (_compileFlags "${_compileFlags} ${CMAKE_${_language}_FLAGS}") + endif() + if (CMAKE_${_language}_FLAGS_${_upperConfig}) + set (_compileFlags "${_compileFlags} ${CMAKE_${_language}_FLAGS_${_upperConfig}}") + endif() + if (_target) + # add target compile flags + get_target_property(_targetflags ${_target} COMPILE_FLAGS) + if (_targetflags) + set (_compileFlags "${_compileFlags} ${_targetflags}") + endif() + endif() + if (UNIX) + separate_arguments(_compileFlags UNIX_COMMAND "${_compileFlags}") + elseif(WIN32) + separate_arguments(_compileFlags WINDOWS_COMMAND "${_compileFlags}") + else() + separate_arguments(_compileFlags) + endif() + # target compile options + if (_target) + get_target_property(_targetOptions ${_target} COMPILE_OPTIONS) + if (_targetOptions) + list (APPEND _compileFlags ${_targetOptions}) + endif() + endif() + # interface compile options from linked library targets + if (_target) + set (_linkedTargets "") + cotire_get_target_usage_requirements(${_target} ${_config} _linkedTargets) + foreach (_linkedTarget ${_linkedTargets}) + get_target_property(_targetOptions ${_linkedTarget} INTERFACE_COMPILE_OPTIONS) + if (_targetOptions) + list (APPEND _compileFlags ${_targetOptions}) + endif() + endforeach() + endif() + # handle language standard properties + if (CMAKE_${_language}_STANDARD_DEFAULT) + # used compiler supports language standard levels + if (_target) + get_target_property(_targetLanguageStandard ${_target} ${_language}_STANDARD) + if (_targetLanguageStandard) + set (_type "EXTENSION") + get_property(_isSet TARGET ${_target} PROPERTY ${_language}_EXTENSIONS SET) + if (_isSet) + get_target_property(_targetUseLanguageExtensions ${_target} ${_language}_EXTENSIONS) + if (NOT _targetUseLanguageExtensions) + set (_type "STANDARD") + endif() + endif() + if (CMAKE_${_language}${_targetLanguageStandard}_${_type}_COMPILE_OPTION) + list (APPEND _compileFlags "${CMAKE_${_language}${_targetLanguageStandard}_${_type}_COMPILE_OPTION}") + endif() + endif() + endif() + endif() + # handle the POSITION_INDEPENDENT_CODE target property + if (_target) + get_target_property(_targetPIC ${_target} POSITION_INDEPENDENT_CODE) + if (_targetPIC) + get_target_property(_targetType ${_target} TYPE) + if (_targetType STREQUAL "EXECUTABLE" AND CMAKE_${_language}_COMPILE_OPTIONS_PIE) + list (APPEND _compileFlags "${CMAKE_${_language}_COMPILE_OPTIONS_PIE}") + elseif (CMAKE_${_language}_COMPILE_OPTIONS_PIC) + list (APPEND _compileFlags "${CMAKE_${_language}_COMPILE_OPTIONS_PIC}") + endif() + endif() + endif() + # handle visibility target properties + if (_target) + get_target_property(_targetVisibility ${_target} ${_language}_VISIBILITY_PRESET) + if (_targetVisibility AND CMAKE_${_language}_COMPILE_OPTIONS_VISIBILITY) + list (APPEND _compileFlags "${CMAKE_${_language}_COMPILE_OPTIONS_VISIBILITY}${_targetVisibility}") + endif() + get_target_property(_targetVisibilityInlines ${_target} VISIBILITY_INLINES_HIDDEN) + if (_targetVisibilityInlines AND CMAKE_${_language}_COMPILE_OPTIONS_VISIBILITY_INLINES_HIDDEN) + list (APPEND _compileFlags "${CMAKE_${_language}_COMPILE_OPTIONS_VISIBILITY_INLINES_HIDDEN}") + endif() + endif() + # platform specific flags + if (APPLE) + get_target_property(_architectures ${_target} OSX_ARCHITECTURES_${_upperConfig}) + if (NOT _architectures) + get_target_property(_architectures ${_target} OSX_ARCHITECTURES) + endif() + if (_architectures) + foreach (_arch ${_architectures}) + list (APPEND _compileFlags "-arch" "${_arch}") + endforeach() + endif() + if (CMAKE_OSX_SYSROOT) + if (CMAKE_${_language}_SYSROOT_FLAG) + list (APPEND _compileFlags "${CMAKE_${_language}_SYSROOT_FLAG}" "${CMAKE_OSX_SYSROOT}") + else() + list (APPEND _compileFlags "-isysroot" "${CMAKE_OSX_SYSROOT}") + endif() + endif() + if (CMAKE_OSX_DEPLOYMENT_TARGET) + if (CMAKE_${_language}_OSX_DEPLOYMENT_TARGET_FLAG) + list (APPEND _compileFlags "${CMAKE_${_language}_OSX_DEPLOYMENT_TARGET_FLAG}${CMAKE_OSX_DEPLOYMENT_TARGET}") + else() + list (APPEND _compileFlags "-mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}") + endif() + endif() + endif() + if (COTIRE_DEBUG AND _compileFlags) + message (STATUS "Target ${_target} compile flags: ${_compileFlags}") + endif() + set (${_flagsVar} ${_compileFlags} PARENT_SCOPE) +endfunction() + +function (cotire_get_target_include_directories _config _language _target _includeDirsVar _systemIncludeDirsVar) + set (_includeDirs "") + set (_systemIncludeDirs "") + # default include dirs + if (CMAKE_INCLUDE_CURRENT_DIR) + list (APPEND _includeDirs "${CMAKE_CURRENT_BINARY_DIR}") + list (APPEND _includeDirs "${CMAKE_CURRENT_SOURCE_DIR}") + endif() + set (_targetFlags "") + cotire_get_target_compile_flags("${_config}" "${_language}" "${_target}" _targetFlags) + # parse additional include directories from target compile flags + if (CMAKE_INCLUDE_FLAG_${_language}) + string (STRIP "${CMAKE_INCLUDE_FLAG_${_language}}" _includeFlag) + string (REGEX REPLACE "^[-/]+" "" _includeFlag "${_includeFlag}") + if (_includeFlag) + set (_dirs "") + cotire_filter_compile_flags("${_language}" "${_includeFlag}" _dirs _ignore ${_targetFlags}) + if (_dirs) + list (APPEND _includeDirs ${_dirs}) + endif() + endif() + endif() + # parse additional system include directories from target compile flags + if (CMAKE_INCLUDE_SYSTEM_FLAG_${_language}) + string (STRIP "${CMAKE_INCLUDE_SYSTEM_FLAG_${_language}}" _includeFlag) + string (REGEX REPLACE "^[-/]+" "" _includeFlag "${_includeFlag}") + if (_includeFlag) + set (_dirs "") + cotire_filter_compile_flags("${_language}" "${_includeFlag}" _dirs _ignore ${_targetFlags}) + if (_dirs) + list (APPEND _systemIncludeDirs ${_dirs}) + endif() + endif() + endif() + # target include directories + get_directory_property(_dirs DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" INCLUDE_DIRECTORIES) + if (_target) + get_target_property(_targetDirs ${_target} INCLUDE_DIRECTORIES) + if (_targetDirs) + list (APPEND _dirs ${_targetDirs}) + endif() + get_target_property(_targetDirs ${_target} INTERFACE_SYSTEM_INCLUDE_DIRECTORIES) + if (_targetDirs) + list (APPEND _systemIncludeDirs ${_targetDirs}) + endif() + endif() + # interface include directories from linked library targets + if (_target) + set (_linkedTargets "") + cotire_get_target_usage_requirements(${_target} ${_config} _linkedTargets) + foreach (_linkedTarget ${_linkedTargets}) + get_target_property(_linkedTargetType ${_linkedTarget} TYPE) + if (CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE AND NOT CMAKE_VERSION VERSION_LESS "3.4.0" AND + _linkedTargetType MATCHES "(STATIC|SHARED|MODULE|OBJECT)_LIBRARY") + # CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE refers to CMAKE_CURRENT_BINARY_DIR and CMAKE_CURRENT_SOURCE_DIR + # at the time, when the target was created. These correspond to the target properties BINARY_DIR and SOURCE_DIR + # which are only available with CMake 3.4 or later. + get_target_property(_targetDirs ${_linkedTarget} BINARY_DIR) + if (_targetDirs) + list (APPEND _dirs ${_targetDirs}) + endif() + get_target_property(_targetDirs ${_linkedTarget} SOURCE_DIR) + if (_targetDirs) + list (APPEND _dirs ${_targetDirs}) + endif() + endif() + get_target_property(_targetDirs ${_linkedTarget} INTERFACE_INCLUDE_DIRECTORIES) + if (_targetDirs) + list (APPEND _dirs ${_targetDirs}) + endif() + get_target_property(_targetDirs ${_linkedTarget} INTERFACE_SYSTEM_INCLUDE_DIRECTORIES) + if (_targetDirs) + list (APPEND _systemIncludeDirs ${_targetDirs}) + endif() + endforeach() + endif() + if (dirs) + list (REMOVE_DUPLICATES _dirs) + endif() + list (LENGTH _includeDirs _projectInsertIndex) + foreach (_dir ${_dirs}) + if (CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE) + cotire_check_is_path_relative_to("${_dir}" _isRelative "${CMAKE_SOURCE_DIR}" "${CMAKE_BINARY_DIR}") + if (_isRelative) + list (LENGTH _includeDirs _len) + if (_len EQUAL _projectInsertIndex) + list (APPEND _includeDirs "${_dir}") + else() + list (INSERT _includeDirs _projectInsertIndex "${_dir}") + endif() + math (EXPR _projectInsertIndex "${_projectInsertIndex} + 1") + else() + list (APPEND _includeDirs "${_dir}") + endif() + else() + list (APPEND _includeDirs "${_dir}") + endif() + endforeach() + list (REMOVE_DUPLICATES _includeDirs) + list (REMOVE_DUPLICATES _systemIncludeDirs) + if (CMAKE_${_language}_IMPLICIT_INCLUDE_DIRECTORIES) + list (REMOVE_ITEM _includeDirs ${CMAKE_${_language}_IMPLICIT_INCLUDE_DIRECTORIES}) + endif() + if (WIN32 AND NOT MINGW) + # convert Windows paths in include directories to CMake paths + if (_includeDirs) + set (_paths "") + foreach (_dir ${_includeDirs}) + file (TO_CMAKE_PATH "${_dir}" _path) + list (APPEND _paths "${_path}") + endforeach() + set (_includeDirs ${_paths}) + endif() + if (_systemIncludeDirs) + set (_paths "") + foreach (_dir ${_systemIncludeDirs}) + file (TO_CMAKE_PATH "${_dir}" _path) + list (APPEND _paths "${_path}") + endforeach() + set (_systemIncludeDirs ${_paths}) + endif() + endif() + if (COTIRE_DEBUG AND _includeDirs) + message (STATUS "Target ${_target} include dirs: ${_includeDirs}") + endif() + set (${_includeDirsVar} ${_includeDirs} PARENT_SCOPE) + if (COTIRE_DEBUG AND _systemIncludeDirs) + message (STATUS "Target ${_target} system include dirs: ${_systemIncludeDirs}") + endif() + set (${_systemIncludeDirsVar} ${_systemIncludeDirs} PARENT_SCOPE) +endfunction() + +function (cotire_get_target_export_symbol _target _exportSymbolVar) + set (_exportSymbol "") + get_target_property(_targetType ${_target} TYPE) + get_target_property(_enableExports ${_target} ENABLE_EXPORTS) + if (_targetType MATCHES "(SHARED|MODULE)_LIBRARY" OR + (_targetType STREQUAL "EXECUTABLE" AND _enableExports)) + get_target_property(_exportSymbol ${_target} DEFINE_SYMBOL) + if (NOT _exportSymbol) + set (_exportSymbol "${_target}_EXPORTS") + endif() + string (MAKE_C_IDENTIFIER "${_exportSymbol}" _exportSymbol) + endif() + set (${_exportSymbolVar} ${_exportSymbol} PARENT_SCOPE) +endfunction() + +function (cotire_get_target_compile_definitions _config _language _target _definitionsVar) + string (TOUPPER "${_config}" _upperConfig) + set (_configDefinitions "") + # CMAKE_INTDIR for multi-configuration build systems + if (NOT "${CMAKE_CFG_INTDIR}" STREQUAL ".") + list (APPEND _configDefinitions "CMAKE_INTDIR=\"${_config}\"") + endif() + # target export define symbol + cotire_get_target_export_symbol("${_target}" _defineSymbol) + if (_defineSymbol) + list (APPEND _configDefinitions "${_defineSymbol}") + endif() + # directory compile definitions + get_directory_property(_definitions DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" COMPILE_DEFINITIONS) + if (_definitions) + list (APPEND _configDefinitions ${_definitions}) + endif() + get_directory_property(_definitions DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" COMPILE_DEFINITIONS_${_upperConfig}) + if (_definitions) + list (APPEND _configDefinitions ${_definitions}) + endif() + # target compile definitions + get_target_property(_definitions ${_target} COMPILE_DEFINITIONS) + if (_definitions) + list (APPEND _configDefinitions ${_definitions}) + endif() + get_target_property(_definitions ${_target} COMPILE_DEFINITIONS_${_upperConfig}) + if (_definitions) + list (APPEND _configDefinitions ${_definitions}) + endif() + # interface compile definitions from linked library targets + set (_linkedTargets "") + cotire_get_target_usage_requirements(${_target} ${_config} _linkedTargets) + foreach (_linkedTarget ${_linkedTargets}) + get_target_property(_definitions ${_linkedTarget} INTERFACE_COMPILE_DEFINITIONS) + if (_definitions) + list (APPEND _configDefinitions ${_definitions}) + endif() + endforeach() + # parse additional compile definitions from target compile flags + # and don't look at directory compile definitions, which we already handled + set (_targetFlags "") + cotire_get_target_compile_flags("${_config}" "${_language}" "${_target}" _targetFlags) + cotire_filter_compile_flags("${_language}" "D" _definitions _ignore ${_targetFlags}) + if (_definitions) + list (APPEND _configDefinitions ${_definitions}) + endif() + list (REMOVE_DUPLICATES _configDefinitions) + if (COTIRE_DEBUG AND _configDefinitions) + message (STATUS "Target ${_target} compile definitions: ${_configDefinitions}") + endif() + set (${_definitionsVar} ${_configDefinitions} PARENT_SCOPE) +endfunction() + +function (cotire_get_target_compiler_flags _config _language _target _compilerFlagsVar) + # parse target compile flags omitting compile definitions and include directives + set (_targetFlags "") + cotire_get_target_compile_flags("${_config}" "${_language}" "${_target}" _targetFlags) + set (_flagFilter "D") + if (CMAKE_INCLUDE_FLAG_${_language}) + string (STRIP "${CMAKE_INCLUDE_FLAG_${_language}}" _includeFlag) + string (REGEX REPLACE "^[-/]+" "" _includeFlag "${_includeFlag}") + if (_includeFlag) + set (_flagFilter "${_flagFilter}|${_includeFlag}") + endif() + endif() + if (CMAKE_INCLUDE_SYSTEM_FLAG_${_language}) + string (STRIP "${CMAKE_INCLUDE_SYSTEM_FLAG_${_language}}" _includeFlag) + string (REGEX REPLACE "^[-/]+" "" _includeFlag "${_includeFlag}") + if (_includeFlag) + set (_flagFilter "${_flagFilter}|${_includeFlag}") + endif() + endif() + set (_compilerFlags "") + cotire_filter_compile_flags("${_language}" "${_flagFilter}" _ignore _compilerFlags ${_targetFlags}) + if (COTIRE_DEBUG AND _compilerFlags) + message (STATUS "Target ${_target} compiler flags: ${_compilerFlags}") + endif() + set (${_compilerFlagsVar} ${_compilerFlags} PARENT_SCOPE) +endfunction() + +function (cotire_add_sys_root_paths _pathsVar) + if (APPLE) + if (CMAKE_OSX_SYSROOT AND CMAKE_${_language}_HAS_ISYSROOT) + foreach (_path IN LISTS ${_pathsVar}) + if (IS_ABSOLUTE "${_path}") + get_filename_component(_path "${CMAKE_OSX_SYSROOT}/${_path}" ABSOLUTE) + if (EXISTS "${_path}") + list (APPEND ${_pathsVar} "${_path}") + endif() + endif() + endforeach() + endif() + endif() + set (${_pathsVar} ${${_pathsVar}} PARENT_SCOPE) +endfunction() + +function (cotire_get_source_extra_properties _sourceFile _pattern _resultVar) + set (_extraProperties ${ARGN}) + set (_result "") + if (_extraProperties) + list (FIND _extraProperties "${_sourceFile}" _index) + if (_index GREATER -1) + math (EXPR _index "${_index} + 1") + list (LENGTH _extraProperties _len) + math (EXPR _len "${_len} - 1") + foreach (_index RANGE ${_index} ${_len}) + list (GET _extraProperties ${_index} _value) + if (_value MATCHES "${_pattern}") + list (APPEND _result "${_value}") + else() + break() + endif() + endforeach() + endif() + endif() + set (${_resultVar} ${_result} PARENT_SCOPE) +endfunction() + +function (cotire_get_source_compile_definitions _config _language _sourceFile _definitionsVar) + set (_compileDefinitions "") + if (NOT CMAKE_SCRIPT_MODE_FILE) + string (TOUPPER "${_config}" _upperConfig) + get_source_file_property(_definitions "${_sourceFile}" COMPILE_DEFINITIONS) + if (_definitions) + list (APPEND _compileDefinitions ${_definitions}) + endif() + get_source_file_property(_definitions "${_sourceFile}" COMPILE_DEFINITIONS_${_upperConfig}) + if (_definitions) + list (APPEND _compileDefinitions ${_definitions}) + endif() + endif() + cotire_get_source_extra_properties("${_sourceFile}" "^[a-zA-Z0-9_]+(=.*)?$" _definitions ${ARGN}) + if (_definitions) + list (APPEND _compileDefinitions ${_definitions}) + endif() + if (COTIRE_DEBUG AND _compileDefinitions) + message (STATUS "Source ${_sourceFile} compile definitions: ${_compileDefinitions}") + endif() + set (${_definitionsVar} ${_compileDefinitions} PARENT_SCOPE) +endfunction() + +function (cotire_get_source_files_compile_definitions _config _language _definitionsVar) + set (_configDefinitions "") + foreach (_sourceFile ${ARGN}) + cotire_get_source_compile_definitions("${_config}" "${_language}" "${_sourceFile}" _sourceDefinitions) + if (_sourceDefinitions) + list (APPEND _configDefinitions "${_sourceFile}" ${_sourceDefinitions} "-") + endif() + endforeach() + set (${_definitionsVar} ${_configDefinitions} PARENT_SCOPE) +endfunction() + +function (cotire_get_source_undefs _sourceFile _property _sourceUndefsVar) + set (_sourceUndefs "") + if (NOT CMAKE_SCRIPT_MODE_FILE) + get_source_file_property(_undefs "${_sourceFile}" ${_property}) + if (_undefs) + list (APPEND _sourceUndefs ${_undefs}) + endif() + endif() + cotire_get_source_extra_properties("${_sourceFile}" "^[a-zA-Z0-9_]+$" _undefs ${ARGN}) + if (_undefs) + list (APPEND _sourceUndefs ${_undefs}) + endif() + if (COTIRE_DEBUG AND _sourceUndefs) + message (STATUS "Source ${_sourceFile} ${_property} undefs: ${_sourceUndefs}") + endif() + set (${_sourceUndefsVar} ${_sourceUndefs} PARENT_SCOPE) +endfunction() + +function (cotire_get_source_files_undefs _property _sourceUndefsVar) + set (_sourceUndefs "") + foreach (_sourceFile ${ARGN}) + cotire_get_source_undefs("${_sourceFile}" ${_property} _undefs) + if (_undefs) + list (APPEND _sourceUndefs "${_sourceFile}" ${_undefs} "-") + endif() + endforeach() + set (${_sourceUndefsVar} ${_sourceUndefs} PARENT_SCOPE) +endfunction() + +macro (cotire_set_cmd_to_prologue _cmdVar) + set (${_cmdVar} "${CMAKE_COMMAND}") + if (COTIRE_DEBUG) + list (APPEND ${_cmdVar} "--warn-uninitialized") + endif() + list (APPEND ${_cmdVar} "-DCOTIRE_BUILD_TYPE:STRING=$") + if (XCODE) + list (APPEND ${_cmdVar} "-DXCODE:BOOL=TRUE") + endif() + if (COTIRE_VERBOSE) + list (APPEND ${_cmdVar} "-DCOTIRE_VERBOSE:BOOL=ON") + elseif("${CMAKE_GENERATOR}" MATCHES "Makefiles") + list (APPEND ${_cmdVar} "-DCOTIRE_VERBOSE:BOOL=$(VERBOSE)") + endif() +endmacro() + +function (cotire_init_compile_cmd _cmdVar _language _compilerLauncher _compilerExe _compilerArg1) + if (NOT _compilerLauncher) + set (_compilerLauncher ${CMAKE_${_language}_COMPILER_LAUNCHER}) + endif() + if (NOT _compilerExe) + set (_compilerExe "${CMAKE_${_language}_COMPILER}") + endif() + if (NOT _compilerArg1) + set (_compilerArg1 ${CMAKE_${_language}_COMPILER_ARG1}) + endif() + string (STRIP "${_compilerArg1}" _compilerArg1) + if ("${CMAKE_GENERATOR}" MATCHES "Make|Ninja") + # compiler launcher is only supported for Makefile and Ninja + set (${_cmdVar} ${_compilerLauncher} "${_compilerExe}" ${_compilerArg1} PARENT_SCOPE) + else() + set (${_cmdVar} "${_compilerExe}" ${_compilerArg1} PARENT_SCOPE) + endif() +endfunction() + +macro (cotire_add_definitions_to_cmd _cmdVar _language) + foreach (_definition ${ARGN}) + if (WIN32 AND CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel") + list (APPEND ${_cmdVar} "/D${_definition}") + else() + list (APPEND ${_cmdVar} "-D${_definition}") + endif() + endforeach() +endmacro() + +function (cotire_add_includes_to_cmd _cmdVar _language _includesVar _systemIncludesVar) + set (_includeDirs ${${_includesVar}} ${${_systemIncludesVar}}) + if (_includeDirs) + list (REMOVE_DUPLICATES _includeDirs) + foreach (_include ${_includeDirs}) + if (WIN32 AND CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel") + file (TO_NATIVE_PATH "${_include}" _include) + list (APPEND ${_cmdVar} "${CMAKE_INCLUDE_FLAG_${_language}}${CMAKE_INCLUDE_FLAG_SEP_${_language}}${_include}") + else() + set (_index -1) + if ("${CMAKE_INCLUDE_SYSTEM_FLAG_${_language}}" MATCHES ".+") + list (FIND ${_systemIncludesVar} "${_include}" _index) + endif() + if (_index GREATER -1) + list (APPEND ${_cmdVar} "${CMAKE_INCLUDE_SYSTEM_FLAG_${_language}}${CMAKE_INCLUDE_FLAG_SEP_${_language}}${_include}") + else() + list (APPEND ${_cmdVar} "${CMAKE_INCLUDE_FLAG_${_language}}${CMAKE_INCLUDE_FLAG_SEP_${_language}}${_include}") + endif() + endif() + endforeach() + endif() + set (${_cmdVar} ${${_cmdVar}} PARENT_SCOPE) +endfunction() + +function (cotire_add_frameworks_to_cmd _cmdVar _language _includesVar _systemIncludesVar) + if (APPLE) + set (_frameworkDirs "") + foreach (_include ${${_includesVar}}) + if (IS_ABSOLUTE "${_include}" AND _include MATCHES "\\.framework$") + get_filename_component(_frameworkDir "${_include}" DIRECTORY) + list (APPEND _frameworkDirs "${_frameworkDir}") + endif() + endforeach() + set (_systemFrameworkDirs "") + foreach (_include ${${_systemIncludesVar}}) + if (IS_ABSOLUTE "${_include}" AND _include MATCHES "\\.framework$") + get_filename_component(_frameworkDir "${_include}" DIRECTORY) + list (APPEND _systemFrameworkDirs "${_frameworkDir}") + endif() + endforeach() + if (_systemFrameworkDirs) + list (APPEND _frameworkDirs ${_systemFrameworkDirs}) + endif() + if (_frameworkDirs) + list (REMOVE_DUPLICATES _frameworkDirs) + foreach (_frameworkDir ${_frameworkDirs}) + set (_index -1) + if ("${CMAKE_${_language}_SYSTEM_FRAMEWORK_SEARCH_FLAG}" MATCHES ".+") + list (FIND _systemFrameworkDirs "${_frameworkDir}" _index) + endif() + if (_index GREATER -1) + list (APPEND ${_cmdVar} "${CMAKE_${_language}_SYSTEM_FRAMEWORK_SEARCH_FLAG}${_frameworkDir}") + else() + list (APPEND ${_cmdVar} "${CMAKE_${_language}_FRAMEWORK_SEARCH_FLAG}${_frameworkDir}") + endif() + endforeach() + endif() + endif() + set (${_cmdVar} ${${_cmdVar}} PARENT_SCOPE) +endfunction() + +macro (cotire_add_compile_flags_to_cmd _cmdVar) + foreach (_flag ${ARGN}) + list (APPEND ${_cmdVar} "${_flag}") + endforeach() +endmacro() + +function (cotire_check_file_up_to_date _fileIsUpToDateVar _file) + if (EXISTS "${_file}") + set (_triggerFile "") + foreach (_dependencyFile ${ARGN}) + if (EXISTS "${_dependencyFile}") + # IS_NEWER_THAN returns TRUE if both files have the same timestamp + # thus we do the comparison in both directions to exclude ties + if ("${_dependencyFile}" IS_NEWER_THAN "${_file}" AND + NOT "${_file}" IS_NEWER_THAN "${_dependencyFile}") + set (_triggerFile "${_dependencyFile}") + break() + endif() + endif() + endforeach() + if (_triggerFile) + if (COTIRE_VERBOSE) + get_filename_component(_fileName "${_file}" NAME) + message (STATUS "${_fileName} update triggered by ${_triggerFile} change.") + endif() + set (${_fileIsUpToDateVar} FALSE PARENT_SCOPE) + else() + if (COTIRE_VERBOSE) + get_filename_component(_fileName "${_file}" NAME) + message (STATUS "${_fileName} is up-to-date.") + endif() + set (${_fileIsUpToDateVar} TRUE PARENT_SCOPE) + endif() + else() + if (COTIRE_VERBOSE) + get_filename_component(_fileName "${_file}" NAME) + message (STATUS "${_fileName} does not exist yet.") + endif() + set (${_fileIsUpToDateVar} FALSE PARENT_SCOPE) + endif() +endfunction() + +macro (cotire_find_closest_relative_path _headerFile _includeDirs _relPathVar) + set (${_relPathVar} "") + foreach (_includeDir ${_includeDirs}) + if (IS_DIRECTORY "${_includeDir}") + file (RELATIVE_PATH _relPath "${_includeDir}" "${_headerFile}") + if (NOT IS_ABSOLUTE "${_relPath}" AND NOT "${_relPath}" MATCHES "^\\.\\.") + string (LENGTH "${${_relPathVar}}" _closestLen) + string (LENGTH "${_relPath}" _relLen) + if (_closestLen EQUAL 0 OR _relLen LESS _closestLen) + set (${_relPathVar} "${_relPath}") + endif() + endif() + elseif ("${_includeDir}" STREQUAL "${_headerFile}") + # if path matches exactly, return short non-empty string + set (${_relPathVar} "1") + break() + endif() + endforeach() +endmacro() + +macro (cotire_check_header_file_location _headerFile _insideIncludeDirs _outsideIncludeDirs _headerIsInside) + # check header path against ignored and honored include directories + cotire_find_closest_relative_path("${_headerFile}" "${_insideIncludeDirs}" _insideRelPath) + if (_insideRelPath) + # header is inside, but could be become outside if there is a shorter outside match + cotire_find_closest_relative_path("${_headerFile}" "${_outsideIncludeDirs}" _outsideRelPath) + if (_outsideRelPath) + string (LENGTH "${_insideRelPath}" _insideRelPathLen) + string (LENGTH "${_outsideRelPath}" _outsideRelPathLen) + if (_outsideRelPathLen LESS _insideRelPathLen) + set (${_headerIsInside} FALSE) + else() + set (${_headerIsInside} TRUE) + endif() + else() + set (${_headerIsInside} TRUE) + endif() + else() + # header is outside + set (${_headerIsInside} FALSE) + endif() +endmacro() + +macro (cotire_check_ignore_header_file_path _headerFile _headerIsIgnoredVar) + if (NOT EXISTS "${_headerFile}") + set (${_headerIsIgnoredVar} TRUE) + elseif (IS_DIRECTORY "${_headerFile}") + set (${_headerIsIgnoredVar} TRUE) + elseif ("${_headerFile}" MATCHES "\\.\\.|[_-]fixed" AND "${_headerFile}" MATCHES "\\.h$") + # heuristic: ignore C headers with embedded parent directory references or "-fixed" or "_fixed" in path + # these often stem from using GCC #include_next tricks, which may break the precompiled header compilation + # with the error message "error: no include path in which to search for header.h" + set (${_headerIsIgnoredVar} TRUE) + else() + set (${_headerIsIgnoredVar} FALSE) + endif() +endmacro() + +macro (cotire_check_ignore_header_file_ext _headerFile _ignoreExtensionsVar _headerIsIgnoredVar) + # check header file extension + cotire_get_source_file_extension("${_headerFile}" _headerFileExt) + set (${_headerIsIgnoredVar} FALSE) + if (_headerFileExt) + list (FIND ${_ignoreExtensionsVar} "${_headerFileExt}" _index) + if (_index GREATER -1) + set (${_headerIsIgnoredVar} TRUE) + endif() + endif() +endmacro() + +macro (cotire_parse_line _line _headerFileVar _headerDepthVar) + if (MSVC) + # cl.exe /showIncludes output looks different depending on the language pack used, e.g.: + # English: "Note: including file: C:\directory\file" + # German: "Hinweis: Einlesen der Datei: C:\directory\file" + # We use a very general regular expression, relying on the presence of the : characters + if (_line MATCHES "( +)([a-zA-Z]:[^:]+)$") + # Visual Studio compiler output + string (LENGTH "${CMAKE_MATCH_1}" ${_headerDepthVar}) + get_filename_component(${_headerFileVar} "${CMAKE_MATCH_2}" ABSOLUTE) + else() + set (${_headerFileVar} "") + set (${_headerDepthVar} 0) + endif() + else() + if (_line MATCHES "^(\\.+) (.*)$") + # GCC like output + string (LENGTH "${CMAKE_MATCH_1}" ${_headerDepthVar}) + if (IS_ABSOLUTE "${CMAKE_MATCH_2}") + set (${_headerFileVar} "${CMAKE_MATCH_2}") + else() + get_filename_component(${_headerFileVar} "${CMAKE_MATCH_2}" REALPATH) + endif() + else() + set (${_headerFileVar} "") + set (${_headerDepthVar} 0) + endif() + endif() +endmacro() + +function (cotire_parse_includes _language _scanOutput _ignoredIncludeDirs _honoredIncludeDirs _ignoredExtensions _selectedIncludesVar _unparsedLinesVar) + if (WIN32) + # prevent CMake macro invocation errors due to backslash characters in Windows paths + string (REPLACE "\\" "/" _scanOutput "${_scanOutput}") + endif() + # canonize slashes + string (REPLACE "//" "/" _scanOutput "${_scanOutput}") + # prevent semicolon from being interpreted as a line separator + string (REPLACE ";" "\\;" _scanOutput "${_scanOutput}") + # then separate lines + string (REGEX REPLACE "\n" ";" _scanOutput "${_scanOutput}") + list (LENGTH _scanOutput _len) + # remove duplicate lines to speed up parsing + list (REMOVE_DUPLICATES _scanOutput) + list (LENGTH _scanOutput _uniqueLen) + if (COTIRE_VERBOSE OR COTIRE_DEBUG) + message (STATUS "Scanning ${_uniqueLen} unique lines of ${_len} for includes") + if (_ignoredExtensions) + message (STATUS "Ignored extensions: ${_ignoredExtensions}") + endif() + if (_ignoredIncludeDirs) + message (STATUS "Ignored paths: ${_ignoredIncludeDirs}") + endif() + if (_honoredIncludeDirs) + message (STATUS "Included paths: ${_honoredIncludeDirs}") + endif() + endif() + set (_sourceFiles ${ARGN}) + set (_selectedIncludes "") + set (_unparsedLines "") + # stack keeps track of inside/outside project status of processed header files + set (_headerIsInsideStack "") + foreach (_line IN LISTS _scanOutput) + if (_line) + cotire_parse_line("${_line}" _headerFile _headerDepth) + if (_headerFile) + cotire_check_header_file_location("${_headerFile}" "${_ignoredIncludeDirs}" "${_honoredIncludeDirs}" _headerIsInside) + if (COTIRE_DEBUG) + message (STATUS "${_headerDepth}: ${_headerFile} ${_headerIsInside}") + endif() + # update stack + list (LENGTH _headerIsInsideStack _stackLen) + if (_headerDepth GREATER _stackLen) + math (EXPR _stackLen "${_stackLen} + 1") + foreach (_index RANGE ${_stackLen} ${_headerDepth}) + list (APPEND _headerIsInsideStack ${_headerIsInside}) + endforeach() + else() + foreach (_index RANGE ${_headerDepth} ${_stackLen}) + list (REMOVE_AT _headerIsInsideStack -1) + endforeach() + list (APPEND _headerIsInsideStack ${_headerIsInside}) + endif() + if (COTIRE_DEBUG) + message (STATUS "${_headerIsInsideStack}") + endif() + # header is a candidate if it is outside project + if (NOT _headerIsInside) + # get parent header file's inside/outside status + if (_headerDepth GREATER 1) + math (EXPR _index "${_headerDepth} - 2") + list (GET _headerIsInsideStack ${_index} _parentHeaderIsInside) + else() + set (_parentHeaderIsInside TRUE) + endif() + # select header file if parent header file is inside project + # (e.g., a project header file that includes a standard header file) + if (_parentHeaderIsInside) + cotire_check_ignore_header_file_path("${_headerFile}" _headerIsIgnored) + if (NOT _headerIsIgnored) + cotire_check_ignore_header_file_ext("${_headerFile}" _ignoredExtensions _headerIsIgnored) + if (NOT _headerIsIgnored) + list (APPEND _selectedIncludes "${_headerFile}") + else() + # fix header's inside status on stack, it is ignored by extension now + list (REMOVE_AT _headerIsInsideStack -1) + list (APPEND _headerIsInsideStack TRUE) + endif() + endif() + if (COTIRE_DEBUG) + message (STATUS "${_headerFile} ${_ignoredExtensions} ${_headerIsIgnored}") + endif() + endif() + endif() + else() + if (MSVC) + # for cl.exe do not keep unparsed lines which solely consist of a source file name + string (FIND "${_sourceFiles}" "${_line}" _index) + if (_index LESS 0) + list (APPEND _unparsedLines "${_line}") + endif() + else() + list (APPEND _unparsedLines "${_line}") + endif() + endif() + endif() + endforeach() + list (REMOVE_DUPLICATES _selectedIncludes) + set (${_selectedIncludesVar} ${_selectedIncludes} PARENT_SCOPE) + set (${_unparsedLinesVar} ${_unparsedLines} PARENT_SCOPE) +endfunction() + +function (cotire_scan_includes _includesVar) + set(_options "") + set(_oneValueArgs COMPILER_ID COMPILER_EXECUTABLE COMPILER_ARG1 COMPILER_VERSION LANGUAGE UNPARSED_LINES SCAN_RESULT) + set(_multiValueArgs COMPILE_DEFINITIONS COMPILE_FLAGS INCLUDE_DIRECTORIES SYSTEM_INCLUDE_DIRECTORIES + IGNORE_PATH INCLUDE_PATH IGNORE_EXTENSIONS INCLUDE_PRIORITY_PATH COMPILER_LAUNCHER) + cmake_parse_arguments(_option "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN}) + set (_sourceFiles ${_option_UNPARSED_ARGUMENTS}) + if (NOT _option_LANGUAGE) + set (_option_LANGUAGE "CXX") + endif() + if (NOT _option_COMPILER_ID) + set (_option_COMPILER_ID "${CMAKE_${_option_LANGUAGE}_ID}") + endif() + if (NOT _option_COMPILER_VERSION) + set (_option_COMPILER_VERSION "${CMAKE_${_option_LANGUAGE}_COMPILER_VERSION}") + endif() + cotire_init_compile_cmd(_cmd "${_option_LANGUAGE}" "${_option_COMPILER_LAUNCHER}" "${_option_COMPILER_EXECUTABLE}" "${_option_COMPILER_ARG1}") + cotire_add_definitions_to_cmd(_cmd "${_option_LANGUAGE}" ${_option_COMPILE_DEFINITIONS}) + cotire_add_compile_flags_to_cmd(_cmd ${_option_COMPILE_FLAGS}) + cotire_add_includes_to_cmd(_cmd "${_option_LANGUAGE}" _option_INCLUDE_DIRECTORIES _option_SYSTEM_INCLUDE_DIRECTORIES) + cotire_add_frameworks_to_cmd(_cmd "${_option_LANGUAGE}" _option_INCLUDE_DIRECTORIES _option_SYSTEM_INCLUDE_DIRECTORIES) + cotire_add_makedep_flags("${_option_LANGUAGE}" "${_option_COMPILER_ID}" "${_option_COMPILER_VERSION}" _cmd) + # only consider existing source files for scanning + set (_existingSourceFiles "") + foreach (_sourceFile ${_sourceFiles}) + if (EXISTS "${_sourceFile}") + list (APPEND _existingSourceFiles "${_sourceFile}") + endif() + endforeach() + if (NOT _existingSourceFiles) + set (${_includesVar} "" PARENT_SCOPE) + return() + endif() + list (APPEND _cmd ${_existingSourceFiles}) + if (COTIRE_VERBOSE) + message (STATUS "execute_process: ${_cmd}") + endif() + if (_option_COMPILER_ID MATCHES "MSVC") + # cl.exe messes with the output streams unless the environment variable VS_UNICODE_OUTPUT is cleared + unset (ENV{VS_UNICODE_OUTPUT}) + endif() + execute_process( + COMMAND ${_cmd} + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + RESULT_VARIABLE _result + OUTPUT_QUIET + ERROR_VARIABLE _output) + if (_result) + message (STATUS "Result ${_result} scanning includes of ${_existingSourceFiles}.") + endif() + cotire_parse_includes( + "${_option_LANGUAGE}" "${_output}" + "${_option_IGNORE_PATH}" "${_option_INCLUDE_PATH}" + "${_option_IGNORE_EXTENSIONS}" + _includes _unparsedLines + ${_sourceFiles}) + if (_option_INCLUDE_PRIORITY_PATH) + set (_sortedIncludes "") + foreach (_priorityPath ${_option_INCLUDE_PRIORITY_PATH}) + foreach (_include ${_includes}) + string (FIND ${_include} ${_priorityPath} _position) + if (_position GREATER -1) + list (APPEND _sortedIncludes ${_include}) + endif() + endforeach() + endforeach() + if (_sortedIncludes) + list (INSERT _includes 0 ${_sortedIncludes}) + list (REMOVE_DUPLICATES _includes) + endif() + endif() + set (${_includesVar} ${_includes} PARENT_SCOPE) + if (_option_UNPARSED_LINES) + set (${_option_UNPARSED_LINES} ${_unparsedLines} PARENT_SCOPE) + endif() + if (_option_SCAN_RESULT) + set (${_option_SCAN_RESULT} ${_result} PARENT_SCOPE) + endif() +endfunction() + +macro (cotire_append_undefs _contentsVar) + set (_undefs ${ARGN}) + if (_undefs) + list (REMOVE_DUPLICATES _undefs) + foreach (_definition ${_undefs}) + list (APPEND ${_contentsVar} "#undef ${_definition}") + endforeach() + endif() +endmacro() + +macro (cotire_comment_str _language _commentText _commentVar) + if ("${_language}" STREQUAL "CMAKE") + set (${_commentVar} "# ${_commentText}") + else() + set (${_commentVar} "/* ${_commentText} */") + endif() +endmacro() + +function (cotire_write_file _language _file _contents _force) + get_filename_component(_moduleName "${COTIRE_CMAKE_MODULE_FILE}" NAME) + cotire_comment_str("${_language}" "${_moduleName} ${COTIRE_CMAKE_MODULE_VERSION} generated file" _header1) + cotire_comment_str("${_language}" "${_file}" _header2) + set (_contents "${_header1}\n${_header2}\n${_contents}") + if (COTIRE_DEBUG) + message (STATUS "${_contents}") + endif() + if (_force OR NOT EXISTS "${_file}") + file (WRITE "${_file}" "${_contents}") + else() + file (READ "${_file}" _oldContents) + if (NOT "${_oldContents}" STREQUAL "${_contents}") + file (WRITE "${_file}" "${_contents}") + else() + if (COTIRE_DEBUG) + message (STATUS "${_file} unchanged") + endif() + endif() + endif() +endfunction() + +function (cotire_generate_unity_source _unityFile) + set(_options "") + set(_oneValueArgs LANGUAGE) + set(_multiValueArgs + DEPENDS SOURCES_COMPILE_DEFINITIONS + PRE_UNDEFS SOURCES_PRE_UNDEFS POST_UNDEFS SOURCES_POST_UNDEFS PROLOGUE EPILOGUE) + cmake_parse_arguments(_option "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN}) + if (_option_DEPENDS) + cotire_check_file_up_to_date(_unityFileIsUpToDate "${_unityFile}" ${_option_DEPENDS}) + if (_unityFileIsUpToDate) + return() + endif() + endif() + set (_sourceFiles ${_option_UNPARSED_ARGUMENTS}) + if (NOT _option_PRE_UNDEFS) + set (_option_PRE_UNDEFS "") + endif() + if (NOT _option_SOURCES_PRE_UNDEFS) + set (_option_SOURCES_PRE_UNDEFS "") + endif() + if (NOT _option_POST_UNDEFS) + set (_option_POST_UNDEFS "") + endif() + if (NOT _option_SOURCES_POST_UNDEFS) + set (_option_SOURCES_POST_UNDEFS "") + endif() + set (_contents "") + if (_option_PROLOGUE) + list (APPEND _contents ${_option_PROLOGUE}) + endif() + if (_option_LANGUAGE AND _sourceFiles) + if ("${_option_LANGUAGE}" STREQUAL "CXX") + list (APPEND _contents "#ifdef __cplusplus") + elseif ("${_option_LANGUAGE}" STREQUAL "C") + list (APPEND _contents "#ifndef __cplusplus") + endif() + endif() + set (_compileUndefinitions "") + foreach (_sourceFile ${_sourceFiles}) + cotire_get_source_compile_definitions( + "${_option_CONFIGURATION}" "${_option_LANGUAGE}" "${_sourceFile}" _compileDefinitions + ${_option_SOURCES_COMPILE_DEFINITIONS}) + cotire_get_source_undefs("${_sourceFile}" COTIRE_UNITY_SOURCE_PRE_UNDEFS _sourcePreUndefs ${_option_SOURCES_PRE_UNDEFS}) + cotire_get_source_undefs("${_sourceFile}" COTIRE_UNITY_SOURCE_POST_UNDEFS _sourcePostUndefs ${_option_SOURCES_POST_UNDEFS}) + if (_option_PRE_UNDEFS) + list (APPEND _compileUndefinitions ${_option_PRE_UNDEFS}) + endif() + if (_sourcePreUndefs) + list (APPEND _compileUndefinitions ${_sourcePreUndefs}) + endif() + if (_compileUndefinitions) + cotire_append_undefs(_contents ${_compileUndefinitions}) + set (_compileUndefinitions "") + endif() + if (_sourcePostUndefs) + list (APPEND _compileUndefinitions ${_sourcePostUndefs}) + endif() + if (_option_POST_UNDEFS) + list (APPEND _compileUndefinitions ${_option_POST_UNDEFS}) + endif() + foreach (_definition ${_compileDefinitions}) + if (_definition MATCHES "^([a-zA-Z0-9_]+)=(.+)$") + list (APPEND _contents "#define ${CMAKE_MATCH_1} ${CMAKE_MATCH_2}") + list (INSERT _compileUndefinitions 0 "${CMAKE_MATCH_1}") + else() + list (APPEND _contents "#define ${_definition}") + list (INSERT _compileUndefinitions 0 "${_definition}") + endif() + endforeach() + # use absolute path as source file location + get_filename_component(_sourceFileLocation "${_sourceFile}" ABSOLUTE) + if (WIN32) + file (TO_NATIVE_PATH "${_sourceFileLocation}" _sourceFileLocation) + endif() + list (APPEND _contents "#include \"${_sourceFileLocation}\"") + endforeach() + if (_compileUndefinitions) + cotire_append_undefs(_contents ${_compileUndefinitions}) + set (_compileUndefinitions "") + endif() + if (_option_LANGUAGE AND _sourceFiles) + list (APPEND _contents "#endif") + endif() + if (_option_EPILOGUE) + list (APPEND _contents ${_option_EPILOGUE}) + endif() + list (APPEND _contents "") + string (REPLACE ";" "\n" _contents "${_contents}") + if (COTIRE_VERBOSE) + message ("${_contents}") + endif() + cotire_write_file("${_option_LANGUAGE}" "${_unityFile}" "${_contents}" TRUE) +endfunction() + +function (cotire_generate_prefix_header _prefixFile) + set(_options "") + set(_oneValueArgs LANGUAGE COMPILER_EXECUTABLE COMPILER_ARG1 COMPILER_ID COMPILER_VERSION) + set(_multiValueArgs DEPENDS COMPILE_DEFINITIONS COMPILE_FLAGS + INCLUDE_DIRECTORIES SYSTEM_INCLUDE_DIRECTORIES IGNORE_PATH INCLUDE_PATH + IGNORE_EXTENSIONS INCLUDE_PRIORITY_PATH COMPILER_LAUNCHER) + cmake_parse_arguments(_option "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN}) + if (NOT _option_COMPILER_ID) + set (_option_COMPILER_ID "${CMAKE_${_option_LANGUAGE}_ID}") + endif() + if (NOT _option_COMPILER_VERSION) + set (_option_COMPILER_VERSION "${CMAKE_${_option_LANGUAGE}_COMPILER_VERSION}") + endif() + if (_option_DEPENDS) + cotire_check_file_up_to_date(_prefixFileIsUpToDate "${_prefixFile}" ${_option_DEPENDS}) + if (_prefixFileIsUpToDate) + # create empty log file + set (_unparsedLinesFile "${_prefixFile}.log") + file (WRITE "${_unparsedLinesFile}" "") + return() + endif() + endif() + set (_prologue "") + set (_epilogue "") + if (_option_COMPILER_ID MATCHES "Clang") + set (_prologue "#pragma clang system_header") + elseif (_option_COMPILER_ID MATCHES "GNU") + set (_prologue "#pragma GCC system_header") + elseif (_option_COMPILER_ID MATCHES "MSVC") + set (_prologue "#pragma warning(push, 0)") + set (_epilogue "#pragma warning(pop)") + elseif (_option_COMPILER_ID MATCHES "Intel") + # Intel compiler requires hdrstop pragma to stop generating PCH file + set (_epilogue "#pragma hdrstop") + endif() + set (_sourceFiles ${_option_UNPARSED_ARGUMENTS}) + cotire_scan_includes(_selectedHeaders ${_sourceFiles} + LANGUAGE "${_option_LANGUAGE}" + COMPILER_LAUNCHER "${_option_COMPILER_LAUNCHER}" + COMPILER_EXECUTABLE "${_option_COMPILER_EXECUTABLE}" + COMPILER_ARG1 "${_option_COMPILER_ARG1}" + COMPILER_ID "${_option_COMPILER_ID}" + COMPILER_VERSION "${_option_COMPILER_VERSION}" + COMPILE_DEFINITIONS ${_option_COMPILE_DEFINITIONS} + COMPILE_FLAGS ${_option_COMPILE_FLAGS} + INCLUDE_DIRECTORIES ${_option_INCLUDE_DIRECTORIES} + SYSTEM_INCLUDE_DIRECTORIES ${_option_SYSTEM_INCLUDE_DIRECTORIES} + IGNORE_PATH ${_option_IGNORE_PATH} + INCLUDE_PATH ${_option_INCLUDE_PATH} + IGNORE_EXTENSIONS ${_option_IGNORE_EXTENSIONS} + INCLUDE_PRIORITY_PATH ${_option_INCLUDE_PRIORITY_PATH} + UNPARSED_LINES _unparsedLines + SCAN_RESULT _scanResult) + cotire_generate_unity_source("${_prefixFile}" + PROLOGUE ${_prologue} EPILOGUE ${_epilogue} LANGUAGE "${_option_LANGUAGE}" ${_selectedHeaders}) + set (_unparsedLinesFile "${_prefixFile}.log") + if (_unparsedLines) + if (COTIRE_VERBOSE OR _scanResult OR NOT _selectedHeaders) + list (LENGTH _unparsedLines _skippedLineCount) + message (STATUS "${_skippedLineCount} line(s) skipped, see ${_unparsedLinesFile}") + endif() + string (REPLACE ";" "\n" _unparsedLines "${_unparsedLines}") + endif() + file (WRITE "${_unparsedLinesFile}" "${_unparsedLines}") +endfunction() + +function (cotire_add_makedep_flags _language _compilerID _compilerVersion _flagsVar) + set (_flags ${${_flagsVar}}) + if (_compilerID MATCHES "MSVC") + # cl.exe options used + # /nologo suppresses display of sign-on banner + # /TC treat all files named on the command line as C source files + # /TP treat all files named on the command line as C++ source files + # /EP preprocess to stdout without #line directives + # /showIncludes list include files + set (_sourceFileTypeC "/TC") + set (_sourceFileTypeCXX "/TP") + if (_flags) + # append to list + list (APPEND _flags /nologo "${_sourceFileType${_language}}" /EP /showIncludes) + else() + # return as a flag string + set (_flags "${_sourceFileType${_language}} /EP /showIncludes") + endif() + elseif (_compilerID MATCHES "GNU") + # GCC options used + # -H print the name of each header file used + # -E invoke preprocessor + # -fdirectives-only do not expand macros, requires GCC >= 4.3 + if (_flags) + # append to list + list (APPEND _flags -H -E) + if (NOT "${_compilerVersion}" VERSION_LESS "4.3.0") + list (APPEND _flags "-fdirectives-only") + endif() + else() + # return as a flag string + set (_flags "-H -E") + if (NOT "${_compilerVersion}" VERSION_LESS "4.3.0") + set (_flags "${_flags} -fdirectives-only") + endif() + endif() + elseif (_compilerID MATCHES "Clang") + # Clang options used + # -H print the name of each header file used + # -E invoke preprocessor + # -fno-color-diagnostics don't prints diagnostics in color + if (_flags) + # append to list + list (APPEND _flags -H -E -fno-color-diagnostics) + else() + # return as a flag string + set (_flags "-H -E -fno-color-diagnostics") + endif() + elseif (_compilerID MATCHES "Intel") + if (WIN32) + # Windows Intel options used + # /nologo do not display compiler version information + # /QH display the include file order + # /EP preprocess to stdout, omitting #line directives + # /TC process all source or unrecognized file types as C source files + # /TP process all source or unrecognized file types as C++ source files + set (_sourceFileTypeC "/TC") + set (_sourceFileTypeCXX "/TP") + if (_flags) + # append to list + list (APPEND _flags /nologo "${_sourceFileType${_language}}" /EP /QH) + else() + # return as a flag string + set (_flags "${_sourceFileType${_language}} /EP /QH") + endif() + else() + # Linux / Mac OS X Intel options used + # -H print the name of each header file used + # -EP preprocess to stdout, omitting #line directives + # -Kc++ process all source or unrecognized file types as C++ source files + if (_flags) + # append to list + if ("${_language}" STREQUAL "CXX") + list (APPEND _flags -Kc++) + endif() + list (APPEND _flags -H -EP) + else() + # return as a flag string + if ("${_language}" STREQUAL "CXX") + set (_flags "-Kc++ ") + endif() + set (_flags "${_flags}-H -EP") + endif() + endif() + else() + message (FATAL_ERROR "cotire: unsupported ${_language} compiler ${_compilerID} version ${_compilerVersion}.") + endif() + set (${_flagsVar} ${_flags} PARENT_SCOPE) +endfunction() + +function (cotire_add_pch_compilation_flags _language _compilerID _compilerVersion _prefixFile _pchFile _hostFile _flagsVar) + set (_flags ${${_flagsVar}}) + if (_compilerID MATCHES "MSVC") + file (TO_NATIVE_PATH "${_prefixFile}" _prefixFileNative) + file (TO_NATIVE_PATH "${_pchFile}" _pchFileNative) + file (TO_NATIVE_PATH "${_hostFile}" _hostFileNative) + # cl.exe options used + # /Yc creates a precompiled header file + # /Fp specifies precompiled header binary file name + # /FI forces inclusion of file + # /TC treat all files named on the command line as C source files + # /TP treat all files named on the command line as C++ source files + # /Zs syntax check only + # /Zm precompiled header memory allocation scaling factor + set (_sourceFileTypeC "/TC") + set (_sourceFileTypeCXX "/TP") + if (_flags) + # append to list + list (APPEND _flags /nologo "${_sourceFileType${_language}}" + "/Yc${_prefixFileNative}" "/Fp${_pchFileNative}" "/FI${_prefixFileNative}" /Zs "${_hostFileNative}") + if (COTIRE_PCH_MEMORY_SCALING_FACTOR) + list (APPEND _flags "/Zm${COTIRE_PCH_MEMORY_SCALING_FACTOR}") + endif() + else() + # return as a flag string + set (_flags "/Yc\"${_prefixFileNative}\" /Fp\"${_pchFileNative}\" /FI\"${_prefixFileNative}\"") + if (COTIRE_PCH_MEMORY_SCALING_FACTOR) + set (_flags "${_flags} /Zm${COTIRE_PCH_MEMORY_SCALING_FACTOR}") + endif() + endif() + elseif (_compilerID MATCHES "GNU|Clang") + # GCC / Clang options used + # -x specify the source language + # -c compile but do not link + # -o place output in file + # note that we cannot use -w to suppress all warnings upon pre-compiling, because turning off a warning may + # alter compile flags as a side effect (e.g., -Wwrite-string implies -fconst-strings) + set (_xLanguage_C "c-header") + set (_xLanguage_CXX "c++-header") + if (_flags) + # append to list + list (APPEND _flags "-x" "${_xLanguage_${_language}}" "-c" "${_prefixFile}" -o "${_pchFile}") + else() + # return as a flag string + set (_flags "-x ${_xLanguage_${_language}} -c \"${_prefixFile}\" -o \"${_pchFile}\"") + endif() + elseif (_compilerID MATCHES "Intel") + if (WIN32) + file (TO_NATIVE_PATH "${_prefixFile}" _prefixFileNative) + file (TO_NATIVE_PATH "${_pchFile}" _pchFileNative) + file (TO_NATIVE_PATH "${_hostFile}" _hostFileNative) + # Windows Intel options used + # /nologo do not display compiler version information + # /Yc create a precompiled header (PCH) file + # /Fp specify a path or file name for precompiled header files + # /FI tells the preprocessor to include a specified file name as the header file + # /TC process all source or unrecognized file types as C source files + # /TP process all source or unrecognized file types as C++ source files + # /Zs syntax check only + # /Wpch-messages enable diagnostics related to pre-compiled headers (requires Intel XE 2013 Update 2) + set (_sourceFileTypeC "/TC") + set (_sourceFileTypeCXX "/TP") + if (_flags) + # append to list + list (APPEND _flags /nologo "${_sourceFileType${_language}}" + "/Yc" "/Fp${_pchFileNative}" "/FI${_prefixFileNative}" /Zs "${_hostFileNative}") + if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0") + list (APPEND _flags "/Wpch-messages") + endif() + else() + # return as a flag string + set (_flags "/Yc /Fp\"${_pchFileNative}\" /FI\"${_prefixFileNative}\"") + if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0") + set (_flags "${_flags} /Wpch-messages") + endif() + endif() + else() + # Linux / Mac OS X Intel options used + # -pch-dir location for precompiled header files + # -pch-create name of the precompiled header (PCH) to create + # -Kc++ process all source or unrecognized file types as C++ source files + # -fsyntax-only check only for correct syntax + # -Wpch-messages enable diagnostics related to pre-compiled headers (requires Intel XE 2013 Update 2) + get_filename_component(_pchDir "${_pchFile}" DIRECTORY) + get_filename_component(_pchName "${_pchFile}" NAME) + set (_xLanguage_C "c-header") + set (_xLanguage_CXX "c++-header") + set (_pchSuppressMessages FALSE) + if ("${CMAKE_${_language}_FLAGS}" MATCHES ".*-Wno-pch-messages.*") + set(_pchSuppressMessages TRUE) + endif() + if (_flags) + # append to list + if ("${_language}" STREQUAL "CXX") + list (APPEND _flags -Kc++) + endif() + list (APPEND _flags "-include" "${_prefixFile}" "-pch-dir" "${_pchDir}" "-pch-create" "${_pchName}" "-fsyntax-only" "${_hostFile}") + if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0") + if (NOT _pchSuppressMessages) + list (APPEND _flags "-Wpch-messages") + endif() + endif() + else() + # return as a flag string + set (_flags "-include \"${_prefixFile}\" -pch-dir \"${_pchDir}\" -pch-create \"${_pchName}\"") + if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0") + if (NOT _pchSuppressMessages) + set (_flags "${_flags} -Wpch-messages") + endif() + endif() + endif() + endif() + else() + message (FATAL_ERROR "cotire: unsupported ${_language} compiler ${_compilerID} version ${_compilerVersion}.") + endif() + set (${_flagsVar} ${_flags} PARENT_SCOPE) +endfunction() + +function (cotire_add_prefix_pch_inclusion_flags _language _compilerID _compilerVersion _prefixFile _pchFile _flagsVar) + set (_flags ${${_flagsVar}}) + if (_compilerID MATCHES "MSVC") + file (TO_NATIVE_PATH "${_prefixFile}" _prefixFileNative) + # cl.exe options used + # /Yu uses a precompiled header file during build + # /Fp specifies precompiled header binary file name + # /FI forces inclusion of file + # /Zm precompiled header memory allocation scaling factor + if (_pchFile) + file (TO_NATIVE_PATH "${_pchFile}" _pchFileNative) + if (_flags) + # append to list + list (APPEND _flags "/Yu${_prefixFileNative}" "/Fp${_pchFileNative}" "/FI${_prefixFileNative}") + if (COTIRE_PCH_MEMORY_SCALING_FACTOR) + list (APPEND _flags "/Zm${COTIRE_PCH_MEMORY_SCALING_FACTOR}") + endif() + else() + # return as a flag string + set (_flags "/Yu\"${_prefixFileNative}\" /Fp\"${_pchFileNative}\" /FI\"${_prefixFileNative}\"") + if (COTIRE_PCH_MEMORY_SCALING_FACTOR) + set (_flags "${_flags} /Zm${COTIRE_PCH_MEMORY_SCALING_FACTOR}") + endif() + endif() + else() + # no precompiled header, force inclusion of prefix header + if (_flags) + # append to list + list (APPEND _flags "/FI${_prefixFileNative}") + else() + # return as a flag string + set (_flags "/FI\"${_prefixFileNative}\"") + endif() + endif() + elseif (_compilerID MATCHES "GNU") + # GCC options used + # -include process include file as the first line of the primary source file + # -Winvalid-pch warns if precompiled header is found but cannot be used + # note: ccache requires the -include flag to be used in order to process precompiled header correctly + if (_flags) + # append to list + list (APPEND _flags "-Winvalid-pch" "-include" "${_prefixFile}") + else() + # return as a flag string + set (_flags "-Winvalid-pch -include \"${_prefixFile}\"") + endif() + elseif (_compilerID MATCHES "Clang") + # Clang options used + # -include process include file as the first line of the primary source file + # -include-pch include precompiled header file + # -Qunused-arguments don't emit warning for unused driver arguments + # note: ccache requires the -include flag to be used in order to process precompiled header correctly + if (_flags) + # append to list + list (APPEND _flags "-Qunused-arguments" "-include" "${_prefixFile}") + else() + # return as a flag string + set (_flags "-Qunused-arguments -include \"${_prefixFile}\"") + endif() + elseif (_compilerID MATCHES "Intel") + if (WIN32) + file (TO_NATIVE_PATH "${_prefixFile}" _prefixFileNative) + # Windows Intel options used + # /Yu use a precompiled header (PCH) file + # /Fp specify a path or file name for precompiled header files + # /FI tells the preprocessor to include a specified file name as the header file + # /Wpch-messages enable diagnostics related to pre-compiled headers (requires Intel XE 2013 Update 2) + if (_pchFile) + file (TO_NATIVE_PATH "${_pchFile}" _pchFileNative) + if (_flags) + # append to list + list (APPEND _flags "/Yu" "/Fp${_pchFileNative}" "/FI${_prefixFileNative}") + if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0") + list (APPEND _flags "/Wpch-messages") + endif() + else() + # return as a flag string + set (_flags "/Yu /Fp\"${_pchFileNative}\" /FI\"${_prefixFileNative}\"") + if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0") + set (_flags "${_flags} /Wpch-messages") + endif() + endif() + else() + # no precompiled header, force inclusion of prefix header + if (_flags) + # append to list + list (APPEND _flags "/FI${_prefixFileNative}") + else() + # return as a flag string + set (_flags "/FI\"${_prefixFileNative}\"") + endif() + endif() + else() + # Linux / Mac OS X Intel options used + # -pch-dir location for precompiled header files + # -pch-use name of the precompiled header (PCH) to use + # -include process include file as the first line of the primary source file + # -Wpch-messages enable diagnostics related to pre-compiled headers (requires Intel XE 2013 Update 2) + if (_pchFile) + get_filename_component(_pchDir "${_pchFile}" DIRECTORY) + get_filename_component(_pchName "${_pchFile}" NAME) + set (_pchSuppressMessages FALSE) + if ("${CMAKE_${_language}_FLAGS}" MATCHES ".*-Wno-pch-messages.*") + set(_pchSuppressMessages TRUE) + endif() + if (_flags) + # append to list + list (APPEND _flags "-include" "${_prefixFile}" "-pch-dir" "${_pchDir}" "-pch-use" "${_pchName}") + if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0") + if (NOT _pchSuppressMessages) + list (APPEND _flags "-Wpch-messages") + endif() + endif() + else() + # return as a flag string + set (_flags "-include \"${_prefixFile}\" -pch-dir \"${_pchDir}\" -pch-use \"${_pchName}\"") + if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0") + if (NOT _pchSuppressMessages) + set (_flags "${_flags} -Wpch-messages") + endif() + endif() + endif() + else() + # no precompiled header, force inclusion of prefix header + if (_flags) + # append to list + list (APPEND _flags "-include" "${_prefixFile}") + else() + # return as a flag string + set (_flags "-include \"${_prefixFile}\"") + endif() + endif() + endif() + else() + message (FATAL_ERROR "cotire: unsupported ${_language} compiler ${_compilerID} version ${_compilerVersion}.") + endif() + set (${_flagsVar} ${_flags} PARENT_SCOPE) +endfunction() + +function (cotire_precompile_prefix_header _prefixFile _pchFile _hostFile) + set(_options "") + set(_oneValueArgs COMPILER_EXECUTABLE COMPILER_ARG1 COMPILER_ID COMPILER_VERSION LANGUAGE) + set(_multiValueArgs COMPILE_DEFINITIONS COMPILE_FLAGS INCLUDE_DIRECTORIES SYSTEM_INCLUDE_DIRECTORIES SYS COMPILER_LAUNCHER) + cmake_parse_arguments(_option "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN}) + if (NOT _option_LANGUAGE) + set (_option_LANGUAGE "CXX") + endif() + if (NOT _option_COMPILER_ID) + set (_option_COMPILER_ID "${CMAKE_${_option_LANGUAGE}_ID}") + endif() + if (NOT _option_COMPILER_VERSION) + set (_option_COMPILER_VERSION "${CMAKE_${_option_LANGUAGE}_COMPILER_VERSION}") + endif() + cotire_init_compile_cmd(_cmd "${_option_LANGUAGE}" "${_option_COMPILER_LAUNCHER}" "${_option_COMPILER_EXECUTABLE}" "${_option_COMPILER_ARG1}") + cotire_add_definitions_to_cmd(_cmd "${_option_LANGUAGE}" ${_option_COMPILE_DEFINITIONS}) + cotire_add_compile_flags_to_cmd(_cmd ${_option_COMPILE_FLAGS}) + cotire_add_includes_to_cmd(_cmd "${_option_LANGUAGE}" _option_INCLUDE_DIRECTORIES _option_SYSTEM_INCLUDE_DIRECTORIES) + cotire_add_frameworks_to_cmd(_cmd "${_option_LANGUAGE}" _option_INCLUDE_DIRECTORIES _option_SYSTEM_INCLUDE_DIRECTORIES) + cotire_add_pch_compilation_flags( + "${_option_LANGUAGE}" "${_option_COMPILER_ID}" "${_option_COMPILER_VERSION}" + "${_prefixFile}" "${_pchFile}" "${_hostFile}" _cmd) + if (COTIRE_VERBOSE) + message (STATUS "execute_process: ${_cmd}") + endif() + if (_option_COMPILER_ID MATCHES "MSVC") + # cl.exe messes with the output streams unless the environment variable VS_UNICODE_OUTPUT is cleared + unset (ENV{VS_UNICODE_OUTPUT}) + elseif (_option_COMPILER_ID MATCHES "GNU|Clang") + if (_option_COMPILER_LAUNCHER MATCHES "ccache" OR + _option_COMPILER_EXECUTABLE MATCHES "ccache") + # Newer versions of Clang and GCC seem to embed a compilation timestamp into the precompiled header binary, + # which results in "file has been modified since the precompiled header was built" errors if ccache is used. + # We work around the problem by disabling ccache upon pre-compiling the prefix header. + set (ENV{CCACHE_DISABLE} "true") + endif() + endif() + execute_process( + COMMAND ${_cmd} + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + RESULT_VARIABLE _result) + if (_result) + message (FATAL_ERROR "cotire: error ${_result} precompiling ${_prefixFile}.") + endif() +endfunction() + +function (cotire_check_precompiled_header_support _language _target _msgVar) + set (_unsupportedCompiler + "Precompiled headers not supported for ${_language} compiler ${CMAKE_${_language}_COMPILER_ID}") + if (CMAKE_${_language}_COMPILER_ID MATCHES "MSVC") + # supported since Visual Studio C++ 6.0 + # and CMake does not support an earlier version + set (${_msgVar} "" PARENT_SCOPE) + elseif (CMAKE_${_language}_COMPILER_ID MATCHES "GNU") + # GCC PCH support requires version >= 3.4 + if ("${CMAKE_${_language}_COMPILER_VERSION}" VERSION_LESS "3.4.0") + set (${_msgVar} "${_unsupportedCompiler} version ${CMAKE_${_language}_COMPILER_VERSION}." PARENT_SCOPE) + else() + set (${_msgVar} "" PARENT_SCOPE) + endif() + elseif (CMAKE_${_language}_COMPILER_ID MATCHES "Clang") + # all Clang versions have PCH support + set (${_msgVar} "" PARENT_SCOPE) + elseif (CMAKE_${_language}_COMPILER_ID MATCHES "Intel") + # Intel PCH support requires version >= 8.0.0 + if ("${CMAKE_${_language}_COMPILER_VERSION}" VERSION_LESS "8.0.0") + set (${_msgVar} "${_unsupportedCompiler} version ${CMAKE_${_language}_COMPILER_VERSION}." PARENT_SCOPE) + else() + set (${_msgVar} "" PARENT_SCOPE) + endif() + else() + set (${_msgVar} "${_unsupportedCompiler}." PARENT_SCOPE) + endif() + get_target_property(_launcher ${_target} ${_language}_COMPILER_LAUNCHER) + if (CMAKE_${_language}_COMPILER MATCHES "ccache" OR _launcher MATCHES "ccache") + if (DEFINED ENV{CCACHE_SLOPPINESS}) + if (NOT "$ENV{CCACHE_SLOPPINESS}" MATCHES "pch_defines" OR NOT "$ENV{CCACHE_SLOPPINESS}" MATCHES "time_macros") + set (${_msgVar} + "ccache requires the environment variable CCACHE_SLOPPINESS to be set to \"pch_defines,time_macros\"." + PARENT_SCOPE) + endif() + else() + if (_launcher MATCHES "ccache") + get_filename_component(_ccacheExe "${_launcher}" REALPATH) + else() + get_filename_component(_ccacheExe "${CMAKE_${_language}_COMPILER}" REALPATH) + endif() + execute_process( + COMMAND "${_ccacheExe}" "--print-config" + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" + RESULT_VARIABLE _result + OUTPUT_VARIABLE _ccacheConfig OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET) + if (_result OR NOT + _ccacheConfig MATCHES "sloppiness.*=.*time_macros" OR NOT + _ccacheConfig MATCHES "sloppiness.*=.*pch_defines") + set (${_msgVar} + "ccache requires configuration setting \"sloppiness\" to be set to \"pch_defines,time_macros\"." + PARENT_SCOPE) + endif() + endif() + endif() + if (APPLE) + # PCH compilation not supported by GCC / Clang for multi-architecture builds (e.g., i386, x86_64) + cotire_get_configuration_types(_configs) + foreach (_config ${_configs}) + set (_targetFlags "") + cotire_get_target_compile_flags("${_config}" "${_language}" "${_target}" _targetFlags) + cotire_filter_compile_flags("${_language}" "arch" _architectures _ignore ${_targetFlags}) + list (LENGTH _architectures _numberOfArchitectures) + if (_numberOfArchitectures GREATER 1) + string (REPLACE ";" ", " _architectureStr "${_architectures}") + set (${_msgVar} + "Precompiled headers not supported on Darwin for multi-architecture builds (${_architectureStr})." + PARENT_SCOPE) + break() + endif() + endforeach() + endif() +endfunction() + +macro (cotire_get_intermediate_dir _cotireDir) + # ${CMAKE_CFG_INTDIR} may reference a build-time variable when using a generator which supports configuration types + get_filename_component(${_cotireDir} "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${COTIRE_INTDIR}" ABSOLUTE) +endmacro() + +macro (cotire_setup_file_extension_variables) + set (_unityFileExt_C ".c") + set (_unityFileExt_CXX ".cxx") + set (_prefixFileExt_C ".h") + set (_prefixFileExt_CXX ".hxx") + set (_prefixSourceFileExt_C ".c") + set (_prefixSourceFileExt_CXX ".cxx") +endmacro() + +function (cotire_make_single_unity_source_file_path _language _target _unityFileVar) + cotire_setup_file_extension_variables() + if (NOT DEFINED _unityFileExt_${_language}) + set (${_unityFileVar} "" PARENT_SCOPE) + return() + endif() + set (_unityFileBaseName "${_target}_${_language}${COTIRE_UNITY_SOURCE_FILENAME_SUFFIX}") + set (_unityFileName "${_unityFileBaseName}${_unityFileExt_${_language}}") + cotire_get_intermediate_dir(_baseDir) + set (_unityFile "${_baseDir}/${_unityFileName}") + set (${_unityFileVar} "${_unityFile}" PARENT_SCOPE) +endfunction() + +function (cotire_make_unity_source_file_paths _language _target _maxIncludes _unityFilesVar) + cotire_setup_file_extension_variables() + if (NOT DEFINED _unityFileExt_${_language}) + set (${_unityFileVar} "" PARENT_SCOPE) + return() + endif() + set (_unityFileBaseName "${_target}_${_language}${COTIRE_UNITY_SOURCE_FILENAME_SUFFIX}") + cotire_get_intermediate_dir(_baseDir) + set (_startIndex 0) + set (_index 0) + set (_unityFiles "") + set (_sourceFiles ${ARGN}) + foreach (_sourceFile ${_sourceFiles}) + get_source_file_property(_startNew "${_sourceFile}" COTIRE_START_NEW_UNITY_SOURCE) + math (EXPR _unityFileCount "${_index} - ${_startIndex}") + if (_startNew OR (_maxIncludes GREATER 0 AND NOT _unityFileCount LESS _maxIncludes)) + if (_index GREATER 0) + # start new unity file segment + math (EXPR _endIndex "${_index} - 1") + set (_unityFileName "${_unityFileBaseName}_${_startIndex}_${_endIndex}${_unityFileExt_${_language}}") + list (APPEND _unityFiles "${_baseDir}/${_unityFileName}") + endif() + set (_startIndex ${_index}) + endif() + math (EXPR _index "${_index} + 1") + endforeach() + list (LENGTH _sourceFiles _numberOfSources) + if (_startIndex EQUAL 0) + # there is only a single unity file + cotire_make_single_unity_source_file_path(${_language} ${_target} _unityFiles) + elseif (_startIndex LESS _numberOfSources) + # end with final unity file segment + math (EXPR _endIndex "${_index} - 1") + set (_unityFileName "${_unityFileBaseName}_${_startIndex}_${_endIndex}${_unityFileExt_${_language}}") + list (APPEND _unityFiles "${_baseDir}/${_unityFileName}") + endif() + set (${_unityFilesVar} ${_unityFiles} PARENT_SCOPE) + if (COTIRE_DEBUG AND _unityFiles) + message (STATUS "unity files: ${_unityFiles}") + endif() +endfunction() + +function (cotire_unity_to_prefix_file_path _language _target _unityFile _prefixFileVar) + cotire_setup_file_extension_variables() + if (NOT DEFINED _unityFileExt_${_language}) + set (${_prefixFileVar} "" PARENT_SCOPE) + return() + endif() + set (_unityFileBaseName "${_target}_${_language}${COTIRE_UNITY_SOURCE_FILENAME_SUFFIX}") + set (_prefixFileBaseName "${_target}_${_language}${COTIRE_PREFIX_HEADER_FILENAME_SUFFIX}") + string (REPLACE "${_unityFileBaseName}" "${_prefixFileBaseName}" _prefixFile "${_unityFile}") + string (REGEX REPLACE "${_unityFileExt_${_language}}$" "${_prefixFileExt_${_language}}" _prefixFile "${_prefixFile}") + set (${_prefixFileVar} "${_prefixFile}" PARENT_SCOPE) +endfunction() + +function (cotire_prefix_header_to_source_file_path _language _prefixHeaderFile _prefixSourceFileVar) + cotire_setup_file_extension_variables() + if (NOT DEFINED _prefixSourceFileExt_${_language}) + set (${_prefixSourceFileVar} "" PARENT_SCOPE) + return() + endif() + string (REGEX REPLACE "${_prefixFileExt_${_language}}$" "${_prefixSourceFileExt_${_language}}" _prefixSourceFile "${_prefixHeaderFile}") + set (${_prefixSourceFileVar} "${_prefixSourceFile}" PARENT_SCOPE) +endfunction() + +function (cotire_make_prefix_file_name _language _target _prefixFileBaseNameVar _prefixFileNameVar) + cotire_setup_file_extension_variables() + if (NOT _language) + set (_prefixFileBaseName "${_target}${COTIRE_PREFIX_HEADER_FILENAME_SUFFIX}") + set (_prefixFileName "${_prefixFileBaseName}${_prefixFileExt_C}") + elseif (DEFINED _prefixFileExt_${_language}) + set (_prefixFileBaseName "${_target}_${_language}${COTIRE_PREFIX_HEADER_FILENAME_SUFFIX}") + set (_prefixFileName "${_prefixFileBaseName}${_prefixFileExt_${_language}}") + else() + set (_prefixFileBaseName "") + set (_prefixFileName "") + endif() + set (${_prefixFileBaseNameVar} "${_prefixFileBaseName}" PARENT_SCOPE) + set (${_prefixFileNameVar} "${_prefixFileName}" PARENT_SCOPE) +endfunction() + +function (cotire_make_prefix_file_path _language _target _prefixFileVar) + cotire_make_prefix_file_name("${_language}" "${_target}" _prefixFileBaseName _prefixFileName) + set (${_prefixFileVar} "" PARENT_SCOPE) + if (_prefixFileName) + if (NOT _language) + set (_language "C") + endif() + if (CMAKE_${_language}_COMPILER_ID MATCHES "GNU|Clang|Intel|MSVC") + cotire_get_intermediate_dir(_baseDir) + set (${_prefixFileVar} "${_baseDir}/${_prefixFileName}" PARENT_SCOPE) + endif() + endif() +endfunction() + +function (cotire_make_pch_file_path _language _target _pchFileVar) + cotire_make_prefix_file_name("${_language}" "${_target}" _prefixFileBaseName _prefixFileName) + set (${_pchFileVar} "" PARENT_SCOPE) + if (_prefixFileBaseName AND _prefixFileName) + cotire_check_precompiled_header_support("${_language}" "${_target}" _msg) + if (NOT _msg) + if (XCODE) + # For Xcode, we completely hand off the compilation of the prefix header to the IDE + return() + endif() + cotire_get_intermediate_dir(_baseDir) + if (CMAKE_${_language}_COMPILER_ID MATCHES "MSVC") + # MSVC uses the extension .pch added to the prefix header base name + set (${_pchFileVar} "${_baseDir}/${_prefixFileBaseName}.pch" PARENT_SCOPE) + elseif (CMAKE_${_language}_COMPILER_ID MATCHES "Clang") + # Clang looks for a precompiled header corresponding to the prefix header with the extension .pch appended + set (${_pchFileVar} "${_baseDir}/${_prefixFileName}.pch" PARENT_SCOPE) + elseif (CMAKE_${_language}_COMPILER_ID MATCHES "GNU") + # GCC looks for a precompiled header corresponding to the prefix header with the extension .gch appended + set (${_pchFileVar} "${_baseDir}/${_prefixFileName}.gch" PARENT_SCOPE) + elseif (CMAKE_${_language}_COMPILER_ID MATCHES "Intel") + # Intel uses the extension .pchi added to the prefix header base name + set (${_pchFileVar} "${_baseDir}/${_prefixFileBaseName}.pchi" PARENT_SCOPE) + endif() + endif() + endif() +endfunction() + +function (cotire_select_unity_source_files _unityFile _sourcesVar) + set (_sourceFiles ${ARGN}) + if (_sourceFiles AND "${_unityFile}" MATCHES "${COTIRE_UNITY_SOURCE_FILENAME_SUFFIX}_([0-9]+)_([0-9]+)") + set (_startIndex ${CMAKE_MATCH_1}) + set (_endIndex ${CMAKE_MATCH_2}) + list (LENGTH _sourceFiles _numberOfSources) + if (NOT _startIndex LESS _numberOfSources) + math (EXPR _startIndex "${_numberOfSources} - 1") + endif() + if (NOT _endIndex LESS _numberOfSources) + math (EXPR _endIndex "${_numberOfSources} - 1") + endif() + set (_files "") + foreach (_index RANGE ${_startIndex} ${_endIndex}) + list (GET _sourceFiles ${_index} _file) + list (APPEND _files "${_file}") + endforeach() + else() + set (_files ${_sourceFiles}) + endif() + set (${_sourcesVar} ${_files} PARENT_SCOPE) +endfunction() + +function (cotire_get_unity_source_dependencies _language _target _dependencySourcesVar) + set (_dependencySources "") + # depend on target's generated source files + get_target_property(_targetSourceFiles ${_target} SOURCES) + cotire_get_objects_with_property_on(_generatedSources GENERATED SOURCE ${_targetSourceFiles}) + if (_generatedSources) + # but omit all generated source files that have the COTIRE_EXCLUDED property set to true + cotire_get_objects_with_property_on(_excludedGeneratedSources COTIRE_EXCLUDED SOURCE ${_generatedSources}) + if (_excludedGeneratedSources) + list (REMOVE_ITEM _generatedSources ${_excludedGeneratedSources}) + endif() + # and omit all generated source files that have the COTIRE_DEPENDENCY property set to false explicitly + cotire_get_objects_with_property_off(_excludedNonDependencySources COTIRE_DEPENDENCY SOURCE ${_generatedSources}) + if (_excludedNonDependencySources) + list (REMOVE_ITEM _generatedSources ${_excludedNonDependencySources}) + endif() + if (_generatedSources) + list (APPEND _dependencySources ${_generatedSources}) + endif() + endif() + if (COTIRE_DEBUG AND _dependencySources) + message (STATUS "${_language} ${_target} unity source dependencies: ${_dependencySources}") + endif() + set (${_dependencySourcesVar} ${_dependencySources} PARENT_SCOPE) +endfunction() + +function (cotire_get_prefix_header_dependencies _language _target _dependencySourcesVar) + set (_dependencySources "") + # depend on target source files marked with custom COTIRE_DEPENDENCY property + get_target_property(_targetSourceFiles ${_target} SOURCES) + cotire_get_objects_with_property_on(_dependencySources COTIRE_DEPENDENCY SOURCE ${_targetSourceFiles}) + if (COTIRE_DEBUG AND _dependencySources) + message (STATUS "${_language} ${_target} prefix header dependencies: ${_dependencySources}") + endif() + set (${_dependencySourcesVar} ${_dependencySources} PARENT_SCOPE) +endfunction() + +function (cotire_generate_target_script _language _configurations _target _targetScriptVar _targetConfigScriptVar) + set (_targetSources ${ARGN}) + cotire_get_prefix_header_dependencies(${_language} ${_target} COTIRE_TARGET_PREFIX_DEPENDS ${_targetSources}) + cotire_get_unity_source_dependencies(${_language} ${_target} COTIRE_TARGET_UNITY_DEPENDS ${_targetSources}) + # set up variables to be configured + set (COTIRE_TARGET_LANGUAGE "${_language}") + get_target_property(COTIRE_TARGET_IGNORE_PATH ${_target} COTIRE_PREFIX_HEADER_IGNORE_PATH) + cotire_add_sys_root_paths(COTIRE_TARGET_IGNORE_PATH) + get_target_property(COTIRE_TARGET_INCLUDE_PATH ${_target} COTIRE_PREFIX_HEADER_INCLUDE_PATH) + cotire_add_sys_root_paths(COTIRE_TARGET_INCLUDE_PATH) + get_target_property(COTIRE_TARGET_PRE_UNDEFS ${_target} COTIRE_UNITY_SOURCE_PRE_UNDEFS) + get_target_property(COTIRE_TARGET_POST_UNDEFS ${_target} COTIRE_UNITY_SOURCE_POST_UNDEFS) + get_target_property(COTIRE_TARGET_MAXIMUM_NUMBER_OF_INCLUDES ${_target} COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES) + get_target_property(COTIRE_TARGET_INCLUDE_PRIORITY_PATH ${_target} COTIRE_PREFIX_HEADER_INCLUDE_PRIORITY_PATH) + cotire_get_source_files_undefs(COTIRE_UNITY_SOURCE_PRE_UNDEFS COTIRE_TARGET_SOURCES_PRE_UNDEFS ${_targetSources}) + cotire_get_source_files_undefs(COTIRE_UNITY_SOURCE_POST_UNDEFS COTIRE_TARGET_SOURCES_POST_UNDEFS ${_targetSources}) + set (COTIRE_TARGET_CONFIGURATION_TYPES "${_configurations}") + foreach (_config ${_configurations}) + string (TOUPPER "${_config}" _upperConfig) + cotire_get_target_include_directories( + "${_config}" "${_language}" "${_target}" COTIRE_TARGET_INCLUDE_DIRECTORIES_${_upperConfig} COTIRE_TARGET_SYSTEM_INCLUDE_DIRECTORIES_${_upperConfig}) + cotire_get_target_compile_definitions( + "${_config}" "${_language}" "${_target}" COTIRE_TARGET_COMPILE_DEFINITIONS_${_upperConfig}) + cotire_get_target_compiler_flags( + "${_config}" "${_language}" "${_target}" COTIRE_TARGET_COMPILE_FLAGS_${_upperConfig}) + cotire_get_source_files_compile_definitions( + "${_config}" "${_language}" COTIRE_TARGET_SOURCES_COMPILE_DEFINITIONS_${_upperConfig} ${_targetSources}) + endforeach() + get_target_property(COTIRE_TARGET_${_language}_COMPILER_LAUNCHER ${_target} ${_language}_COMPILER_LAUNCHER) + # set up COTIRE_TARGET_SOURCES + set (COTIRE_TARGET_SOURCES "") + foreach (_sourceFile ${_targetSources}) + get_source_file_property(_generated "${_sourceFile}" GENERATED) + if (_generated) + # use absolute paths for generated files only, retrieving the LOCATION property is an expensive operation + get_source_file_property(_sourceLocation "${_sourceFile}" LOCATION) + list (APPEND COTIRE_TARGET_SOURCES "${_sourceLocation}") + else() + list (APPEND COTIRE_TARGET_SOURCES "${_sourceFile}") + endif() + endforeach() + # copy variable definitions to cotire target script + get_cmake_property(_vars VARIABLES) + string (REGEX MATCHALL "COTIRE_[A-Za-z0-9_]+" _matchVars "${_vars}") + # omit COTIRE_*_INIT variables + string (REGEX MATCHALL "COTIRE_[A-Za-z0-9_]+_INIT" _initVars "${_matchVars}") + if (_initVars) + list (REMOVE_ITEM _matchVars ${_initVars}) + endif() + # omit COTIRE_VERBOSE which is passed as a CMake define on command line + list (REMOVE_ITEM _matchVars COTIRE_VERBOSE) + set (_contents "") + set (_contentsHasGeneratorExpressions FALSE) + foreach (_var IN LISTS _matchVars ITEMS + XCODE MSVC CMAKE_GENERATOR CMAKE_BUILD_TYPE CMAKE_CONFIGURATION_TYPES + CMAKE_${_language}_COMPILER_ID CMAKE_${_language}_COMPILER_VERSION + CMAKE_${_language}_COMPILER_LAUNCHER CMAKE_${_language}_COMPILER CMAKE_${_language}_COMPILER_ARG1 + CMAKE_INCLUDE_FLAG_${_language} CMAKE_INCLUDE_FLAG_SEP_${_language} + CMAKE_INCLUDE_SYSTEM_FLAG_${_language} + CMAKE_${_language}_FRAMEWORK_SEARCH_FLAG + CMAKE_${_language}_SYSTEM_FRAMEWORK_SEARCH_FLAG + CMAKE_${_language}_SOURCE_FILE_EXTENSIONS) + if (DEFINED ${_var}) + string (REPLACE "\"" "\\\"" _value "${${_var}}") + set (_contents "${_contents}set (${_var} \"${_value}\")\n") + if (NOT _contentsHasGeneratorExpressions) + if ("${_value}" MATCHES "\\$<.*>") + set (_contentsHasGeneratorExpressions TRUE) + endif() + endif() + endif() + endforeach() + # generate target script file + get_filename_component(_moduleName "${COTIRE_CMAKE_MODULE_FILE}" NAME) + set (_targetCotireScript "${CMAKE_CURRENT_BINARY_DIR}/${_target}_${_language}_${_moduleName}") + cotire_write_file("CMAKE" "${_targetCotireScript}" "${_contents}" FALSE) + if (_contentsHasGeneratorExpressions) + # use file(GENERATE ...) to expand generator expressions in the target script at CMake generate-time + set (_configNameOrNoneGeneratorExpression "$<$:None>$<$>:$>") + set (_targetCotireConfigScript "${CMAKE_CURRENT_BINARY_DIR}/${_target}_${_language}_${_configNameOrNoneGeneratorExpression}_${_moduleName}") + file (GENERATE OUTPUT "${_targetCotireConfigScript}" INPUT "${_targetCotireScript}") + else() + set (_targetCotireConfigScript "${_targetCotireScript}") + endif() + set (${_targetScriptVar} "${_targetCotireScript}" PARENT_SCOPE) + set (${_targetConfigScriptVar} "${_targetCotireConfigScript}" PARENT_SCOPE) +endfunction() + +function (cotire_setup_pch_file_compilation _language _target _targetScript _prefixFile _pchFile _hostFile) + set (_sourceFiles ${ARGN}) + if (CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel") + # for Visual Studio and Intel, we attach the precompiled header compilation to the host file + # the remaining files include the precompiled header, see cotire_setup_pch_file_inclusion + if (_sourceFiles) + set (_flags "") + cotire_add_pch_compilation_flags( + "${_language}" "${CMAKE_${_language}_COMPILER_ID}" "${CMAKE_${_language}_COMPILER_VERSION}" + "${_prefixFile}" "${_pchFile}" "${_hostFile}" _flags) + set_property (SOURCE ${_hostFile} APPEND_STRING PROPERTY COMPILE_FLAGS " ${_flags} ") + set_property (SOURCE ${_hostFile} APPEND PROPERTY OBJECT_OUTPUTS "${_pchFile}") + # make object file generated from host file depend on prefix header + set_property (SOURCE ${_hostFile} APPEND PROPERTY OBJECT_DEPENDS "${_prefixFile}") + # mark host file as cotired to prevent it from being used in another cotired target + set_property (SOURCE ${_hostFile} PROPERTY COTIRE_TARGET "${_target}") + endif() + elseif ("${CMAKE_GENERATOR}" MATCHES "Make|Ninja") + # for makefile based generator, we add a custom command to precompile the prefix header + if (_targetScript) + cotire_set_cmd_to_prologue(_cmds) + list (APPEND _cmds -P "${COTIRE_CMAKE_MODULE_FILE}" "precompile" "${_targetScript}" "${_prefixFile}" "${_pchFile}" "${_hostFile}") + if (MSVC_IDE) + file (TO_NATIVE_PATH "${_pchFile}" _pchFileLogPath) + else() + file (RELATIVE_PATH _pchFileLogPath "${CMAKE_BINARY_DIR}" "${_pchFile}") + endif() + # make precompiled header compilation depend on the actual compiler executable used to force + # re-compilation when the compiler executable is updated. This prevents "created by a different GCC executable" + # warnings when the precompiled header is included. + get_filename_component(_realCompilerExe "${CMAKE_${_language}_COMPILER}" ABSOLUTE) + if (COTIRE_DEBUG) + message (STATUS "add_custom_command: OUTPUT ${_pchFile} ${_cmds} DEPENDS ${_prefixFile} ${_realCompilerExe} IMPLICIT_DEPENDS ${_language} ${_prefixFile}") + endif() + set_property (SOURCE "${_pchFile}" PROPERTY GENERATED TRUE) + add_custom_command( + OUTPUT "${_pchFile}" + COMMAND ${_cmds} + DEPENDS "${_prefixFile}" "${_realCompilerExe}" + IMPLICIT_DEPENDS ${_language} "${_prefixFile}" + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + COMMENT "Building ${_language} precompiled header ${_pchFileLogPath}" + VERBATIM) + endif() + endif() +endfunction() + +function (cotire_setup_pch_file_inclusion _language _target _wholeTarget _prefixFile _pchFile _hostFile) + if (CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel") + # for Visual Studio and Intel, we include the precompiled header in all but the host file + # the host file does the precompiled header compilation, see cotire_setup_pch_file_compilation + set (_sourceFiles ${ARGN}) + list (LENGTH _sourceFiles _numberOfSourceFiles) + if (_numberOfSourceFiles GREATER 0) + # mark sources as cotired to prevent them from being used in another cotired target + set_source_files_properties(${_sourceFiles} PROPERTIES COTIRE_TARGET "${_target}") + set (_flags "") + cotire_add_prefix_pch_inclusion_flags( + "${_language}" "${CMAKE_${_language}_COMPILER_ID}" "${CMAKE_${_language}_COMPILER_VERSION}" + "${_prefixFile}" "${_pchFile}" _flags) + set_property (SOURCE ${_sourceFiles} APPEND_STRING PROPERTY COMPILE_FLAGS " ${_flags} ") + # make object files generated from source files depend on precompiled header + set_property (SOURCE ${_sourceFiles} APPEND PROPERTY OBJECT_DEPENDS "${_pchFile}") + endif() + elseif ("${CMAKE_GENERATOR}" MATCHES "Make|Ninja") + set (_sourceFiles ${_hostFile} ${ARGN}) + if (NOT _wholeTarget) + # for makefile based generator, we force the inclusion of the prefix header for a subset + # of the source files, if this is a multi-language target or has excluded files + set (_flags "") + cotire_add_prefix_pch_inclusion_flags( + "${_language}" "${CMAKE_${_language}_COMPILER_ID}" "${CMAKE_${_language}_COMPILER_VERSION}" + "${_prefixFile}" "${_pchFile}" _flags) + set_property (SOURCE ${_sourceFiles} APPEND_STRING PROPERTY COMPILE_FLAGS " ${_flags} ") + # mark sources as cotired to prevent them from being used in another cotired target + set_source_files_properties(${_sourceFiles} PROPERTIES COTIRE_TARGET "${_target}") + endif() + # make object files generated from source files depend on precompiled header + set_property (SOURCE ${_sourceFiles} APPEND PROPERTY OBJECT_DEPENDS "${_pchFile}") + endif() +endfunction() + +function (cotire_setup_prefix_file_inclusion _language _target _prefixFile) + set (_sourceFiles ${ARGN}) + # force the inclusion of the prefix header for the given source files + set (_flags "") + set (_pchFile "") + cotire_add_prefix_pch_inclusion_flags( + "${_language}" "${CMAKE_${_language}_COMPILER_ID}" "${CMAKE_${_language}_COMPILER_VERSION}" + "${_prefixFile}" "${_pchFile}" _flags) + set_property (SOURCE ${_sourceFiles} APPEND_STRING PROPERTY COMPILE_FLAGS " ${_flags} ") + # mark sources as cotired to prevent them from being used in another cotired target + set_source_files_properties(${_sourceFiles} PROPERTIES COTIRE_TARGET "${_target}") + # make object files generated from source files depend on prefix header + set_property (SOURCE ${_sourceFiles} APPEND PROPERTY OBJECT_DEPENDS "${_prefixFile}") +endfunction() + +function (cotire_get_first_set_property_value _propertyValueVar _type _object) + set (_properties ${ARGN}) + foreach (_property ${_properties}) + get_property(_propertyValue ${_type} "${_object}" PROPERTY ${_property}) + if (_propertyValue) + set (${_propertyValueVar} ${_propertyValue} PARENT_SCOPE) + return() + endif() + endforeach() + set (${_propertyValueVar} "" PARENT_SCOPE) +endfunction() + +function (cotire_setup_combine_command _language _targetScript _joinedFile _cmdsVar) + set (_files ${ARGN}) + set (_filesPaths "") + foreach (_file ${_files}) + get_filename_component(_filePath "${_file}" ABSOLUTE) + list (APPEND _filesPaths "${_filePath}") + endforeach() + cotire_set_cmd_to_prologue(_prefixCmd) + list (APPEND _prefixCmd -P "${COTIRE_CMAKE_MODULE_FILE}" "combine") + if (_targetScript) + list (APPEND _prefixCmd "${_targetScript}") + endif() + list (APPEND _prefixCmd "${_joinedFile}" ${_filesPaths}) + if (COTIRE_DEBUG) + message (STATUS "add_custom_command: OUTPUT ${_joinedFile} COMMAND ${_prefixCmd} DEPENDS ${_files}") + endif() + set_property (SOURCE "${_joinedFile}" PROPERTY GENERATED TRUE) + if (MSVC_IDE) + file (TO_NATIVE_PATH "${_joinedFile}" _joinedFileLogPath) + else() + file (RELATIVE_PATH _joinedFileLogPath "${CMAKE_BINARY_DIR}" "${_joinedFile}") + endif() + get_filename_component(_joinedFileBaseName "${_joinedFile}" NAME_WE) + get_filename_component(_joinedFileExt "${_joinedFile}" EXT) + if (_language AND _joinedFileBaseName MATCHES "${COTIRE_UNITY_SOURCE_FILENAME_SUFFIX}$") + set (_comment "Generating ${_language} unity source ${_joinedFileLogPath}") + elseif (_language AND _joinedFileBaseName MATCHES "${COTIRE_PREFIX_HEADER_FILENAME_SUFFIX}$") + if (_joinedFileExt MATCHES "^\\.c") + set (_comment "Generating ${_language} prefix source ${_joinedFileLogPath}") + else() + set (_comment "Generating ${_language} prefix header ${_joinedFileLogPath}") + endif() + else() + set (_comment "Generating ${_joinedFileLogPath}") + endif() + add_custom_command( + OUTPUT "${_joinedFile}" + COMMAND ${_prefixCmd} + DEPENDS ${_files} + COMMENT "${_comment}" + WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" + VERBATIM) + list (APPEND ${_cmdsVar} COMMAND ${_prefixCmd}) + set (${_cmdsVar} ${${_cmdsVar}} PARENT_SCOPE) +endfunction() + +function (cotire_setup_target_pch_usage _languages _target _wholeTarget) + if (XCODE) + # for Xcode, we attach a pre-build action to generate the unity sources and prefix headers + set (_prefixFiles "") + foreach (_language ${_languages}) + get_property(_prefixFile TARGET ${_target} PROPERTY COTIRE_${_language}_PREFIX_HEADER) + if (_prefixFile) + list (APPEND _prefixFiles "${_prefixFile}") + endif() + endforeach() + set (_cmds ${ARGN}) + list (LENGTH _prefixFiles _numberOfPrefixFiles) + if (_numberOfPrefixFiles GREATER 1) + # we also generate a generic, single prefix header which includes all language specific prefix headers + set (_language "") + set (_targetScript "") + cotire_make_prefix_file_path("${_language}" ${_target} _prefixHeader) + cotire_setup_combine_command("${_language}" "${_targetScript}" "${_prefixHeader}" _cmds ${_prefixFiles}) + else() + set (_prefixHeader "${_prefixFiles}") + endif() + if (COTIRE_DEBUG) + message (STATUS "add_custom_command: TARGET ${_target} PRE_BUILD ${_cmds}") + endif() + # because CMake PRE_BUILD command does not support dependencies, + # we check dependencies explicity in cotire script mode when the pre-build action is run + add_custom_command( + TARGET "${_target}" + PRE_BUILD ${_cmds} + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + COMMENT "Updating target ${_target} prefix headers" + VERBATIM) + # make Xcode precompile the generated prefix header with ProcessPCH and ProcessPCH++ + set_target_properties(${_target} PROPERTIES XCODE_ATTRIBUTE_GCC_PRECOMPILE_PREFIX_HEADER "YES") + set_target_properties(${_target} PROPERTIES XCODE_ATTRIBUTE_GCC_PREFIX_HEADER "${_prefixHeader}") + elseif ("${CMAKE_GENERATOR}" MATCHES "Make|Ninja") + # for makefile based generator, we force inclusion of the prefix header for all target source files + # if this is a single-language target without any excluded files + if (_wholeTarget) + set (_language "${_languages}") + # for Visual Studio and Intel, precompiled header inclusion is always done on the source file level + # see cotire_setup_pch_file_inclusion + if (NOT CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel") + get_property(_prefixFile TARGET ${_target} PROPERTY COTIRE_${_language}_PREFIX_HEADER) + if (_prefixFile) + get_property(_pchFile TARGET ${_target} PROPERTY COTIRE_${_language}_PRECOMPILED_HEADER) + set (_options COMPILE_OPTIONS) + cotire_add_prefix_pch_inclusion_flags( + "${_language}" "${CMAKE_${_language}_COMPILER_ID}" "${CMAKE_${_language}_COMPILER_VERSION}" + "${_prefixFile}" "${_pchFile}" _options) + set_property(TARGET ${_target} APPEND PROPERTY ${_options}) + endif() + endif() + endif() + endif() +endfunction() + +function (cotire_setup_unity_generation_commands _language _target _targetScript _targetConfigScript _unityFiles _cmdsVar) + set (_dependencySources "") + cotire_get_unity_source_dependencies(${_language} ${_target} _dependencySources ${ARGN}) + foreach (_unityFile ${_unityFiles}) + set_property (SOURCE "${_unityFile}" PROPERTY GENERATED TRUE) + # set up compiled unity source dependencies via OBJECT_DEPENDS + # this ensures that missing source files are generated before the unity file is compiled + if (COTIRE_DEBUG AND _dependencySources) + message (STATUS "${_unityFile} OBJECT_DEPENDS ${_dependencySources}") + endif() + if (_dependencySources) + # the OBJECT_DEPENDS property requires a list of full paths + set (_objectDependsPaths "") + foreach (_sourceFile ${_dependencySources}) + get_source_file_property(_sourceLocation "${_sourceFile}" LOCATION) + list (APPEND _objectDependsPaths "${_sourceLocation}") + endforeach() + set_property (SOURCE "${_unityFile}" PROPERTY OBJECT_DEPENDS ${_objectDependsPaths}) + endif() + if (WIN32 AND CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel") + # unity file compilation results in potentially huge object file, thus use /bigobj by default unter MSVC and Windows Intel + set_property (SOURCE "${_unityFile}" APPEND_STRING PROPERTY COMPILE_FLAGS "/bigobj") + endif() + cotire_set_cmd_to_prologue(_unityCmd) + list (APPEND _unityCmd -P "${COTIRE_CMAKE_MODULE_FILE}" "unity" "${_targetConfigScript}" "${_unityFile}") + if (CMAKE_VERSION VERSION_LESS "3.1.0") + set (_unityCmdDepends "${_targetScript}") + else() + # CMake 3.1.0 supports generator expressions in arguments to DEPENDS + set (_unityCmdDepends "${_targetConfigScript}") + endif() + if (MSVC_IDE) + file (TO_NATIVE_PATH "${_unityFile}" _unityFileLogPath) + else() + file (RELATIVE_PATH _unityFileLogPath "${CMAKE_BINARY_DIR}" "${_unityFile}") + endif() + if (COTIRE_DEBUG) + message (STATUS "add_custom_command: OUTPUT ${_unityFile} COMMAND ${_unityCmd} DEPENDS ${_unityCmdDepends}") + endif() + add_custom_command( + OUTPUT "${_unityFile}" + COMMAND ${_unityCmd} + DEPENDS ${_unityCmdDepends} + COMMENT "Generating ${_language} unity source ${_unityFileLogPath}" + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + VERBATIM) + list (APPEND ${_cmdsVar} COMMAND ${_unityCmd}) + endforeach() + set (${_cmdsVar} ${${_cmdsVar}} PARENT_SCOPE) +endfunction() + +function (cotire_setup_prefix_generation_command _language _target _targetScript _prefixFile _unityFiles _cmdsVar) + set (_sourceFiles ${ARGN}) + set (_dependencySources "") + cotire_get_prefix_header_dependencies(${_language} ${_target} _dependencySources ${_sourceFiles}) + cotire_set_cmd_to_prologue(_prefixCmd) + list (APPEND _prefixCmd -P "${COTIRE_CMAKE_MODULE_FILE}" "prefix" "${_targetScript}" "${_prefixFile}" ${_unityFiles}) + set_property (SOURCE "${_prefixFile}" PROPERTY GENERATED TRUE) + # make prefix header generation depend on the actual compiler executable used to force + # re-generation when the compiler executable is updated. This prevents "file not found" + # errors for compiler version specific system header files. + get_filename_component(_realCompilerExe "${CMAKE_${_language}_COMPILER}" ABSOLUTE) + if (COTIRE_DEBUG) + message (STATUS "add_custom_command: OUTPUT ${_prefixFile} COMMAND ${_prefixCmd} DEPENDS ${_unityFile} ${_dependencySources} ${_realCompilerExe}") + endif() + if (MSVC_IDE) + file (TO_NATIVE_PATH "${_prefixFile}" _prefixFileLogPath) + else() + file (RELATIVE_PATH _prefixFileLogPath "${CMAKE_BINARY_DIR}" "${_prefixFile}") + endif() + get_filename_component(_prefixFileExt "${_prefixFile}" EXT) + if (_prefixFileExt MATCHES "^\\.c") + set (_comment "Generating ${_language} prefix source ${_prefixFileLogPath}") + else() + set (_comment "Generating ${_language} prefix header ${_prefixFileLogPath}") + endif() + # prevent pre-processing errors upon generating the prefix header when a target's generated include file does not yet exist + # we do not add a file-level dependency for the target's generated files though, because we only want to depend on their existence + # thus we make the prefix header generation depend on a custom helper target which triggers the generation of the files + set (_preTargetName "${_target}${COTIRE_PCH_TARGET_SUFFIX}_pre") + if (TARGET ${_preTargetName}) + # custom helper target has already been generated while processing a different language + list (APPEND _dependencySources ${_preTargetName}) + else() + get_target_property(_targetSourceFiles ${_target} SOURCES) + cotire_get_objects_with_property_on(_generatedSources GENERATED SOURCE ${_targetSourceFiles}) + if (_generatedSources) + add_custom_target("${_preTargetName}" DEPENDS ${_generatedSources}) + cotire_init_target("${_preTargetName}") + list (APPEND _dependencySources ${_preTargetName}) + endif() + endif() + add_custom_command( + OUTPUT "${_prefixFile}" "${_prefixFile}.log" + COMMAND ${_prefixCmd} + DEPENDS ${_unityFiles} ${_dependencySources} "${_realCompilerExe}" + COMMENT "${_comment}" + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + VERBATIM) + list (APPEND ${_cmdsVar} COMMAND ${_prefixCmd}) + set (${_cmdsVar} ${${_cmdsVar}} PARENT_SCOPE) +endfunction() + +function (cotire_setup_prefix_generation_from_unity_command _language _target _targetScript _prefixFile _unityFiles _cmdsVar) + set (_sourceFiles ${ARGN}) + if (CMAKE_${_language}_COMPILER_ID MATCHES "GNU|Clang") + # GNU and Clang require indirect compilation of the prefix header to make them honor the system_header pragma + cotire_prefix_header_to_source_file_path(${_language} "${_prefixFile}" _prefixSourceFile) + else() + set (_prefixSourceFile "${_prefixFile}") + endif() + cotire_setup_prefix_generation_command( + ${_language} ${_target} "${_targetScript}" + "${_prefixSourceFile}" "${_unityFiles}" ${_cmdsVar} ${_sourceFiles}) + if (CMAKE_${_language}_COMPILER_ID MATCHES "GNU|Clang") + # set up generation of a prefix source file which includes the prefix header + cotire_setup_combine_command(${_language} "${_targetScript}" "${_prefixFile}" _cmds ${_prefixSourceFile}) + endif() + set (${_cmdsVar} ${${_cmdsVar}} PARENT_SCOPE) +endfunction() + +function (cotire_setup_prefix_generation_from_provided_command _language _target _targetScript _prefixFile _cmdsVar) + set (_prefixHeaderFiles ${ARGN}) + if (CMAKE_${_language}_COMPILER_ID MATCHES "GNU|Clang") + # GNU and Clang require indirect compilation of the prefix header to make them honor the system_header pragma + cotire_prefix_header_to_source_file_path(${_language} "${_prefixFile}" _prefixSourceFile) + else() + set (_prefixSourceFile "${_prefixFile}") + endif() + cotire_setup_combine_command(${_language} "${_targetScript}" "${_prefixSourceFile}" _cmds ${_prefixHeaderFiles}) + if (CMAKE_${_language}_COMPILER_ID MATCHES "GNU|Clang") + # set up generation of a prefix source file which includes the prefix header + cotire_setup_combine_command(${_language} "${_targetScript}" "${_prefixFile}" _cmds ${_prefixSourceFile}) + endif() + set (${_cmdsVar} ${${_cmdsVar}} PARENT_SCOPE) +endfunction() + +function (cotire_init_cotire_target_properties _target) + get_property(_isSet TARGET ${_target} PROPERTY COTIRE_ENABLE_PRECOMPILED_HEADER SET) + if (NOT _isSet) + set_property(TARGET ${_target} PROPERTY COTIRE_ENABLE_PRECOMPILED_HEADER TRUE) + endif() + get_property(_isSet TARGET ${_target} PROPERTY COTIRE_ADD_UNITY_BUILD SET) + if (NOT _isSet) + set_property(TARGET ${_target} PROPERTY COTIRE_ADD_UNITY_BUILD TRUE) + endif() + get_property(_isSet TARGET ${_target} PROPERTY COTIRE_ADD_CLEAN SET) + if (NOT _isSet) + set_property(TARGET ${_target} PROPERTY COTIRE_ADD_CLEAN FALSE) + endif() + get_property(_isSet TARGET ${_target} PROPERTY COTIRE_PREFIX_HEADER_IGNORE_PATH SET) + if (NOT _isSet) + set_property(TARGET ${_target} PROPERTY COTIRE_PREFIX_HEADER_IGNORE_PATH "${CMAKE_SOURCE_DIR}") + cotire_check_is_path_relative_to("${CMAKE_BINARY_DIR}" _isRelative "${CMAKE_SOURCE_DIR}") + if (NOT _isRelative) + set_property(TARGET ${_target} APPEND PROPERTY COTIRE_PREFIX_HEADER_IGNORE_PATH "${CMAKE_BINARY_DIR}") + endif() + endif() + get_property(_isSet TARGET ${_target} PROPERTY COTIRE_PREFIX_HEADER_INCLUDE_PATH SET) + if (NOT _isSet) + set_property(TARGET ${_target} PROPERTY COTIRE_PREFIX_HEADER_INCLUDE_PATH "") + endif() + get_property(_isSet TARGET ${_target} PROPERTY COTIRE_PREFIX_HEADER_INCLUDE_PRIORITY_PATH SET) + if (NOT _isSet) + set_property(TARGET ${_target} PROPERTY COTIRE_PREFIX_HEADER_INCLUDE_PRIORITY_PATH "") + endif() + get_property(_isSet TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_PRE_UNDEFS SET) + if (NOT _isSet) + set_property(TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_PRE_UNDEFS "") + endif() + get_property(_isSet TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_POST_UNDEFS SET) + if (NOT _isSet) + set_property(TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_POST_UNDEFS "") + endif() + get_property(_isSet TARGET ${_target} PROPERTY COTIRE_UNITY_LINK_LIBRARIES_INIT SET) + if (NOT _isSet) + set_property(TARGET ${_target} PROPERTY COTIRE_UNITY_LINK_LIBRARIES_INIT "COPY_UNITY") + endif() + get_property(_isSet TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES SET) + if (NOT _isSet) + if (COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES) + set_property(TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES "${COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES}") + else() + set_property(TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES "") + endif() + endif() +endfunction() + +function (cotire_make_target_message _target _languages _disableMsg _targetMsgVar) + get_target_property(_targetUsePCH ${_target} COTIRE_ENABLE_PRECOMPILED_HEADER) + get_target_property(_targetAddSCU ${_target} COTIRE_ADD_UNITY_BUILD) + string (REPLACE ";" " " _languagesStr "${_languages}") + math (EXPR _numberOfExcludedFiles "${ARGC} - 4") + if (_numberOfExcludedFiles EQUAL 0) + set (_excludedStr "") + elseif (COTIRE_VERBOSE OR _numberOfExcludedFiles LESS 4) + string (REPLACE ";" ", " _excludedStr "excluding ${ARGN}") + else() + set (_excludedStr "excluding ${_numberOfExcludedFiles} files") + endif() + set (_targetMsg "") + if (NOT _languages) + set (_targetMsg "Target ${_target} cannot be cotired.") + if (_disableMsg) + set (_targetMsg "${_targetMsg} ${_disableMsg}") + endif() + elseif (NOT _targetUsePCH AND NOT _targetAddSCU) + set (_targetMsg "${_languagesStr} target ${_target} cotired without unity build and precompiled header.") + if (_disableMsg) + set (_targetMsg "${_targetMsg} ${_disableMsg}") + endif() + elseif (NOT _targetUsePCH) + if (_excludedStr) + set (_targetMsg "${_languagesStr} target ${_target} cotired without precompiled header ${_excludedStr}.") + else() + set (_targetMsg "${_languagesStr} target ${_target} cotired without precompiled header.") + endif() + if (_disableMsg) + set (_targetMsg "${_targetMsg} ${_disableMsg}") + endif() + elseif (NOT _targetAddSCU) + if (_excludedStr) + set (_targetMsg "${_languagesStr} target ${_target} cotired without unity build ${_excludedStr}.") + else() + set (_targetMsg "${_languagesStr} target ${_target} cotired without unity build.") + endif() + else() + if (_excludedStr) + set (_targetMsg "${_languagesStr} target ${_target} cotired ${_excludedStr}.") + else() + set (_targetMsg "${_languagesStr} target ${_target} cotired.") + endif() + endif() + set (${_targetMsgVar} "${_targetMsg}" PARENT_SCOPE) +endfunction() + +function (cotire_choose_target_languages _target _targetLanguagesVar _wholeTargetVar) + set (_languages ${ARGN}) + set (_allSourceFiles "") + set (_allExcludedSourceFiles "") + set (_allCotiredSourceFiles "") + set (_targetLanguages "") + set (_pchEligibleTargetLanguages "") + get_target_property(_targetType ${_target} TYPE) + get_target_property(_targetSourceFiles ${_target} SOURCES) + get_target_property(_targetUsePCH ${_target} COTIRE_ENABLE_PRECOMPILED_HEADER) + get_target_property(_targetAddSCU ${_target} COTIRE_ADD_UNITY_BUILD) + set (_disableMsg "") + foreach (_language ${_languages}) + get_target_property(_prefixHeader ${_target} COTIRE_${_language}_PREFIX_HEADER) + get_target_property(_unityBuildFile ${_target} COTIRE_${_language}_UNITY_SOURCE) + if (_prefixHeader OR _unityBuildFile) + message (STATUS "cotire: target ${_target} has already been cotired.") + set (${_targetLanguagesVar} "" PARENT_SCOPE) + return() + endif() + if (_targetUsePCH AND "${_language}" MATCHES "^C|CXX$" AND DEFINED CMAKE_${_language}_COMPILER_ID) + if (CMAKE_${_language}_COMPILER_ID) + cotire_check_precompiled_header_support("${_language}" "${_target}" _disableMsg) + if (_disableMsg) + set (_targetUsePCH FALSE) + endif() + endif() + endif() + set (_sourceFiles "") + set (_excludedSources "") + set (_cotiredSources "") + cotire_filter_language_source_files(${_language} ${_target} _sourceFiles _excludedSources _cotiredSources ${_targetSourceFiles}) + if (_sourceFiles OR _excludedSources OR _cotiredSources) + list (APPEND _targetLanguages ${_language}) + endif() + if (_sourceFiles) + list (APPEND _allSourceFiles ${_sourceFiles}) + endif() + list (LENGTH _sourceFiles _numberOfSources) + if (NOT _numberOfSources LESS ${COTIRE_MINIMUM_NUMBER_OF_TARGET_SOURCES}) + list (APPEND _pchEligibleTargetLanguages ${_language}) + endif() + if (_excludedSources) + list (APPEND _allExcludedSourceFiles ${_excludedSources}) + endif() + if (_cotiredSources) + list (APPEND _allCotiredSourceFiles ${_cotiredSources}) + endif() + endforeach() + set (_targetMsgLevel STATUS) + if (NOT _targetLanguages) + string (REPLACE ";" " or " _languagesStr "${_languages}") + set (_disableMsg "No ${_languagesStr} source files.") + set (_targetUsePCH FALSE) + set (_targetAddSCU FALSE) + endif() + if (_targetUsePCH) + if (_allCotiredSourceFiles) + cotire_get_source_file_property_values(_cotireTargets COTIRE_TARGET ${_allCotiredSourceFiles}) + list (REMOVE_DUPLICATES _cotireTargets) + string (REPLACE ";" ", " _cotireTargetsStr "${_cotireTargets}") + set (_disableMsg "Target sources already include a precompiled header for target(s) ${_cotireTargets}.") + set (_disableMsg "${_disableMsg} Set target property COTIRE_ENABLE_PRECOMPILED_HEADER to FALSE for targets ${_target},") + set (_disableMsg "${_disableMsg} ${_cotireTargetsStr} to get a workable build system.") + set (_targetMsgLevel SEND_ERROR) + set (_targetUsePCH FALSE) + elseif (NOT _pchEligibleTargetLanguages) + set (_disableMsg "Too few applicable sources.") + set (_targetUsePCH FALSE) + elseif (XCODE AND _allExcludedSourceFiles) + # for Xcode, we cannot apply the precompiled header to individual sources, only to the whole target + set (_disableMsg "Exclusion of source files not supported for generator Xcode.") + set (_targetUsePCH FALSE) + elseif (XCODE AND "${_targetType}" STREQUAL "OBJECT_LIBRARY") + # for Xcode, we cannot apply the required PRE_BUILD action to generate the prefix header to an OBJECT_LIBRARY target + set (_disableMsg "Required PRE_BUILD action not supported for OBJECT_LIBRARY targets for generator Xcode.") + set (_targetUsePCH FALSE) + endif() + endif() + set_property(TARGET ${_target} PROPERTY COTIRE_ENABLE_PRECOMPILED_HEADER ${_targetUsePCH}) + set_property(TARGET ${_target} PROPERTY COTIRE_ADD_UNITY_BUILD ${_targetAddSCU}) + cotire_make_target_message(${_target} "${_targetLanguages}" "${_disableMsg}" _targetMsg ${_allExcludedSourceFiles}) + if (_targetMsg) + if (NOT DEFINED COTIREMSG_${_target}) + set (COTIREMSG_${_target} "") + endif() + if (COTIRE_VERBOSE OR NOT "${_targetMsgLevel}" STREQUAL "STATUS" OR + NOT "${COTIREMSG_${_target}}" STREQUAL "${_targetMsg}") + # cache message to avoid redundant messages on re-configure + set (COTIREMSG_${_target} "${_targetMsg}" CACHE INTERNAL "${_target} cotire message.") + message (${_targetMsgLevel} "${_targetMsg}") + endif() + endif() + list (LENGTH _targetLanguages _numberOfLanguages) + if (_numberOfLanguages GREATER 1 OR _allExcludedSourceFiles) + set (${_wholeTargetVar} FALSE PARENT_SCOPE) + else() + set (${_wholeTargetVar} TRUE PARENT_SCOPE) + endif() + set (${_targetLanguagesVar} ${_targetLanguages} PARENT_SCOPE) +endfunction() + +function (cotire_compute_unity_max_number_of_includes _target _maxIncludesVar) + set (_sourceFiles ${ARGN}) + get_target_property(_maxIncludes ${_target} COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES) + if (_maxIncludes MATCHES "(-j|--parallel|--jobs) ?([0-9]*)") + set (_numberOfThreads "${CMAKE_MATCH_2}") + if (NOT _numberOfThreads) + # use all available cores + ProcessorCount(_numberOfThreads) + endif() + list (LENGTH _sourceFiles _numberOfSources) + math (EXPR _maxIncludes "(${_numberOfSources} + ${_numberOfThreads} - 1) / ${_numberOfThreads}") + elseif (NOT _maxIncludes MATCHES "[0-9]+") + set (_maxIncludes 0) + endif() + if (COTIRE_DEBUG) + message (STATUS "${_target} unity source max includes: ${_maxIncludes}") + endif() + set (${_maxIncludesVar} ${_maxIncludes} PARENT_SCOPE) +endfunction() + +function (cotire_process_target_language _language _configurations _target _wholeTarget _cmdsVar) + set (${_cmdsVar} "" PARENT_SCOPE) + get_target_property(_targetSourceFiles ${_target} SOURCES) + set (_sourceFiles "") + set (_excludedSources "") + set (_cotiredSources "") + cotire_filter_language_source_files(${_language} ${_target} _sourceFiles _excludedSources _cotiredSources ${_targetSourceFiles}) + if (NOT _sourceFiles AND NOT _cotiredSources) + return() + endif() + set (_cmds "") + # check for user provided unity source file list + get_property(_unitySourceFiles TARGET ${_target} PROPERTY COTIRE_${_language}_UNITY_SOURCE_INIT) + if (NOT _unitySourceFiles) + set (_unitySourceFiles ${_sourceFiles} ${_cotiredSources}) + endif() + cotire_generate_target_script( + ${_language} "${_configurations}" ${_target} _targetScript _targetConfigScript ${_unitySourceFiles}) + # set up unity files for parallel compilation + cotire_compute_unity_max_number_of_includes(${_target} _maxIncludes ${_unitySourceFiles}) + cotire_make_unity_source_file_paths(${_language} ${_target} ${_maxIncludes} _unityFiles ${_unitySourceFiles}) + list (LENGTH _unityFiles _numberOfUnityFiles) + if (_numberOfUnityFiles EQUAL 0) + return() + elseif (_numberOfUnityFiles GREATER 1) + cotire_setup_unity_generation_commands( + ${_language} ${_target} "${_targetScript}" "${_targetConfigScript}" "${_unityFiles}" _cmds ${_unitySourceFiles}) + endif() + # set up single unity file for prefix header generation + cotire_make_single_unity_source_file_path(${_language} ${_target} _unityFile) + cotire_setup_unity_generation_commands( + ${_language} ${_target} "${_targetScript}" "${_targetConfigScript}" "${_unityFile}" _cmds ${_unitySourceFiles}) + cotire_make_prefix_file_path(${_language} ${_target} _prefixFile) + # set up prefix header + if (_prefixFile) + # check for user provided prefix header files + get_property(_prefixHeaderFiles TARGET ${_target} PROPERTY COTIRE_${_language}_PREFIX_HEADER_INIT) + if (_prefixHeaderFiles) + cotire_setup_prefix_generation_from_provided_command( + ${_language} ${_target} "${_targetConfigScript}" "${_prefixFile}" _cmds ${_prefixHeaderFiles}) + else() + cotire_setup_prefix_generation_from_unity_command( + ${_language} ${_target} "${_targetConfigScript}" "${_prefixFile}" "${_unityFile}" _cmds ${_unitySourceFiles}) + endif() + # check if selected language has enough sources at all + list (LENGTH _sourceFiles _numberOfSources) + if (_numberOfSources LESS ${COTIRE_MINIMUM_NUMBER_OF_TARGET_SOURCES}) + set (_targetUsePCH FALSE) + else() + get_target_property(_targetUsePCH ${_target} COTIRE_ENABLE_PRECOMPILED_HEADER) + endif() + if (_targetUsePCH) + cotire_make_pch_file_path(${_language} ${_target} _pchFile) + if (_pchFile) + # first file in _sourceFiles is passed as the host file + cotire_setup_pch_file_compilation( + ${_language} ${_target} "${_targetConfigScript}" "${_prefixFile}" "${_pchFile}" ${_sourceFiles}) + cotire_setup_pch_file_inclusion( + ${_language} ${_target} ${_wholeTarget} "${_prefixFile}" "${_pchFile}" ${_sourceFiles}) + endif() + elseif (_prefixHeaderFiles) + # user provided prefix header must be included unconditionally + cotire_setup_prefix_file_inclusion(${_language} ${_target} "${_prefixFile}" ${_sourceFiles}) + endif() + endif() + # mark target as cotired for language + set_property(TARGET ${_target} PROPERTY COTIRE_${_language}_UNITY_SOURCE "${_unityFiles}") + if (_prefixFile) + set_property(TARGET ${_target} PROPERTY COTIRE_${_language}_PREFIX_HEADER "${_prefixFile}") + if (_targetUsePCH AND _pchFile) + set_property(TARGET ${_target} PROPERTY COTIRE_${_language}_PRECOMPILED_HEADER "${_pchFile}") + endif() + endif() + set (${_cmdsVar} ${_cmds} PARENT_SCOPE) +endfunction() + +function (cotire_setup_clean_target _target) + set (_cleanTargetName "${_target}${COTIRE_CLEAN_TARGET_SUFFIX}") + if (NOT TARGET "${_cleanTargetName}") + cotire_set_cmd_to_prologue(_cmds) + get_filename_component(_outputDir "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}" ABSOLUTE) + list (APPEND _cmds -P "${COTIRE_CMAKE_MODULE_FILE}" "cleanup" "${_outputDir}" "${COTIRE_INTDIR}" "${_target}") + add_custom_target(${_cleanTargetName} + COMMAND ${_cmds} + WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" + COMMENT "Cleaning up target ${_target} cotire generated files" + VERBATIM) + cotire_init_target("${_cleanTargetName}") + endif() +endfunction() + +function (cotire_setup_pch_target _languages _configurations _target) + if ("${CMAKE_GENERATOR}" MATCHES "Make|Ninja") + # for makefile based generators, we add a custom target to trigger the generation of the cotire related files + set (_dependsFiles "") + foreach (_language ${_languages}) + set (_props COTIRE_${_language}_PREFIX_HEADER COTIRE_${_language}_UNITY_SOURCE) + if (NOT CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel") + # Visual Studio and Intel only create precompiled header as a side effect + list (INSERT _props 0 COTIRE_${_language}_PRECOMPILED_HEADER) + endif() + cotire_get_first_set_property_value(_dependsFile TARGET ${_target} ${_props}) + if (_dependsFile) + list (APPEND _dependsFiles "${_dependsFile}") + endif() + endforeach() + if (_dependsFiles) + set (_pchTargetName "${_target}${COTIRE_PCH_TARGET_SUFFIX}") + add_custom_target("${_pchTargetName}" DEPENDS ${_dependsFiles}) + cotire_init_target("${_pchTargetName}") + cotire_add_to_pch_all_target(${_pchTargetName}) + endif() + else() + # for other generators, we add the "clean all" target to clean up the precompiled header + cotire_setup_clean_all_target() + endif() +endfunction() + +function (cotire_filter_object_libraries _target _objectLibrariesVar) + set (_objectLibraries "") + foreach (_source ${ARGN}) + if (_source MATCHES "^\\$$") + list (APPEND _objectLibraries "${_source}") + endif() + endforeach() + set (${_objectLibrariesVar} ${_objectLibraries} PARENT_SCOPE) +endfunction() + +function (cotire_collect_unity_target_sources _target _languages _unityTargetSourcesVar) + get_target_property(_targetSourceFiles ${_target} SOURCES) + set (_unityTargetSources ${_targetSourceFiles}) + foreach (_language ${_languages}) + get_property(_unityFiles TARGET ${_target} PROPERTY COTIRE_${_language}_UNITY_SOURCE) + if (_unityFiles) + # remove source files that are included in the unity source + set (_sourceFiles "") + set (_excludedSources "") + set (_cotiredSources "") + cotire_filter_language_source_files(${_language} ${_target} _sourceFiles _excludedSources _cotiredSources ${_targetSourceFiles}) + if (_sourceFiles OR _cotiredSources) + list (REMOVE_ITEM _unityTargetSources ${_sourceFiles} ${_cotiredSources}) + endif() + # add unity source files instead + list (APPEND _unityTargetSources ${_unityFiles}) + endif() + endforeach() + get_target_property(_linkLibrariesStrategy ${_target} COTIRE_UNITY_LINK_LIBRARIES_INIT) + if ("${_linkLibrariesStrategy}" MATCHES "^COPY_UNITY$") + cotire_filter_object_libraries(${_target} _objectLibraries ${_targetSourceFiles}) + if (_objectLibraries) + cotire_map_libraries("${_linkLibrariesStrategy}" _unityObjectLibraries ${_objectLibraries}) + list (REMOVE_ITEM _unityTargetSources ${_objectLibraries}) + list (APPEND _unityTargetSources ${_unityObjectLibraries}) + endif() + endif() + set (${_unityTargetSourcesVar} ${_unityTargetSources} PARENT_SCOPE) +endfunction() + +function (cotire_setup_unity_target_pch_usage _languages _target) + foreach (_language ${_languages}) + get_property(_unityFiles TARGET ${_target} PROPERTY COTIRE_${_language}_UNITY_SOURCE) + if (_unityFiles) + get_property(_userPrefixFile TARGET ${_target} PROPERTY COTIRE_${_language}_PREFIX_HEADER_INIT) + get_property(_prefixFile TARGET ${_target} PROPERTY COTIRE_${_language}_PREFIX_HEADER) + if (_userPrefixFile AND _prefixFile) + # user provided prefix header must be included unconditionally by unity sources + cotire_setup_prefix_file_inclusion(${_language} ${_target} "${_prefixFile}" ${_unityFiles}) + endif() + endif() + endforeach() +endfunction() + +function (cotire_setup_unity_build_target _languages _configurations _target) + get_target_property(_unityTargetName ${_target} COTIRE_UNITY_TARGET_NAME) + if (NOT _unityTargetName) + set (_unityTargetName "${_target}${COTIRE_UNITY_BUILD_TARGET_SUFFIX}") + endif() + # determine unity target sub type + get_target_property(_targetType ${_target} TYPE) + if ("${_targetType}" STREQUAL "EXECUTABLE") + set (_unityTargetSubType "") + elseif (_targetType MATCHES "(STATIC|SHARED|MODULE|OBJECT)_LIBRARY") + set (_unityTargetSubType "${CMAKE_MATCH_1}") + else() + message (WARNING "cotire: target ${_target} has unknown target type ${_targetType}.") + return() + endif() + # determine unity target sources + set (_unityTargetSources "") + cotire_collect_unity_target_sources(${_target} "${_languages}" _unityTargetSources) + # handle automatic Qt processing + get_target_property(_targetAutoMoc ${_target} AUTOMOC) + get_target_property(_targetAutoUic ${_target} AUTOUIC) + get_target_property(_targetAutoRcc ${_target} AUTORCC) + if (_targetAutoMoc OR _targetAutoUic OR _targetAutoRcc) + # if the original target sources are subject to CMake's automatic Qt processing, + # also include implicitly generated _automoc.cpp file + if (CMAKE_VERSION VERSION_LESS "3.8.0") + list (APPEND _unityTargetSources "${_target}_automoc.cpp") + set_property (SOURCE "${_target}_automoc.cpp" PROPERTY GENERATED TRUE) + else() + list (APPEND _unityTargetSources "${_target}_autogen/moc_compilation.cpp") + set_property (SOURCE "${_target}_autogen/moc_compilation.cpp" PROPERTY GENERATED TRUE) + endif() + endif() + # prevent AUTOMOC, AUTOUIC and AUTORCC properties from being set when the unity target is created + set (CMAKE_AUTOMOC OFF) + set (CMAKE_AUTOUIC OFF) + set (CMAKE_AUTORCC OFF) + if (COTIRE_DEBUG) + message (STATUS "add target ${_targetType} ${_unityTargetName} ${_unityTargetSubType} EXCLUDE_FROM_ALL ${_unityTargetSources}") + endif() + # generate unity target + if ("${_targetType}" STREQUAL "EXECUTABLE") + add_executable(${_unityTargetName} ${_unityTargetSubType} EXCLUDE_FROM_ALL ${_unityTargetSources}) + else() + add_library(${_unityTargetName} ${_unityTargetSubType} EXCLUDE_FROM_ALL ${_unityTargetSources}) + endif() + if ("${CMAKE_GENERATOR}" MATCHES "Visual Studio") + # depend on original target's automoc target, if it exists + if (TARGET ${_target}_automoc) + add_dependencies(${_unityTargetName} ${_target}_automoc) + endif() + else() + if (_targetAutoMoc OR _targetAutoUic OR _targetAutoRcc) + # depend on the original target's implicity generated _automoc target + if (CMAKE_VERSION VERSION_LESS "3.8.0") + add_dependencies(${_unityTargetName} ${_target}_automoc) + else() + add_dependencies(${_unityTargetName} ${_target}_autogen) + endif() + endif() + endif() + # copy output location properties + set (_outputDirProperties + ARCHIVE_OUTPUT_DIRECTORY ARCHIVE_OUTPUT_DIRECTORY_ + LIBRARY_OUTPUT_DIRECTORY LIBRARY_OUTPUT_DIRECTORY_ + RUNTIME_OUTPUT_DIRECTORY RUNTIME_OUTPUT_DIRECTORY_) + if (COTIRE_UNITY_OUTPUT_DIRECTORY) + set (_setDefaultOutputDir TRUE) + if (IS_ABSOLUTE "${COTIRE_UNITY_OUTPUT_DIRECTORY}") + set (_outputDir "${COTIRE_UNITY_OUTPUT_DIRECTORY}") + else() + # append relative COTIRE_UNITY_OUTPUT_DIRECTORY to target's actual output directory + cotire_copy_set_properties("${_configurations}" TARGET ${_target} ${_unityTargetName} ${_outputDirProperties}) + cotire_resolve_config_properties("${_configurations}" _properties ${_outputDirProperties}) + foreach (_property ${_properties}) + get_property(_outputDir TARGET ${_target} PROPERTY ${_property}) + if (_outputDir) + get_filename_component(_outputDir "${_outputDir}/${COTIRE_UNITY_OUTPUT_DIRECTORY}" ABSOLUTE) + set_property(TARGET ${_unityTargetName} PROPERTY ${_property} "${_outputDir}") + set (_setDefaultOutputDir FALSE) + endif() + endforeach() + if (_setDefaultOutputDir) + get_filename_component(_outputDir "${CMAKE_CURRENT_BINARY_DIR}/${COTIRE_UNITY_OUTPUT_DIRECTORY}" ABSOLUTE) + endif() + endif() + if (_setDefaultOutputDir) + set_target_properties(${_unityTargetName} PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY "${_outputDir}" + LIBRARY_OUTPUT_DIRECTORY "${_outputDir}" + RUNTIME_OUTPUT_DIRECTORY "${_outputDir}") + endif() + else() + cotire_copy_set_properties("${_configurations}" TARGET ${_target} ${_unityTargetName} + ${_outputDirProperties}) + endif() + # copy output name + cotire_copy_set_properties("${_configurations}" TARGET ${_target} ${_unityTargetName} + ARCHIVE_OUTPUT_NAME ARCHIVE_OUTPUT_NAME_ + LIBRARY_OUTPUT_NAME LIBRARY_OUTPUT_NAME_ + OUTPUT_NAME OUTPUT_NAME_ + RUNTIME_OUTPUT_NAME RUNTIME_OUTPUT_NAME_ + PREFIX _POSTFIX SUFFIX + IMPORT_PREFIX IMPORT_SUFFIX) + # copy compile stuff + cotire_copy_set_properties("${_configurations}" TARGET ${_target} ${_unityTargetName} + COMPILE_DEFINITIONS COMPILE_DEFINITIONS_ + COMPILE_FLAGS COMPILE_OPTIONS + Fortran_FORMAT Fortran_MODULE_DIRECTORY + INCLUDE_DIRECTORIES + INTERPROCEDURAL_OPTIMIZATION INTERPROCEDURAL_OPTIMIZATION_ + POSITION_INDEPENDENT_CODE + C_COMPILER_LAUNCHER CXX_COMPILER_LAUNCHER + C_INCLUDE_WHAT_YOU_USE CXX_INCLUDE_WHAT_YOU_USE + C_VISIBILITY_PRESET CXX_VISIBILITY_PRESET VISIBILITY_INLINES_HIDDEN + C_CLANG_TIDY CXX_CLANG_TIDY) + # copy compile features + cotire_copy_set_properties("${_configurations}" TARGET ${_target} ${_unityTargetName} + C_EXTENSIONS C_STANDARD C_STANDARD_REQUIRED + CXX_EXTENSIONS CXX_STANDARD CXX_STANDARD_REQUIRED + COMPILE_FEATURES) + # copy interface stuff + cotire_copy_set_properties("${_configurations}" TARGET ${_target} ${_unityTargetName} + COMPATIBLE_INTERFACE_BOOL COMPATIBLE_INTERFACE_NUMBER_MAX COMPATIBLE_INTERFACE_NUMBER_MIN + COMPATIBLE_INTERFACE_STRING + INTERFACE_COMPILE_DEFINITIONS INTERFACE_COMPILE_FEATURES INTERFACE_COMPILE_OPTIONS + INTERFACE_INCLUDE_DIRECTORIES INTERFACE_SOURCES + INTERFACE_POSITION_INDEPENDENT_CODE INTERFACE_SYSTEM_INCLUDE_DIRECTORIES + INTERFACE_AUTOUIC_OPTIONS NO_SYSTEM_FROM_IMPORTED) + # copy link stuff + cotire_copy_set_properties("${_configurations}" TARGET ${_target} ${_unityTargetName} + BUILD_WITH_INSTALL_RPATH INSTALL_RPATH INSTALL_RPATH_USE_LINK_PATH SKIP_BUILD_RPATH + LINKER_LANGUAGE LINK_DEPENDS LINK_DEPENDS_NO_SHARED + LINK_FLAGS LINK_FLAGS_ + LINK_INTERFACE_LIBRARIES LINK_INTERFACE_LIBRARIES_ + LINK_INTERFACE_MULTIPLICITY LINK_INTERFACE_MULTIPLICITY_ + LINK_SEARCH_START_STATIC LINK_SEARCH_END_STATIC + STATIC_LIBRARY_FLAGS STATIC_LIBRARY_FLAGS_ + NO_SONAME SOVERSION VERSION + LINK_WHAT_YOU_USE BUILD_RPATH) + # copy cmake stuff + cotire_copy_set_properties("${_configurations}" TARGET ${_target} ${_unityTargetName} + IMPLICIT_DEPENDS_INCLUDE_TRANSFORM RULE_LAUNCH_COMPILE RULE_LAUNCH_CUSTOM RULE_LAUNCH_LINK) + # copy Apple platform specific stuff + cotire_copy_set_properties("${_configurations}" TARGET ${_target} ${_unityTargetName} + BUNDLE BUNDLE_EXTENSION FRAMEWORK FRAMEWORK_VERSION INSTALL_NAME_DIR + MACOSX_BUNDLE MACOSX_BUNDLE_INFO_PLIST MACOSX_FRAMEWORK_INFO_PLIST MACOSX_RPATH + OSX_ARCHITECTURES OSX_ARCHITECTURES_ PRIVATE_HEADER PUBLIC_HEADER RESOURCE XCTEST + IOS_INSTALL_COMBINED XCODE_EXPLICIT_FILE_TYPE XCODE_PRODUCT_TYPE) + # copy Windows platform specific stuff + cotire_copy_set_properties("${_configurations}" TARGET ${_target} ${_unityTargetName} + GNUtoMS + COMPILE_PDB_NAME COMPILE_PDB_NAME_ + COMPILE_PDB_OUTPUT_DIRECTORY COMPILE_PDB_OUTPUT_DIRECTORY_ + PDB_NAME PDB_NAME_ PDB_OUTPUT_DIRECTORY PDB_OUTPUT_DIRECTORY_ + VS_DESKTOP_EXTENSIONS_VERSION VS_DOTNET_REFERENCES VS_DOTNET_TARGET_FRAMEWORK_VERSION + VS_GLOBAL_KEYWORD VS_GLOBAL_PROJECT_TYPES VS_GLOBAL_ROOTNAMESPACE + VS_IOT_EXTENSIONS_VERSION VS_IOT_STARTUP_TASK + VS_KEYWORD VS_MOBILE_EXTENSIONS_VERSION + VS_SCC_AUXPATH VS_SCC_LOCALPATH VS_SCC_PROJECTNAME VS_SCC_PROVIDER + VS_WINDOWS_TARGET_PLATFORM_MIN_VERSION + VS_WINRT_COMPONENT VS_WINRT_EXTENSIONS VS_WINRT_REFERENCES + WIN32_EXECUTABLE WINDOWS_EXPORT_ALL_SYMBOLS + DEPLOYMENT_REMOTE_DIRECTORY VS_CONFIGURATION_TYPE + VS_SDK_REFERENCES VS_USER_PROPS VS_DEBUGGER_WORKING_DIRECTORY) + # copy Android platform specific stuff + cotire_copy_set_properties("${_configurations}" TARGET ${_target} ${_unityTargetName} + ANDROID_API ANDROID_API_MIN ANDROID_GUI + ANDROID_ANT_ADDITIONAL_OPTIONS ANDROID_ARCH ANDROID_ASSETS_DIRECTORIES + ANDROID_JAR_DEPENDENCIES ANDROID_JAR_DIRECTORIES ANDROID_JAVA_SOURCE_DIR + ANDROID_NATIVE_LIB_DEPENDENCIES ANDROID_NATIVE_LIB_DIRECTORIES + ANDROID_PROCESS_MAX ANDROID_PROGUARD ANDROID_PROGUARD_CONFIG_PATH + ANDROID_SECURE_PROPS_PATH ANDROID_SKIP_ANT_STEP ANDROID_STL_TYPE) + # use output name from original target + get_target_property(_targetOutputName ${_unityTargetName} OUTPUT_NAME) + if (NOT _targetOutputName) + set_property(TARGET ${_unityTargetName} PROPERTY OUTPUT_NAME "${_target}") + endif() + # use export symbol from original target + cotire_get_target_export_symbol("${_target}" _defineSymbol) + if (_defineSymbol) + set_property(TARGET ${_unityTargetName} PROPERTY DEFINE_SYMBOL "${_defineSymbol}") + if ("${_targetType}" STREQUAL "EXECUTABLE") + set_property(TARGET ${_unityTargetName} PROPERTY ENABLE_EXPORTS TRUE) + endif() + endif() + cotire_init_target(${_unityTargetName}) + cotire_add_to_unity_all_target(${_unityTargetName}) + set_property(TARGET ${_target} PROPERTY COTIRE_UNITY_TARGET_NAME "${_unityTargetName}") +endfunction(cotire_setup_unity_build_target) + +function (cotire_target _target) + set(_options "") + set(_oneValueArgs "") + set(_multiValueArgs LANGUAGES CONFIGURATIONS) + cmake_parse_arguments(_option "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN}) + if (NOT _option_LANGUAGES) + get_property (_option_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) + endif() + if (NOT _option_CONFIGURATIONS) + cotire_get_configuration_types(_option_CONFIGURATIONS) + endif() + # check if cotire can be applied to target at all + cotire_is_target_supported(${_target} _isSupported) + if (NOT _isSupported) + get_target_property(_imported ${_target} IMPORTED) + get_target_property(_targetType ${_target} TYPE) + if (_imported) + message (WARNING "cotire: imported ${_targetType} target ${_target} cannot be cotired.") + else() + message (STATUS "cotire: ${_targetType} target ${_target} cannot be cotired.") + endif() + return() + endif() + # resolve alias + get_target_property(_aliasName ${_target} ALIASED_TARGET) + if (_aliasName) + if (COTIRE_DEBUG) + message (STATUS "${_target} is an alias. Applying cotire to aliased target ${_aliasName} instead.") + endif() + set (_target ${_aliasName}) + endif() + # check if target needs to be cotired for build type + # when using configuration types, the test is performed at build time + cotire_init_cotire_target_properties(${_target}) + if (NOT CMAKE_CONFIGURATION_TYPES) + if (CMAKE_BUILD_TYPE) + list (FIND _option_CONFIGURATIONS "${CMAKE_BUILD_TYPE}" _index) + else() + list (FIND _option_CONFIGURATIONS "None" _index) + endif() + if (_index EQUAL -1) + if (COTIRE_DEBUG) + message (STATUS "CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} not cotired (${_option_CONFIGURATIONS})") + endif() + return() + endif() + endif() + # when not using configuration types, immediately create cotire intermediate dir + if (NOT CMAKE_CONFIGURATION_TYPES) + cotire_get_intermediate_dir(_baseDir) + file (MAKE_DIRECTORY "${_baseDir}") + endif() + # choose languages that apply to the target + cotire_choose_target_languages("${_target}" _targetLanguages _wholeTarget ${_option_LANGUAGES}) + if (NOT _targetLanguages) + return() + endif() + set (_cmds "") + foreach (_language ${_targetLanguages}) + cotire_process_target_language("${_language}" "${_option_CONFIGURATIONS}" ${_target} ${_wholeTarget} _cmd) + if (_cmd) + list (APPEND _cmds ${_cmd}) + endif() + endforeach() + get_target_property(_targetAddSCU ${_target} COTIRE_ADD_UNITY_BUILD) + if (_targetAddSCU) + cotire_setup_unity_build_target("${_targetLanguages}" "${_option_CONFIGURATIONS}" ${_target}) + endif() + get_target_property(_targetUsePCH ${_target} COTIRE_ENABLE_PRECOMPILED_HEADER) + if (_targetUsePCH) + cotire_setup_target_pch_usage("${_targetLanguages}" ${_target} ${_wholeTarget} ${_cmds}) + cotire_setup_pch_target("${_targetLanguages}" "${_option_CONFIGURATIONS}" ${_target}) + if (_targetAddSCU) + cotire_setup_unity_target_pch_usage("${_targetLanguages}" ${_target}) + endif() + endif() + get_target_property(_targetAddCleanTarget ${_target} COTIRE_ADD_CLEAN) + if (_targetAddCleanTarget) + cotire_setup_clean_target(${_target}) + endif() +endfunction(cotire_target) + +function (cotire_map_libraries _strategy _mappedLibrariesVar) + set (_mappedLibraries "") + foreach (_library ${ARGN}) + if (_library MATCHES "^\\$$") + set (_libraryName "${CMAKE_MATCH_1}") + set (_linkOnly TRUE) + set (_objectLibrary FALSE) + elseif (_library MATCHES "^\\$$") + set (_libraryName "${CMAKE_MATCH_1}") + set (_linkOnly FALSE) + set (_objectLibrary TRUE) + else() + set (_libraryName "${_library}") + set (_linkOnly FALSE) + set (_objectLibrary FALSE) + endif() + if ("${_strategy}" MATCHES "COPY_UNITY") + cotire_is_target_supported(${_libraryName} _isSupported) + if (_isSupported) + # use target's corresponding unity target, if available + get_target_property(_libraryUnityTargetName ${_libraryName} COTIRE_UNITY_TARGET_NAME) + if (TARGET "${_libraryUnityTargetName}") + if (_linkOnly) + list (APPEND _mappedLibraries "$") + elseif (_objectLibrary) + list (APPEND _mappedLibraries "$") + else() + list (APPEND _mappedLibraries "${_libraryUnityTargetName}") + endif() + else() + list (APPEND _mappedLibraries "${_library}") + endif() + else() + list (APPEND _mappedLibraries "${_library}") + endif() + else() + list (APPEND _mappedLibraries "${_library}") + endif() + endforeach() + list (REMOVE_DUPLICATES _mappedLibraries) + set (${_mappedLibrariesVar} ${_mappedLibraries} PARENT_SCOPE) +endfunction() + +function (cotire_target_link_libraries _target) + cotire_is_target_supported(${_target} _isSupported) + if (NOT _isSupported) + return() + endif() + get_target_property(_unityTargetName ${_target} COTIRE_UNITY_TARGET_NAME) + if (TARGET "${_unityTargetName}") + get_target_property(_linkLibrariesStrategy ${_target} COTIRE_UNITY_LINK_LIBRARIES_INIT) + if (COTIRE_DEBUG) + message (STATUS "unity target ${_unityTargetName} link strategy: ${_linkLibrariesStrategy}") + endif() + if ("${_linkLibrariesStrategy}" MATCHES "^(COPY|COPY_UNITY)$") + get_target_property(_linkLibraries ${_target} LINK_LIBRARIES) + if (_linkLibraries) + cotire_map_libraries("${_linkLibrariesStrategy}" _unityLinkLibraries ${_linkLibraries}) + set_target_properties(${_unityTargetName} PROPERTIES LINK_LIBRARIES "${_unityLinkLibraries}") + if (COTIRE_DEBUG) + message (STATUS "unity target ${_unityTargetName} link libraries: ${_unityLinkLibraries}") + endif() + endif() + get_target_property(_interfaceLinkLibraries ${_target} INTERFACE_LINK_LIBRARIES) + if (_interfaceLinkLibraries) + cotire_map_libraries("${_linkLibrariesStrategy}" _unityLinkInterfaceLibraries ${_interfaceLinkLibraries}) + set_target_properties(${_unityTargetName} PROPERTIES INTERFACE_LINK_LIBRARIES "${_unityLinkInterfaceLibraries}") + if (COTIRE_DEBUG) + message (STATUS "unity target ${_unityTargetName} interface link libraries: ${_unityLinkInterfaceLibraries}") + endif() + endif() + endif() + endif() +endfunction(cotire_target_link_libraries) + +function (cotire_cleanup _binaryDir _cotireIntermediateDirName _targetName) + if (_targetName) + file (GLOB_RECURSE _cotireFiles "${_binaryDir}/${_targetName}*.*") + else() + file (GLOB_RECURSE _cotireFiles "${_binaryDir}/*.*") + endif() + # filter files in intermediate directory + set (_filesToRemove "") + foreach (_file ${_cotireFiles}) + get_filename_component(_dir "${_file}" DIRECTORY) + get_filename_component(_dirName "${_dir}" NAME) + if ("${_dirName}" STREQUAL "${_cotireIntermediateDirName}") + list (APPEND _filesToRemove "${_file}") + endif() + endforeach() + if (_filesToRemove) + if (COTIRE_VERBOSE) + message (STATUS "cleaning up ${_filesToRemove}") + endif() + file (REMOVE ${_filesToRemove}) + endif() +endfunction() + +function (cotire_init_target _targetName) + if (COTIRE_TARGETS_FOLDER) + set_target_properties(${_targetName} PROPERTIES FOLDER "${COTIRE_TARGETS_FOLDER}") + endif() + set_target_properties(${_targetName} PROPERTIES EXCLUDE_FROM_ALL TRUE) + if (MSVC_IDE) + set_target_properties(${_targetName} PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD TRUE) + endif() +endfunction() + +function (cotire_add_to_pch_all_target _pchTargetName) + set (_targetName "${COTIRE_PCH_ALL_TARGET_NAME}") + if (NOT TARGET "${_targetName}") + add_custom_target("${_targetName}" + WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" + VERBATIM) + cotire_init_target("${_targetName}") + endif() + cotire_setup_clean_all_target() + add_dependencies(${_targetName} ${_pchTargetName}) +endfunction() + +function (cotire_add_to_unity_all_target _unityTargetName) + set (_targetName "${COTIRE_UNITY_BUILD_ALL_TARGET_NAME}") + if (NOT TARGET "${_targetName}") + add_custom_target("${_targetName}" + WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" + VERBATIM) + cotire_init_target("${_targetName}") + endif() + cotire_setup_clean_all_target() + add_dependencies(${_targetName} ${_unityTargetName}) +endfunction() + +function (cotire_setup_clean_all_target) + set (_targetName "${COTIRE_CLEAN_ALL_TARGET_NAME}") + if (NOT TARGET "${_targetName}") + cotire_set_cmd_to_prologue(_cmds) + list (APPEND _cmds -P "${COTIRE_CMAKE_MODULE_FILE}" "cleanup" "${CMAKE_BINARY_DIR}" "${COTIRE_INTDIR}") + add_custom_target(${_targetName} + COMMAND ${_cmds} + WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" + COMMENT "Cleaning up all cotire generated files" + VERBATIM) + cotire_init_target("${_targetName}") + endif() +endfunction() + +function (cotire) + set(_options "") + set(_oneValueArgs "") + set(_multiValueArgs LANGUAGES CONFIGURATIONS) + cmake_parse_arguments(_option "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN}) + set (_targets ${_option_UNPARSED_ARGUMENTS}) + foreach (_target ${_targets}) + if (TARGET ${_target}) + cotire_target(${_target} LANGUAGES ${_option_LANGUAGES} CONFIGURATIONS ${_option_CONFIGURATIONS}) + else() + message (WARNING "cotire: ${_target} is not a target.") + endif() + endforeach() + foreach (_target ${_targets}) + if (TARGET ${_target}) + cotire_target_link_libraries(${_target}) + endif() + endforeach() +endfunction() + +if (CMAKE_SCRIPT_MODE_FILE) + + # cotire is being run in script mode + # locate -P on command args + set (COTIRE_ARGC -1) + foreach (_index RANGE ${CMAKE_ARGC}) + if (COTIRE_ARGC GREATER -1) + set (COTIRE_ARGV${COTIRE_ARGC} "${CMAKE_ARGV${_index}}") + math (EXPR COTIRE_ARGC "${COTIRE_ARGC} + 1") + elseif ("${CMAKE_ARGV${_index}}" STREQUAL "-P") + set (COTIRE_ARGC 0) + endif() + endforeach() + + # include target script if available + if ("${COTIRE_ARGV2}" MATCHES "\\.cmake$") + # the included target scripts sets up additional variables relating to the target (e.g., COTIRE_TARGET_SOURCES) + include("${COTIRE_ARGV2}") + endif() + + if (COTIRE_DEBUG) + message (STATUS "${COTIRE_ARGV0} ${COTIRE_ARGV1} ${COTIRE_ARGV2} ${COTIRE_ARGV3} ${COTIRE_ARGV4} ${COTIRE_ARGV5}") + endif() + + if (NOT COTIRE_BUILD_TYPE) + set (COTIRE_BUILD_TYPE "None") + endif() + string (TOUPPER "${COTIRE_BUILD_TYPE}" _upperConfig) + set (_includeDirs ${COTIRE_TARGET_INCLUDE_DIRECTORIES_${_upperConfig}}) + set (_systemIncludeDirs ${COTIRE_TARGET_SYSTEM_INCLUDE_DIRECTORIES_${_upperConfig}}) + set (_compileDefinitions ${COTIRE_TARGET_COMPILE_DEFINITIONS_${_upperConfig}}) + set (_compileFlags ${COTIRE_TARGET_COMPILE_FLAGS_${_upperConfig}}) + # check if target has been cotired for actual build type COTIRE_BUILD_TYPE + list (FIND COTIRE_TARGET_CONFIGURATION_TYPES "${COTIRE_BUILD_TYPE}" _index) + if (_index GREATER -1) + set (_sources ${COTIRE_TARGET_SOURCES}) + set (_sourcesDefinitions ${COTIRE_TARGET_SOURCES_COMPILE_DEFINITIONS_${_upperConfig}}) + else() + if (COTIRE_DEBUG) + message (STATUS "COTIRE_BUILD_TYPE=${COTIRE_BUILD_TYPE} not cotired (${COTIRE_TARGET_CONFIGURATION_TYPES})") + endif() + set (_sources "") + set (_sourcesDefinitions "") + endif() + set (_targetPreUndefs ${COTIRE_TARGET_PRE_UNDEFS}) + set (_targetPostUndefs ${COTIRE_TARGET_POST_UNDEFS}) + set (_sourcesPreUndefs ${COTIRE_TARGET_SOURCES_PRE_UNDEFS}) + set (_sourcesPostUndefs ${COTIRE_TARGET_SOURCES_POST_UNDEFS}) + + if ("${COTIRE_ARGV1}" STREQUAL "unity") + + if (XCODE) + # executing pre-build action under Xcode, check dependency on target script + set (_dependsOption DEPENDS "${COTIRE_ARGV2}") + else() + # executing custom command, no need to re-check for dependencies + set (_dependsOption "") + endif() + + cotire_select_unity_source_files("${COTIRE_ARGV3}" _sources ${_sources}) + + cotire_generate_unity_source( + "${COTIRE_ARGV3}" ${_sources} + LANGUAGE "${COTIRE_TARGET_LANGUAGE}" + SOURCES_COMPILE_DEFINITIONS ${_sourcesDefinitions} + PRE_UNDEFS ${_targetPreUndefs} + POST_UNDEFS ${_targetPostUndefs} + SOURCES_PRE_UNDEFS ${_sourcesPreUndefs} + SOURCES_POST_UNDEFS ${_sourcesPostUndefs} + ${_dependsOption}) + + elseif ("${COTIRE_ARGV1}" STREQUAL "prefix") + + if (XCODE) + # executing pre-build action under Xcode, check dependency on unity file and prefix dependencies + set (_dependsOption DEPENDS "${COTIRE_ARGV4}" ${COTIRE_TARGET_PREFIX_DEPENDS}) + else() + # executing custom command, no need to re-check for dependencies + set (_dependsOption "") + endif() + + set (_files "") + foreach (_index RANGE 4 ${COTIRE_ARGC}) + if (COTIRE_ARGV${_index}) + list (APPEND _files "${COTIRE_ARGV${_index}}") + endif() + endforeach() + + cotire_generate_prefix_header( + "${COTIRE_ARGV3}" ${_files} + COMPILER_LAUNCHER "${COTIRE_TARGET_${COTIRE_TARGET_LANGUAGE}_COMPILER_LAUNCHER}" + COMPILER_EXECUTABLE "${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER}" + COMPILER_ARG1 ${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER_ARG1} + COMPILER_ID "${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER_ID}" + COMPILER_VERSION "${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER_VERSION}" + LANGUAGE "${COTIRE_TARGET_LANGUAGE}" + IGNORE_PATH "${COTIRE_TARGET_IGNORE_PATH};${COTIRE_ADDITIONAL_PREFIX_HEADER_IGNORE_PATH}" + INCLUDE_PATH ${COTIRE_TARGET_INCLUDE_PATH} + IGNORE_EXTENSIONS "${CMAKE_${COTIRE_TARGET_LANGUAGE}_SOURCE_FILE_EXTENSIONS};${COTIRE_ADDITIONAL_PREFIX_HEADER_IGNORE_EXTENSIONS}" + INCLUDE_PRIORITY_PATH ${COTIRE_TARGET_INCLUDE_PRIORITY_PATH} + INCLUDE_DIRECTORIES ${_includeDirs} + SYSTEM_INCLUDE_DIRECTORIES ${_systemIncludeDirs} + COMPILE_DEFINITIONS ${_compileDefinitions} + COMPILE_FLAGS ${_compileFlags} + ${_dependsOption}) + + elseif ("${COTIRE_ARGV1}" STREQUAL "precompile") + + set (_files "") + foreach (_index RANGE 5 ${COTIRE_ARGC}) + if (COTIRE_ARGV${_index}) + list (APPEND _files "${COTIRE_ARGV${_index}}") + endif() + endforeach() + + cotire_precompile_prefix_header( + "${COTIRE_ARGV3}" "${COTIRE_ARGV4}" "${COTIRE_ARGV5}" + COMPILER_LAUNCHER "${COTIRE_TARGET_${COTIRE_TARGET_LANGUAGE}_COMPILER_LAUNCHER}" + COMPILER_EXECUTABLE "${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER}" + COMPILER_ARG1 ${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER_ARG1} + COMPILER_ID "${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER_ID}" + COMPILER_VERSION "${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER_VERSION}" + LANGUAGE "${COTIRE_TARGET_LANGUAGE}" + INCLUDE_DIRECTORIES ${_includeDirs} + SYSTEM_INCLUDE_DIRECTORIES ${_systemIncludeDirs} + COMPILE_DEFINITIONS ${_compileDefinitions} + COMPILE_FLAGS ${_compileFlags}) + + elseif ("${COTIRE_ARGV1}" STREQUAL "combine") + + if (COTIRE_TARGET_LANGUAGE) + set (_combinedFile "${COTIRE_ARGV3}") + set (_startIndex 4) + else() + set (_combinedFile "${COTIRE_ARGV2}") + set (_startIndex 3) + endif() + set (_files "") + foreach (_index RANGE ${_startIndex} ${COTIRE_ARGC}) + if (COTIRE_ARGV${_index}) + list (APPEND _files "${COTIRE_ARGV${_index}}") + endif() + endforeach() + + if (XCODE) + # executing pre-build action under Xcode, check dependency on files to be combined + set (_dependsOption DEPENDS ${_files}) + else() + # executing custom command, no need to re-check for dependencies + set (_dependsOption "") + endif() + + if (COTIRE_TARGET_LANGUAGE) + cotire_generate_unity_source( + "${_combinedFile}" ${_files} + LANGUAGE "${COTIRE_TARGET_LANGUAGE}" + ${_dependsOption}) + else() + cotire_generate_unity_source("${_combinedFile}" ${_files} ${_dependsOption}) + endif() + + elseif ("${COTIRE_ARGV1}" STREQUAL "cleanup") + + cotire_cleanup("${COTIRE_ARGV2}" "${COTIRE_ARGV3}" "${COTIRE_ARGV4}") + + else() + message (FATAL_ERROR "cotire: unknown command \"${COTIRE_ARGV1}\".") + endif() + +else() + + # cotire is being run in include mode + # set up all variable and property definitions + + if (NOT DEFINED COTIRE_DEBUG_INIT) + if (DEFINED COTIRE_DEBUG) + set (COTIRE_DEBUG_INIT ${COTIRE_DEBUG}) + else() + set (COTIRE_DEBUG_INIT FALSE) + endif() + endif() + option (COTIRE_DEBUG "Enable cotire debugging output?" ${COTIRE_DEBUG_INIT}) + + if (NOT DEFINED COTIRE_VERBOSE_INIT) + if (DEFINED COTIRE_VERBOSE) + set (COTIRE_VERBOSE_INIT ${COTIRE_VERBOSE}) + else() + set (COTIRE_VERBOSE_INIT FALSE) + endif() + endif() + option (COTIRE_VERBOSE "Enable cotire verbose output?" ${COTIRE_VERBOSE_INIT}) + + set (COTIRE_ADDITIONAL_PREFIX_HEADER_IGNORE_EXTENSIONS "inc;inl;ipp" CACHE STRING + "Ignore headers with the listed file extensions from the generated prefix header.") + + set (COTIRE_ADDITIONAL_PREFIX_HEADER_IGNORE_PATH "" CACHE STRING + "Ignore headers from these directories when generating the prefix header.") + + set (COTIRE_UNITY_SOURCE_EXCLUDE_EXTENSIONS "m;mm" CACHE STRING + "Ignore sources with the listed file extensions from the generated unity source.") + + set (COTIRE_MINIMUM_NUMBER_OF_TARGET_SOURCES "3" CACHE STRING + "Minimum number of sources in target required to enable use of precompiled header.") + + if (NOT DEFINED COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES_INIT) + if (DEFINED COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES) + set (COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES_INIT ${COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES}) + elseif ("${CMAKE_GENERATOR}" MATCHES "JOM|Ninja|Visual Studio") + # enable parallelization for generators that run multiple jobs by default + set (COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES_INIT "-j") + else() + set (COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES_INIT "0") + endif() + endif() + set (COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES "${COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES_INIT}" CACHE STRING + "Maximum number of source files to include in a single unity source file.") + + if (NOT COTIRE_PREFIX_HEADER_FILENAME_SUFFIX) + set (COTIRE_PREFIX_HEADER_FILENAME_SUFFIX "_prefix") + endif() + if (NOT COTIRE_UNITY_SOURCE_FILENAME_SUFFIX) + set (COTIRE_UNITY_SOURCE_FILENAME_SUFFIX "_unity") + endif() + if (NOT COTIRE_INTDIR) + set (COTIRE_INTDIR "cotire") + endif() + if (NOT COTIRE_PCH_ALL_TARGET_NAME) + set (COTIRE_PCH_ALL_TARGET_NAME "all_pch") + endif() + if (NOT COTIRE_UNITY_BUILD_ALL_TARGET_NAME) + set (COTIRE_UNITY_BUILD_ALL_TARGET_NAME "all_unity") + endif() + if (NOT COTIRE_CLEAN_ALL_TARGET_NAME) + set (COTIRE_CLEAN_ALL_TARGET_NAME "clean_cotire") + endif() + if (NOT COTIRE_CLEAN_TARGET_SUFFIX) + set (COTIRE_CLEAN_TARGET_SUFFIX "_clean_cotire") + endif() + if (NOT COTIRE_PCH_TARGET_SUFFIX) + set (COTIRE_PCH_TARGET_SUFFIX "_pch") + endif() + if (MSVC) + # MSVC default PCH memory scaling factor of 100 percent (75 MB) is too small for template heavy C++ code + # use a bigger default factor of 170 percent (128 MB) + if (NOT DEFINED COTIRE_PCH_MEMORY_SCALING_FACTOR) + set (COTIRE_PCH_MEMORY_SCALING_FACTOR "170") + endif() + endif() + if (NOT COTIRE_UNITY_BUILD_TARGET_SUFFIX) + set (COTIRE_UNITY_BUILD_TARGET_SUFFIX "_unity") + endif() + if (NOT DEFINED COTIRE_TARGETS_FOLDER) + set (COTIRE_TARGETS_FOLDER "cotire") + endif() + if (NOT DEFINED COTIRE_UNITY_OUTPUT_DIRECTORY) + if ("${CMAKE_GENERATOR}" MATCHES "Ninja") + # generated Ninja build files do not work if the unity target produces the same output file as the cotired target + set (COTIRE_UNITY_OUTPUT_DIRECTORY "unity") + else() + set (COTIRE_UNITY_OUTPUT_DIRECTORY "") + endif() + endif() + + # define cotire cache variables + + define_property( + CACHED_VARIABLE PROPERTY "COTIRE_ADDITIONAL_PREFIX_HEADER_IGNORE_PATH" + BRIEF_DOCS "Ignore headers from these directories when generating the prefix header." + FULL_DOCS + "The variable can be set to a semicolon separated list of include directories." + "If a header file is found in one of these directories or sub-directories, it will be excluded from the generated prefix header." + "If not defined, defaults to empty list." + ) + + define_property( + CACHED_VARIABLE PROPERTY "COTIRE_ADDITIONAL_PREFIX_HEADER_IGNORE_EXTENSIONS" + BRIEF_DOCS "Ignore includes with the listed file extensions from the generated prefix header." + FULL_DOCS + "The variable can be set to a semicolon separated list of file extensions." + "If a header file extension matches one in the list, it will be excluded from the generated prefix header." + "Includes with an extension in CMAKE__SOURCE_FILE_EXTENSIONS are always ignored." + "If not defined, defaults to inc;inl;ipp." + ) + + define_property( + CACHED_VARIABLE PROPERTY "COTIRE_UNITY_SOURCE_EXCLUDE_EXTENSIONS" + BRIEF_DOCS "Exclude sources with the listed file extensions from the generated unity source." + FULL_DOCS + "The variable can be set to a semicolon separated list of file extensions." + "If a source file extension matches one in the list, it will be excluded from the generated unity source file." + "Source files with an extension in CMAKE__IGNORE_EXTENSIONS are always excluded." + "If not defined, defaults to m;mm." + ) + + define_property( + CACHED_VARIABLE PROPERTY "COTIRE_MINIMUM_NUMBER_OF_TARGET_SOURCES" + BRIEF_DOCS "Minimum number of sources in target required to enable use of precompiled header." + FULL_DOCS + "The variable can be set to an integer > 0." + "If a target contains less than that number of source files, cotire will not enable the use of the precompiled header for the target." + "If not defined, defaults to 3." + ) + + define_property( + CACHED_VARIABLE PROPERTY "COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES" + BRIEF_DOCS "Maximum number of source files to include in a single unity source file." + FULL_DOCS + "This may be set to an integer >= 0." + "If 0, cotire will only create a single unity source file." + "If a target contains more than that number of source files, cotire will create multiple unity source files for it." + "Can be set to \"-j\" to optimize the count of unity source files for the number of available processor cores." + "Can be set to \"-j jobs\" to optimize the number of unity source files for the given number of simultaneous jobs." + "Is used to initialize the target property COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES." + "Defaults to \"-j\" for the generators Visual Studio, JOM or Ninja. Defaults to 0 otherwise." + ) + + # define cotire directory properties + + define_property( + DIRECTORY PROPERTY "COTIRE_ENABLE_PRECOMPILED_HEADER" + BRIEF_DOCS "Modify build command of cotired targets added in this directory to make use of the generated precompiled header." + FULL_DOCS + "See target property COTIRE_ENABLE_PRECOMPILED_HEADER." + ) + + define_property( + DIRECTORY PROPERTY "COTIRE_ADD_UNITY_BUILD" + BRIEF_DOCS "Add a new target that performs a unity build for cotired targets added in this directory." + FULL_DOCS + "See target property COTIRE_ADD_UNITY_BUILD." + ) + + define_property( + DIRECTORY PROPERTY "COTIRE_ADD_CLEAN" + BRIEF_DOCS "Add a new target that cleans all cotire generated files for cotired targets added in this directory." + FULL_DOCS + "See target property COTIRE_ADD_CLEAN." + ) + + define_property( + DIRECTORY PROPERTY "COTIRE_PREFIX_HEADER_IGNORE_PATH" + BRIEF_DOCS "Ignore headers from these directories when generating the prefix header." + FULL_DOCS + "See target property COTIRE_PREFIX_HEADER_IGNORE_PATH." + ) + + define_property( + DIRECTORY PROPERTY "COTIRE_PREFIX_HEADER_INCLUDE_PATH" + BRIEF_DOCS "Honor headers from these directories when generating the prefix header." + FULL_DOCS + "See target property COTIRE_PREFIX_HEADER_INCLUDE_PATH." + ) + + define_property( + DIRECTORY PROPERTY "COTIRE_PREFIX_HEADER_INCLUDE_PRIORITY_PATH" + BRIEF_DOCS "Header paths matching one of these directories are put at the top of the prefix header." + FULL_DOCS + "See target property COTIRE_PREFIX_HEADER_INCLUDE_PRIORITY_PATH." + ) + + define_property( + DIRECTORY PROPERTY "COTIRE_UNITY_SOURCE_PRE_UNDEFS" + BRIEF_DOCS "Preprocessor undefs to place in the generated unity source file before the inclusion of each source file." + FULL_DOCS + "See target property COTIRE_UNITY_SOURCE_PRE_UNDEFS." + ) + + define_property( + DIRECTORY PROPERTY "COTIRE_UNITY_SOURCE_POST_UNDEFS" + BRIEF_DOCS "Preprocessor undefs to place in the generated unity source file after the inclusion of each source file." + FULL_DOCS + "See target property COTIRE_UNITY_SOURCE_POST_UNDEFS." + ) + + define_property( + DIRECTORY PROPERTY "COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES" + BRIEF_DOCS "Maximum number of source files to include in a single unity source file." + FULL_DOCS + "See target property COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES." + ) + + define_property( + DIRECTORY PROPERTY "COTIRE_UNITY_LINK_LIBRARIES_INIT" + BRIEF_DOCS "Define strategy for setting up the unity target's link libraries." + FULL_DOCS + "See target property COTIRE_UNITY_LINK_LIBRARIES_INIT." + ) + + # define cotire target properties + + define_property( + TARGET PROPERTY "COTIRE_ENABLE_PRECOMPILED_HEADER" INHERITED + BRIEF_DOCS "Modify this target's build command to make use of the generated precompiled header." + FULL_DOCS + "If this property is set to TRUE, cotire will modify the build command to make use of the generated precompiled header." + "Irrespective of the value of this property, cotire will setup custom commands to generate the unity source and prefix header for the target." + "For makefile based generators cotire will also set up a custom target to manually invoke the generation of the precompiled header." + "The target name will be set to this target's name with the suffix _pch appended." + "Inherited from directory." + "Defaults to TRUE." + ) + + define_property( + TARGET PROPERTY "COTIRE_ADD_UNITY_BUILD" INHERITED + BRIEF_DOCS "Add a new target that performs a unity build for this target." + FULL_DOCS + "If this property is set to TRUE, cotire creates a new target of the same type that uses the generated unity source file instead of the target sources." + "Most of the relevant target properties will be copied from this target to the new unity build target." + "Target dependencies and linked libraries have to be manually set up for the new unity build target." + "The unity target name will be set to this target's name with the suffix _unity appended." + "Inherited from directory." + "Defaults to TRUE." + ) + + define_property( + TARGET PROPERTY "COTIRE_ADD_CLEAN" INHERITED + BRIEF_DOCS "Add a new target that cleans all cotire generated files for this target." + FULL_DOCS + "If this property is set to TRUE, cotire creates a new target that clean all files (unity source, prefix header, precompiled header)." + "The clean target name will be set to this target's name with the suffix _clean_cotire appended." + "Inherited from directory." + "Defaults to FALSE." + ) + + define_property( + TARGET PROPERTY "COTIRE_PREFIX_HEADER_IGNORE_PATH" INHERITED + BRIEF_DOCS "Ignore headers from these directories when generating the prefix header." + FULL_DOCS + "The property can be set to a list of directories." + "If a header file is found in one of these directories or sub-directories, it will be excluded from the generated prefix header." + "Inherited from directory." + "If not set, this property is initialized to \${CMAKE_SOURCE_DIR};\${CMAKE_BINARY_DIR}." + ) + + define_property( + TARGET PROPERTY "COTIRE_PREFIX_HEADER_INCLUDE_PATH" INHERITED + BRIEF_DOCS "Honor headers from these directories when generating the prefix header." + FULL_DOCS + "The property can be set to a list of directories." + "If a header file is found in one of these directories or sub-directories, it will be included in the generated prefix header." + "If a header file is both selected by COTIRE_PREFIX_HEADER_IGNORE_PATH and COTIRE_PREFIX_HEADER_INCLUDE_PATH," + "the option which yields the closer relative path match wins." + "Inherited from directory." + "If not set, this property is initialized to the empty list." + ) + + define_property( + TARGET PROPERTY "COTIRE_PREFIX_HEADER_INCLUDE_PRIORITY_PATH" INHERITED + BRIEF_DOCS "Header paths matching one of these directories are put at the top of prefix header." + FULL_DOCS + "The property can be set to a list of directories." + "Header file paths matching one of these directories will be inserted at the beginning of the generated prefix header." + "Header files are sorted according to the order of the directories in the property." + "If not set, this property is initialized to the empty list." + ) + + define_property( + TARGET PROPERTY "COTIRE_UNITY_SOURCE_PRE_UNDEFS" INHERITED + BRIEF_DOCS "Preprocessor undefs to place in the generated unity source file before the inclusion of each target source file." + FULL_DOCS + "This may be set to a semicolon-separated list of preprocessor symbols." + "cotire will add corresponding #undef directives to the generated unit source file before each target source file." + "Inherited from directory." + "Defaults to empty string." + ) + + define_property( + TARGET PROPERTY "COTIRE_UNITY_SOURCE_POST_UNDEFS" INHERITED + BRIEF_DOCS "Preprocessor undefs to place in the generated unity source file after the inclusion of each target source file." + FULL_DOCS + "This may be set to a semicolon-separated list of preprocessor symbols." + "cotire will add corresponding #undef directives to the generated unit source file after each target source file." + "Inherited from directory." + "Defaults to empty string." + ) + + define_property( + TARGET PROPERTY "COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES" INHERITED + BRIEF_DOCS "Maximum number of source files to include in a single unity source file." + FULL_DOCS + "This may be set to an integer > 0." + "If a target contains more than that number of source files, cotire will create multiple unity build files for it." + "If not set, cotire will only create a single unity source file." + "Inherited from directory." + "Defaults to empty." + ) + + define_property( + TARGET PROPERTY "COTIRE__UNITY_SOURCE_INIT" + BRIEF_DOCS "User provided unity source file to be used instead of the automatically generated one." + FULL_DOCS + "If set, cotire will only add the given file(s) to the generated unity source file." + "If not set, cotire will add all the target source files to the generated unity source file." + "The property can be set to a user provided unity source file." + "Defaults to empty." + ) + + define_property( + TARGET PROPERTY "COTIRE__PREFIX_HEADER_INIT" + BRIEF_DOCS "User provided prefix header file to be used instead of the automatically generated one." + FULL_DOCS + "If set, cotire will add the given header file(s) to the generated prefix header file." + "If not set, cotire will generate a prefix header by tracking the header files included by the unity source file." + "The property can be set to a user provided prefix header file (e.g., stdafx.h)." + "Defaults to empty." + ) + + define_property( + TARGET PROPERTY "COTIRE_UNITY_LINK_LIBRARIES_INIT" INHERITED + BRIEF_DOCS "Define strategy for setting up unity target's link libraries." + FULL_DOCS + "If this property is empty or set to NONE, the generated unity target's link libraries have to be set up manually." + "If this property is set to COPY, the unity target's link libraries will be copied from this target." + "If this property is set to COPY_UNITY, the unity target's link libraries will be copied from this target with considering existing unity targets." + "Inherited from directory." + "Defaults to empty." + ) + + define_property( + TARGET PROPERTY "COTIRE__UNITY_SOURCE" + BRIEF_DOCS "Read-only property. The generated unity source file(s)." + FULL_DOCS + "cotire sets this property to the path of the generated single computation unit source file for the target." + "Defaults to empty string." + ) + + define_property( + TARGET PROPERTY "COTIRE__PREFIX_HEADER" + BRIEF_DOCS "Read-only property. The generated prefix header file." + FULL_DOCS + "cotire sets this property to the full path of the generated language prefix header for the target." + "Defaults to empty string." + ) + + define_property( + TARGET PROPERTY "COTIRE__PRECOMPILED_HEADER" + BRIEF_DOCS "Read-only property. The generated precompiled header file." + FULL_DOCS + "cotire sets this property to the full path of the generated language precompiled header binary for the target." + "Defaults to empty string." + ) + + define_property( + TARGET PROPERTY "COTIRE_UNITY_TARGET_NAME" + BRIEF_DOCS "The name of the generated unity build target corresponding to this target." + FULL_DOCS + "This property can be set to the desired name of the unity target that will be created by cotire." + "If not set, the unity target name will be set to this target's name with the suffix _unity appended." + "After this target has been processed by cotire, the property is set to the actual name of the generated unity target." + "Defaults to empty string." + ) + + # define cotire source properties + + define_property( + SOURCE PROPERTY "COTIRE_EXCLUDED" + BRIEF_DOCS "Do not modify source file's build command." + FULL_DOCS + "If this property is set to TRUE, the source file's build command will not be modified to make use of the precompiled header." + "The source file will also be excluded from the generated unity source file." + "Source files that have their COMPILE_FLAGS property set will be excluded by default." + "Defaults to FALSE." + ) + + define_property( + SOURCE PROPERTY "COTIRE_DEPENDENCY" + BRIEF_DOCS "Add this source file to dependencies of the automatically generated prefix header file." + FULL_DOCS + "If this property is set to TRUE, the source file is added to dependencies of the generated prefix header file." + "If the file is modified, cotire will re-generate the prefix header source upon build." + "Defaults to FALSE." + ) + + define_property( + SOURCE PROPERTY "COTIRE_UNITY_SOURCE_PRE_UNDEFS" + BRIEF_DOCS "Preprocessor undefs to place in the generated unity source file before the inclusion of this source file." + FULL_DOCS + "This may be set to a semicolon-separated list of preprocessor symbols." + "cotire will add corresponding #undef directives to the generated unit source file before this file is included." + "Defaults to empty string." + ) + + define_property( + SOURCE PROPERTY "COTIRE_UNITY_SOURCE_POST_UNDEFS" + BRIEF_DOCS "Preprocessor undefs to place in the generated unity source file after the inclusion of this source file." + FULL_DOCS + "This may be set to a semicolon-separated list of preprocessor symbols." + "cotire will add corresponding #undef directives to the generated unit source file after this file is included." + "Defaults to empty string." + ) + + define_property( + SOURCE PROPERTY "COTIRE_START_NEW_UNITY_SOURCE" + BRIEF_DOCS "Start a new unity source file which includes this source file as the first one." + FULL_DOCS + "If this property is set to TRUE, cotire will complete the current unity file and start a new one." + "The new unity source file will include this source file as the first one." + "This property essentially works as a separator for unity source files." + "Defaults to FALSE." + ) + + define_property( + SOURCE PROPERTY "COTIRE_TARGET" + BRIEF_DOCS "Read-only property. Mark this source file as cotired for the given target." + FULL_DOCS + "cotire sets this property to the name of target, that the source file's build command has been altered for." + "Defaults to empty string." + ) + + message (STATUS "cotire ${COTIRE_CMAKE_MODULE_VERSION} loaded.") + +endif() + diff --git a/src/common/Common.h b/src/common/Common.h index b1f63683..5d6b0488 100644 --- a/src/common/Common.h +++ b/src/common/Common.h @@ -14,8 +14,9 @@ namespace Core { namespace Common { - // 99 is the last spawn id that seems to spawn any actor - const uint8_t MAX_DISPLAYED_ACTORS = 99; + // 99 is the last spawn id that seems to spawn any actor + const uint8_t MAX_DISPLAYED_ACTORS = 99; + const uint8_t MAX_DISPLAYED_EOBJS = 40; const int32_t INVALID_GAME_OBJECT_ID = 0xE0000000; @@ -403,70 +404,21 @@ namespace Core { Gathering, Fishing, - BeingRaised, - BetweenAreas, - Stealthed, - InnRoom, - Jumping, - AutoRun, - Occupied6, - BetweenAreas1, - SystemError, - LoggingOut, + enum PlayerStateFlag : uint8_t + { + HideUILockChar = 0, // as the name suggests, hides the ui and logs the char... + InCombat = 1, // in Combat, locks gearchange/return/teleport + Casting = 2, + InNpcEvent = 7, // when talking to an npc, locks ui giving "occupied" message - InvalidLocation, - WaitingForDuty, - BoundByDuty1, - Mounting, - WatchingCutscene, - WaitingForDutyFinder, - CreatingCharacter, - Jumping1, - PvpDisplay, - StatusAfflication2, + InNpcEvent1 = 10, // Sent together with InNpcEvent, when waiting for input? just a guess... - Mounting1, - CarryingItem, - UsingPartyFinder, - HousingFunctions, - Transformed, - FreeTrail, - BeingMoved, - Mounting2, - StatusAffliction3, - StatusAffliction4, + BetweenAreas = 24, + BoundByDuty = 28, + WatchingCutscene = 50, // this is actually just a dummy, this id is different - RegisteringRaceOrMatch, - WaitingForRaceOrMatch, - WaitingForTripleTriadMatch, - InFlight, - WatchingCutscene1, - DeepDungeon, - Swimming, - Diving, - RegisteringTripleTriad, - WaitingTripleTriad, - InCrossWorldParty - }; - - enum struct FateStatus : uint8_t - { - Active = 2, - Inactive = 4, - Preparing = 7, - Completed = 8, - }; - - enum ActorControlType : uint16_t - { - ToggleWeapon = 0x01, - SetStatus = 0x02, - CastStart = 0x03, - ToggleAggro = 0x04, - ClassJobChange = 0x05, - DefeatMsg = 0x06, - GainExpMsg = 0x07, + }; LevelUpEffect = 0x0A, @@ -778,13 +730,7 @@ namespace Core { FreeCompany = 5, }; - enum SocialListType : uint8_t - { - PartyList = 0x02, - FriendList = 0x0b, - SearchList = 0x0e, - }; - + typedef std::vector< PlayerStateFlag > PlayerStateFlagList; // todo: rename SocialRequestAction and SocialRequestResponse cause they seem ambiguous enum class SocialRequestAction : uint8_t diff --git a/src/common/CommonGen.h b/src/common/CommonGen.h index e0005ada..5e9bf149 100644 --- a/src/common/CommonGen.h +++ b/src/common/CommonGen.h @@ -463,6 +463,7 @@ namespace Common { //Weather.exd enum class Weather : uint8_t { + None = 0, ClearSkies = 1, FairSkies = 2, Clouds = 3, diff --git a/src/common/Exd/ExdDataGenerated.cpp b/src/common/Exd/ExdDataGenerated.cpp index 859998d2..eda4a919 100644 --- a/src/common/Exd/ExdDataGenerated.cpp +++ b/src/common/Exd/ExdDataGenerated.cpp @@ -172,6 +172,7 @@ Core::Data::Aetheryte::Aetheryte( uint32_t row_id, Core::Data::ExdDataGenerated* placeName = exdData->getField< uint16_t >( row, 8 ); aethernetName = exdData->getField< uint16_t >( row, 9 ); territory = exdData->getField< uint16_t >( row, 10 ); + levelId = exdData->getField< uint32_t >( row, 11 ); isAetheryte = exdData->getField< bool >( row, 15 ); aethernetGroup = exdData->getField< uint8_t >( row, 16 ); map = exdData->getField< uint16_t >( row, 19 ); diff --git a/src/common/Exd/ExdDataGenerated.h b/src/common/Exd/ExdDataGenerated.h index 0cc29084..d17edf4a 100644 --- a/src/common/Exd/ExdDataGenerated.h +++ b/src/common/Exd/ExdDataGenerated.h @@ -496,6 +496,7 @@ struct Aetheryte uint16_t placeName; uint16_t aethernetName; uint16_t territory; + uint32_t levelId; bool isAetheryte; uint8_t aethernetGroup; uint16_t map; diff --git a/src/common/Network/PacketDef/Ipcs.h b/src/common/Network/PacketDef/Ipcs.h index 12071465..a33ba72a 100644 --- a/src/common/Network/PacketDef/Ipcs.h +++ b/src/common/Network/PacketDef/Ipcs.h @@ -72,8 +72,8 @@ namespace Packets { Playtime = 0x00DF, // updated 4.2 CFRegistered = 0x00B8, // updated 4.1 - - + SocialRequestResponse = 0x00BB, // updated 4.1 + CancelAllianceForming = 0x00C6, // updated 4.2 Chat = 0x00E1, // updated 4.2 SocialRequestResponse = 0x00E5, // updated 4.1 SocialRequestReceive = 0x00E6, // updated 4.2 @@ -104,15 +104,18 @@ namespace Packets { NpcSpawn = 0x015D, // updated 4.2 ActorMove = 0x015E, // updated 4.2 ActorSetPos = 0x0160, // updated 4.2 + ActorCast = 0x0162, // updated 4.2 + HateList = 0x0165, // updated 4.2 + ObjectSpawn = 0x0167, // updated 4.2 + ObjectDespawn = 0x0168, // updated 4.2 UpdateClassInfo = 0x0169, // updated 4.2 + InitUI = 0x016B, // updated 4.2 - - ActorOwner = 0x016D, // updated 4.2 ? - PlayerStats = 0x016C, // updated 4.2 + ActorOwner = 0x016D, // updated 4.2 ? PlayerStateFlags = 0x016E, // updated 4.2 PlayerClassInfo = 0x016F, // updated 4.2 ModelEquip = 0x0170, // updated 4.2 @@ -127,6 +130,7 @@ namespace Packets { UpdateInventorySlot = 0x0181, // updated 4.2 EventPlay = 0x018E, // updated 4.2 + DirectorPlayScene = 0x0192, // updated 4.2 EventStart = 0x0198, // updated 4.2 EventFinish = 0x0199, // updated 4.2 @@ -209,6 +213,8 @@ namespace Packets { FcInfoReqHandler = 0x011A, // updated 4.2 + + ReqJoinNoviceNetwork = 0x0129, // updated 4.2 ReqCountdownInitiate = 0x012C, // updated 4.2 ReqCountdownCancel = 0x012D, // updated 4.2 @@ -230,8 +236,8 @@ namespace Packets { OutOfRangeEventHandler = 0x014B, // updated 4.2 EnterTeriEventHandler = 0x014C, // updated 4.2 - ReturnEventHandler = 0x0151, // updated 4.2 ? - TradeReturnEventHandler = 0x0152, // updated 4.2 ? + ReturnEventHandler = 0x0151, // updated 4.2 + TradeReturnEventHandler = 0x0152, // updated 4.2 LinkshellEventHandler = 0x0144, // updated 4.1 ?? diff --git a/src/common/Network/PacketDef/Zone/ServerZoneDef.h b/src/common/Network/PacketDef/Zone/ServerZoneDef.h index 9d39555e..29088cf8 100644 --- a/src/common/Network/PacketDef/Zone/ServerZoneDef.h +++ b/src/common/Network/PacketDef/Zone/ServerZoneDef.h @@ -233,7 +233,7 @@ struct FFXIVIpcLinkshellList : FFXIVIpcBasePacket struct FFXIVIpcStatusEffectList : FFXIVIpcBasePacket { uint8_t classId; - uint8_t classId1; + uint8_t level1; uint16_t level; uint32_t current_hp; uint32_t max_hp; @@ -427,7 +427,7 @@ struct FFXIVIpcPlayerSpawn : FFXIVIpcBasePacket uint8_t spawnIndex; uint8_t state; uint8_t persistantEmote; - uint8_t type; + uint8_t modelType; // modelType -> eventSystemDefine uint8_t subtype; uint8_t voice; uint16_t u25c; @@ -459,8 +459,7 @@ struct FFXIVIpcPlayerSpawn : FFXIVIpcBasePacket */ struct FFXIVIpcNpcSpawn : FFXIVIpcBasePacket { - uint16_t title; - uint16_t u1b; + uint32_t gimmickId; // needs to be existing in the map, mob will snap to it uint8_t u2b; uint8_t u2ab; uint8_t gmRank; @@ -503,7 +502,7 @@ struct FFXIVIpcNpcSpawn : FFXIVIpcBasePacket uint8_t spawnIndex; uint8_t state; uint8_t persistantEmote; - uint8_t type; + uint8_t modelType; uint8_t subtype; uint8_t voice; uint16_t u25c; @@ -611,7 +610,7 @@ struct FFXIVIpcHateList : FFXIVIpcBasePacket struct FFXIVIpcUpdateClassInfo : FFXIVIpcBasePacket { uint8_t classId; - uint8_t classId1; + uint8_t level1; uint16_t level; uint32_t nextLevelIndex; uint32_t currentExp; @@ -674,7 +673,7 @@ struct FFXIVIpcInitUI : FFXIVIpcBasePacket uint8_t namedayDay; uint8_t cityState; uint8_t homepoint; - uint8_t unknown26; + uint8_t unknown26; // 2 if "warrior of light" uint8_t petHotBar; uint8_t companionRank; uint8_t companionStars; @@ -871,10 +870,8 @@ struct FFXIVIpcActorOwner : FFXIVIpcBasePacket */ struct FFXIVIpcPlayerStateFlags : FFXIVIpcBasePacket { - uint8_t flags[7]; - uint8_t padding1[3]; - uint32_t padding2; - uint16_t padding; + uint8_t flags[12]; + uint32_t padding; }; /** @@ -1047,7 +1044,7 @@ struct FFXIVIpcEventStart : FFXIVIpcBasePacket /* 000D */ uint8_t param2; /* 000E */ uint16_t padding; /* 0010 */ uint32_t param3; - /* 0014 */ uint32_t padding1; + /* 0014 */ uint32_t contentId; }; @@ -1069,6 +1066,24 @@ struct FFXIVIpcEventPlay : FFXIVIpcBasePacket uint8_t unknown[8]; }; + /** +* Structural representation of the packet sent by the server +* to play an event +*/ +struct FFXIVIpcDirectorPlayScene : FFXIVIpcBasePacket +{ + uint64_t actorId; + uint32_t eventId; + uint16_t scene; + uint16_t padding; + uint32_t flags; + uint32_t param3; + uint8_t param4; + uint8_t padding1[3]; + uint32_t param5; + uint8_t unknown[0x40]; +}; + /** * Structural representation of the packet sent by the server * to finish an event @@ -1363,24 +1378,33 @@ struct FFXIVIpcMSQTrackerComplete : FFXIVIpcBasePacket struct FFXIVIpcObjectSpawn : FFXIVIpcBasePacket { - uint8_t count; + uint8_t spawnIndex; uint8_t objKind; - uint8_t unknown2; uint8_t state; + uint8_t unknown3; uint32_t objId; uint32_t actorId; uint32_t levelId; uint32_t unknown10; uint32_t someActorId14; - uint32_t hierachyId; - uint32_t unknown1C; - uint32_t unknown20; - uint32_t unknown24; - uint32_t unknown28; - uint32_t unknown2c; + uint32_t gimmickId; + float scale; + int16_t unknown20a; + uint16_t rotation; + int16_t unknown24a; + int16_t unknown24b; + uint16_t unknown28a; + int16_t unknown28c; + uint32_t unknown2C; Common::FFXIVARR_POSITION3 position; - int16_t rotation; - int16_t unknown; + int16_t unknown3C; + int16_t unknown3E; +}; + +struct FFXIVIpcObjectDespawn : FFXIVIpcBasePacket +{ + uint8_t spawnIndex; + uint8_t padding[7]; }; diff --git a/src/common/Util/SpawnIndexAllocator.h b/src/common/Util/SpawnIndexAllocator.h new file mode 100644 index 00000000..732f652c --- /dev/null +++ b/src/common/Util/SpawnIndexAllocator.h @@ -0,0 +1,109 @@ +#ifndef SAPPHIRE_SPAWNINDEXALLOCATOR_H +#define SAPPHIRE_SPAWNINDEXALLOCATOR_H + +#include +#include +#include + +namespace Core +{ +namespace Util +{ + template< typename T, typename ActorIdType = uint32_t > + class SpawnIndexAllocator + { + public: + static_assert( std::is_same< T, uint8_t >::value || std::is_same< T, uint16_t >::value || + std::is_same< T, uint32_t >::value || std::is_same< T, uint64_t >::value, + "T must be uint8_t, uint16_t, uint32_t, uint64_t" ); + + SpawnIndexAllocator() : + m_maxSlotId( 0 ), + m_reserveFirstSlot( false ) + { } + + void init( T maxSlotId, bool reserveFirstSlot = false ) + { + m_maxSlotId = maxSlotId; + m_reserveFirstSlot = reserveFirstSlot; + + setupQueue(); + + // todo: reserve max slot id in map to prevent any runtime reshashing + } + + T freeUsedSpawnIndex( ActorIdType actorId ) + { + auto it = m_actorIdToAllocatedMap.find( actorId ); + if( it == m_actorIdToAllocatedMap.end() ) + return 0; + + auto index = it->second; + m_availableIds.push( index ); + m_actorIdToAllocatedMap.erase( it ); + + return index; + } + + T getNextFreeSpawnIndex( ActorIdType actorId ) + { + assert( m_maxSlotId != 0 ); + + if( m_availableIds.empty() ) + return getAllocFailId(); + + auto nextId = m_availableIds.front(); + m_availableIds.pop(); + + m_actorIdToAllocatedMap[actorId] = nextId; + + return nextId; + } + + void freeAllSpawnIndexes() + { + setupQueue(); + + m_actorIdToAllocatedMap.clear(); + } + + bool isSpawnIndexValid( T spawnIndex ) + { + return spawnIndex != getAllocFailId(); + } + + constexpr T getAllocFailId() + { + return static_cast< T >( -1 ); + } + + protected: + void setupQueue() + { + assert( m_maxSlotId != 0 ); + + while( !m_availableIds.empty() ) + m_availableIds.pop(); + + uint32_t start = 0; + + // slot 0 is reserved when used for spawning actors/players otherwise the local player actor spawnIndex + // will be used by another actor and despawn the local player + if( m_reserveFirstSlot ) + start = 1; + + for( uint32_t i = start; i < m_maxSlotId; i++ ) + m_availableIds.push( i ); + } + + std::queue< T > m_availableIds; + std::unordered_map< ActorIdType, T > m_actorIdToAllocatedMap; + + T m_maxSlotId; + bool m_reserveFirstSlot; + }; +} +} + + +#endif //SAPPHIRE_SPAWNINDEXALLOCATOR_H diff --git a/src/servers/sapphire_api/Forwards.h b/src/servers/sapphire_api/Forwards.h index bdffff9f..73b6429d 100644 --- a/src/servers/sapphire_api/Forwards.h +++ b/src/servers/sapphire_api/Forwards.h @@ -34,11 +34,11 @@ namespace Core namespace Entity { - class Actor; + class Chara; class Player; class BattleNpc; - typedef boost::shared_ptr ActorPtr; + typedef boost::shared_ptr ActorPtr; typedef boost::shared_ptr PlayerPtr; typedef boost::shared_ptr BattleNpcPtr; } @@ -91,10 +91,10 @@ namespace Core namespace Scripting { - typedef std::function< void( Entity::Player&, uint32_t, uint16_t, uint16_t, uint16_t, uint16_t ) > EventReturnCallback; + using EventReturnCallback = std::function< void( Entity::Player&, uint32_t, uint16_t, uint16_t, uint16_t, uint16_t ) >; } - typedef std::function< void( Entity::Player&, uint32_t, uint64_t ) > ActionCallback; + using ActionCallback = std::function< void( Entity::Player&, uint32_t, uint64_t ) >; } diff --git a/src/servers/sapphire_api/PlayerMinimal.cpp b/src/servers/sapphire_api/PlayerMinimal.cpp index ba95c96f..1fa2604d 100644 --- a/src/servers/sapphire_api/PlayerMinimal.cpp +++ b/src/servers/sapphire_api/PlayerMinimal.cpp @@ -148,13 +148,13 @@ namespace Core { { std::vector< uint8_t > customize( 26 ); - std::vector< uint8_t > howTo( 32 ); - std::vector< uint8_t > aetherytes( 12 ); - std::vector< uint8_t > discovery( 411 ); + std::vector< uint8_t > howTo( 33 ); + std::vector< uint8_t > aetherytes( 16 ); + std::vector< uint8_t > discovery( 421 ); std::vector< uint8_t > questComplete( 396 ); std::vector< uint8_t > unlocks( 64 ); - std::vector< uint8_t > mountGuide( 13 ); - std::vector< uint8_t > orchestrion( 38 ); + std::vector< uint8_t > mountGuide( 15 ); + std::vector< uint8_t > orchestrion( 40 ); std::vector< uint8_t > modelEquip( 40 ); std::vector< uint8_t > questTracking8( 10 ); std::vector< int16_t > questTracking = { -1, -1, -1, -1, -1 }; diff --git a/src/servers/sapphire_zone/Action/Action.cpp b/src/servers/sapphire_zone/Action/Action.cpp index 56512f09..ed3c3e36 100644 --- a/src/servers/sapphire_zone/Action/Action.cpp +++ b/src/servers/sapphire_zone/Action/Action.cpp @@ -22,7 +22,7 @@ Core::Common::HandleActionType Core::Action::Action::getHandleActionType() const return m_handleActionType; } -Core::Entity::ActorPtr Core::Action::Action::getTargetActor() const +Core::Entity::CharaPtr Core::Action::Action::getTargetChara() const { return m_pTarget; } @@ -57,7 +57,7 @@ void Core::Action::Action::setCastTime( uint32_t castTime ) m_castTime = castTime; } -Core::Entity::ActorPtr Core::Action::Action::getActionSource() const +Core::Entity::CharaPtr Core::Action::Action::getActionSource() const { return m_pSource; } diff --git a/src/servers/sapphire_zone/Action/Action.h b/src/servers/sapphire_zone/Action/Action.h index 8cd4c3cc..995835dd 100644 --- a/src/servers/sapphire_zone/Action/Action.h +++ b/src/servers/sapphire_zone/Action/Action.h @@ -18,7 +18,7 @@ namespace Action { Common::HandleActionType getHandleActionType() const; - Entity::ActorPtr getTargetActor() const; + Entity::CharaPtr getTargetChara() const; bool isInterrupted() const; @@ -32,7 +32,7 @@ namespace Action { void setCastTime( uint32_t castTime ); - Entity::ActorPtr getActionSource() const; + Entity::CharaPtr getActionSource() const; virtual void onStart() {}; virtual void onFinish() {}; @@ -48,8 +48,8 @@ namespace Action { uint64_t m_startTime; uint32_t m_castTime; - Entity::ActorPtr m_pSource; - Entity::ActorPtr m_pTarget; + Entity::CharaPtr m_pSource; + Entity::CharaPtr m_pTarget; bool m_bInterrupt; diff --git a/src/servers/sapphire_zone/Action/ActionCast.cpp b/src/servers/sapphire_zone/Action/ActionCast.cpp index efdccf10..7d49afb3 100644 --- a/src/servers/sapphire_zone/Action/ActionCast.cpp +++ b/src/servers/sapphire_zone/Action/ActionCast.cpp @@ -27,7 +27,7 @@ Core::Action::ActionCast::ActionCast() m_handleActionType = Common::HandleActionType::Event; } -Core::Action::ActionCast::ActionCast( Entity::ActorPtr pActor, Entity::ActorPtr pTarget, uint16_t actionId ) +Core::Action::ActionCast::ActionCast( Entity::CharaPtr pActor, Entity::CharaPtr pTarget, uint16_t actionId ) { m_startTime = 0; m_id = actionId; @@ -84,7 +84,7 @@ void Core::Action::ActionCast::onInterrupt() if( !m_pSource ) return; - m_pSource->getAsPlayer()->unsetStateFlag( PlayerStateFlag::Occupied1 ); + //m_pSource->getAsPlayer()->unsetStateFlag( PlayerStateFlag::Occupied1 ); m_pSource->getAsPlayer()->unsetStateFlag( PlayerStateFlag::Casting ); auto control = ActorControlPacket142( m_pSource->getId(), ActorControlType::CastInterrupt, diff --git a/src/servers/sapphire_zone/Action/ActionCast.h b/src/servers/sapphire_zone/Action/ActionCast.h index b656b391..c1dac482 100644 --- a/src/servers/sapphire_zone/Action/ActionCast.h +++ b/src/servers/sapphire_zone/Action/ActionCast.h @@ -15,7 +15,7 @@ namespace Action { ActionCast(); ~ActionCast(); - ActionCast( Entity::ActorPtr pActor, Entity::ActorPtr pTarget, uint16_t actionId ); + ActionCast( Entity::CharaPtr pActor, Entity::CharaPtr pTarget, uint16_t actionId ); void onStart() override; void onFinish() override; diff --git a/src/servers/sapphire_zone/Action/ActionCollision.cpp b/src/servers/sapphire_zone/Action/ActionCollision.cpp index a66d234c..ddf39bfe 100644 --- a/src/servers/sapphire_zone/Action/ActionCollision.cpp +++ b/src/servers/sapphire_zone/Action/ActionCollision.cpp @@ -1,8 +1,11 @@ #include #include +#include + #include "ActionCollision.h" #include "Actor/Actor.h" +#include "Actor/Chara.h" #include "Actor/Player.h" #include @@ -31,7 +34,7 @@ bool ActionCollision::isActorApplicable( Actor& actor, TargetFilter targetFilter case TargetFilter::Allies: { // todo: implement ally NPCs - actorApplicable = !actor.isBattleNpc(); + // actorApplicable = !chara.isBattleNpc(); break; } case TargetFilter::Party: @@ -42,12 +45,12 @@ bool ActionCollision::isActorApplicable( Actor& actor, TargetFilter targetFilter } case TargetFilter::Enemies: { - actorApplicable = actor.isBattleNpc(); + //actorApplicable = chara.isBattleNpc(); break; } } - return ( actorApplicable && actor.isAlive() ); + return ( actorApplicable && actor.getAsChara()->isAlive() ); } std::set< Core::Entity::ActorPtr > ActionCollision::getActorsHitFromAction( FFXIVARR_POSITION3 aoePosition, diff --git a/src/servers/sapphire_zone/Action/ActionCollision.h b/src/servers/sapphire_zone/Action/ActionCollision.h index 0c0da597..fd0b562b 100644 --- a/src/servers/sapphire_zone/Action/ActionCollision.h +++ b/src/servers/sapphire_zone/Action/ActionCollision.h @@ -2,12 +2,14 @@ #define _ACTIONCOLLISION_H #include -#include - -#include "Actor/Actor.h" #include "Action.h" + namespace Core { +namespace Data +{ + struct Action; +} namespace Entity { enum class TargetFilter diff --git a/src/servers/sapphire_zone/Action/ActionMount.cpp b/src/servers/sapphire_zone/Action/ActionMount.cpp index 0be35f6e..b513e03b 100644 --- a/src/servers/sapphire_zone/Action/ActionMount.cpp +++ b/src/servers/sapphire_zone/Action/ActionMount.cpp @@ -25,7 +25,7 @@ Core::Action::ActionMount::ActionMount() m_handleActionType = HandleActionType::Event; } -Core::Action::ActionMount::ActionMount( Entity::ActorPtr pActor, uint16_t mountId ) +Core::Action::ActionMount::ActionMount( Entity::CharaPtr pActor, uint16_t mountId ) { m_startTime = 0; m_id = mountId; @@ -79,7 +79,7 @@ void Core::Action::ActionMount::onFinish() effectPacket.data().unknown_62 = 13; effectPacket.data().actionTextId = 4; effectPacket.data().numEffects = 1; - effectPacket.data().rotation = Math::Util::floatToUInt16Rot( pPlayer->getRotation() ); + effectPacket.data().rotation = Math::Util::floatToUInt16Rot( pPlayer->getRot() ); effectPacket.data().effectTarget = INVALID_GAME_OBJECT_ID; effectPacket.data().effects[0].effectType = ActionEffectType::Mount; effectPacket.data().effects[0].hitSeverity = ActionHitSeverityType::CritDamage; @@ -95,7 +95,7 @@ void Core::Action::ActionMount::onInterrupt() if( !m_pSource ) return; - m_pSource->getAsPlayer()->unsetStateFlag( PlayerStateFlag::Occupied1 ); + //m_pSource->getAsPlayer()->unsetStateFlag( PlayerStateFlag::Occupied1 ); m_pSource->getAsPlayer()->unsetStateFlag( PlayerStateFlag::Casting ); auto control = ActorControlPacket142( m_pSource->getId(), ActorControlType::CastInterrupt, diff --git a/src/servers/sapphire_zone/Action/ActionMount.h b/src/servers/sapphire_zone/Action/ActionMount.h index ba32817d..3450a2b2 100644 --- a/src/servers/sapphire_zone/Action/ActionMount.h +++ b/src/servers/sapphire_zone/Action/ActionMount.h @@ -15,7 +15,7 @@ namespace Action { ActionMount(); ~ActionMount(); - ActionMount( Entity::ActorPtr pActor, uint16_t mountId ); + ActionMount( Entity::CharaPtr pActor, uint16_t mountId ); void onStart() override; void onFinish() override; diff --git a/src/servers/sapphire_zone/Action/ActionTeleport.cpp b/src/servers/sapphire_zone/Action/ActionTeleport.cpp index e9fabb43..c028c6e5 100644 --- a/src/servers/sapphire_zone/Action/ActionTeleport.cpp +++ b/src/servers/sapphire_zone/Action/ActionTeleport.cpp @@ -22,7 +22,7 @@ Core::Action::ActionTeleport::ActionTeleport() m_handleActionType = HandleActionType::Event; } -Core::Action::ActionTeleport::ActionTeleport( Entity::ActorPtr pActor, uint16_t targetZone, uint16_t cost ) +Core::Action::ActionTeleport::ActionTeleport( Entity::CharaPtr pActor, uint16_t targetZone, uint16_t cost ) { m_startTime = 0; m_id = 5; @@ -89,7 +89,7 @@ void Core::Action::ActionTeleport::onFinish() effectPacket.data().actionTextId = 5; effectPacket.data().unknown_5 = 1; effectPacket.data().numEffects = 1; - effectPacket.data().rotation = static_cast< uint16_t >( 0x8000 * ( ( pPlayer->getRotation() + 3.1415926 ) ) / 3.1415926 ); + effectPacket.data().rotation = static_cast< uint16_t >( 0x8000 * ( ( pPlayer->getRot() + 3.1415926 ) ) / 3.1415926 ); effectPacket.data().effectTarget = pPlayer->getId(); pPlayer->sendToInRangeSet( effectPacket, true ); diff --git a/src/servers/sapphire_zone/Action/ActionTeleport.h b/src/servers/sapphire_zone/Action/ActionTeleport.h index 480090eb..fab98d06 100644 --- a/src/servers/sapphire_zone/Action/ActionTeleport.h +++ b/src/servers/sapphire_zone/Action/ActionTeleport.h @@ -17,7 +17,7 @@ namespace Action { ActionTeleport(); ~ActionTeleport(); - ActionTeleport( Entity::ActorPtr pActor, uint16_t action, uint16_t cost ); + ActionTeleport( Entity::CharaPtr pActor, uint16_t action, uint16_t cost ); void onStart() override; void onFinish() override; diff --git a/src/servers/sapphire_zone/Action/EventAction.cpp b/src/servers/sapphire_zone/Action/EventAction.cpp index 82546ef7..bfc874e7 100644 --- a/src/servers/sapphire_zone/Action/EventAction.cpp +++ b/src/servers/sapphire_zone/Action/EventAction.cpp @@ -23,7 +23,7 @@ Core::Action::EventAction::EventAction() m_handleActionType = HandleActionType::Event; } -Core::Action::EventAction::EventAction( Entity::ActorPtr pActor, uint32_t eventId, uint16_t action, +Core::Action::EventAction::EventAction( Entity::CharaPtr pActor, uint32_t eventId, uint16_t action, ActionCallback finishRef, ActionCallback interruptRef, uint64_t additional ) { m_additional = additional; @@ -55,7 +55,8 @@ void Core::Action::EventAction::onStart() if( m_pSource->isPlayer() ) { m_pSource->sendToInRangeSet( control, true ); - m_pSource->getAsPlayer()->setStateFlag( PlayerStateFlag::Occupied2 ); + if( m_pSource->getAsPlayer()->hasStateFlag( PlayerStateFlag::InNpcEvent ) ) + m_pSource->getAsPlayer()->unsetStateFlag( PlayerStateFlag::InNpcEvent ); } else m_pSource->sendToInRangeSet( control ); @@ -84,7 +85,7 @@ void Core::Action::EventAction::onFinish() if( m_pSource->isPlayer() ) { - m_pSource->getAsPlayer()->unsetStateFlag( PlayerStateFlag::Occupied2 ); + //m_pSource->getAsPlayer()->unsetStateFlag( PlayerStateFlag::Occupied2 ); m_pSource->sendToInRangeSet( control, true ); } else @@ -112,8 +113,8 @@ void Core::Action::EventAction::onInterrupt() { auto control1 = ActorControlPacket143( m_pSource->getId(), ActorControlType::FreeEventPos, m_eventId ); - m_pSource->getAsPlayer()->unsetStateFlag( PlayerStateFlag::NoCombat ); - m_pSource->getAsPlayer()->unsetStateFlag( PlayerStateFlag::Occupied1 ); + //m_pSource->getAsPlayer()->unsetStateFlag( PlayerStateFlag::NoCombat ); + //m_pSource->getAsPlayer()->unsetStateFlag( PlayerStateFlag::Occupied1 ); m_pSource->sendToInRangeSet( control ); m_pSource->sendToInRangeSet( control1 ); diff --git a/src/servers/sapphire_zone/Action/EventAction.h b/src/servers/sapphire_zone/Action/EventAction.h index f0857d34..a92831ef 100644 --- a/src/servers/sapphire_zone/Action/EventAction.h +++ b/src/servers/sapphire_zone/Action/EventAction.h @@ -16,7 +16,7 @@ namespace Action { EventAction(); ~EventAction(); - EventAction( Entity::ActorPtr pActor, uint32_t eventId, uint16_t action, + EventAction( Entity::CharaPtr pActor, uint32_t eventId, uint16_t action, ActionCallback finishRef, ActionCallback interruptRef, uint64_t additional ); void onStart() override; diff --git a/src/servers/sapphire_zone/Action/EventItemAction.cpp b/src/servers/sapphire_zone/Action/EventItemAction.cpp index c20c13bc..b1023426 100644 --- a/src/servers/sapphire_zone/Action/EventItemAction.cpp +++ b/src/servers/sapphire_zone/Action/EventItemAction.cpp @@ -24,7 +24,7 @@ Core::Action::EventItemAction::EventItemAction() m_handleActionType = HandleActionType::Event; } -Core::Action::EventItemAction::EventItemAction( Entity::ActorPtr pActor, uint32_t eventId, uint16_t action, +Core::Action::EventItemAction::EventItemAction( Entity::CharaPtr pActor, uint32_t eventId, uint16_t action, ActionCallback finishRef, ActionCallback interruptRef, uint64_t additional ) { m_additional = additional; @@ -75,7 +75,7 @@ void Core::Action::EventItemAction::onFinish() effectPacket.data().actionTextId = m_id; effectPacket.data().unknown_5 = 2; effectPacket.data().numEffects = 1; - effectPacket.data().rotation = Math::Util::floatToUInt16Rot( m_pSource->getRotation() ); + effectPacket.data().rotation = Math::Util::floatToUInt16Rot( m_pSource->getRot() ); effectPacket.data().effectTarget = static_cast< uint32_t >( m_additional ); m_pSource->getAsPlayer()->unsetStateFlag( Common::PlayerStateFlag::Casting ); diff --git a/src/servers/sapphire_zone/Action/EventItemAction.h b/src/servers/sapphire_zone/Action/EventItemAction.h index bd9eee9d..6c69e1aa 100644 --- a/src/servers/sapphire_zone/Action/EventItemAction.h +++ b/src/servers/sapphire_zone/Action/EventItemAction.h @@ -14,7 +14,7 @@ namespace Action { EventItemAction(); ~EventItemAction(); - EventItemAction( Entity::ActorPtr pActor, uint32_t eventId, uint16_t action, + EventItemAction( Entity::CharaPtr pActor, uint32_t eventId, uint16_t action, ActionCallback finishRef, ActionCallback interruptRef, uint64_t additional ); void onStart() override; diff --git a/src/servers/sapphire_zone/Actor/Actor.cpp b/src/servers/sapphire_zone/Actor/Actor.cpp index 61a0f57a..dc8b4915 100644 --- a/src/servers/sapphire_zone/Actor/Actor.cpp +++ b/src/servers/sapphire_zone/Actor/Actor.cpp @@ -1,8 +1,9 @@ +#include "Actor.h" + +#include +#include #include #include -#include -#include -#include #include "Forwards.h" #include "Action/Action.h" @@ -11,10 +12,15 @@ #include "Zone/Zone.h" #include "Network/GameConnection.h" -#include "Network/PacketWrappers/ActorControlPacket142.h" -#include "Network/PacketWrappers/ActorControlPacket143.h" -#include "Network/PacketWrappers/ActorControlPacket144.h" -#include "Network/PacketWrappers/UpdateHpMpTpPacket.h" + +#include "Chara.h" +#include "EventObject.h" +#include "Player.h" + +#include "ServerZone.h" +#include "Session.h" +#include "Zone/Zone.h" + #include "Zone/TerritoryMgr.h" @@ -33,238 +39,35 @@ extern Core::Framework g_framework; using namespace Core::Common; using namespace Core::Network::Packets; -using namespace Core::Network::Packets::Server; +//using namespace Core::Network::Packets::Server; Core::Entity::Actor::Actor( ObjKind type ) : - GameObject( type ) + m_objKind( type ) { - // initialize the free slot queue - for( uint8_t i = 0; i < MAX_STATUS_EFFECTS; i++ ) - { - m_statusEffectFreeSlotQueue.push( i ); - } -} - -Core::Entity::Actor::~Actor() -{ -} - -/*! \return the actors name */ -std::string Core::Entity::Actor::getName() const -{ - return std::string( m_name ); -} - -/*! \return list of actors currently in range */ -std::set< Core::Entity::ActorPtr > Core::Entity::Actor::getInRangeActors( bool includeSelf ) -{ - auto tempInRange = m_inRangeActors; - - if( includeSelf ) - tempInRange.insert( getAsActor() ); - - return tempInRange; - } - -/*! \return current stance of the actors */ -Core::Entity::Actor::Stance Core::Entity::Actor::getStance() const -{ - return m_currentStance; -} - -/*! \return actor stats */ -Core::Entity::Actor::ActorStats Core::Entity::Actor::getStats() const -{ - return m_baseStats; -} - -/*! \return current HP */ -uint32_t Core::Entity::Actor::getHp() const -{ - return m_hp; -} - -/*! \return current MP */ -uint32_t Core::Entity::Actor::getMp() const -{ - return m_mp; -} - -/*! \return current TP */ -uint16_t Core::Entity::Actor::getTp() const -{ - return m_tp; -} - -/*! \return current GP */ -uint16_t Core::Entity::Actor::getGp() const -{ - return m_gp; -} - -/*! \return current invincibility type */ -InvincibilityType Core::Entity::Actor::getInvincibilityType() const -{ - return m_invincibilityType; -} - -/*! \return current class or job */ -Core::Common::ClassJob Core::Entity::Actor::getClass() const -{ - return m_class; -} - -/*! \return current class or job as int32_t ( this feels pointless ) */ -uint8_t Core::Entity::Actor::getClassAsInt() const -{ - return static_cast< uint8_t >( m_class ); -} - -/*! \param ClassJob to set */ -void Core::Entity::Actor::setClass( Common::ClassJob classJob ) -{ - m_class = classJob; -} - -/*! \param Id of the target to set */ -void Core::Entity::Actor::setTargetId( uint64_t targetId ) -{ - m_targetId = targetId; -} - -/*! \return Id of the current target */ -uint64_t Core::Entity::Actor::getTargetId() const -{ - return m_targetId; -} - -/*! \return True if the actor is alive */ -bool Core::Entity::Actor::isAlive() const -{ - return ( m_hp > 0 ); -} - -/*! \return max hp for the actor */ -uint32_t Core::Entity::Actor::getMaxHp() const -{ - return m_baseStats.max_hp; -} - -/*! \return max mp for the actor */ -uint32_t Core::Entity::Actor::getMaxMp() const -{ - return m_baseStats.max_mp; -} - -/*! \return reset hp to current max hp */ -void Core::Entity::Actor::resetHp() -{ - m_hp = getMaxHp(); - sendStatusUpdate( true ); -} - -/*! \return reset mp to current max mp */ -void Core::Entity::Actor::resetMp() -{ - m_mp = getMaxMp(); - sendStatusUpdate( true ); -} - -/*! \param hp amount to set ( caps to maxHp ) */ -void Core::Entity::Actor::setHp( uint32_t hp ) -{ - m_hp = hp < getMaxHp() ? hp : getMaxHp(); - sendStatusUpdate( true ); -} - -/*! \param mp amount to set ( caps to maxMp ) */ -void Core::Entity::Actor::setMp( uint32_t mp ) -{ - m_mp = mp < getMaxMp() ? mp : getMaxMp(); - sendStatusUpdate( true ); -} - -/*! \param gp amount to set*/ -void Core::Entity::Actor::setGp( uint32_t gp ) -{ - m_gp = gp; - sendStatusUpdate( true ); -} - -/*! \param type invincibility type to set */ -void Core::Entity::Actor::setInvincibilityType( Common::InvincibilityType type ) -{ - m_invincibilityType = type; -} - -/*! \return current status of the actor */ -Core::Entity::Actor::ActorStatus Core::Entity::Actor::getStatus() const -{ - return m_status; -} - -/*! \param status to set */ -void Core::Entity::Actor::setStatus( ActorStatus status ) -{ - m_status = status; -} - -/*! -Performs necessary steps to mark an actor dead. -Sets hp/mp/tp, sets status, plays animation and fires onDeath event -*/ -void Core::Entity::Actor::die() -{ - m_status = ActorStatus::Dead; - m_hp = 0; - m_mp = 0; - m_tp = 0; - - // fire onDeath event - onDeath(); - - // if the actor is a player, the update needs to be send to himself too - bool selfNeedsUpdate = isPlayer(); - - sendToInRangeSet( ActorControlPacket142( m_id, SetStatus, static_cast< uint8_t >( ActorStatus::Dead ) ), selfNeedsUpdate ); - - // TODO: not all actor show the death animation when they die, some quest npcs might just despawn - // although that might be handled by setting the HP to 1 and doing some script magic - sendToInRangeSet( ActorControlPacket142( m_id, DeathAnimation, 0, 0, 0, 0x20 ), selfNeedsUpdate ); } -/*! -Calculates and sets the rotation to look towards a specified -position - -\param Position to look towards -*/ -bool Core::Entity::Actor::face( const Common::FFXIVARR_POSITION3& p ) +uint32_t Core::Entity::Actor::getId() const { - float oldRot = getRotation(); - float rot = Math::Util::calcAngFrom( getPos().x, getPos().z, p.x, p.z ); - float newRot = PI - rot + ( PI / 2 ); - - m_pCell = nullptr; - - setRotation( newRot ); - - return oldRot != newRot ? true : false; + return m_id; } -/*! -Sets the actors position and notifies the zone to propagate the change - -\param Position to set -*/ -void Core::Entity::Actor::setPosition( const Common::FFXIVARR_POSITION3& pos ) +void Core::Entity::Actor::setId( uint32_t id ) { - m_pos = pos; - m_pCurrentZone->updateActorPosition( *this ); + m_id = id; } -void Core::Entity::Actor::setPosition( float x, float y, float z ) +Core::Entity::Actor::ObjKind Core::Entity::Actor::getObjKind() const +{ + return m_objKind; +} + +Core::Common::FFXIVARR_POSITION3& Core::Entity::Actor::getPos() +{ + return m_pos; +} + +void Core::Entity::Actor::setPos( float x, float y, float z ) { m_pos.x = x; m_pos.y = y; @@ -272,131 +75,159 @@ void Core::Entity::Actor::setPosition( float x, float y, float z ) m_pCurrentZone->updateActorPosition( *this ); } -/*! -Set and propagate the actor stance to in range players -( not the actor himself ) - -\param stance to set -*/ -void Core::Entity::Actor::setStance( Stance stance ) +void Core::Entity::Actor::setPos( const Core::Common::FFXIVARR_POSITION3& pos ) { - m_currentStance = stance; + m_pos = pos; + m_pCurrentZone->updateActorPosition( *this ); +} - sendToInRangeSet( ActorControlPacket142( m_id, ToggleAggro, stance, 1 ) ); +float Core::Entity::Actor::getRot() const +{ + return m_rot; +} + +void Core::Entity::Actor::setRot( float rot ) +{ + m_rot = rot; +} + +bool Core::Entity::Actor::isChara() const +{ + return isPlayer() || isBattleNpc() || isEventNpc() || isRetainer() || isCompanion(); +} + +bool Core::Entity::Actor::isPlayer() const +{ + return m_objKind == ObjKind::Player; +} + +bool Core::Entity::Actor::isEventNpc() const +{ + return m_objKind == ObjKind::EventNpc; +} + +bool Core::Entity::Actor::isBattleNpc() const +{ + return m_objKind == ObjKind::BattleNpc; +} + +bool Core::Entity::Actor::isRetainer() const +{ + return m_objKind == ObjKind::Retainer; +} + +bool Core::Entity::Actor::isCompanion() const +{ + return m_objKind == ObjKind::Companion; +} + +bool Core::Entity::Actor::isEventObj() const +{ + return m_objKind == ObjKind::EventObj; +} + +bool Core::Entity::Actor::isHousingEventObj() const +{ + return m_objKind == ObjKind::Housing; +} + +bool Core::Entity::Actor::isAetheryte() const +{ + return m_objKind == ObjKind::Aetheryte; +} + + +/*! \return pointer to this instance as ActorPtr */ +Core::Entity::CharaPtr Core::Entity::Actor::getAsChara() +{ + if( !isChara() ) + return nullptr; + return boost::dynamic_pointer_cast< Entity::Chara, Entity::Actor >( shared_from_this() ); +} + +/*! \return pointer to this instance as PlayerPtr */ +Core::Entity::PlayerPtr Core::Entity::Actor::getAsPlayer() +{ + if( !isPlayer() ) + return nullptr; + return boost::dynamic_pointer_cast< Entity::Player, Entity::Actor >( shared_from_this() ); +} + +/*! \return pointer to this instance as PlayerPtr */ +Core::Entity::EventObjectPtr Core::Entity::Actor::getAsEventObj() +{ + if( !isEventObj() ) + return nullptr; + return boost::dynamic_pointer_cast< Entity::EventObject, Entity::Actor >( shared_from_this() ); } /*! -Check if an action is queued for execution, if so update it -and if fully performed, clean up again. +Add a given actor to the fitting in range set according to type +but also to the global actor map -\return true if a queued action has been updated +\param ActorPtr to add */ -bool Core::Entity::Actor::checkAction() +void Core::Entity::Actor::addInRangeActor( ActorPtr pActor ) { - if( m_pCurrentAction == nullptr ) - return false; + // if this is null, something went wrong + assert( pActor ); - if( m_pCurrentAction->update() ) - m_pCurrentAction.reset(); + // add actor to in range set + m_inRangeActor.insert( pActor ); - return true; - -} - -/*! -Change the current target and propagate to in range players - -\param target actor id -*/ -void Core::Entity::Actor::changeTarget( uint64_t targetId ) -{ - setTargetId( targetId ); - sendToInRangeSet( ActorControlPacket144( m_id, SetTarget, 0, 0, 0, 0, targetId ) ); -} - -/*! -Dummy function \return 0 -*/ -uint8_t Core::Entity::Actor::getLevel() const -{ - return 0; -} - -/*! -Let an actor take damage and perform necessary steps -according to resulting hp, propagates new hp value to players -in range -TODO: eventually this needs to distinguish between physical and -magical dmg and take status effects into account - -\param amount of damage to be taken -*/ -void Core::Entity::Actor::takeDamage( uint32_t damage ) -{ - if( damage >= m_hp ) + if( pActor->isPlayer() ) { - switch( m_invincibilityType ) { - case InvincibilityNone: - setHp( 0 ); - die(); - break; - case InvincibilityRefill: - resetHp(); - break; - case InvincibilityStayAlive: - setHp( 0 ); - break; - } - } - else - m_hp -= damage; + auto pPlayer = pActor->getAsPlayer(); - sendStatusUpdate( false ); + spawn( pPlayer ); + + // if actor is a player, add it to the in range player set + m_inRangePlayers.insert( pPlayer ); + } } /*! -Let an actor get healed and perform necessary steps -according to resulting hp, propagates new hp value to players -in range +Remove a given actor from the matching in range set according to type +but also to the global actor map -\param amount of hp to be healed +\param ActorPtr to remove */ -void Core::Entity::Actor::heal( uint32_t amount ) +void Core::Entity::Actor::removeInRangeActor( Actor& actor ) { - if( ( m_hp + amount ) > getMaxHp() ) + // call virtual event + onRemoveInRangeActor( actor ); + + // remove actor from in range actor set + m_inRangeActor.erase( actor.shared_from_this() ); + + // if actor is a player, despawn ourself for him + // TODO: move to virtual onRemove? + if( isPlayer() ) + actor.despawn( getAsPlayer() ); + + if( actor.isPlayer() ) + m_inRangePlayers.erase( actor.getAsPlayer() ); +} + +/*! \return true if there is at least one actor in the in range set */ +bool Core::Entity::Actor::hasInRangeActor() const +{ + return ( m_inRangeActor.size() > 0 ); +} + +void Core::Entity::Actor::removeFromInRange() +{ + if( !hasInRangeActor() ) + return; + + Entity::ActorPtr pCurAct; + + for( auto& pCurAct : m_inRangeActor ) { - m_hp = getMaxHp(); + pCurAct->removeInRangeActor( *this ); } - else - m_hp += amount; - sendStatusUpdate( false ); -} - -/*! -Send an HpMpTp update to players in range ( and potentially to self ) -TODO: poor naming, should be changed. Status is not HP. Also should be virtual -so players can have their own version and we can abolish the param. - -\param true if the update should also be sent to the actor ( player ) himself -*/ -void Core::Entity::Actor::sendStatusUpdate( bool toSelf ) -{ - UpdateHpMpTpPacket updateHpPacket( *this ); - sendToInRangeSet( updateHpPacket ); -} - -/*! \return ActionPtr of the currently registered action, or nullptr */ -Core::Action::ActionPtr Core::Entity::Actor::getCurrentAction() const -{ - return m_pCurrentAction; -} - -/*! \param ActionPtr of the action to be registered */ -void Core::Entity::Actor::setCurrentAction( Core::Action::ActionPtr pAction ) -{ - m_pCurrentAction = pAction; } /*! @@ -407,13 +238,14 @@ check if a given actor is in the actors in range set */ bool Core::Entity::Actor::isInRangeSet( ActorPtr pActor ) const { - return !( m_inRangeActors.find( pActor ) == m_inRangeActors.end() ); + return !( m_inRangeActor.find( pActor ) == m_inRangeActor.end() ); } + /*! \return ActorPtr of the closest actor in range, if none, nullptr */ Core::Entity::ActorPtr Core::Entity::Actor::getClosestActor() { - if( m_inRangeActors.empty() ) + if( m_inRangeActor.empty() ) // no actors in range, don't bother return nullptr; @@ -422,7 +254,7 @@ Core::Entity::ActorPtr Core::Entity::Actor::getClosestActor() // arbitrary high number float minDistance = 10000; - for( const auto& pCurAct : m_inRangeActors ) + for( const auto& pCurAct : m_inRangeActor ) { float distance = Math::Util::distance( getPos().x, getPos().y, @@ -441,6 +273,13 @@ Core::Entity::ActorPtr Core::Entity::Actor::getClosestActor() return tmpActor; } +/*! Clear the whole in range set, this does no cleanup */ +void Core::Entity::Actor::clearInRangeSet() +{ + m_inRangeActor.clear(); + m_inRangePlayers.clear(); +} + /*! Send a packet to all players in range, potentially to self if set and is player @@ -468,116 +307,21 @@ void Core::Entity::Actor::sendToInRangeSet( Network::Packets::GamePacketPtr pPac { assert( pCurAct ); pPacket->setValAt< uint32_t >( 0x04, m_id ); - pPacket->setValAt< uint32_t >( 0x08, pCurAct->m_id ); + pPacket->setValAt< uint32_t >( 0x08, pCurAct->getId() ); // it might be that the player DC'd in which case the session would be invalid pCurAct->queuePacket( pPacket ); } } -/*! -Add a given actor to the fitting in range set according to type -but also to the global actor map - -\param ActorPtr to add -*/ -void Core::Entity::Actor::addInRangeActor( ActorPtr pActor ) +/*! \return list of actors currently in range */ +std::set< Core::Entity::ActorPtr > Core::Entity::Actor::getInRangeActors( bool includeSelf ) { + auto tempInRange = m_inRangeActor; - // if this is null, something went wrong - assert( pActor ); + if( includeSelf ) + tempInRange.insert( shared_from_this() ); - // add actor to in range set - m_inRangeActors.insert( pActor ); - - if( pActor->isPlayer() ) - { - auto pPlayer = pActor->getAsPlayer(); - - // if actor is a player, add it to the in range player set - m_inRangePlayers.insert( pPlayer ); - } -} - -/*! -Remove a given actor from the matching in range set according to type -but also to the global actor map - -\param ActorPtr to remove -*/ -void Core::Entity::Actor::removeInRangeActor( Actor& actor ) -{ - // call virtual event - onRemoveInRangeActor( actor ); - - // remove actor from in range actor set - m_inRangeActors.erase( actor.getAsActor() ); - - // if actor is a player, despawn ourself for him - // TODO: move to virtual onRemove? - if( isPlayer() ) - actor.despawn( getAsPlayer() ); - - if( actor.isPlayer() ) - m_inRangePlayers.erase( actor.getAsPlayer() ); -} - -/*! \return true if there is at least one actor in the in range set */ -bool Core::Entity::Actor::hasInRangeActor() const -{ - return ( m_inRangeActors.size() > 0 ); -} - -void Core::Entity::Actor::removeFromInRange() -{ - if( !hasInRangeActor() ) - return; - - Entity::ActorPtr pCurAct; - - for( auto& pCurAct : m_inRangeActors ) - { - pCurAct->removeInRangeActor( *this ); - } - -} - -void Core::Entity::Actor::checkInRangeActors() -{ - if( hasInRangeActor() ) - { - Entity::ActorPtr pCurAct; - - float fRange = 70.0f; - for( auto iter = m_inRangeActors.begin(); iter != m_inRangeActors.end();) - { - pCurAct = *iter; - auto iter2 = iter++; - - float distance = Math::Util::distance( pCurAct->getPos().x, pCurAct->getPos().y, pCurAct->getPos().z, - getPos().x, getPos().y, getPos().z ); - - if( fRange > 0.0f && distance > fRange ) - { - pCurAct->removeInRangeActor( *this ); - - if( getCurrentZone() != pCurAct->getCurrentZone() ) - return; - - removeInRangeActor( **iter2 ); - - // @TODO FIXME! - // this break is more or less a hack, iteration will break otherwise after removing - break; - } - } - } -} - -/*! Clear the whole in range set, this does no cleanup */ -void Core::Entity::Actor::clearInRangeSet() -{ - m_inRangeActors.clear(); - m_inRangePlayers.clear(); + return tempInRange; } /*! \return ZonePtr to the current zone, nullptr if not set */ @@ -592,12 +336,21 @@ void Core::Entity::Actor::setCurrentZone( ZonePtr currZone ) m_pCurrentZone = currZone; } +/*! \return InstanceContentPtr to the current instance, nullptr if not an instance or not set */ +Core::InstanceContentPtr Core::Entity::Actor::getCurrentInstance() const +{ + if( m_pCurrentZone ) + return m_pCurrentZone->getAsInstanceContent(); + + return nullptr; +} + /*! Get the current cell of a region the actor is in \return Cell* */ -Core::Cell * Core::Entity::Actor::getCell() const +Core::Cell * Core::Entity::Actor::getCellPtr() { return m_pCell; } @@ -611,399 +364,3 @@ void Core::Entity::Actor::setCell( Cell * pCell ) { m_pCell = pCell; } - -/*! -Autoattack prototype implementation -TODO: move the check if the autoAttack can be performed to the callee -also rename autoAttack to autoAttack as that is more elaborate -On top of that, this only solves attacks from melee classes. -Will have to be extended for ranged attacks. - -\param ActorPtr the autoAttack is performed on -*/ -void Core::Entity::Actor::autoAttack( ActorPtr pTarget ) -{ - - uint64_t tick = Util::getTimeMs(); - - if( ( tick - m_lastAttack ) > 2500 ) - { - pTarget->onActionHostile( *this ); - m_lastAttack = tick; - srand( static_cast< uint32_t >( tick ) ); - - uint16_t damage = static_cast< uint16_t >( 10 + rand() % 12 ); - uint32_t variation = static_cast< uint32_t >( 0 + rand() % 4 ); - - ZoneChannelPacket< FFXIVIpcEffect > effectPacket( getId() ); - effectPacket.data().targetId = pTarget->getId(); - effectPacket.data().actionAnimationId = 0x366; - effectPacket.data().unknown_2 = variation; -// effectPacket.data().unknown_3 = 1; - effectPacket.data().actionTextId = 0x366; - effectPacket.data().numEffects = 1; - effectPacket.data().rotation = Math::Util::floatToUInt16Rot( getRotation() ); - effectPacket.data().effectTarget = pTarget->getId(); - effectPacket.data().effects[0].value = damage; - effectPacket.data().effects[0].effectType = ActionEffectType::Damage; - effectPacket.data().effects[0].hitSeverity = static_cast< ActionHitSeverityType >( variation ); - effectPacket.data().effects[0].unknown_3 = 7; - - sendToInRangeSet( effectPacket ); - - if( this->isPlayer() ) - this->getAsPlayer()->queuePacket( effectPacket ); - - pTarget->takeDamage( damage ); - } -} - -/*! -ChaiScript Skill Handler. - -\param GamePacketPtr to send -\param bool should be send to self? -*/ -void Core::Entity::Actor::handleScriptSkill( uint32_t type, uint16_t actionId, uint64_t param1, - uint64_t param2, Entity::Actor& target ) -{ - - if( isPlayer() ) - { - getAsPlayer()->sendDebug( std::to_string( target.getId() ) ); - getAsPlayer()->sendDebug( "Handle script skill type: " + std::to_string( type ) ); - } - - auto actionInfoPtr = g_framework.getExdDataGen().get< Core::Data::Action >( actionId ); - - // Todo: Effect packet generator. 90% of this is basically setting params and it's basically unreadable. - // Prepare packet. This is seemingly common for all packets in the action handler. - - ZoneChannelPacket< FFXIVIpcEffect > effectPacket( getId() ); - effectPacket.data().targetId = target.getId(); - effectPacket.data().actionAnimationId = actionId; - effectPacket.data().unknown_62 = 1; // Affects displaying action name next to number in floating text - effectPacket.data().unknown_2 = 1; // This seems to have an effect on the "double-cast finish" animation - effectPacket.data().actionTextId = actionId; - effectPacket.data().numEffects = 1; - effectPacket.data().rotation = Math::Util::floatToUInt16Rot( getRotation() ); - effectPacket.data().effectTarget = target.getId(); - - // Todo: for each actor, calculate how much damage the calculated value should deal to them - 2-step damage calc. we only have 1-step - switch( type ) - { - - case ActionEffectType::Damage: - { - effectPacket.data().effects[0].value = static_cast< uint16_t >( param1 ); - effectPacket.data().effects[0].effectType = ActionEffectType::Damage; - effectPacket.data().effects[0].hitSeverity = ActionHitSeverityType::NormalDamage; - effectPacket.data().effects[0].unknown_3 = 7; - - if( actionInfoPtr->castType == 1 && actionInfoPtr->effectRange != 0 || actionInfoPtr->castType != 1 ) - { - // If action on this specific target is valid... - if ( isPlayer() && !ActionCollision::isActorApplicable( target, TargetFilter::Enemies ) ) - break; - - sendToInRangeSet( effectPacket, true ); - - if ( target.isAlive() ) - target.onActionHostile( *this ); - - target.takeDamage( static_cast< uint32_t >( param1 ) ); - - } - else - { - - auto actorsCollided = ActionCollision::getActorsHitFromAction( target.getPos(), getInRangeActors( true ), - actionInfoPtr, TargetFilter::Enemies ); - - for( const auto& pHitActor : actorsCollided ) - { - effectPacket.data().targetId = pHitActor->getId(); - effectPacket.data().effectTarget = pHitActor->getId(); - - // todo: send to range of what? ourselves? when mob script hits this is going to be lacking - sendToInRangeSet( effectPacket, true ); - - - if( pHitActor->isAlive() ) - pHitActor->onActionHostile( *this ); - - pHitActor->takeDamage( static_cast< uint32_t >( param1 ) ); - - // Debug - if ( isPlayer() ) - { - if ( pHitActor->isPlayer() ) - getAsPlayer()->sendDebug( "AoE hit actor " + std::to_string( pHitActor->getId() ) + " (" + pHitActor->getName() + ")" ); - else - getAsPlayer()->sendDebug( "AoE hit actor " + std::to_string( pHitActor->getId() ) ); - } - } - } - - break; - } - - case ActionEffectType::Heal: - { - uint32_t calculatedHeal = Math::CalcBattle::calculateHealValue( getAsPlayer(), static_cast< uint32_t >( param1 ) ); - - effectPacket.data().effects[0].value = calculatedHeal; - effectPacket.data().effects[0].effectType = ActionEffectType::Heal; - effectPacket.data().effects[0].hitSeverity = ActionHitSeverityType::NormalHeal; - - if( actionInfoPtr->castType == 1 && actionInfoPtr->effectRange != 0 || actionInfoPtr->castType != 1 ) - { - if( isPlayer() && !ActionCollision::isActorApplicable( target, TargetFilter::Allies ) ) - break; - - sendToInRangeSet( effectPacket, true ); - target.heal( calculatedHeal ); - } - else - { - // todo: get proper packets: the following was just kind of thrown together from what we know. - // atm buggy (packets look "delayed" from client) - - auto actorsCollided = ActionCollision::getActorsHitFromAction( target.getPos(), getInRangeActors( true ), - actionInfoPtr, TargetFilter::Allies ); - - for( auto pHitActor : actorsCollided ) - { - effectPacket.data().targetId = target.getId(); - effectPacket.data().effectTarget = pHitActor->getId(); - - sendToInRangeSet( effectPacket, true ); - pHitActor->heal( calculatedHeal ); - - // Debug - if( isPlayer() ) - { - if( pHitActor->isPlayer() ) - getAsPlayer()->sendDebug( "AoE hit actor " + std::to_string( pHitActor->getId() ) + " (" + pHitActor->getName() + ")" ); - else - getAsPlayer()->sendDebug( "AoE hit actor " + std::to_string( pHitActor->getId() ) ); - } - } - } - break; - } - - default: - break; - } -} - -/*! \param StatusEffectPtr to be applied to the actor */ -void Core::Entity::Actor::addStatusEffect( StatusEffect::StatusEffectPtr pEffect ) -{ - int8_t nextSlot = getStatusEffectFreeSlot(); - // if there is no slot left, do not add the effect - if( nextSlot == -1 ) - return; - - pEffect->applyStatus(); - m_statusEffectMap[nextSlot] = pEffect; - - ZoneChannelPacket< Server::FFXIVIpcAddStatusEffect > statusEffectAdd( getId() ); - statusEffectAdd.data().actor_id = pEffect->getTargetActorId(); - statusEffectAdd.data().actor_id1 = pEffect->getSrcActorId(); - statusEffectAdd.data().current_hp = getHp(); - statusEffectAdd.data().current_mp = getMp(); - statusEffectAdd.data().current_tp = getTp(); - statusEffectAdd.data().duration = static_cast< float >( pEffect->getDuration() ) / 1000; - statusEffectAdd.data().effect_id = pEffect->getId(); - statusEffectAdd.data().effect_index = nextSlot; - statusEffectAdd.data().max_hp = getMaxHp(); - statusEffectAdd.data().max_mp = getMaxMp(); - statusEffectAdd.data().max_something = 1; - //statusEffectAdd.data().unknown2 = 28; - statusEffectAdd.data().param = pEffect->getParam(); - - sendToInRangeSet( statusEffectAdd, isPlayer() ); -} - -/*! \param StatusEffectPtr to be applied to the actor */ -void Core::Entity::Actor::addStatusEffectById( uint32_t id, int32_t duration, Entity::Actor& source, uint16_t param ) -{ - auto effect = StatusEffect::make_StatusEffect( id, source.getAsActor(), getAsActor(), duration, 3000 ); - effect->setParam( param ); - addStatusEffect( effect ); -} - -/*! \param StatusEffectPtr to be applied to the actor */ -void Core::Entity::Actor::addStatusEffectByIdIfNotExist( uint32_t id, int32_t duration, Entity::Actor& source, uint16_t param ) -{ - if( hasStatusEffect( id ) ) - return; - - auto effect = StatusEffect::make_StatusEffect( id, source.getAsActor(), getAsActor(), duration, 3000 ); - effect->setParam( param ); - addStatusEffect( effect ); - -} - -float Core::Entity::Actor::getRotation() const -{ - return m_rot; -} - -void Core::Entity::Actor::setRotation( float rot ) -{ - m_rot = rot; -} - -int8_t Core::Entity::Actor::getStatusEffectFreeSlot() -{ - int8_t freeEffectSlot = -1; - - if( m_statusEffectFreeSlotQueue.empty() ) - return freeEffectSlot; - - freeEffectSlot = m_statusEffectFreeSlotQueue.front(); - m_statusEffectFreeSlotQueue.pop(); - - return freeEffectSlot; -} - -void Core::Entity::Actor::statusEffectFreeSlot( uint8_t slotId ) -{ - m_statusEffectFreeSlotQueue.push( slotId ); -} - -void Core::Entity::Actor::removeSingleStatusEffectById( uint32_t id ) -{ - for( auto effectIt : m_statusEffectMap ) - { - if( effectIt.second->getId() == id ) - { - removeStatusEffect( effectIt.first ); - break; - } - } -} - -void Core::Entity::Actor::removeStatusEffect( uint8_t effectSlotId ) -{ - auto pEffectIt = m_statusEffectMap.find( effectSlotId ); - if( pEffectIt == m_statusEffectMap.end() ) - return; - - statusEffectFreeSlot( effectSlotId ); - - auto pEffect = pEffectIt->second; - pEffect->removeStatus(); - - sendToInRangeSet( ActorControlPacket142( getId(), StatusEffectLose, pEffect->getId() ), isPlayer() ); - - m_statusEffectMap.erase( effectSlotId ); - - sendStatusEffectUpdate(); -} - -std::map< uint8_t, Core::StatusEffect::StatusEffectPtr > Core::Entity::Actor::getStatusEffectMap() const -{ - return m_statusEffectMap; -} - -void Core::Entity::Actor::sendStatusEffectUpdate() -{ - uint64_t currentTimeMs = Util::getTimeMs(); - - ZoneChannelPacket< Server::FFXIVIpcStatusEffectList > statusEffectList( getId() ); - - statusEffectList.data().current_hp = getHp(); - statusEffectList.data().current_mp = getMp(); - statusEffectList.data().currentTp = getTp(); - statusEffectList.data().max_hp = getMaxHp(); - statusEffectList.data().max_mp = getMaxMp(); - uint8_t slot = 0; - for( auto effectIt : m_statusEffectMap ) - { - float timeLeft = static_cast< float >( effectIt.second->getDuration() - - ( currentTimeMs - effectIt.second->getStartTimeMs() ) ) / 1000; - statusEffectList.data().effect[slot].duration = timeLeft; - statusEffectList.data().effect[slot].effect_id = effectIt.second->getId(); - statusEffectList.data().effect[slot].sourceActorId = effectIt.second->getSrcActorId(); - slot++; - } - - sendToInRangeSet( statusEffectList, isPlayer() ); - -} - -void Core::Entity::Actor::updateStatusEffects() -{ - uint64_t currentTimeMs = Util::getTimeMs(); - - uint32_t thisTickDmg = 0; - uint32_t thisTickHeal = 0; - - for( auto effectIt : m_statusEffectMap ) - { - uint8_t effectIndex = effectIt.first; - auto effect = effectIt.second; - - uint64_t lastTick = effect->getLastTickMs(); - uint64_t startTime = effect->getStartTimeMs(); - uint32_t duration = effect->getDuration(); - uint32_t tickRate = effect->getTickRate(); - - if( ( currentTimeMs - startTime ) > duration ) - { - // remove status effect - removeStatusEffect( effectIndex ); - // break because removing invalidates iterators - break; - } - - if( ( currentTimeMs - lastTick ) > tickRate ) - { - effect->setLastTick( currentTimeMs ); - effect->onTick(); - - auto thisEffect = effect->getTickEffect(); - - switch( thisEffect.first ) - { - - case 1: - { - thisTickDmg += thisEffect.second; - break; - } - - case 2: - { - thisTickHeal += thisEffect.second; - break; - } - - } - } - - } - - if( thisTickDmg != 0 ) - { - takeDamage( thisTickDmg ); - sendToInRangeSet( ActorControlPacket142( getId(), HPFloatingText, 0, static_cast< uint8_t >( ActionEffectType::Damage ), thisTickDmg ) ); - } - - if( thisTickHeal != 0 ) - { - heal( thisTickDmg ); - sendToInRangeSet( ActorControlPacket142( getId(), HPFloatingText, 0, static_cast< uint8_t >( ActionEffectType::Heal ), thisTickHeal ) ); - } -} - -bool Core::Entity::Actor::hasStatusEffect( uint32_t id ) -{ - if( m_statusEffectMap.find( id ) != m_statusEffectMap.end() ) - return true; - return false; -} diff --git a/src/servers/sapphire_zone/Actor/Actor.h b/src/servers/sapphire_zone/Actor/Actor.h index e65d1b0a..5c8f7e45 100644 --- a/src/servers/sapphire_zone/Actor/Actor.h +++ b/src/servers/sapphire_zone/Actor/Actor.h @@ -1,11 +1,10 @@ -#ifndef _ACTOR_H_ -#define _ACTOR_H_ +#ifndef _GAME_OBJECT_H_ +#define _GAME_OBJECT_H_ #include #include #include "Forwards.h" -#include "GameObject.h" #include #include #include @@ -14,284 +13,127 @@ namespace Core { namespace Entity { /*! -\class Actor -\brief Base class for all actors +\class GameObject +\brief Base class for all actor/objects */ -class Actor : public GameObject -{ -public: - enum Stance : uint8_t + class Actor : public boost::enable_shared_from_this< Actor > { - Passive = 0, - Active = 1, + public: + enum ObjKind : uint8_t + { + None = 0x00, + Player = 0x01, + BattleNpc = 0x02, + EventNpc = 0x03, + Treasure = 0x04, + Aetheryte = 0x05, + GatheringPoint = 0x06, + EventObj = 0x07, + Mount = 0x08, + Companion = 0x09, // this probably actually means minion + Retainer = 0x0A, + Area = 0x0B, + Housing = 0x0C, + Cutscene = 0x0D, + CardStand = 0x0E, + }; + + + protected: + /*! Position of the object */ + Common::FFXIVARR_POSITION3 m_pos; + /*! Rotation of the object */ + float m_rot; + /*! Id of the actor */ + uint32_t m_id; + /*! Type of the actor */ + ObjKind m_objKind; + /*! Id of the zone the actor currently is in */ + uint32_t m_zoneId; + /*! Ptr to the ZoneObj the actor belongs to */ + ZonePtr m_pCurrentZone; + + /*! list of various actors in range */ + std::set< ActorPtr > m_inRangeActor; + std::set< PlayerPtr > m_inRangePlayers; + + /*! Parent cell in the zone */ + Core::Cell* m_pCell; + + public: + explicit Actor( ObjKind type ); + virtual ~Actor() {}; + + virtual void spawn( PlayerPtr pTarget ) {} + virtual void despawn( PlayerPtr pTarget ) {} + + uint32_t getId() const; + void setId( uint32_t id ); + + ObjKind getObjKind() const; + + Common::FFXIVARR_POSITION3& getPos(); + void setPos( const Common::FFXIVARR_POSITION3& pos ); + void setPos( float x, float y, float z ); + + float getRot() const; + void setRot( float rot ); + + bool isChara() const; + bool isPlayer() const; + bool isEventNpc() const; + bool isBattleNpc() const; + bool isRetainer() const; + bool isCompanion() const; + bool isEventObj() const; + bool isHousingEventObj() const; + bool isAetheryte() const; + + ///// IN RANGE LOGIC /////////////////////////////// + virtual void onRemoveInRangeActor( Actor& pActor ) {} + + // check if another actor is in the actors in range set + bool isInRangeSet( ActorPtr pActor ) const; + + ActorPtr getClosestActor(); + + void sendToInRangeSet( Network::Packets::GamePacketPtr pPacket, bool bToSelf = false ); + + // add an actor to in range set + void addInRangeActor( ActorPtr pActor ); + + // remove an actor from the in range set + void removeInRangeActor( Actor& actor ); + + // return true if there is at least one actor in the in range set + bool hasInRangeActor() const; + + void removeFromInRange(); + + // clear the whole in range set, this does no cleanup + virtual void clearInRangeSet(); + + std::set< ActorPtr > getInRangeActors( bool includeSelf = false ); + + //////////////////////////////////////////////////// + + CharaPtr getAsChara(); + PlayerPtr getAsPlayer(); + EventObjectPtr getAsEventObj(); + + ZonePtr getCurrentZone() const; + void setCurrentZone( ZonePtr currZone ); + + InstanceContentPtr getCurrentInstance() const; + + // get the current cell of a region the actor is in + Cell* getCellPtr(); + // set the current cell + void setCell( Cell* pCell ); + }; - enum DisplayFlags : uint16_t - { - ActiveStance = 0x001, - Invisible = 0x020, - HideHead = 0x040, - HideWeapon = 0x080, - Faded = 0x100, - Visor = 0x800, - }; - - enum struct ActorStatus : uint8_t - { - Idle = 0x01, - Dead = 0x02, - Sitting = 0x03, - Mounted = 0x04, - Crafting = 0x05, - Gathering = 0x06, - Melding = 0x07, - SMachine = 0x08 - }; - - struct ActorStats - { - uint32_t max_mp = 0; - uint32_t max_hp = 0; - - uint32_t str = 0; - uint32_t dex = 0; - uint32_t vit = 0; - uint32_t inte = 0; - uint32_t mnd = 0; - uint32_t pie = 0; - - uint32_t tenacity = 0; - uint32_t attack = 0; - uint32_t defense = 0; - uint32_t accuracy = 0; - uint32_t spellSpeed = 0; - uint32_t magicDefense = 0; - uint32_t critHitRate = 0; - uint32_t resistSlash = 0; - uint32_t resistPierce = 0; - uint32_t resistBlunt = 0; - uint32_t attackPotMagic = 0; - uint32_t healingPotMagic = 0; - uint32_t determination = 0; - uint32_t skillSpeed = 0; - - uint32_t resistSlow = 0; - uint32_t resistSilence = 0; - uint32_t resistBlind = 0; - uint32_t resistPoison = 0; - uint32_t resistStun = 0; - uint32_t resistSleep = 0; - uint32_t resistBind = 0; - uint32_t resistHeavy = 0; - - uint32_t resistFire = 0; - uint32_t resistIce = 0; - uint32_t resistWind = 0; - uint32_t resistEarth = 0; - uint32_t resistLightning = 0; - uint32_t resistWater = 0; - - } m_baseStats; - -protected: - char m_name[34]; - /*! Id of the zone the actor currently is in */ - uint32_t m_zoneId; - /*! Ptr to the ZoneObj the actor belongs to */ - ZonePtr m_pCurrentZone; - /*! Last tick time for the actor ( in ms ) */ - uint64_t m_lastTickTime; - /*! Last time the actor performed an autoAttack ( in ms ) */ - uint64_t m_lastAttack; - /*! Last time the actor was updated ( in ms ) */ - uint64_t m_lastUpdate; - /*! Current stance of the actor */ - Stance m_currentStance; - /*! Current staus of the actor */ - ActorStatus m_status; - /*! Max HP of the actor ( based on job / class ) */ - uint32_t m_maxHp; - /*! Max MP of the actor ( based on job / class ) */ - uint32_t m_maxMp; - /*! Current HP of the actor */ - uint32_t m_hp; - /*! Current MP of the actor */ - uint32_t m_mp; - /*! Current TP of the actor */ - uint16_t m_tp; - /*! Current GP of the actor */ - uint16_t m_gp; - /*! Additional look info of the actor */ - uint8_t m_customize[26]; - /*! Current class of the actor */ - Common::ClassJob m_class; - /*! Id of the currently selected target actor */ - uint64_t m_targetId; - /*! Ptr to a queued action */ - Action::ActionPtr m_pCurrentAction; - /*! Invincibility type */ - Common::InvincibilityType m_invincibilityType; - - /*! Status effects */ - const uint8_t MAX_STATUS_EFFECTS = 30; - std::queue< uint8_t > m_statusEffectFreeSlotQueue; - std::vector< std::pair< uint8_t, uint32_t> > m_statusEffectList; - std::map< uint8_t, StatusEffect::StatusEffectPtr > m_statusEffectMap; - - std::set< ActorPtr > m_inRangeActors; - std::set< PlayerPtr > m_inRangePlayers; - -public: - Actor( ObjKind type ); - - virtual ~Actor() override; - - virtual void calculateStats() {}; - - /// Status effect functions - void addStatusEffect( StatusEffect::StatusEffectPtr pEffect ); - void removeStatusEffect( uint8_t effectSlotId ); - void removeSingleStatusEffectById( uint32_t id ); - void updateStatusEffects(); - - bool hasStatusEffect( uint32_t id ); - - int8_t getStatusEffectFreeSlot(); - void statusEffectFreeSlot( uint8_t slotId ); - - std::map< uint8_t, Core::StatusEffect::StatusEffectPtr > getStatusEffectMap() const; - - void sendStatusEffectUpdate(); - // add a status effect by id - void addStatusEffectById( uint32_t id, int32_t duration, Entity::Actor& pSource, uint16_t param = 0 ); - - // add a status effect by id if it doesn't exist - void addStatusEffectByIdIfNotExist( uint32_t id, int32_t duration, Entity::Actor& pSource, uint16_t param = 0 ); - - // remove a status effect by id - void removeSingleStatusEffectFromId( uint32_t id ); - /// End Status Effect Functions - - void setPosition( const Common::FFXIVARR_POSITION3& pos ); - void setPosition( float x, float y, float z ); - - void setRotation( float rot ); - - float getRotation() const; - - std::string getName() const; - - std::set< ActorPtr > getInRangeActors( bool includeSelf = false ); - - bool face( const Common::FFXIVARR_POSITION3& p ); - - Stance getStance() const; - - void setStance( Stance stance ); - - ActorStats getStats() const; - - uint32_t getHp() const; - uint32_t getMp() const; - uint16_t getTp() const; - uint16_t getGp() const; - - Common::InvincibilityType getInvincibilityType() const; - - Common::ClassJob getClass() const; - - uint8_t getClassAsInt() const; - - void setClass( Common::ClassJob classJob ); - - void setTargetId( uint64_t targetId ); - - uint64_t getTargetId() const; - - bool isAlive() const; - - virtual uint32_t getMaxHp() const; - virtual uint32_t getMaxMp() const; - - void resetHp(); - void resetMp(); - - void setHp( uint32_t hp ); - void setMp( uint32_t mp ); - void setGp( uint32_t gp ); - - void setInvincibilityType( Common::InvincibilityType type ); - - void die(); - - ActorStatus getStatus() const; - - void setStatus( ActorStatus status ); - - void handleScriptSkill( uint32_t type, uint16_t actionId, uint64_t param1, uint64_t param2, Entity::Actor& target ); - - virtual void autoAttack( ActorPtr pTarget ); - - virtual void onRemoveInRangeActor( Actor& pActor ) {} - - virtual void onDeath() {}; - virtual void onDamageTaken( Actor& pSource ) {}; - virtual void onActionHostile( Actor& source ) {}; - virtual void onActionFriendly( Actor& pSource ) {}; - virtual void onTick() {}; - - virtual void changeTarget( uint64_t targetId ); - virtual uint8_t getLevel() const; - virtual void sendStatusUpdate( bool toSelf = true ); - virtual void takeDamage( uint32_t damage ); - virtual void heal( uint32_t amount ); - virtual bool checkAction(); - virtual void update( int64_t currTime ) {}; - - Action::ActionPtr getCurrentAction() const; - - void setCurrentAction( Action::ActionPtr pAction ); - - ///// IN RANGE LOGIC ///// - - // check if another actor is in the actors in range set - bool isInRangeSet( ActorPtr pActor ) const; - - ActorPtr getClosestActor(); - - void sendToInRangeSet( Network::Packets::GamePacketPtr pPacket, bool bToSelf = false ); - - // add an actor to in range set - void addInRangeActor( ActorPtr pActor ); - - // remove an actor from the in range set - void removeInRangeActor( Actor& pActor ); - - // return true if there is at least one actor in the in range set - bool hasInRangeActor() const; - - void checkInRangeActors(); - - void removeFromInRange(); - - // clear the whole in range set, this does no cleanup - virtual void clearInRangeSet(); - - ZonePtr getCurrentZone() const; - - void setCurrentZone( ZonePtr currZone ); - - // get the current cell of a region the actor is in - Cell* getCell() const; - - // set the current cell - void setCell( Cell* pCell ); - - Core::Cell* m_pCell; - -}; - } } #endif diff --git a/src/servers/sapphire_zone/Actor/BattleNpc.cpp b/src/servers/sapphire_zone/Actor/BattleNpc.cpp deleted file mode 100644 index 9c8262e6..00000000 --- a/src/servers/sapphire_zone/Actor/BattleNpc.cpp +++ /dev/null @@ -1,569 +0,0 @@ -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "Network/PacketWrappers/MoveActorPacket.h" -#include "Network/PacketWrappers/ActorControlPacket142.h" -#include "Network/PacketWrappers/ActorControlPacket143.h" - -#include "Player.h" -#include "BattleNpc.h" -#include "Framework.h" - -using namespace Core::Common; -using namespace Core::Network::Packets; -using namespace Core::Network::Packets::Server; - -extern Core::Framework g_framework; - -uint32_t Core::Entity::BattleNpc::m_nextID = 1149241694; - -Core::Entity::BattleNpc::BattleNpc() : - Actor( ObjKind::BattleNpc ) -{ - m_id = 0; - m_status = ActorStatus::Idle; -} - -Core::Entity::BattleNpc::~BattleNpc() -{ - -} - -Core::Entity::BattleNpc::BattleNpc( uint16_t modelId, uint16_t nameid, const Common::FFXIVARR_POSITION3& spawnPos, - uint16_t bnpcBaseId, uint32_t type, uint8_t level, uint8_t behaviour, - uint32_t mobType ) : - Actor( ObjKind::BattleNpc ) -{ - BattleNpc::m_nextID++; - m_id = BattleNpc::m_nextID; - //strcpy( m_name, pBNpc->m_name.c_str() ); - - m_pos = spawnPos; - m_posOrigin = spawnPos; - - m_mode = MODE_IDLE; - m_targetId = static_cast< uint64_t >( INVALID_GAME_OBJECT_ID ); - - m_maxHp = 150; - m_maxMp = 100; - - m_baseStats.max_hp = m_maxHp; - m_baseStats.max_mp = m_maxMp; - - m_hp = m_maxHp; - m_mp = m_maxMp; - - m_currentStance = Stance::Passive; - - m_class = ClassJob::Gladiator; - m_level = level > uint8_t{0} ? level : uint8_t{70}; - - m_modelId = modelId; - m_nameId = nameid; - - m_behavior = behaviour; - - m_bnpcBaseId = bnpcBaseId; - - m_status = ActorStatus::Idle; - - m_pOwner = nullptr; - - m_mobType = mobType; - - m_invincibilityType = InvincibilityType::InvincibilityNone; - - //m_type = static_cast< Common::ObjKind >( type ); - -} - -// spawn this player for pTarget -void Core::Entity::BattleNpc::spawn( PlayerPtr pTarget ) -{ - //GamePacketNew< FFXIVIpcActorSpawn > spawnPacket( getId(), pTarget->getId() ); - - //spawnPacket.data().unknown_0 = 0; - //spawnPacket.data().ownerId = m_pOwner == nullptr ? INVALID_GAME_OBJECT_ID : m_pOwner->getId(); - //spawnPacket.data().targetId = INVALID_GAME_OBJECT_ID & 0xFFFFFFFF; - //spawnPacket.data().hPCurr = m_hp; - //spawnPacket.data().hPMax = m_baseStats.max_hp; - //spawnPacket.data().level = m_level; - ////spawnPacket.data().tPCurr = 1000; - //spawnPacket.data().model = m_modelId; - //spawnPacket.data().bnpcBaseId = m_bnpcBaseId; - //spawnPacket.data().nameId = m_nameId; - //spawnPacket.data().spawnIndex = pTarget->getSpawnIdForActorId( getId() ); - //g_framework.getLogger().info(std::to_string(spawnPacket.data().spawnIndex) + " " + std::to_string(getId())); - //spawnPacket.data().status = static_cast< uint8_t >( m_status ); - //spawnPacket.data().mobAgressive = m_behavior; - //spawnPacket.data().type = static_cast< uint8_t >( m_type ); - //spawnPacket.data().mobTypeIcon = m_mobType; - //spawnPacket.data().unknown_33 = 5; - //spawnPacket.data().typeFlags = 4; - //spawnPacket.data().pos.x = m_pos.x; - //spawnPacket.data().pos.y = m_pos.y; - //spawnPacket.data().pos.z = m_pos.z; - //spawnPacket.data().rotation = Math::Util::floatToUInt16Rot( getRotation() ); - ////spawnPacket.data().unknown_B0[11] = 1; - ////spawnPacket.data().unknown_B0[12] = 4; - ////spawnPacket.data().unknown_B0[14] = 20; - - //pTarget->queuePacket( spawnPacket ); - - ZoneChannelPacket< FFXIVIpcNpcSpawn > spawnPacket( getId(), pTarget->getId() ); - - - spawnPacket.data().pos.x = m_pos.x; - spawnPacket.data().pos.y = m_pos.y; - spawnPacket.data().pos.z = m_pos.z; - - spawnPacket.data().targetId = INVALID_GAME_OBJECT_ID & 0xFFFFFFFF; - spawnPacket.data().hPCurr = m_hp; - spawnPacket.data().hPMax = m_baseStats.max_hp; - spawnPacket.data().level = m_level; - - spawnPacket.data().subtype = 5; - spawnPacket.data().enemyType = 4; - - spawnPacket.data().modelChara = m_modelId; - spawnPacket.data().bNPCBase = m_bnpcBaseId; - spawnPacket.data().bNPCName = m_nameId; - spawnPacket.data().spawnIndex = pTarget->getSpawnIdForActorId( getId() ); - - spawnPacket.data().rotation = Math::Util::floatToUInt16Rot( getRotation() ); - - spawnPacket.data().type = static_cast< uint8_t >( m_objKind ); - - spawnPacket.data().state = static_cast< uint8_t >( m_status ); - - pTarget->queuePacket( spawnPacket ); -} - -// despawn -void Core::Entity::BattleNpc::despawn( PlayerPtr pPlayer ) -{ - pPlayer->freePlayerSpawnId( getId() ); - - ActorControlPacket143 controlPacket( m_id, DespawnZoneScreenMsg, 0x04, getId(), 0x01 ); - pPlayer->queuePacket( controlPacket ); - -} - -uint8_t Core::Entity::BattleNpc::getLevel() const -{ - return m_level; -} - -Core::Entity::StateMode Core::Entity::BattleNpc::getMode() const -{ - return m_mode; -} - -void Core::Entity::BattleNpc::setMode( StateMode mode ) -{ - m_mode = mode; -} - -uint8_t Core::Entity::BattleNpc::getbehavior() const -{ - return m_behavior; -} - -void Core::Entity::BattleNpc::hateListAdd( Actor& actor, int32_t hateAmount ) -{ - auto hateEntry = new HateListEntry(); - hateEntry->m_hateAmount = hateAmount; - hateEntry->m_pActor = actor.getAsActor(); - - m_hateList.insert( hateEntry ); -} - -Core::Entity::ActorPtr Core::Entity::BattleNpc::hateListGetHighest() -{ - - auto it = m_hateList.begin(); - uint32_t maxHate = 0; - HateListEntry* entry = nullptr; - for( ; it != m_hateList.end(); ++it ) - { - if( ( *it )->m_hateAmount > maxHate ) - { - maxHate = ( *it )->m_hateAmount; - entry = *it; - } - } - - if( entry && maxHate != 0 ) - return entry->m_pActor; - - return nullptr; -} - -void Core::Entity::BattleNpc::setOwner( PlayerPtr pPlayer ) -{ - m_pOwner = pPlayer; - - if( pPlayer != nullptr ) - { - ZoneChannelPacket< FFXIVIpcActorOwner > setOwnerPacket( getId(), pPlayer->getId() ); - setOwnerPacket.data().type = 0x01; - setOwnerPacket.data().actorId = pPlayer->getId(); - sendToInRangeSet( setOwnerPacket ); - } - else - { - ZoneChannelPacket< FFXIVIpcActorOwner > setOwnerPacket(getId(), INVALID_GAME_OBJECT_ID ); - setOwnerPacket.data().type = 0x01; - setOwnerPacket.data().actorId = INVALID_GAME_OBJECT_ID; - sendToInRangeSet( setOwnerPacket ); - } - -} - -void Core::Entity::BattleNpc::sendPositionUpdate() -{ - MoveActorPacket movePacket( *this, 0x3A, 0x00, 0, 0x5A ); - sendToInRangeSet( movePacket ); -} - -bool Core::Entity::BattleNpc::moveTo( Common::FFXIVARR_POSITION3& pos ) -{ - - if( Math::Util::distance( getPos().x, getPos().y, getPos().z, - pos.x, pos.y, pos.z ) <= 4 ) - // reached destination - return true; - - float rot = Math::Util::calcAngFrom( getPos().x, getPos().z, pos.x, pos.z ); - float newRot = PI - rot + ( PI / 2 ); - - face( pos ); - float angle = Math::Util::calcAngFrom( getPos().x, getPos().z, pos.x, pos.z ) + PI; - - float x = static_cast< float >( cosf( angle ) * 1.1f ); - float y = ( getPos().y + pos.y ) * 0.5f; // fake value while there is no collision - float z = static_cast< float >( sinf( angle ) * 1.1f ); - - Common::FFXIVARR_POSITION3 newPos{}; - - newPos.x = getPos().x + x; - newPos.y = y; - newPos.z = getPos().z + z; - - setPosition( newPos ); - - Common::FFXIVARR_POSITION3 tmpPos{}; - tmpPos.x = getPos().x + x; - tmpPos.y = y; - tmpPos.z = getPos().z + z; - - setPosition( tmpPos ); - setRotation(newRot); - - sendPositionUpdate(); - - return false; - -} - -void Core::Entity::BattleNpc::aggro( Actor& actor ) -{ - - m_lastAttack = Util::getTimeMs(); - hateListUpdate( actor, 1 ); - - changeTarget( actor.getId() ); - setStance( Stance::Active ); - m_mode = MODE_COMBAT; - - if( actor.isPlayer() ) - { - PlayerPtr tmpPlayer = actor.getAsPlayer(); - tmpPlayer->queuePacket( ActorControlPacket142( getId(), 0, 1, 1 ) ); - tmpPlayer->onMobAggro( getAsBattleNpc() ); - } -} - -void Core::Entity::BattleNpc::deaggro( Actor& actor ) -{ - if( !hateListHasActor( actor ) ) - hateListRemove( actor ); - - if( actor.isPlayer() ) - { - PlayerPtr tmpPlayer = actor.getAsPlayer(); - tmpPlayer->onMobDeaggro( getAsBattleNpc() ); - } -} - -void Core::Entity::BattleNpc::hateListClear() -{ - auto it = m_hateList.begin(); - for( ; it != m_hateList.end(); ++it ) - { - if( isInRangeSet( ( *it )->m_pActor ) ) - deaggro( *( *it )->m_pActor ); - HateListEntry* tmpListEntry = ( *it ); - delete tmpListEntry; - } - m_hateList.clear(); -} - - -void Core::Entity::BattleNpc::hateListRemove( Actor& actor ) -{ - auto it = m_hateList.begin(); - for( ; it != m_hateList.end(); ++it ) - { - if( ( *it )->m_pActor->getId() == actor.getId() ) - { - HateListEntry* pEntry = *it; - m_hateList.erase( it ); - delete pEntry; - if( actor.isPlayer() ) - { - PlayerPtr tmpPlayer = actor.getAsPlayer(); - tmpPlayer->onMobDeaggro( getAsBattleNpc() ); - } - return; - } - } -} - -bool Core::Entity::BattleNpc::hateListHasActor( Actor& actor ) -{ - auto it = m_hateList.begin(); - for( ; it != m_hateList.end(); ++it ) - { - if( ( *it )->m_pActor->getId() == actor.getId() ) - return true; - } - return false; -} - -void Core::Entity::BattleNpc::resetPos() -{ - m_pos = m_posOrigin; -} - -uint32_t Core::Entity::BattleNpc::getNameId() const -{ - return m_nameId; -} - -void Core::Entity::BattleNpc::hateListUpdate( Actor& actor, int32_t hateAmount ) -{ - - auto it = m_hateList.begin(); - for( ; it != m_hateList.end(); ++it ) - { - if( ( *it )->m_pActor->getId() == actor.getId() ) - { - ( *it )->m_hateAmount += hateAmount; - return; - } - } - - auto hateEntry = new HateListEntry(); - hateEntry->m_hateAmount = hateAmount; - hateEntry->m_pActor = actor.getAsActor(); - m_hateList.insert( hateEntry ); -} - -void Core::Entity::BattleNpc::onDeath() -{ - //LuaManager->onMobDeath( this ); - - setTimeOfDeath( static_cast< uint32_t >( time( nullptr ) ) ); - setTargetId( INVALID_GAME_OBJECT_ID ); - m_currentStance = Stance::Passive; - m_mode = MODE_IDLE; - m_hp = 0; - setOwner( nullptr ); - - // todo: fully ghetto retarded exp reward pls fix - { - uint32_t minHate = -1; - uint32_t maxHate = 0; - uint32_t totalHate = 0; - for( auto& pHateEntry : m_hateList ) - { - if( pHateEntry->m_pActor->isPlayer() ) - { - if( pHateEntry->m_hateAmount < minHate ) - minHate = pHateEntry->m_hateAmount; - else if( pHateEntry->m_hateAmount > maxHate ) - maxHate = pHateEntry->m_hateAmount; - } - totalHate += pHateEntry->m_hateAmount; - } - - //uint32_t plsBeHatedThisMuchAtLeast = totalHate / ( maxHate + 2 ) / clamp( m_hateList.size(), 1.0f, 1.5f ); - - for( auto& pHateEntry : m_hateList ) - { - // todo: this is pure retarded - // todo: check for companion - if( pHateEntry->m_pActor->isPlayer() ) // && pHateEntry->m_hateAmount >= plsBeHatedThisMuchAtLeast ) - { - uint8_t level = pHateEntry->m_pActor->getLevel(); - auto levelDiff = static_cast< int32_t >( this->m_level ) - level; - auto cappedLevelDiff = Math::Util::clamp( levelDiff, 1, 6 ); - - auto expNeeded = g_framework.getExdDataGen().get< Core::Data::ParamGrow >( m_level + cappedLevelDiff - 1 )->expToNext; - int32_t exp = 0; - - // todo: arbitrary numbers pulled out of my ass - if( m_level <= 14 ) - exp = ( expNeeded / ( 100 - levelDiff) ) + cappedLevelDiff + ( cappedLevelDiff * ( ( rand() % ( cappedLevelDiff * 9 ) ) + 1 ) ); - else if( m_level <= 24 ) - exp = ( expNeeded / ( 150 - levelDiff) ) + cappedLevelDiff + ( cappedLevelDiff * ( ( rand() % ( cappedLevelDiff * 8 ) ) + 1 ) ); - else if( m_level <= 34 ) - exp = ( expNeeded / ( 350 - levelDiff ) ) + cappedLevelDiff + ( cappedLevelDiff * ( ( rand() % ( cappedLevelDiff * 7 ) ) + 1 ) ); - else if( m_level <= 44 ) - exp = ( expNeeded / ( 550 - levelDiff ) ) + cappedLevelDiff + ( cappedLevelDiff * ( ( rand() % ( cappedLevelDiff * 6 ) ) + 1 ) ); - else if( m_level <= 50 ) - exp = ( expNeeded / ( 750 - levelDiff ) ) + cappedLevelDiff + ( cappedLevelDiff * ( ( rand() % ( cappedLevelDiff * 5 ) ) + 1 ) ); - else - exp = ( expNeeded / ( 1200 - levelDiff ) ) + cappedLevelDiff + ( cappedLevelDiff * ( ( rand() % ( cappedLevelDiff * 4 ) ) + 1 ) ); - - - // todo: this is actually retarded, we need real rand() - srand( static_cast< uint32_t > ( time( nullptr ) ) ); - - auto pPlayer = pHateEntry->m_pActor->getAsPlayer(); - pPlayer->gainExp( exp ); - pPlayer->onMobKill( m_nameId ); - } - } - } - hateListClear(); -} - -void Core::Entity::BattleNpc::onActionHostile( Actor& source ) -{ - - if( hateListGetHighest() == nullptr ) - aggro( source ); - - if( getClaimer() == nullptr ) - setOwner( source.getAsPlayer() ); -} - -Core::Entity::ActorPtr Core::Entity::BattleNpc::getClaimer() const -{ - return m_pOwner; -} - - -// HACK: this is highly experimental code, will have to be changed eventually -// since there are different types of mobs... (stationary, moving...) likely to be -// handled by scripts entirely. -void Core::Entity::BattleNpc::update( int64_t currTime ) -{ - - if( !isAlive() ) - { - m_status = ActorStatus::Idle; - m_mode = MODE_IDLE; - return; - } - - updateStatusEffects(); - float distance = Math::Util::distance( m_pos.x, m_pos.y, m_pos.z, - m_posOrigin.x, m_posOrigin.y, m_posOrigin.z ); - - if( ( distance > 70 ) && m_mode != MODE_RETREAT ) - { - changeTarget( INVALID_GAME_OBJECT_ID ); - m_mode = MODE_RETREAT; - hateListClear(); - setOwner( nullptr ); - } - - switch( m_mode ) - { - - case MODE_RETREAT: - { - if( moveTo( m_posOrigin ) ) - m_mode = MODE_IDLE; - } - break; - - case MODE_IDLE: - { - ActorPtr pClosestActor = getClosestActor(); - - if( pClosestActor && pClosestActor->isAlive() ) - { - distance = Math::Util::distance( getPos().x, getPos().y, getPos().z, - pClosestActor->getPos().x, - pClosestActor->getPos().y, - pClosestActor->getPos().z ); - - //if( distance < 8 && getbehavior() == 2 ) - // aggro( pClosestActor ); - } - } - break; - - case MODE_COMBAT: - { - ActorPtr pClosestActor = hateListGetHighest(); - - if( pClosestActor != nullptr && !pClosestActor->isAlive() ) - { - hateListRemove( *pClosestActor ); - pClosestActor = hateListGetHighest(); - } - - if( pClosestActor != nullptr ) - { - distance = Math::Util::distance( getPos().x, getPos().y, getPos().z, - pClosestActor->getPos().x, - pClosestActor->getPos().y, - pClosestActor->getPos().z ); - - if( distance > 4 ) - moveTo( pClosestActor->getPos() ); - else - { - if( face( pClosestActor->getPos() ) ) - sendPositionUpdate(); - // in combat range. ATTACK! - autoAttack( pClosestActor ); - } - } - else - { - changeTarget( INVALID_GAME_OBJECT_ID ); - setStance( Stance::Passive ); - setOwner( nullptr ); - m_mode = MODE_RETREAT; - } - - } - break; - } - -} - -uint32_t Core::Entity::BattleNpc::getTimeOfDeath() const -{ - return m_timeOfDeath; -} - -void Core::Entity::BattleNpc::setTimeOfDeath( uint32_t tod ) -{ - m_timeOfDeath = tod; -} - diff --git a/src/servers/sapphire_zone/Actor/BattleNpc.h b/src/servers/sapphire_zone/Actor/BattleNpc.h deleted file mode 100644 index b80e6340..00000000 --- a/src/servers/sapphire_zone/Actor/BattleNpc.h +++ /dev/null @@ -1,115 +0,0 @@ -#ifndef _BATTLENPC_H -#define _BATTLENPC_H - -#include "Actor.h" - -namespace Core { -namespace Entity { - -enum StateMode -{ - MODE_COMBAT, - MODE_RETREAT, - MODE_IDLE, -}; - -typedef struct -{ - uint32_t m_hateAmount; - ActorPtr m_pActor; -} HateListEntry; - -// class for Mobs inheriting from Actor -class BattleNpc : public Actor -{ -public: - BattleNpc(); - virtual ~BattleNpc() override; - - BattleNpc( uint16_t modelId, uint16_t nameid, const Common::FFXIVARR_POSITION3& spawnPos, uint16_t bnpcBaseId = 0, - uint32_t type = 2, uint8_t level = 0, uint8_t behaviour = 1, uint32_t mobType = 0 ); - - //BattleNpc( uint32_t modelId, - // uint32_t nameId, - // uint32_t bnpcBaseId, - // uint32_t level, - // const Common::FFXIVARR_POSITION3& spawnPos, - // uint32_t type = 2, uint32_t behaviour = 1, uint32_t mobType = 0 ); - - void initStatusEffectContainer(); - - // send spawn packets to pTarget - void spawn( PlayerPtr pTarget ) override; - - // send despawn packets to pTarget - void despawn( PlayerPtr pTarget ) override; - - uint8_t getLevel() const override; - - StateMode getMode() const; - - void setMode( StateMode mode ); - - uint8_t getbehavior() const; - - void hateListAdd( Actor& actor, int32_t hateAmount ); - - void hateListUpdate( Actor& actor, int32_t hateAmount ); - void hateListRemove( Actor& actor ); - - bool hateListHasActor( Actor& actor ); - - void resetPos(); - - uint32_t getNameId() const; - - void hateListClear(); - - ActorPtr hateListGetHighest(); - - void aggro( Actor& actor ); - - void deaggro( Actor& actor ); - - void setOwner( PlayerPtr pPlayer ); - - void onDeath() override; - - void onActionHostile( Actor& source ) override; - - ActorPtr getClaimer() const; - - void sendPositionUpdate(); - - // return true if it reached the position - bool moveTo( Common::FFXIVARR_POSITION3& pos ); - - void update( int64_t currTime ) override; - - uint32_t getTimeOfDeath() const; - - void setTimeOfDeath( uint32_t tod ); - -private: - - static uint32_t m_nextID; - StateMode m_mode; - Common::FFXIVARR_POSITION3 m_posOrigin; - uint8_t m_level; - uint16_t m_modelId; - uint16_t m_nameId; - uint16_t m_bnpcBaseId; - uint8_t m_behavior; - uint32_t m_unk1; - uint32_t m_unk2; - std::set< HateListEntry* > m_hateList; - ActorPtr m_pOwner; - uint32_t m_timeOfDeath; - uint32_t m_mobType; - -}; - -} -} - -#endif diff --git a/src/servers/sapphire_zone/Actor/BattleNpcTemplate.cpp b/src/servers/sapphire_zone/Actor/BattleNpcTemplate.cpp deleted file mode 100644 index 62c21ab1..00000000 --- a/src/servers/sapphire_zone/Actor/BattleNpcTemplate.cpp +++ /dev/null @@ -1,37 +0,0 @@ -#include "BattleNpcTemplate.h" - -Core::Entity::BattleNpcTemplate::BattleNpcTemplate() -{ - -} - -Core::Entity::BattleNpcTemplate::BattleNpcTemplate( std::string templateName, uint32_t bnpcBaseId, - uint32_t bnpcNameId, uint32_t modelId, std::string aiName ) : - m_templateName( templateName ), - m_bnpcBaseId( bnpcBaseId ), - m_bnpcNameId( bnpcNameId ), - m_modelId( modelId ), - m_aiName( aiName ) -{ - -} - -Core::Entity::BattleNpcTemplate::~BattleNpcTemplate() -{ - -} - -uint32_t Core::Entity::BattleNpcTemplate::getBnpcBaseId() const -{ - return m_bnpcBaseId; -} - -uint32_t Core::Entity::BattleNpcTemplate::getBnpcNameId() const -{ - return m_bnpcNameId; -} - -uint32_t Core::Entity::BattleNpcTemplate::getModelId() const -{ - return m_modelId; -} diff --git a/src/servers/sapphire_zone/Actor/BattleNpcTemplate.h b/src/servers/sapphire_zone/Actor/BattleNpcTemplate.h deleted file mode 100644 index 07e8f5b9..00000000 --- a/src/servers/sapphire_zone/Actor/BattleNpcTemplate.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef _BATTLENPCTEMPLATE_H -#define _BATTLENPCTEMPLATE_H - -#include -#include - -namespace Core { -namespace Entity { - - /** - * \brief BattleNpcTemplate - Class which defines a template specific BNpcs can be created from - */ - class BattleNpcTemplate - { - public: - BattleNpcTemplate(); - BattleNpcTemplate( std::string templateName, uint32_t bnpcBaseId, uint32_t bnpcNameId, uint32_t modelId, std::string aiName ); - - ~BattleNpcTemplate(); - - uint32_t getBnpcBaseId() const; - uint32_t getBnpcNameId() const; - uint32_t getModelId() const; - - private: - std::string m_templateName; - std::string m_aiName; - uint32_t m_bnpcBaseId = 0; - uint32_t m_bnpcNameId = 0; - uint32_t m_modelId = 0; - - - }; - -} -} - -#endif \ No newline at end of file diff --git a/src/servers/sapphire_zone/Actor/Chara.cpp b/src/servers/sapphire_zone/Actor/Chara.cpp new file mode 100644 index 00000000..efd2927a --- /dev/null +++ b/src/servers/sapphire_zone/Actor/Chara.cpp @@ -0,0 +1,763 @@ +#include +#include +#include +#include +#include + +#include "Forwards.h" +#include "Action/Action.h" + +#include "Zone/Zone.h" + +#include "Network/GameConnection.h" +#include "Network/PacketWrappers/ActorControlPacket142.h" +#include "Network/PacketWrappers/ActorControlPacket143.h" +#include "Network/PacketWrappers/ActorControlPacket144.h" +#include "Network/PacketWrappers/UpdateHpMpTpPacket.h" + +#include "StatusEffect/StatusEffect.h" +#include "Action/ActionCollision.h" +#include "ServerZone.h" +#include "Session.h" +#include "Math/CalcBattle.h" +#include "Chara.h" +#include "Player.h" +#include "Zone/TerritoryMgr.h" +#include "Framework.h" + +extern Core::Framework g_framework; + +using namespace Core::Common; +using namespace Core::Network::Packets; +using namespace Core::Network::Packets::Server; + +Core::Entity::Chara::Chara( ObjKind type ) : + Actor( type ) +{ + // initialize the free slot queue + for( uint8_t i = 0; i < MAX_STATUS_EFFECTS; i++ ) + { + m_statusEffectFreeSlotQueue.push( i ); + } +} + +Core::Entity::Chara::~Chara() +{ +} + +/*! \return the actors name */ +std::string Core::Entity::Chara::getName() const +{ + return std::string( m_name ); +} + + +/*! \return current stance of the actors */ +Core::Entity::Chara::Stance Core::Entity::Chara::getStance() const +{ + return m_currentStance; +} + +/*! \return actor stats */ +Core::Entity::Chara::ActorStats Core::Entity::Chara::getStats() const +{ + return m_baseStats; +} + +/*! \return current HP */ +uint32_t Core::Entity::Chara::getHp() const +{ + return m_hp; +} + +/*! \return current MP */ +uint32_t Core::Entity::Chara::getMp() const +{ + return m_mp; +} + +/*! \return current TP */ +uint16_t Core::Entity::Chara::getTp() const +{ + return m_tp; +} + +/*! \return current GP */ +uint16_t Core::Entity::Chara::getGp() const +{ + return m_gp; +} + +/*! \return current invincibility type */ +InvincibilityType Core::Entity::Chara::getInvincibilityType() const +{ + return m_invincibilityType; +} + +/*! \return current class or job */ +Core::Common::ClassJob Core::Entity::Chara::getClass() const +{ + return m_class; +} + +/*! \return current class or job as int32_t ( this feels pointless ) */ +uint8_t Core::Entity::Chara::getClassAsInt() const +{ + return static_cast< uint8_t >( m_class ); +} + +/*! \param ClassJob to set */ +void Core::Entity::Chara::setClass( Common::ClassJob classJob ) +{ + m_class = classJob; +} + +/*! \param Id of the target to set */ +void Core::Entity::Chara::setTargetId( uint64_t targetId ) +{ + m_targetId = targetId; +} + +/*! \return Id of the current target */ +uint64_t Core::Entity::Chara::getTargetId() const +{ + return m_targetId; +} + +/*! \return True if the actor is alive */ +bool Core::Entity::Chara::isAlive() const +{ + return ( m_hp > 0 ); +} + +/*! \return max hp for the actor */ +uint32_t Core::Entity::Chara::getMaxHp() const +{ + return m_baseStats.max_hp; +} + +/*! \return max mp for the actor */ +uint32_t Core::Entity::Chara::getMaxMp() const +{ + return m_baseStats.max_mp; +} + +/*! \return reset hp to current max hp */ +void Core::Entity::Chara::resetHp() +{ + m_hp = getMaxHp(); + sendStatusUpdate( true ); +} + +/*! \return reset mp to current max mp */ +void Core::Entity::Chara::resetMp() +{ + m_mp = getMaxMp(); + sendStatusUpdate( true ); +} + +/*! \param hp amount to set ( caps to maxHp ) */ +void Core::Entity::Chara::setHp( uint32_t hp ) +{ + m_hp = hp < getMaxHp() ? hp : getMaxHp(); + sendStatusUpdate( true ); +} + +/*! \param mp amount to set ( caps to maxMp ) */ +void Core::Entity::Chara::setMp( uint32_t mp ) +{ + m_mp = mp < getMaxMp() ? mp : getMaxMp(); + sendStatusUpdate( true ); +} + +/*! \param gp amount to set*/ +void Core::Entity::Chara::setGp( uint32_t gp ) +{ + m_gp = gp; + sendStatusUpdate( true ); +} + +/*! \param type invincibility type to set */ +void Core::Entity::Chara::setInvincibilityType( Common::InvincibilityType type ) +{ + m_invincibilityType = type; +} + +/*! \return current status of the actor */ +Core::Entity::Chara::ActorStatus Core::Entity::Chara::getStatus() const +{ + return m_status; +} + +/*! \param status to set */ +void Core::Entity::Chara::setStatus( ActorStatus status ) +{ + m_status = status; +} + +/*! +Performs necessary steps to mark an actor dead. +Sets hp/mp/tp, sets status, plays animation and fires onDeath event +*/ +void Core::Entity::Chara::die() +{ + m_status = ActorStatus::Dead; + m_hp = 0; + m_mp = 0; + m_tp = 0; + + // fire onDeath event + onDeath(); + + // if the actor is a player, the update needs to be send to himself too + bool selfNeedsUpdate = isPlayer(); + + sendToInRangeSet( ActorControlPacket142( m_id, SetStatus, static_cast< uint8_t >( ActorStatus::Dead ) ), selfNeedsUpdate ); + + // TODO: not all actor show the death animation when they die, some quest npcs might just despawn + // although that might be handled by setting the HP to 1 and doing some script magic + sendToInRangeSet( ActorControlPacket142( m_id, DeathAnimation, 0, 0, 0, 0x20 ), selfNeedsUpdate ); + +} + +/*! +Calculates and sets the rotation to look towards a specified +position + +\param Position to look towards +*/ +bool Core::Entity::Chara::face( const Common::FFXIVARR_POSITION3& p ) +{ + float oldRot = getRot(); + float rot = Math::Util::calcAngFrom( getPos().x, getPos().z, p.x, p.z ); + float newRot = PI - rot + ( PI / 2 ); + + m_pCell = nullptr; + + setRot( newRot ); + + return oldRot != newRot ? true : false; +} + +/*! +Set and propagate the actor stance to in range players +( not the actor himself ) + +\param stance to set +*/ +void Core::Entity::Chara::setStance( Stance stance ) +{ + m_currentStance = stance; + + sendToInRangeSet( ActorControlPacket142( m_id, ToggleAggro, stance, 1 ) ); +} + +/*! +Check if an action is queued for execution, if so update it +and if fully performed, clean up again. + +\return true if a queued action has been updated +*/ +bool Core::Entity::Chara::checkAction() +{ + + if( m_pCurrentAction == nullptr ) + return false; + + if( m_pCurrentAction->update() ) + m_pCurrentAction.reset(); + + return true; + +} + +/*! +Change the current target and propagate to in range players + +\param target actor id +*/ +void Core::Entity::Chara::changeTarget( uint64_t targetId ) +{ + setTargetId( targetId ); + sendToInRangeSet( ActorControlPacket144( m_id, SetTarget, 0, 0, 0, 0, targetId ) ); +} + +/*! +Dummy function \return 0 +*/ +uint8_t Core::Entity::Chara::getLevel() const +{ + return 0; +} + +/*! +Let an actor take damage and perform necessary steps +according to resulting hp, propagates new hp value to players +in range +TODO: eventually this needs to distinguish between physical and +magical dmg and take status effects into account + +\param amount of damage to be taken +*/ +void Core::Entity::Chara::takeDamage( uint32_t damage ) +{ + if( damage >= m_hp ) + { + switch( m_invincibilityType ) { + case InvincibilityNone: + setHp( 0 ); + die(); + break; + case InvincibilityRefill: + resetHp(); + break; + case InvincibilityStayAlive: + setHp( 0 ); + break; + } + } + else + m_hp -= damage; + + sendStatusUpdate( false ); +} + +/*! +Let an actor get healed and perform necessary steps +according to resulting hp, propagates new hp value to players +in range + +\param amount of hp to be healed +*/ +void Core::Entity::Chara::heal( uint32_t amount ) +{ + if( ( m_hp + amount ) > getMaxHp() ) + { + m_hp = getMaxHp(); + } + else + m_hp += amount; + + sendStatusUpdate( false ); +} + +/*! +Send an HpMpTp update to players in range ( and potentially to self ) +TODO: poor naming, should be changed. Status is not HP. Also should be virtual +so players can have their own version and we can abolish the param. + +\param true if the update should also be sent to the actor ( player ) himself +*/ +void Core::Entity::Chara::sendStatusUpdate( bool toSelf ) +{ + UpdateHpMpTpPacket updateHpPacket( *this ); + sendToInRangeSet( updateHpPacket ); +} + +/*! \return ActionPtr of the currently registered action, or nullptr */ +Core::Action::ActionPtr Core::Entity::Chara::getCurrentAction() const +{ + return m_pCurrentAction; +} + +/*! \param ActionPtr of the action to be registered */ +void Core::Entity::Chara::setCurrentAction( Core::Action::ActionPtr pAction ) +{ + m_pCurrentAction = pAction; +} + +/*! +Autoattack prototype implementation +TODO: move the check if the autoAttack can be performed to the callee +also rename autoAttack to autoAttack as that is more elaborate +On top of that, this only solves attacks from melee classes. +Will have to be extended for ranged attacks. + +\param ActorPtr the autoAttack is performed on +*/ +void Core::Entity::Chara::autoAttack( CharaPtr pTarget ) +{ + + uint64_t tick = Util::getTimeMs(); + + if( ( tick - m_lastAttack ) > 2500 ) + { + pTarget->onActionHostile( *this ); + m_lastAttack = tick; + srand( static_cast< uint32_t >( tick ) ); + + uint16_t damage = static_cast< uint16_t >( 10 + rand() % 12 ); + uint32_t variation = static_cast< uint32_t >( 0 + rand() % 4 ); + + ZoneChannelPacket< FFXIVIpcEffect > effectPacket( getId() ); + effectPacket.data().targetId = pTarget->getId(); + effectPacket.data().actionAnimationId = 0x366; + effectPacket.data().unknown_2 = variation; +// effectPacket.data().unknown_3 = 1; + effectPacket.data().actionTextId = 0x366; + effectPacket.data().numEffects = 1; + effectPacket.data().rotation = Math::Util::floatToUInt16Rot( getRot() ); + effectPacket.data().effectTarget = pTarget->getId(); + effectPacket.data().effects[0].value = damage; + effectPacket.data().effects[0].effectType = ActionEffectType::Damage; + effectPacket.data().effects[0].hitSeverity = static_cast< ActionHitSeverityType >( variation ); + effectPacket.data().effects[0].unknown_3 = 7; + + sendToInRangeSet( effectPacket ); + + if( isPlayer() ) + getAsPlayer()->queuePacket( effectPacket ); + + pTarget->takeDamage( damage ); + } +} + +/*! +ChaiScript Skill Handler. + +\param GamePacketPtr to send +\param bool should be send to self? +*/ +void Core::Entity::Chara::handleScriptSkill( uint32_t type, uint16_t actionId, uint64_t param1, + uint64_t param2, Entity::Chara& target ) +{ + + if( isPlayer() ) + { + getAsPlayer()->sendDebug( std::to_string( target.getId() ) ); + getAsPlayer()->sendDebug( "Handle script skill type: " + std::to_string( type ) ); + } + + auto actionInfoPtr = g_framework.getExdDataGen().get< Core::Data::Action >( actionId ); + + // Todo: Effect packet generator. 90% of this is basically setting params and it's basically unreadable. + // Prepare packet. This is seemingly common for all packets in the action handler. + + ZoneChannelPacket< FFXIVIpcEffect > effectPacket( getId() ); + effectPacket.data().targetId = target.getId(); + effectPacket.data().actionAnimationId = actionId; + effectPacket.data().unknown_62 = 1; // Affects displaying action name next to number in floating text + effectPacket.data().unknown_2 = 1; // This seems to have an effect on the "double-cast finish" animation + effectPacket.data().actionTextId = actionId; + effectPacket.data().numEffects = 1; + effectPacket.data().rotation = Math::Util::floatToUInt16Rot( getRot() ); + effectPacket.data().effectTarget = target.getId(); + + // Todo: for each actor, calculate how much damage the calculated value should deal to them - 2-step damage calc. we only have 1-step + switch( type ) + { + + case ActionEffectType::Damage: + { + effectPacket.data().effects[0].value = static_cast< uint16_t >( param1 ); + effectPacket.data().effects[0].effectType = ActionEffectType::Damage; + effectPacket.data().effects[0].hitSeverity = ActionHitSeverityType::NormalDamage; + effectPacket.data().effects[0].unknown_3 = 7; + + if( actionInfoPtr->castType == 1 && actionInfoPtr->effectRange != 0 || actionInfoPtr->castType != 1 ) + { + // If action on this specific target is valid... + if ( isPlayer() && !ActionCollision::isActorApplicable( target, TargetFilter::Enemies ) ) + break; + + sendToInRangeSet( effectPacket, true ); + + if ( target.isAlive() ) + target.onActionHostile( *this ); + + target.takeDamage( static_cast< uint32_t >( param1 ) ); + + } + else + { + + auto actorsCollided = ActionCollision::getActorsHitFromAction( target.getPos(), getInRangeActors( true ), + actionInfoPtr, TargetFilter::Enemies ); + + for( const auto& pHitActor : actorsCollided ) + { + effectPacket.data().targetId = pHitActor->getId(); + effectPacket.data().effectTarget = pHitActor->getId(); + + // todo: send to range of what? ourselves? when mob script hits this is going to be lacking + sendToInRangeSet( effectPacket, true ); + + + if( pHitActor->getAsChara()->isAlive() ) + pHitActor->getAsChara()->onActionHostile( *this ); + + pHitActor->getAsChara()->takeDamage( static_cast< uint32_t >( param1 ) ); + + // Debug + if ( isPlayer() ) + { + if ( pHitActor->isPlayer() ) + getAsPlayer()->sendDebug( "AoE hit actor " + std::to_string( pHitActor->getId() ) + + " (" + pHitActor->getAsChara()->getName() + ")" ); + else + getAsPlayer()->sendDebug( "AoE hit actor " + std::to_string( pHitActor->getId() ) ); + } + } + } + + break; + } + + case ActionEffectType::Heal: + { + uint32_t calculatedHeal = Math::CalcBattle::calculateHealValue( getAsPlayer(), static_cast< uint32_t >( param1 ) ); + + effectPacket.data().effects[0].value = calculatedHeal; + effectPacket.data().effects[0].effectType = ActionEffectType::Heal; + effectPacket.data().effects[0].hitSeverity = ActionHitSeverityType::NormalHeal; + + if( actionInfoPtr->castType == 1 && actionInfoPtr->effectRange != 0 || actionInfoPtr->castType != 1 ) + { + if( isPlayer() && !ActionCollision::isActorApplicable( target, TargetFilter::Allies ) ) + break; + + sendToInRangeSet( effectPacket, true ); + target.heal( calculatedHeal ); + } + else + { + // todo: get proper packets: the following was just kind of thrown together from what we know. + // atm buggy (packets look "delayed" from client) + + auto actorsCollided = ActionCollision::getActorsHitFromAction( target.getPos(), getInRangeActors(true), + actionInfoPtr, TargetFilter::Allies ); + + for( auto pHitActor : actorsCollided ) + { + effectPacket.data().targetId = target.getId(); + effectPacket.data().effectTarget = pHitActor->getId(); + + sendToInRangeSet( effectPacket, true ); + pHitActor->getAsChara()->heal( calculatedHeal ); + + // Debug + if( isPlayer() ) + { + if( pHitActor->isPlayer() ) + getAsPlayer()->sendDebug( "AoE hit actor " + std::to_string( pHitActor->getId() ) + + " (" + pHitActor->getAsChara()->getName() + ")" ); + else + getAsPlayer()->sendDebug( "AoE hit actor " + std::to_string( pHitActor->getId() ) ); + } + } + } + break; + } + + default: + break; + } +} + +/*! \param StatusEffectPtr to be applied to the actor */ +void Core::Entity::Chara::addStatusEffect( StatusEffect::StatusEffectPtr pEffect ) +{ + int8_t nextSlot = getStatusEffectFreeSlot(); + // if there is no slot left, do not add the effect + if( nextSlot == -1 ) + return; + + pEffect->applyStatus(); + m_statusEffectMap[nextSlot] = pEffect; + + ZoneChannelPacket< Server::FFXIVIpcAddStatusEffect > statusEffectAdd( getId() ); + statusEffectAdd.data().actor_id = pEffect->getTargetActorId(); + statusEffectAdd.data().actor_id1 = pEffect->getSrcActorId(); + statusEffectAdd.data().current_hp = getHp(); + statusEffectAdd.data().current_mp = getMp(); + statusEffectAdd.data().current_tp = getTp(); + statusEffectAdd.data().duration = static_cast< float >( pEffect->getDuration() ) / 1000; + statusEffectAdd.data().effect_id = pEffect->getId(); + statusEffectAdd.data().effect_index = nextSlot; + statusEffectAdd.data().max_hp = getMaxHp(); + statusEffectAdd.data().max_mp = getMaxMp(); + statusEffectAdd.data().max_something = 1; + //statusEffectAdd.data().unknown2 = 28; + statusEffectAdd.data().param = pEffect->getParam(); + + sendToInRangeSet( statusEffectAdd, isPlayer() ); +} + +/*! \param StatusEffectPtr to be applied to the actor */ +void Core::Entity::Chara::addStatusEffectById( uint32_t id, int32_t duration, Entity::Chara& source, uint16_t param ) +{ + auto effect = StatusEffect::make_StatusEffect( id, source.getAsChara(), getAsChara(), duration, 3000 ); + effect->setParam( param ); + addStatusEffect( effect ); +} + +/*! \param StatusEffectPtr to be applied to the actor */ +void Core::Entity::Chara::addStatusEffectByIdIfNotExist( uint32_t id, int32_t duration, Entity::Chara& source, uint16_t param ) +{ + if( hasStatusEffect( id ) ) + return; + + auto effect = StatusEffect::make_StatusEffect( id, source.getAsChara(), getAsChara(), duration, 3000 ); + effect->setParam( param ); + addStatusEffect( effect ); + +} + +int8_t Core::Entity::Chara::getStatusEffectFreeSlot() +{ + int8_t freeEffectSlot = -1; + + if( m_statusEffectFreeSlotQueue.empty() ) + return freeEffectSlot; + + freeEffectSlot = m_statusEffectFreeSlotQueue.front(); + m_statusEffectFreeSlotQueue.pop(); + + return freeEffectSlot; +} + +void Core::Entity::Chara::statusEffectFreeSlot( uint8_t slotId ) +{ + m_statusEffectFreeSlotQueue.push( slotId ); +} + +void Core::Entity::Chara::removeSingleStatusEffectById( uint32_t id ) +{ + for( auto effectIt : m_statusEffectMap ) + { + if( effectIt.second->getId() == id ) + { + removeStatusEffect( effectIt.first ); + break; + } + } +} + +void Core::Entity::Chara::removeStatusEffect( uint8_t effectSlotId ) +{ + auto pEffectIt = m_statusEffectMap.find( effectSlotId ); + if( pEffectIt == m_statusEffectMap.end() ) + return; + + statusEffectFreeSlot( effectSlotId ); + + auto pEffect = pEffectIt->second; + pEffect->removeStatus(); + + sendToInRangeSet( ActorControlPacket142( getId(), StatusEffectLose, pEffect->getId() ), isPlayer() ); + + m_statusEffectMap.erase( effectSlotId ); + + sendStatusEffectUpdate(); +} + +std::map< uint8_t, Core::StatusEffect::StatusEffectPtr > Core::Entity::Chara::getStatusEffectMap() const +{ + return m_statusEffectMap; +} + +void Core::Entity::Chara::sendStatusEffectUpdate() +{ + uint64_t currentTimeMs = Util::getTimeMs(); + + ZoneChannelPacket< Server::FFXIVIpcStatusEffectList > statusEffectList( getId() ); + + statusEffectList.data().classId = static_cast< uint8_t >( getClass() ); + statusEffectList.data().level = getLevel(); + statusEffectList.data().level1 = getLevel(); + statusEffectList.data().current_hp = getHp(); + statusEffectList.data().current_mp = getMp(); + statusEffectList.data().currentTp = getTp(); + statusEffectList.data().max_hp = getMaxHp(); + statusEffectList.data().max_mp = getMaxMp(); + uint8_t slot = 0; + for( auto effectIt : m_statusEffectMap ) + { + float timeLeft = static_cast< float >( effectIt.second->getDuration() - + ( currentTimeMs - effectIt.second->getStartTimeMs() ) ) / 1000; + statusEffectList.data().effect[slot].duration = timeLeft; + statusEffectList.data().effect[slot].effect_id = effectIt.second->getId(); + statusEffectList.data().effect[slot].sourceActorId = effectIt.second->getSrcActorId(); + slot++; + } + + sendToInRangeSet( statusEffectList, isPlayer() ); + +} + +void Core::Entity::Chara::updateStatusEffects() +{ + uint64_t currentTimeMs = Util::getTimeMs(); + + uint32_t thisTickDmg = 0; + uint32_t thisTickHeal = 0; + + for( auto effectIt : m_statusEffectMap ) + { + uint8_t effectIndex = effectIt.first; + auto effect = effectIt.second; + + uint64_t lastTick = effect->getLastTickMs(); + uint64_t startTime = effect->getStartTimeMs(); + uint32_t duration = effect->getDuration(); + uint32_t tickRate = effect->getTickRate(); + + if( ( currentTimeMs - startTime ) > duration ) + { + // remove status effect + removeStatusEffect( effectIndex ); + // break because removing invalidates iterators + break; + } + + if( ( currentTimeMs - lastTick ) > tickRate ) + { + effect->setLastTick( currentTimeMs ); + effect->onTick(); + + auto thisEffect = effect->getTickEffect(); + + switch( thisEffect.first ) + { + + case 1: + { + thisTickDmg += thisEffect.second; + break; + } + + case 2: + { + thisTickHeal += thisEffect.second; + break; + } + + } + } + + } + + if( thisTickDmg != 0 ) + { + takeDamage( thisTickDmg ); + sendToInRangeSet( ActorControlPacket142( getId(), HPFloatingText, 0, static_cast< uint8_t >( ActionEffectType::Damage ), thisTickDmg ) ); + } + + if( thisTickHeal != 0 ) + { + heal( thisTickDmg ); + sendToInRangeSet( ActorControlPacket142( getId(), HPFloatingText, 0, static_cast< uint8_t >( ActionEffectType::Heal ), thisTickHeal ) ); + } +} + +bool Core::Entity::Chara::hasStatusEffect( uint32_t id ) +{ + if( m_statusEffectMap.find( id ) != m_statusEffectMap.end() ) + return true; + return false; +} + +Chara::ModelType Chara::getModelType() const +{ + return m_modelType; +} diff --git a/src/servers/sapphire_zone/Actor/Chara.h b/src/servers/sapphire_zone/Actor/Chara.h new file mode 100644 index 00000000..dd5bb340 --- /dev/null +++ b/src/servers/sapphire_zone/Actor/Chara.h @@ -0,0 +1,256 @@ +#ifndef _ACTOR_H_ +#define _ACTOR_H_ + +#include +#include + +#include "Forwards.h" +#include "Actor.h" +#include +#include +#include + +namespace Core { +namespace Entity { + +/*! +\class Chara +\brief Base class for all animate actors + +*/ +class Chara : public Actor +{ +public: + enum Stance : uint8_t + { + Passive = 0, + Active = 1, + }; + + enum DisplayFlags : uint16_t + { + ActiveStance = 0x001, + Invisible = 0x020, + HideHead = 0x040, + HideWeapon = 0x080, + Faded = 0x100, + Visor = 0x800, + }; + + enum struct ActorStatus : uint8_t + { + Idle = 0x01, + Dead = 0x02, + Sitting = 0x03, + Mounted = 0x04, + Crafting = 0x05, + Gathering = 0x06, + Melding = 0x07, + SMachine = 0x08 + }; + + /*! ModelType as found in eventsystemdefine.exd */ + enum ModelType : uint8_t + { + Human = 1, + DemiHuman = 2, + Monster = 3, + SharedGroup = 4, + Parts = 5 + }; + + struct ActorStats + { + uint32_t max_mp = 0; + uint32_t max_hp = 0; + + uint32_t str = 0; + uint32_t dex = 0; + uint32_t vit = 0; + uint32_t inte = 0; + uint32_t mnd = 0; + uint32_t pie = 0; + + uint32_t tenacity = 0; + uint32_t attack = 0; + uint32_t defense = 0; + uint32_t accuracy = 0; + uint32_t spellSpeed = 0; + uint32_t magicDefense = 0; + uint32_t critHitRate = 0; + uint32_t resistSlash = 0; + uint32_t resistPierce = 0; + uint32_t resistBlunt = 0; + uint32_t attackPotMagic = 0; + uint32_t healingPotMagic = 0; + uint32_t determination = 0; + uint32_t skillSpeed = 0; + + uint32_t resistSlow = 0; + uint32_t resistSilence = 0; + uint32_t resistBlind = 0; + uint32_t resistPoison = 0; + uint32_t resistStun = 0; + uint32_t resistSleep = 0; + uint32_t resistBind = 0; + uint32_t resistHeavy = 0; + + uint32_t resistFire = 0; + uint32_t resistIce = 0; + uint32_t resistWind = 0; + uint32_t resistEarth = 0; + uint32_t resistLightning = 0; + uint32_t resistWater = 0; + + } m_baseStats; + +protected: + char m_name[34]; + /*! Last tick time for the actor ( in ms ) */ + uint64_t m_lastTickTime; + /*! Last time the actor performed an autoAttack ( in ms ) */ + uint64_t m_lastAttack; + /*! Last time the actor was updated ( in ms ) */ + uint64_t m_lastUpdate; + /*! Current stance of the actor */ + Stance m_currentStance; + /*! Current staus of the actor */ + ActorStatus m_status; + /*! Max HP of the actor ( based on job / class ) */ + uint32_t m_maxHp; + /*! Max MP of the actor ( based on job / class ) */ + uint32_t m_maxMp; + /*! Current HP of the actor */ + uint32_t m_hp; + /*! Current MP of the actor */ + uint32_t m_mp; + /*! Current TP of the actor */ + uint16_t m_tp; + /*! Current GP of the actor */ + uint16_t m_gp; + /*! Additional look info of the actor */ + uint8_t m_customize[26]; + /*! Current class of the actor */ + Common::ClassJob m_class; + /*! Id of the currently selected target actor */ + uint64_t m_targetId; + /*! Ptr to a queued action */ + Action::ActionPtr m_pCurrentAction; + /*! Invincibility type */ + Common::InvincibilityType m_invincibilityType; + /*! Type of model to use, humanoid for actors that use look data */ + ModelType m_modelType; + + /*! Status effects */ + const uint8_t MAX_STATUS_EFFECTS = 30; + std::queue< uint8_t > m_statusEffectFreeSlotQueue; + std::vector< std::pair< uint8_t, uint32_t> > m_statusEffectList; + std::map< uint8_t, StatusEffect::StatusEffectPtr > m_statusEffectMap; + +public: + Chara( ObjKind type ); + + virtual ~Chara() override; + + virtual void calculateStats() {}; + + /// Status effect functions + void addStatusEffect( StatusEffect::StatusEffectPtr pEffect ); + void removeStatusEffect( uint8_t effectSlotId ); + void removeSingleStatusEffectById( uint32_t id ); + void updateStatusEffects(); + + bool hasStatusEffect( uint32_t id ); + + int8_t getStatusEffectFreeSlot(); + void statusEffectFreeSlot( uint8_t slotId ); + + std::map< uint8_t, Core::StatusEffect::StatusEffectPtr > getStatusEffectMap() const; + + void sendStatusEffectUpdate(); + // add a status effect by id + void addStatusEffectById( uint32_t id, int32_t duration, Entity::Chara& source, uint16_t param = 0 ); + + // add a status effect by id if it doesn't exist + void addStatusEffectByIdIfNotExist( uint32_t id, int32_t duration, Entity::Chara& source, uint16_t param = 0 ); + + // remove a status effect by id + void removeSingleStatusEffectFromId( uint32_t id ); + /// End Status Effect Functions + + std::string getName() const; + + bool face( const Common::FFXIVARR_POSITION3& p ); + + Stance getStance() const; + + void setStance( Stance stance ); + + ActorStats getStats() const; + + uint32_t getHp() const; + uint32_t getMp() const; + uint16_t getTp() const; + uint16_t getGp() const; + + Common::InvincibilityType getInvincibilityType() const; + + Common::ClassJob getClass() const; + + ModelType getModelType() const; + + uint8_t getClassAsInt() const; + + void setClass( Common::ClassJob classJob ); + + void setTargetId( uint64_t targetId ); + + uint64_t getTargetId() const; + + bool isAlive() const; + + virtual uint32_t getMaxHp() const; + virtual uint32_t getMaxMp() const; + + void resetHp(); + void resetMp(); + + void setHp( uint32_t hp ); + void setMp( uint32_t mp ); + void setGp( uint32_t gp ); + + void setInvincibilityType( Common::InvincibilityType type ); + + void die(); + + ActorStatus getStatus() const; + + void setStatus( ActorStatus status ); + + void handleScriptSkill( uint32_t type, uint16_t actionId, uint64_t param1, uint64_t param2, Entity::Chara& target ); + + virtual void autoAttack( CharaPtr pTarget ); + + virtual void onDeath() {}; + virtual void onDamageTaken( Chara& pSource ) {}; + virtual void onActionHostile( Chara& source ) {}; + virtual void onActionFriendly( Chara& pSource ) {}; + virtual void onTick() {}; + + virtual void changeTarget( uint64_t targetId ); + virtual uint8_t getLevel() const; + virtual void sendStatusUpdate( bool toSelf = true ); + virtual void takeDamage( uint32_t damage ); + virtual void heal( uint32_t amount ); + virtual bool checkAction(); + virtual void update( int64_t currTime ) {}; + + Action::ActionPtr getCurrentAction() const; + + void setCurrentAction( Action::ActionPtr pAction ); + +}; + +} +} +#endif diff --git a/src/servers/sapphire_zone/Actor/EventNpc.cpp b/src/servers/sapphire_zone/Actor/EventNpc.cpp deleted file mode 100644 index d7690605..00000000 --- a/src/servers/sapphire_zone/Actor/EventNpc.cpp +++ /dev/null @@ -1,119 +0,0 @@ -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "Network/PacketWrappers/MoveActorPacket.h" -#include "Network/PacketWrappers/ActorControlPacket142.h" -#include "Network/PacketWrappers/ActorControlPacket143.h" - -#include "Player.h" -#include "EventNpc.h" -#include "Framework.h" - -using namespace Core::Common; -using namespace Core::Network::Packets; -using namespace Core::Network::Packets::Server; - -extern Core::Framework g_framework; - -uint32_t Core::Entity::EventNpc::m_nextID = 1249241694; - -Core::Entity::EventNpc::EventNpc() : - Actor( ObjKind::EventNpc ) -{ - m_id = 0; - m_status = ActorStatus::Idle; -} - -Core::Entity::EventNpc::~EventNpc() -{ - -} - -Core::Entity::EventNpc::EventNpc( uint32_t enpcId, const Common::FFXIVARR_POSITION3& spawnPos, float rotation ) : - Actor( ObjKind::EventNpc ) -{ - EventNpc::m_nextID++; - m_id = EventNpc::m_nextID; - - m_pos = spawnPos; - m_posOrigin = spawnPos; - - m_targetId = static_cast< uint64_t >( INVALID_GAME_OBJECT_ID ); - - m_maxHp = 150; - m_maxMp = 100; - - m_baseStats.max_hp = m_maxHp; - m_baseStats.max_mp = m_maxMp; - - m_hp = m_maxHp; - m_mp = m_maxMp; - - m_currentStance = Stance::Passive; - - m_eNpcId = enpcId; - - m_status = ActorStatus::Idle; - - m_invincibilityType = InvincibilityType::InvincibilityNone; - m_rot = rotation; - -} - -// spawn this player for pTarget -/*! TODO: Retail additionally sends Look+Models for EventNpcs even though it is not needed, - add when the new exd reader is implemented(also counts for BNPCs) */ -void Core::Entity::EventNpc::spawn( PlayerPtr pTarget ) -{ - ZoneChannelPacket< FFXIVIpcNpcSpawn > spawnPacket( getId(), pTarget->getId() ); - - - spawnPacket.data().pos.x = m_pos.x; - spawnPacket.data().pos.y = m_pos.y; - spawnPacket.data().pos.z = m_pos.z; - - spawnPacket.data().targetId = pTarget->getId(); - spawnPacket.data().hPCurr = 1; - spawnPacket.data().hPMax = 1; - - spawnPacket.data().bNPCBase = m_eNpcId; - spawnPacket.data().bNPCName = m_eNpcId; - spawnPacket.data().spawnIndex = pTarget->getSpawnIdForActorId( getId() ); - - spawnPacket.data().rotation = Math::Util::floatToUInt16Rot( getRotation() ); - - spawnPacket.data().type = static_cast< uint8_t >( m_objKind ); - - pTarget->queuePacket( spawnPacket ); -} - -// despawn -void Core::Entity::EventNpc::despawn( PlayerPtr pTarget ) -{ - pTarget->freePlayerSpawnId( getId() ); - - ActorControlPacket143 controlPacket( m_id, DespawnZoneScreenMsg, 0x04, getId(), 0x01 ); - pTarget->queuePacket( controlPacket ); -} - -uint8_t Core::Entity::EventNpc::getLevel() const -{ - return m_level; -} - -void Core::Entity::EventNpc::resetPos() -{ - m_pos = m_posOrigin; -} - -uint32_t Core::Entity::EventNpc::getEnpcId() const -{ - return m_eNpcId; -} \ No newline at end of file diff --git a/src/servers/sapphire_zone/Actor/EventNpc.h b/src/servers/sapphire_zone/Actor/EventNpc.h deleted file mode 100644 index 726eae94..00000000 --- a/src/servers/sapphire_zone/Actor/EventNpc.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef _EVENTNPC_H -#define _EVENTNPC_H - -#include "Actor.h" - -namespace Core { -namespace Entity { - -// class for Mobs inheriting from Actor -class EventNpc : public Actor -{ -public: - EventNpc(); - virtual ~EventNpc() override; - - EventNpc( uint32_t enpcId, const Common::FFXIVARR_POSITION3& spawnPos, float rotation ); - - // send spawn packets to pTarget - void spawn( PlayerPtr pTarget ) override; - - // send despawn packets to pTarget - void despawn( PlayerPtr pTarget ) override; - - uint8_t getLevel() const override; - - void resetPos(); - - uint32_t getEnpcId() const; - -private: - - static uint32_t m_nextID; - Common::FFXIVARR_POSITION3 m_posOrigin; - uint8_t m_level; - uint32_t m_eNpcId; - -}; - -} -} - -#endif diff --git a/src/servers/sapphire_zone/Actor/EventObject.cpp b/src/servers/sapphire_zone/Actor/EventObject.cpp new file mode 100644 index 00000000..1f683ec0 --- /dev/null +++ b/src/servers/sapphire_zone/Actor/EventObject.cpp @@ -0,0 +1,132 @@ +#include "EventObject.h" + +#include "Zone/InstanceContent.h" +#include "Actor/Player.h" + +#include +#include +#include +#include +#include +#include +#include + +using namespace Core::Common; +using namespace Core::Network::Packets; +using namespace Core::Network::Packets::Server; + +extern Core::Logger g_log; + + +Core::Entity::EventObject::EventObject( uint32_t actorId, uint32_t objectId, uint32_t gimmickId, + uint8_t initialState, Common::FFXIVARR_POSITION3 pos, + float rotation, const std::string& givenName ) : + Core::Entity::Actor( ObjKind::EventObj ), + m_gimmickId( gimmickId ), + m_state( initialState ), + m_objectId( objectId ), + m_name( givenName ) +{ + m_id = actorId; + m_pos.x = pos.x; + m_pos.y = pos.y; + m_pos.z = pos.z; + m_rot = rotation; +} + +uint32_t Core::Entity::EventObject::getGimmickId() const +{ + return m_gimmickId; +} + +uint32_t Core::Entity::EventObject::getObjectId() const +{ + return m_objectId; +} + +float Core::Entity::EventObject::getScale() const +{ + return m_scale; +} + +void Core::Entity::EventObject::setScale( float scale ) +{ + m_scale = scale; +} + +Core::Entity::EventObject::OnTalkEventHandler Core::Entity::EventObject::getOnTalkHandler() const +{ + return m_onTalkEventHandler; +} + +void Core::Entity::EventObject::setOnTalkHandler( Core::Entity::EventObject::OnTalkEventHandler handler ) +{ + m_onTalkEventHandler = handler; +} + +void Core::Entity::EventObject::setGimmickId( uint32_t gimmickId ) +{ + m_gimmickId = gimmickId; +} + +uint8_t Core::Entity::EventObject::getState() const +{ + return m_state; +} + +void Core::Entity::EventObject::setState( uint8_t state ) +{ + m_state = state; + + for( const auto& player : m_inRangePlayers ) + { + ZoneChannelPacket< FFXIVIpcActorControl142 > eobjUpdatePacket( getId(), player->getId() ); + eobjUpdatePacket.data().category = Common::ActorControlType::DirectorEObjMod; + eobjUpdatePacket.data().param1 = state; + + player->queuePacket( eobjUpdatePacket ); + } +} + +void Core::Entity::EventObject::setParentInstance( Core::InstanceContentPtr instance ) +{ + m_parentInstance = instance; +} + +Core::InstanceContentPtr Core::Entity::EventObject::getParentInstance() const +{ + return m_parentInstance; +} + +void Core::Entity::EventObject::spawn( Core::Entity::PlayerPtr pTarget ) +{ + auto spawnIndex = pTarget->getNextObjSpawnIndexForActorId( getId( ) ); + if( !pTarget->isObjSpawnIndexValid( spawnIndex ) ) + return; + + g_log.debug( "Spawning EObj: id:" + std::to_string( getId() ) + " name:" + getName() ); + ZoneChannelPacket< FFXIVIpcObjectSpawn > eobjStatePacket( getId(), pTarget->getId() ); + eobjStatePacket.data().spawnIndex = spawnIndex; + eobjStatePacket.data().objKind = getObjKind(); + eobjStatePacket.data().state = getState(); + eobjStatePacket.data().objId = getObjectId(); + eobjStatePacket.data().gimmickId = getGimmickId(); + eobjStatePacket.data().position = getPos(); + eobjStatePacket.data().scale = getScale(); + eobjStatePacket.data().actorId = getId(); + eobjStatePacket.data().rotation = Math::Util::floatToUInt16Rot( getRot() ); + pTarget->queuePacket( eobjStatePacket ); +} + + +void Core::Entity::EventObject::despawn( Core::Entity::PlayerPtr pTarget ) +{ + g_log.debug( "despawn eobj: " + std::to_string( getId() ) ); + + pTarget->freeObjSpawnIndexForActorId( getId( ) ); +} + +const std::string& Core::Entity::EventObject::getName() const +{ + return m_name; +} diff --git a/src/servers/sapphire_zone/Actor/EventObject.h b/src/servers/sapphire_zone/Actor/EventObject.h new file mode 100644 index 00000000..363d5077 --- /dev/null +++ b/src/servers/sapphire_zone/Actor/EventObject.h @@ -0,0 +1,52 @@ +#ifndef SAPPHIRE_INSTANCEOBJECT_H +#define SAPPHIRE_INSTANCEOBJECT_H + +#include "Actor.h" + +namespace Core +{ +namespace Entity +{ + class EventObject : public Actor + { + public: + EventObject( uint32_t actorId, uint32_t objectId, uint32_t gimmickId, uint8_t initialState, Common::FFXIVARR_POSITION3 pos, + float rotation, const std::string& givenName = "none" ); + + using OnTalkEventHandler = std::function< void( Entity::Player&, Entity::EventObjectPtr, InstanceContentPtr, uint64_t ) >; + + uint32_t getGimmickId() const; + void setGimmickId( uint32_t gimmickId ); + + uint8_t getState() const; + void setState( uint8_t state ); + + float getScale() const; + void setScale( float scale ); + + void setOnTalkHandler( OnTalkEventHandler handler ); + OnTalkEventHandler getOnTalkHandler() const; + + uint32_t getObjectId() const; + + const std::string& getName() const; + + InstanceContentPtr getParentInstance() const; + void setParentInstance( InstanceContentPtr instance ); + + void spawn( PlayerPtr pTarget ) override; + void despawn( PlayerPtr pTarget ) override; + + protected: + uint32_t m_gimmickId; + uint32_t m_objectId; + uint8_t m_state; + float m_scale; + std::string m_name; + InstanceContentPtr m_parentInstance; + OnTalkEventHandler m_onTalkEventHandler; + }; +} +} + +#endif //SAPPHIRE_INSTANCEOBJECT_H diff --git a/src/servers/sapphire_zone/Actor/GameObject.cpp b/src/servers/sapphire_zone/Actor/GameObject.cpp deleted file mode 100644 index 16c042ab..00000000 --- a/src/servers/sapphire_zone/Actor/GameObject.cpp +++ /dev/null @@ -1,94 +0,0 @@ -#include "GameObject.h" - -#include "Player.h" -#include "Actor.h" -#include "BattleNpc.h" -#include "EventNpc.h" - -Core::Entity::GameObject::GameObject( ObjKind type ) : - m_objKind( type ) -{ - -} - -uint32_t Core::Entity::GameObject::getId() const -{ - return m_id; -} - -Core::Entity::GameObject::ObjKind Core::Entity::GameObject::getObjKind() const -{ - return m_objKind; -} - -Core::Common::FFXIVARR_POSITION3& Core::Entity::GameObject::getPos() -{ - return m_pos; -} - -void Core::Entity::GameObject::setPos( float x, float y, float z ) -{ - m_pos.x = x; - m_pos.y = y; - m_pos.z = z; -} - -void Core::Entity::GameObject::setPos( const Core::Common::FFXIVARR_POSITION3& pos ) -{ - m_pos = pos; -} - -float Core::Entity::GameObject::getRot() const -{ - return m_rot; -} - -void Core::Entity::GameObject::setRot( float rot ) -{ - m_rot = rot; -} - -bool Core::Entity::GameObject::isPlayer() const -{ - return m_objKind == ObjKind::Player; -} - -bool Core::Entity::GameObject::isBattleNpc() const -{ - return m_objKind == ObjKind::BattleNpc; -} - -bool Core::Entity::GameObject::isEventNpc() const -{ - return m_objKind == ObjKind::EventNpc; -} - -/*! \return pointer to this instance as ActorPtr */ -Core::Entity::ActorPtr Core::Entity::GameObject::getAsActor() -{ - return boost::dynamic_pointer_cast< Entity::Actor, Entity::GameObject >( shared_from_this() ); -} - -/*! \return pointer to this instance as PlayerPtr */ -Core::Entity::PlayerPtr Core::Entity::GameObject::getAsPlayer() -{ - if( !isPlayer() ) - return nullptr; - return boost::dynamic_pointer_cast< Entity::Player, Entity::GameObject >( shared_from_this() ); -} - -/*! \return pointer to this instance as BattleNpcPtr */ -Core::Entity::BattleNpcPtr Core::Entity::GameObject::getAsBattleNpc() -{ - if( !isBattleNpc() ) - return nullptr; - return boost::dynamic_pointer_cast< Entity::BattleNpc, Entity::GameObject >( shared_from_this() ); -} - -/*! \return pointer to this instance as EventNpcPtr */ -Core::Entity::EventNpcPtr Core::Entity::GameObject::getAsEventNpc() -{ - if( !isEventNpc() ) - return nullptr; - return boost::dynamic_pointer_cast< Entity::EventNpc, Entity::GameObject >( shared_from_this() ); -} diff --git a/src/servers/sapphire_zone/Actor/GameObject.h b/src/servers/sapphire_zone/Actor/GameObject.h deleted file mode 100644 index d1f70b3b..00000000 --- a/src/servers/sapphire_zone/Actor/GameObject.h +++ /dev/null @@ -1,82 +0,0 @@ -#ifndef _GAME_OBJECT_H_ -#define _GAME_OBJECT_H_ - -#include -#include - -#include "Forwards.h" -#include -#include -#include - -namespace Core { -namespace Entity { - -/*! -\class GameObject -\brief Base class for all actor/objects - -*/ - class GameObject : public boost::enable_shared_from_this< GameObject > - { - public: - enum ObjKind : uint8_t - { - None = 0x00, - Player = 0x01, - BattleNpc = 0x02, - EventNpc = 0x03, - Treasure = 0x04, - Aetheryte = 0x05, - GatheringPoint = 0x06, - EventObj = 0x07, - Mount = 0x08, - Companion = 0x09, - Retainer = 0x0A, - Area = 0x0B, - Housing = 0x0C, - Cutscene = 0x0D, - CardStand = 0x0E, - }; - - protected: - /*! Position of the object */ - Common::FFXIVARR_POSITION3 m_pos; - /*! Rotation of the object */ - float m_rot; - /*! Id of the actor */ - uint32_t m_id; - /*! Type of the actor */ - ObjKind m_objKind; - - public: - explicit GameObject( ObjKind type ); - virtual ~GameObject() {}; - - virtual void spawn( PlayerPtr pTarget ) {} - virtual void despawn( PlayerPtr pTarget ) {} - - uint32_t getId() const; - - ObjKind getObjKind() const; - - Common::FFXIVARR_POSITION3& getPos(); - void setPos( const Common::FFXIVARR_POSITION3& pos ); - void setPos( float x, float y, float z ); - - float getRot() const; - void setRot( float rot ); - - bool isPlayer() const; - bool isBattleNpc() const; - bool isEventNpc() const; - - ActorPtr getAsActor(); - PlayerPtr getAsPlayer(); - BattleNpcPtr getAsBattleNpc(); - EventNpcPtr getAsEventNpc(); - }; - -} -} -#endif diff --git a/src/servers/sapphire_zone/Actor/InstanceObject.cpp b/src/servers/sapphire_zone/Actor/InstanceObject.cpp deleted file mode 100644 index a182b66b..00000000 --- a/src/servers/sapphire_zone/Actor/InstanceObject.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include "InstanceObject.h" -#include "Zone/InstanceContent.h" - -Core::Entity::InstanceObject::InstanceObject( uint32_t objectId, uint32_t mapLinkId ) : - Core::Entity::GameObject( ObjKind::EventObj ), - m_mapLinkId( mapLinkId ), - m_state( 0 ) -{ - m_id = objectId; -} - -Core::Entity::InstanceObject::InstanceObject( uint32_t objectId, uint32_t mapLinkId, Common::FFXIVARR_POSITION3 pos ) : - InstanceObject( objectId, mapLinkId ) -{ - -} - -uint32_t Core::Entity::InstanceObject::getHierachyId() const -{ - return m_mapLinkId; -} - -void Core::Entity::InstanceObject::setHierachyId( uint32_t hierachyId ) -{ - m_mapLinkId = hierachyId; -} - -uint8_t Core::Entity::InstanceObject::getState() const -{ - return m_state; -} - -void Core::Entity::InstanceObject::setState( uint8_t state ) -{ - m_state = state; - - //m_parentInstance->updateInstanceObj( InstanceObjectPtr( this ) ); -} - -void Core::Entity::InstanceObject::setParentInstance( Core::InstanceContentPtr instance ) -{ - m_parentInstance = instance; -} - -Core::InstanceContentPtr Core::Entity::InstanceObject::getParentInstance() const -{ - return m_parentInstance; -} \ No newline at end of file diff --git a/src/servers/sapphire_zone/Actor/InstanceObject.h b/src/servers/sapphire_zone/Actor/InstanceObject.h deleted file mode 100644 index 6bd61e0f..00000000 --- a/src/servers/sapphire_zone/Actor/InstanceObject.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef SAPPHIRE_INSTANCEOBJECT_H -#define SAPPHIRE_INSTANCEOBJECT_H - -#include "GameObject.h" - -namespace Core -{ -namespace Entity -{ - class InstanceObject : public GameObject - { - public: - InstanceObject( uint32_t objectId, uint32_t mapLinkId ); - InstanceObject( uint32_t objectId, uint32_t mapLinkId, Common::FFXIVARR_POSITION3 pos ); - - uint32_t getHierachyId() const; - void setHierachyId( uint32_t hierachyId ); - - uint8_t getState() const; - void setState( uint8_t state ); - - InstanceContentPtr getParentInstance() const; - void setParentInstance( InstanceContentPtr instance ); - - protected: - uint32_t m_mapLinkId; - uint8_t m_state; - InstanceContentPtr m_parentInstance; - }; -} -} - -#endif //SAPPHIRE_INSTANCEOBJECT_H diff --git a/src/servers/sapphire_zone/Actor/Player.cpp b/src/servers/sapphire_zone/Actor/Player.cpp index 0c0b619a..2b5ae09f 100644 --- a/src/servers/sapphire_zone/Actor/Player.cpp +++ b/src/servers/sapphire_zone/Actor/Player.cpp @@ -11,7 +11,6 @@ #include "Session.h" #include "Player.h" -#include "BattleNpc.h" #include "Zone/TerritoryMgr.h" #include "Zone/Zone.h" @@ -20,6 +19,7 @@ #include "Network/GameConnection.h" #include "Network/PacketWrappers/ActorControlPacket142.h" #include "Network/PacketWrappers/ActorControlPacket143.h" +#include "Network/PacketWrappers/ActorControlPacket144.h" #include "Network/PacketWrappers/InitUIPacket.h" #include "Network/PacketWrappers/ServerNoticePacket.h" #include "Network/PacketWrappers/ChatPacket.h" @@ -40,6 +40,7 @@ #include "Event/EventHandler.h" #include "Action/Action.h" +#include "Action/ActionTeleport.h" #include "Action/EventAction.h" #include "Action/EventItemAction.h" @@ -57,7 +58,7 @@ using namespace Core::Network::Packets::Server; // player constructor Core::Entity::Player::Player() : - Actor( ObjKind::Player ), + Chara( ObjKind::Player ), m_lastWrite( 0 ), m_lastPing( 0 ), m_bIsLogin( false ), @@ -75,7 +76,8 @@ Core::Entity::Player::Player() : m_bAutoattack( false ), m_markedForRemoval( false ), m_mount( 0 ), - m_directorInitialized( false ) + m_directorInitialized( false ), + m_onEnterEventDone( false ) { m_id = 0; m_currentStance = Stance::Passive; @@ -83,6 +85,7 @@ Core::Entity::Player::Player() : m_queuedZoneing = nullptr; m_status = ActorStatus::Idle; m_invincibilityType = InvincibilityType::InvincibilityNone; + m_modelType = ModelType::Human; memset( m_questTracking, 0, sizeof( m_questTracking ) ); memset( m_name, 0, sizeof( m_name ) ); @@ -90,12 +93,22 @@ Core::Entity::Player::Player() : memset( m_searchMessage, 0, sizeof( m_searchMessage ) ); memset( m_classArray, 0, sizeof( m_classArray ) ); memset( m_expArray, 0, sizeof( m_expArray ) ); + + m_objSpawnIndexAllocator.init( MAX_DISPLAYED_EOBJS ); + m_actorSpawnIndexAllocator.init( MAX_DISPLAYED_ACTORS, true ); } Core::Entity::Player::~Player() { } +void Core::Entity::Player::injectPacket( std::string path ) +{ + auto session = g_framework.getServerZone().getSession( getId() ); + if( session ) + session->getZoneConnection()->injectPacket( path, *this ); +} + // TODO: add a proper calculation based on race / job / level / gear uint32_t Core::Entity::Player::getMaxHp() { @@ -181,7 +194,7 @@ Core::Common::OnlineStatus Core::Entity::Player::getOnlineStatus() if( m_onlineStatus & rpMask ) status = OnlineStatus::Roleplaying; - if( hasStateFlag( PlayerStateFlag::WatchingCutscene ) || hasStateFlag( PlayerStateFlag::WatchingCutscene1 ) ) + if( hasStateFlag( PlayerStateFlag::WatchingCutscene ) ) status = OnlineStatus::ViewingCutscene; // TODO: add all the logic for returning the proper online status, there probably is a better way for this alltogether @@ -252,7 +265,7 @@ void Core::Entity::Player::calculateStats() } -void Core::Entity::Player::setAutoattack(bool mode) +void Core::Entity::Player::setAutoattack( bool mode ) { m_bAutoattack = mode; m_lastAttack = Util::getTimeMs(); @@ -307,8 +320,7 @@ void Core::Entity::Player::teleport( uint16_t aetheryteId, uint8_t type ) } setStateFlag( PlayerStateFlag::BetweenAreas ); - - auto z_pos = g_framework.getTerritoryMgr().getTerritoryPosition( data->territory ); + auto targetPos = g_framework.getTerritoryMgr().getTerritoryPosition( data->levelId ); Common::FFXIVARR_POSITION3 pos; pos.x = 0; @@ -316,10 +328,10 @@ void Core::Entity::Player::teleport( uint16_t aetheryteId, uint8_t type ) pos.z = 0; float rot = 0; - if( z_pos != nullptr ) + if( targetPos != nullptr ) { - pos = z_pos->getTargetPosition(); - rot = z_pos->getTargetRotation(); + pos = targetPos->getTargetPosition(); + rot = targetPos->getTargetRotation(); } sendDebug( "Teleport: " + g_framework.getExdDataGen().get< Core::Data::PlaceName >( data->placeName )->name + " " + @@ -365,6 +377,7 @@ void Core::Entity::Player::returnToHomepoint() void Core::Entity::Player::setZone( uint32_t zoneId ) { + m_onEnterEventDone = false; if( !g_framework.getTerritoryMgr().movePlayer( zoneId, getAsPlayer() ) ) { // todo: this will require proper handling, for now just return the player to their previous area @@ -381,6 +394,7 @@ void Core::Entity::Player::setZone( uint32_t zoneId ) bool Core::Entity::Player::setInstance( uint32_t instanceContentId ) { + m_onEnterEventDone = false; auto instance = g_framework.getTerritoryMgr().getInstanceZonePtr( instanceContentId ); if( !instance ) return false; @@ -390,6 +404,7 @@ bool Core::Entity::Player::setInstance( uint32_t instanceContentId ) bool Core::Entity::Player::setInstance( ZonePtr instance ) { + m_onEnterEventDone = false; if( !instance ) return false; @@ -440,43 +455,27 @@ uint8_t Core::Entity::Player::getGender() const void Core::Entity::Player::initSpawnIdQueue() { - while( !m_freeSpawnIdQueue.empty() ) - { - m_freeSpawnIdQueue.pop(); - } - - for( int32_t i = 1; i < MAX_DISPLAYED_ACTORS; i++ ) - { - m_freeSpawnIdQueue.push( i ); - } + m_actorSpawnIndexAllocator.freeAllSpawnIndexes(); } uint8_t Core::Entity::Player::getSpawnIdForActorId( uint32_t actorId ) { - if( m_freeSpawnIdQueue.empty() ) - return 0; - - uint8_t spawnId = m_freeSpawnIdQueue.front(); - m_freeSpawnIdQueue.pop(); - m_playerIdToSpawnIdMap[actorId] = spawnId; - return spawnId; + return m_actorSpawnIndexAllocator.getNextFreeSpawnIndex( actorId ); } -void Core::Entity::Player::assignSpawnIdToPlayerId( uint32_t actorId, uint8_t spawnId ) +bool Core::Entity::Player::isActorSpawnIdValid( uint8_t spawnIndex ) { - m_playerIdToSpawnIdMap[actorId] = spawnId; + return m_actorSpawnIndexAllocator.isSpawnIndexValid( spawnIndex ); } void Core::Entity::Player::registerAetheryte( uint8_t aetheryteId ) { - uint16_t index; uint8_t value; Util::valueToFlagByteIndexValue( aetheryteId, value, index ); m_aetheryte[index] |= value; queuePacket( ActorControlPacket143( getId(), LearnTeleport, aetheryteId, 1 ) ); - } bool Core::Entity::Player::isAetheryteRegistered( uint8_t aetheryteId ) const @@ -639,7 +638,7 @@ void Core::Entity::Player::gainLevel() ZoneChannelPacket< FFXIVIpcStatusEffectList > effectListPacket( getId() ); effectListPacket.data().classId = static_cast< uint8_t > ( getClass() ); - effectListPacket.data().classId1 = static_cast< uint8_t > ( getClass() ); + effectListPacket.data().level1 = getLevel(); effectListPacket.data().level = getLevel(); effectListPacket.data().current_hp = getMaxHp(); effectListPacket.data().current_mp = getMaxMp(); @@ -654,23 +653,12 @@ void Core::Entity::Player::gainLevel() ZoneChannelPacket< FFXIVIpcUpdateClassInfo > classInfoPacket( getId() ); classInfoPacket.data().classId = static_cast< uint8_t > ( getClass() ); - classInfoPacket.data().classId1 = static_cast< uint8_t > ( getClass() ); + classInfoPacket.data().level1 = getLevel(); classInfoPacket.data().level = getLevel(); classInfoPacket.data().nextLevelIndex = getLevel(); classInfoPacket.data().currentExp = getExp(); queuePacket( classInfoPacket ); - - - - - - -} - -void Core::Entity::Player::unlock() -{ - queuePacket( PlayerStateFlagsPacket( *getAsPlayer(), PlayerStateFlagList{} ) ); } void Core::Entity::Player::sendStatusUpdate( bool toSelf ) @@ -831,6 +819,8 @@ void Core::Entity::Player::despawn( Entity::PlayerPtr pTarget ) { auto pPlayer = pTarget; + g_framework.getLogger().debug( "despawning " + getName() + " for " + pTarget->getName() ); + pPlayer->freePlayerSpawnId( getId() ); pPlayer->queuePacket( ActorControlPacket143( getId(), DespawnZoneScreenMsg, 0x04, getId(), 0x01 ) ); @@ -839,7 +829,7 @@ void Core::Entity::Player::despawn( Entity::PlayerPtr pTarget ) Core::Entity::ActorPtr Core::Entity::Player::lookupTargetById( uint64_t targetId ) { ActorPtr targetActor; - auto inRange = getInRangeActors( true ); + auto inRange = getInRangeActors(true); for( auto actor : inRange ) { if( actor->getId() == targetId ) @@ -989,7 +979,7 @@ void Core::Entity::Player::update( int64_t currTime ) setActorPosPacket.data().y = targetPos.y; setActorPosPacket.data().z = targetPos.z; sendToInRangeSet( setActorPosPacket, true ); - setPosition( targetPos ); + setPos( targetPos ); } m_queuedZoneing.reset(); return; @@ -1007,14 +997,14 @@ void Core::Entity::Player::update( int64_t currTime ) if( !checkAction() ) { - if( m_targetId && m_currentStance == Entity::Actor::Stance::Active && isAutoattackOn() ) + if( m_targetId && m_currentStance == Entity::Chara::Stance::Active && isAutoattackOn() ) { auto mainWeap = m_pInventory->getItemAt( Inventory::GearSet0, Inventory::EquipSlot::MainHand ); // @TODO i dislike this, iterating over all in range actors when you already know the id of the actor you need... - for( auto actor : m_inRangeActors ) + for( auto actor : m_inRangeActor ) { - if( actor->getId() == m_targetId && actor->isAlive() && mainWeap ) + if( actor->getId() == m_targetId && actor->getAsChara()->isAlive() && mainWeap ) { // default autoattack range // TODO make this dependant on bnpc size @@ -1034,7 +1024,7 @@ void Core::Entity::Player::update( int64_t currTime ) if( ( currTime - m_lastAttack ) > mainWeap->getDelay() ) { m_lastAttack = currTime; - autoAttack( actor ); + autoAttack( actor->getAsChara() ); } } @@ -1059,9 +1049,7 @@ void Core::Entity::Player::onMobKill( uint16_t nameId ) void Core::Entity::Player::freePlayerSpawnId( uint32_t actorId ) { - uint8_t spawnId = m_playerIdToSpawnIdMap[actorId]; - m_playerIdToSpawnIdMap.erase( actorId ); - m_freeSpawnIdQueue.push( spawnId ); + auto spawnId = m_actorSpawnIndexAllocator.freeUsedSpawnIndex( actorId ); ZoneChannelPacket< FFXIVIpcActorFreeSpawn > freeActorSpawnPacket( getId() ); freeActorSpawnPacket.data().actorId = actorId; @@ -1206,7 +1194,7 @@ void Core::Entity::Player::performZoning( uint16_t zoneId, const Common::FFXIVAR m_pos = pos; m_zoneId = zoneId; m_bMarkedForZoning = true; - setRotation( rotation ); + setRot( rotation ); setZone( zoneId ); } @@ -1273,64 +1261,6 @@ void Core::Entity::Player::updateHowtosSeen( uint32_t howToId ) m_howTo[index] |= value; } - -void Core::Entity::Player::onMobAggro( BattleNpcPtr pBNpc ) -{ - hateListAdd( pBNpc ); - - queuePacket( ActorControlPacket142( getId(), ToggleAggro, 1 ) ); -} - -void Core::Entity::Player::onMobDeaggro( BattleNpcPtr pBNpc ) -{ - hateListRemove( pBNpc ); - - if( m_actorIdTohateSlotMap.empty() ) - queuePacket( ActorControlPacket142( getId(), ToggleAggro ) ); -} - -void Core::Entity::Player::hateListAdd( BattleNpcPtr pBNpc ) - -{ - if( m_freeHateSlotQueue.empty() ) - return; - uint8_t hateId = m_freeHateSlotQueue.front(); - m_freeHateSlotQueue.pop(); - m_actorIdTohateSlotMap[pBNpc->getId()] = hateId; - sendHateList(); - -} - -void Core::Entity::Player::hateListRemove( BattleNpcPtr pBNpc ) -{ - - auto it = m_actorIdTohateSlotMap.begin(); - for( ; it != m_actorIdTohateSlotMap.end(); ++it ) - { - if( it->first == pBNpc->getId() ) - { - uint8_t hateSlot = it->second; - m_freeHateSlotQueue.push( hateSlot ); - m_actorIdTohateSlotMap.erase( it ); - sendHateList(); - - return; - } - } -} - -bool Core::Entity::Player::hateListHasMob( BattleNpcPtr pBNpc ) -{ - - auto it = m_actorIdTohateSlotMap.begin(); - for( ; it != m_actorIdTohateSlotMap.end(); ++it ) - { - if( it->first == pBNpc->getId() ) - return true; - } - return false; -} - void Core::Entity::Player::initHateSlotQueue() { m_freeHateSlotQueue = std::queue< uint8_t >(); @@ -1366,6 +1296,11 @@ uint8_t* Core::Entity::Player::getTitleList() return m_titleList; } +const uint8_t* Core::Entity::Player::getTitleList() const +{ + return m_titleList; +} + uint16_t Core::Entity::Player::getTitle() const { return m_activeTitle; @@ -1410,7 +1345,7 @@ uint8_t Core::Entity::Player::getEquipDisplayFlags() const void Core::Entity::Player::mount( uint32_t id ) { m_mount = id; - sendToInRangeSet( ActorControlPacket142( getId(), ActorControlType::SetStatus, static_cast< uint8_t >( Entity::Actor::ActorStatus::Mounted )), true ); + sendToInRangeSet( ActorControlPacket142( getId(), ActorControlType::SetStatus, static_cast< uint8_t >( Entity::Chara::ActorStatus::Mounted )), true ); sendToInRangeSet( ActorControlPacket143( getId(), 0x39e, 12 ), true ); //? ZoneChannelPacket< FFXIVIpcMount > mountPacket( getId() ); @@ -1421,7 +1356,7 @@ void Core::Entity::Player::mount( uint32_t id ) void Core::Entity::Player::dismount() { sendToInRangeSet( ActorControlPacket142( getId(), ActorControlType::SetStatus, - static_cast< uint8_t >( Entity::Actor::ActorStatus::Idle )), true ); + static_cast< uint8_t >( Entity::Chara::ActorStatus::Idle )), true ); sendToInRangeSet( ActorControlPacket143( getId(), ActorControlType::Dismount, 1 ), true ); m_mount = 0; } @@ -1431,7 +1366,7 @@ uint8_t Core::Entity::Player::getCurrentMount() const return m_mount; } -void Core::Entity::Player::autoAttack( ActorPtr pTarget ) +void Core::Entity::Player::autoAttack( CharaPtr pTarget ) { auto mainWeap = m_pInventory->getItemAt( Inventory::GearSet0, @@ -1444,11 +1379,9 @@ void Core::Entity::Player::autoAttack( ActorPtr pTarget ) uint32_t damage = static_cast< uint32_t >( mainWeap->getAutoAttackDmg() ); uint32_t variation = 0 + rand() % 3; - if( getClass() == ClassJob::Machinist || - getClass() == ClassJob::Bard || - getClass() == ClassJob::Archer ) + if( getClass() == ClassJob::Machinist || getClass() == ClassJob::Bard || getClass() == ClassJob::Archer ) { - ZoneChannelPacket< FFXIVIpcEffect > effectPacket(getId()); + ZoneChannelPacket< FFXIVIpcEffect > effectPacket( getId() ); effectPacket.data().targetId = pTarget->getId(); effectPacket.data().actionAnimationId = 8; // effectPacket.data().unknown_2 = variation; @@ -1456,7 +1389,7 @@ void Core::Entity::Player::autoAttack( ActorPtr pTarget ) effectPacket.data().unknown_61 = 1; effectPacket.data().unknown_62 = 1; effectPacket.data().actionTextId = 8; - effectPacket.data().rotation = Math::Util::floatToUInt16Rot(getRotation()); + effectPacket.data().rotation = Math::Util::floatToUInt16Rot( getRot() ); effectPacket.data().effectTargetId = pTarget->getId(); effectPacket.data().effectTarget = pTarget->getId(); effectPacket.data().effects[0].value = damage; @@ -1469,7 +1402,7 @@ void Core::Entity::Player::autoAttack( ActorPtr pTarget ) else { - ZoneChannelPacket< FFXIVIpcEffect > effectPacket(getId()); + ZoneChannelPacket< FFXIVIpcEffect > effectPacket( getId() ); effectPacket.data().targetId = pTarget->getId(); effectPacket.data().actionAnimationId = 7; // effectPacket.data().unknown_2 = variation; @@ -1477,7 +1410,7 @@ void Core::Entity::Player::autoAttack( ActorPtr pTarget ) effectPacket.data().unknown_61 = 1; effectPacket.data().unknown_62 = 1; effectPacket.data().actionTextId = 7; - effectPacket.data().rotation = Math::Util::floatToUInt16Rot(getRotation()); + effectPacket.data().rotation = Math::Util::floatToUInt16Rot( getRot() ); effectPacket.data().effectTarget = pTarget->getId(); effectPacket.data().effects[0].value = damage; effectPacket.data().effects[0].effectType = Common::ActionEffectType::Damage; @@ -1550,18 +1483,20 @@ void Core::Entity::Player::setEorzeaTimeOffset( uint64_t timestamp ) queuePacket( packet ); } -void Player::setTerritoryId( uint32_t territoryId ) +void Core::Entity::Player::setTerritoryId( uint32_t territoryId ) { m_zoneId = territoryId; } -uint32_t Player::getTerritoryId() const +uint32_t Core::Entity::Player::getTerritoryId() const { return m_zoneId; } -void Player::sendZonePackets() +void Core::Entity::Player::sendZonePackets() { + getCurrentZone()->onBeforePlayerZoneIn( *this ); + ZoneChannelPacket< FFXIVIpcInit > initPacket( getId() ); initPacket.data().charId = getId(); queuePacket( initPacket ); @@ -1570,7 +1505,7 @@ void Player::sendZonePackets() if( isLogin() ) { - queuePacket(ActorControlPacket143( getId(), SetCharaGearParamUI, m_equipDisplayFlags, 1 ) ); + queuePacket( ActorControlPacket143( getId(), SetCharaGearParamUI, m_equipDisplayFlags, 1 ) ); } // set flags, will be reset automatically by zoning ( only on client side though ) @@ -1627,17 +1562,135 @@ void Player::sendZonePackets() if( getLastPing() == 0 ) sendQuestInfo(); - getCurrentZone()->onEnterTerritory( *this ); + getCurrentZone()->onPlayerZoneIn( *this ); m_bMarkedForZoning = false; } -void Player::setDirectorInitialized( bool isInitialized ) +void Core::Entity::Player::setDirectorInitialized( bool isInitialized ) { m_directorInitialized = isInitialized; } -bool Player::isDirectorInitialized() const +bool Core::Entity::Player::isDirectorInitialized() const { return m_directorInitialized; } + +void Core::Entity::Player::sendTitleList() +{ + ZoneChannelPacket< FFXIVIpcPlayerTitleList > titleListPacket( getId() ); + memcpy( titleListPacket.data().titleList, getTitleList(), sizeof( titleListPacket.data().titleList ) ); + + queuePacket( titleListPacket ); +} + +void Core::Entity::Player::finishZoning() +{ + switch( getZoningType() ) + { + case ZoneingType::None: + sendToInRangeSet( ActorControlPacket143( getId(), ZoneIn, 0x01 ), true ); + break; + + case ZoneingType::Teleport: + sendToInRangeSet( ActorControlPacket143( getId(), ZoneIn, 0x01, 0, 0, 110 ), true ); + break; + + case ZoneingType::Return: + case ZoneingType::ReturnDead: + { + if( getStatus() == Entity::Chara::ActorStatus::Dead ) + { + resetHp(); + resetMp(); + setStatus( Entity::Chara::ActorStatus::Idle ); + + sendToInRangeSet( ActorControlPacket143( getId(), ZoneIn, 0x01, 0x01, 0, 111 ), true ); + sendToInRangeSet( ActorControlPacket142( getId(), SetStatus, + static_cast< uint8_t >( Entity::Chara::ActorStatus::Idle ) ), true ); + } + else + sendToInRangeSet( ActorControlPacket143( getId(), ZoneIn, 0x01, 0x00, 0, 111 ), true ); + } + break; + + case ZoneingType::FadeIn: + break; + } + + setZoningType( Common::ZoneingType::None ); + unsetStateFlag( PlayerStateFlag::BetweenAreas ); +} + +void Core::Entity::Player::emote( uint32_t emoteId, uint64_t targetId ) +{ + sendToInRangeSet( ActorControlPacket144( getId(), ActorControlType::Emote, emoteId, 0, 0, 0, targetId ) ); +} + +void Core::Entity::Player::teleportQuery( uint16_t aetheryteId ) +{ + // TODO: only register this action if enough gil is in possession + auto& exdDataGen = g_framework.getExdDataGen(); + auto targetAetheryte = exdDataGen.get< Core::Data::Aetheryte >( aetheryteId ); + + if( targetAetheryte ) + { + auto fromAetheryte = exdDataGen.get< Core::Data::Aetheryte >( + exdDataGen.get< Core::Data::TerritoryType >( getZoneId() )->aetheryte ); + + // calculate cost - does not apply for favorite points or homepoints neither checks for aether tickets + auto cost = static_cast< uint16_t > ( ( sqrt( pow( fromAetheryte->aetherstreamX - targetAetheryte->aetherstreamX, 2 ) + + pow( fromAetheryte->aetherstreamY - targetAetheryte->aetherstreamY, 2 ) ) / 2 ) + 100 ); + + // cap at 999 gil + cost = cost > uint16_t{999} ? uint16_t{999} : cost; + + bool insufficientGil = getCurrency( Inventory::CurrencyType::Gil ) < cost; + // TODO: figure out what param1 really does + queuePacket( ActorControlPacket143( getId(), TeleportStart, insufficientGil ? 2 : 0, aetheryteId ) ); + + if( !insufficientGil ) + { + Action::ActionPtr pActionTeleport; + pActionTeleport = Action::make_ActionTeleport( getAsPlayer(), aetheryteId, cost ); + setCurrentAction( pActionTeleport ); + } + } +} + +uint8_t Core::Entity::Player::getNextObjSpawnIndexForActorId( uint32_t actorId ) +{ + return m_objSpawnIndexAllocator.getNextFreeSpawnIndex( actorId ); +} + +void Core::Entity::Player::resetObjSpawnIndex() +{ + m_objSpawnIndexAllocator.freeAllSpawnIndexes(); +} + +void Core::Entity::Player::freeObjSpawnIndexForActorId( uint32_t actorId ) +{ + auto spawnId = m_objSpawnIndexAllocator.freeUsedSpawnIndex( actorId ); + + ZoneChannelPacket< FFXIVIpcObjectDespawn > freeObjectSpawnPacket( getId() ); + freeObjectSpawnPacket.data().spawnIndex = spawnId; + queuePacket( freeObjectSpawnPacket ); +} + +bool Core::Entity::Player::isObjSpawnIndexValid( uint8_t index ) +{ + return m_objSpawnIndexAllocator.isSpawnIndexValid( index ); +} + +void Core::Entity::Player::setOnEnterEventDone( bool isDone ) +{ + m_onEnterEventDone = isDone; +} + +bool Core::Entity::Player::isOnEnterEventDone() const +{ + return m_onEnterEventDone; +} + + diff --git a/src/servers/sapphire_zone/Actor/Player.h b/src/servers/sapphire_zone/Actor/Player.h index cd2b549f..8852dc7e 100644 --- a/src/servers/sapphire_zone/Actor/Player.h +++ b/src/servers/sapphire_zone/Actor/Player.h @@ -8,8 +8,9 @@ #include #include #include +#include -#include "Actor.h" +#include "Chara.h" #include "Inventory/Inventory.h" #include "Event/EventHandler.h" #include @@ -37,7 +38,7 @@ struct QueuedZoning * Inheriting from Actor * */ -class Player : public Actor +class Player : public Chara { public: /*! Contructor */ @@ -46,7 +47,9 @@ public: /*! Destructor */ ~Player(); - void autoAttack( ActorPtr pTarget ) override; + void autoAttack( CharaPtr pTarget ) override; + + void injectPacket( std::string path ); // EventHandlers ////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -55,9 +58,12 @@ public: /*! start an event item action */ void eventItemActionStart( uint32_t eventId, uint32_t action, ActionCallback finishCallback, ActionCallback interruptCallback, uint64_t additional ); /*! start/register a normal event */ - void eventStart( uint64_t actorId, uint32_t eventId, Event::EventHandler::EventType eventParam, uint8_t eventParam1, uint32_t eventParam2 ); + void eventStart( uint64_t actorId, uint32_t eventId, Event::EventHandler::EventType eventParam, uint8_t eventParam1, uint32_t eventParam2, uint32_t contentId = 0 ); /*! play a subevent */ void eventPlay( uint32_t eventId, uint32_t scene, uint32_t flags, uint32_t eventParam2, uint32_t eventParam3 ); + + void directorPlayScene( uint32_t eventId, uint32_t scene, uint32_t flags, uint32_t eventParam2, uint32_t eventParam3 ); + /*! play a subevent */ void eventPlay( uint32_t eventId, uint32_t scene, uint32_t flags, uint32_t eventParam2, uint32_t eventParam3, Event::EventHandler::SceneReturnCallback eventReturnCallback ); @@ -347,16 +353,21 @@ public: uint64_t getOnlineStatusMask() const; /*! perform a teleport of a specified type ( teleport,return,aethernet ) */ void teleport( uint16_t aetheryteId, uint8_t type = 1 ); + /*! query teleport of a specified type */ + void teleportQuery( uint16_t aetheryteId ); /*! prepares zoning / fades out the screen */ void prepareZoning( uint16_t targetZone, bool fadeOut, uint8_t fadoutTime = 0, uint16_t animation = 0 ); /*! get player's title list (available titles) */ uint8_t* getTitleList(); + const uint8_t* getTitleList() const; /*! get player's active title */ uint16_t getTitle() const; /*! add title to player title list */ void addTitle( uint16_t titleId ); /*! change player's active title */ void setTitle( uint16_t titleId ); + /*! send the players title list */ + void sendTitleList(); /*! change gear param state */ void setEquipDisplayFlags( uint8_t state ); /*! get gear param state */ @@ -423,10 +434,10 @@ public: void initSpawnIdQueue(); /*! get the spawn id mapped to a specific actorId */ uint8_t getSpawnIdForActorId( uint32_t actorId ); - /*! assigns the given spawnId to the actor */ - void assignSpawnIdToPlayerId( uint32_t actorId, uint8_t spawnId ); /*! frees the spawnId assigned to the given actor */ void freePlayerSpawnId( uint32_t actorId ); + /*! checks if the given spawn id is valid */ + bool isActorSpawnIdValid( uint8_t spawnId ); /*! send spawn packets to pTarget */ void spawn( PlayerPtr pTarget ) override; /*! send despawn packets to pTarget */ @@ -444,8 +455,6 @@ public: bool hasStateFlag( Common::PlayerStateFlag flag ) const; /* reset a specified flag */ void unsetStateFlag( Common::PlayerStateFlag flag ); - /* helper function, send an empty state flag update */ - void unlock(); // Player Session Handling ////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -494,6 +503,10 @@ public: /*! return true if the player is marked for zoning */ bool isMarkedForZoning() const; + void emote( uint32_t emoteId, uint64_t targetId ); + + void finishZoning(); + void sendZonePackets(); Common::ZoneingType getZoningType() const; @@ -513,14 +526,7 @@ public: // Player Battle Handling ////////////////////////////////////////////////////////////////////////////////////////////////////// - void onMobAggro( BattleNpcPtr pBNpc ); - void onMobDeaggro( BattleNpcPtr pBNpc ); - void initHateSlotQueue(); - void hateListAdd( BattleNpcPtr pBNpc ); - void hateListRemove( BattleNpcPtr pBNpc ); - - bool hateListHasMob( BattleNpcPtr pBNpc ); void sendHateList(); @@ -566,6 +572,18 @@ public: void setMarkedForRemoval(); bool isMarkedForRemoval() const; + void setOnEnterEventDone( bool isDone ); + bool isOnEnterEventDone() const; + + /*! gets the next available obj count */ + uint8_t getNextObjSpawnIndexForActorId( uint32_t actorId ); + /*! resets the players obj count */ + void resetObjSpawnIndex(); + /*! frees an obj count to be used by another eobj */ + void freeObjSpawnIndexForActorId( uint32_t actorId ); + /*! checks if a spawn index is valid */ + bool isObjSpawnIndexValid( uint8_t index ); + private: uint32_t m_lastWrite; uint32_t m_lastPing; @@ -580,6 +598,8 @@ private: bool m_directorInitialized; + bool m_onEnterEventDone; + private: Common::FFXIVARR_POSITION3 m_prevPos; @@ -636,8 +656,6 @@ private: std::map< uint32_t, Event::EventHandlerPtr > m_eventHandlerMap; - std::map< uint32_t, uint8_t > m_playerIdToSpawnIdMap; // maps player to spawn id - std::queue< uint8_t > m_freeSpawnIdQueue; // queue with spawn ids free to be assigned std::queue< uint8_t > m_freeHateSlotQueue; // queue with "hate slots" free to be assigned std::map< uint32_t, uint8_t > m_actorIdTohateSlotMap; @@ -646,7 +664,7 @@ private: boost::shared_ptr< Common::QuestActive > m_activeQuests[30]; int16_t m_questTracking[5]; - uint8_t m_stateFlags[7]; + uint8_t m_stateFlags[12]; uint8_t m_gmRank; uint16_t zoneId; @@ -680,6 +698,9 @@ private: uint32_t m_cfPenaltyUntil; // unix time uint8_t m_mount; + + Util::SpawnIndexAllocator< uint8_t > m_objSpawnIndexAllocator; + Util::SpawnIndexAllocator< uint8_t > m_actorSpawnIndexAllocator; }; } diff --git a/src/servers/sapphire_zone/Actor/PlayerEvent.cpp b/src/servers/sapphire_zone/Actor/PlayerEvent.cpp index 0fa9a2f8..c9849eab 100644 --- a/src/servers/sapphire_zone/Actor/PlayerEvent.cpp +++ b/src/servers/sapphire_zone/Actor/PlayerEvent.cpp @@ -11,6 +11,7 @@ #include "Network/PacketWrappers/EventStartPacket.h" #include "Network/PacketWrappers/EventPlayPacket.h" #include "Network/PacketWrappers/EventFinishPacket.h" +#include "Network/PacketWrappers/DirectorPlayScenePacket.h" #include "Action/EventAction.h" #include "Action/EventItemAction.h" @@ -74,18 +75,39 @@ void Core::Entity::Player::checkEvent( uint32_t eventId ) } +void Core::Entity::Player::directorPlayScene( uint32_t eventId, uint32_t scene, uint32_t flags, uint32_t eventParam2, + uint32_t eventParam3 ) +{ + if( flags & 0x02 ) + setStateFlag( PlayerStateFlag::WatchingCutscene ); + + auto pEvent = getEvent( eventId ); + if( !pEvent ) + { + g_framework.getLogger().error( "Could not find event " + std::to_string( eventId ) + ", event has not been started!" ); + return; + } + + pEvent->setPlayedScene( true ); + pEvent->setEventReturnCallback( nullptr ); + DirectorPlayScenePacket eventPlay( getId(), getId(), pEvent->getId(), + scene, flags, eventParam2, eventParam3 ); + + queuePacket( eventPlay ); +} + void Core::Entity::Player::eventStart( uint64_t actorId, uint32_t eventId, Event::EventHandler::EventType eventType, uint8_t eventParam1, - uint32_t eventParam2 ) + uint32_t eventParam2, uint32_t contentId ) { auto newEvent = Event::make_EventHandler( this, actorId, eventId, eventType, eventParam2 ); addEvent( newEvent ); - setStateFlag( PlayerStateFlag::Occupied2 ); + setStateFlag( PlayerStateFlag::InNpcEvent ); - EventStartPacket eventStart( getId(), actorId, eventId, eventType, eventParam1, eventParam2 ); + EventStartPacket eventStart( getId(), actorId, eventId, eventType, eventParam1, eventParam2, contentId ); queuePacket( eventStart ); @@ -217,7 +239,7 @@ void Core::Entity::Player::eventFinish( uint32_t eventId, uint32_t freePlayer ) removeEvent( pEvent->getId() ); if( freePlayer == 1 ) - unsetStateFlag( PlayerStateFlag::Occupied2 ); + unsetStateFlag( PlayerStateFlag::InNpcEvent ); } void Core::Entity::Player::eventActionStart( uint32_t eventId, @@ -226,7 +248,7 @@ void Core::Entity::Player::eventActionStart( uint32_t eventId, ActionCallback interruptCallback, uint64_t additional ) { - auto pEventAction = Action::make_EventAction( getAsActor(), eventId, action, + auto pEventAction = Action::make_EventAction( getAsChara(), eventId, action, finishCallback, interruptCallback, additional ); setCurrentAction( pEventAction ); @@ -256,7 +278,7 @@ void Core::Entity::Player::eventItemActionStart( uint32_t eventId, ActionCallback interruptCallback, uint64_t additional ) { - Action::ActionPtr pEventItemAction = Action::make_EventItemAction( getAsActor(), eventId, action, + Action::ActionPtr pEventItemAction = Action::make_EventItemAction( getAsChara(), eventId, action, finishCallback, interruptCallback, additional ); setCurrentAction( pEventItemAction ); @@ -289,7 +311,6 @@ void Core::Entity::Player::onDeath() } - // TODO: slightly ugly here and way too static. Needs too be done properly void Core::Entity::Player::onTick() { diff --git a/src/servers/sapphire_zone/Actor/PlayerSql.cpp b/src/servers/sapphire_zone/Actor/PlayerSql.cpp index 96ec864a..aa0e6709 100644 --- a/src/servers/sapphire_zone/Actor/PlayerSql.cpp +++ b/src/servers/sapphire_zone/Actor/PlayerSql.cpp @@ -58,7 +58,7 @@ bool Core::Entity::Player::load( uint32_t charId, SessionPtr pSession ) m_pos.x = res->getFloat( "PosX" ); m_pos.y = res->getFloat( "PosY" ); m_pos.z = res->getFloat( "PosZ" ); - setRotation( res->getFloat( "PosR" ) ); + setRot( res->getFloat( "PosR" ) ); m_prevPos.x = res->getFloat( "OPosX" ); m_prevPos.y = res->getFloat( "OPosY" ); @@ -79,7 +79,7 @@ bool Core::Entity::Player::load( uint32_t charId, SessionPtr pSession ) m_pos.x = m_prevPos.x; m_pos.y = m_prevPos.y; m_pos.z = m_prevPos.z; - setRotation( m_prevRot ); + setRot( m_prevRot ); pCurrZone = g_framework.getTerritoryMgr().getZoneByTerriId( zoneId ); } } @@ -104,7 +104,7 @@ bool Core::Entity::Player::load( uint32_t charId, SessionPtr pSession ) m_pos.x = 0.0f; m_pos.y = 0.0f; m_pos.z = 0.0f; - setRotation( 0.0f ); + setRot( 0.0f ); } // Stats @@ -230,9 +230,6 @@ bool Core::Entity::Player::load( uint32_t charId, SessionPtr pSession ) initSpawnIdQueue(); - if( !m_playerIdToSpawnIdMap.empty() ) - m_playerIdToSpawnIdMap.clear(); - if( !g_framework.getTerritoryMgr().movePlayer( pCurrZone, getAsPlayer() ) ) return false; @@ -307,7 +304,10 @@ bool Core::Entity::Player::loadSearchInfo() m_searchSelectClass = res->getUInt8( 1 ); m_searchSelectRegion = res->getUInt8( 2 ); - sprintf( m_searchMessage, res->getString( 3 ).c_str() ); + + // todo: internally use an std::string instead of a char[] + auto searchMessage = res->getString( 3 ); + std::copy( searchMessage.begin(), searchMessage.end(), m_searchMessage ); return true; } @@ -360,7 +360,7 @@ void Core::Entity::Player::updateSql() stmt->setDouble( 20, m_pos.x ); stmt->setDouble( 21, m_pos.y ); stmt->setDouble( 22, m_pos.z ); - stmt->setDouble( 23, getRotation() ); + stmt->setDouble( 23, getRot() ); stmt->setInt( 24, m_prevZoneType ); // OTerritoryType stmt->setInt( 25, m_prevZoneId ); // OTerritoryId diff --git a/src/servers/sapphire_zone/CMakeLists.txt b/src/servers/sapphire_zone/CMakeLists.txt index 804b9a77..af9cdde3 100644 --- a/src/servers/sapphire_zone/CMakeLists.txt +++ b/src/servers/sapphire_zone/CMakeLists.txt @@ -70,3 +70,4 @@ else() endif() target_link_libraries( sapphire_zone ${Boost_LIBRARIES} ) +cotire( sapphire_zone ) diff --git a/src/servers/sapphire_zone/DebugCommand/DebugCommand.h b/src/servers/sapphire_zone/DebugCommand/DebugCommand.h index 03c01a30..6c8bdd40 100644 --- a/src/servers/sapphire_zone/DebugCommand/DebugCommand.h +++ b/src/servers/sapphire_zone/DebugCommand/DebugCommand.h @@ -3,7 +3,6 @@ #include -#include "Actor/Player.h" #include "Forwards.h" namespace Core { diff --git a/src/servers/sapphire_zone/DebugCommand/DebugCommandHandler.cpp b/src/servers/sapphire_zone/DebugCommand/DebugCommandHandler.cpp index a7134ba5..7ac15d20 100644 --- a/src/servers/sapphire_zone/DebugCommand/DebugCommandHandler.cpp +++ b/src/servers/sapphire_zone/DebugCommand/DebugCommandHandler.cpp @@ -27,8 +27,7 @@ #include "Script/NativeScriptMgr.h" #include "Actor/Player.h" -#include "Actor/BattleNpc.h" -#include "Actor/EventNpc.h" +#include "Actor/EventObject.h" #include "Zone/Zone.h" #include "Zone/InstanceContent.h" @@ -54,7 +53,6 @@ Core::DebugCommandHandler::DebugCommandHandler() registerCommand( "replay", &DebugCommandHandler::replay, "Replays a saved capture folder.", 1 ); registerCommand( "nudge", &DebugCommandHandler::nudge, "Nudges you forward/up/down.", 1 ); registerCommand( "info", &DebugCommandHandler::serverInfo, "Show server info.", 0 ); - registerCommand( "unlock", &DebugCommandHandler::unlockCharacter, "Unlock character.", 1 ); registerCommand( "help", &DebugCommandHandler::help, "Shows registered commands.", 0 ); registerCommand( "script", &DebugCommandHandler::script, "Server script utilities.", 1 ); registerCommand( "instance", &DebugCommandHandler::instance, "Instance utilities", 1 ); @@ -173,13 +171,13 @@ void Core::DebugCommandHandler::set( char * data, Entity::Player& player, boost: } if( subCommand == "pos" ) - player.setPosition( static_cast< float >( posX ), - static_cast< float >( posY ), - static_cast< float >( posZ ) ); + player.setPos( static_cast< float >( posX ), + static_cast< float >( posY ), + static_cast< float >( posZ ) ); else - player.setPosition( player.getPos().x + static_cast< float >( posX ), - player.getPos().y + static_cast< float >( posY ), - player.getPos().z + static_cast< float >( posZ ) ); + player.setPos( player.getPos().x + static_cast< float >( posX ), + player.getPos().y + static_cast< float >( posY ), + player.getPos().z + static_cast< float >( posZ ) ); Network::Packets::ZoneChannelPacket< Network::Packets::Server::FFXIVIpcActorSetPos > setActorPosPacket( player.getId() ); @@ -302,6 +300,14 @@ void Core::DebugCommandHandler::set( char * data, Entity::Player& player, boost: player.sendDebug( "MSQ Guide updated " ); } + else if( subCommand == "weatheroverride" || subCommand == "wo" ) + { + uint32_t weatherId; + + sscanf( params.c_str(), "%d", &weatherId ); + + player.getCurrentZone()->setWeatherOverride( static_cast< Common::Weather >( weatherId ) ); + } else { player.sendUrgent( subCommand + " is not a valid SET command." ); @@ -354,43 +360,6 @@ void Core::DebugCommandHandler::add( char * data, Entity::Player& player, boost: player.addTitle( titleId ); player.sendNotice( "Added title (ID: " + std::to_string( titleId ) + ")" ); } - else if( subCommand == "spawn" ) - { - int32_t model, name; - - sscanf( params.c_str(), "%d %d", &model, &name ); - - auto pBNpc = Entity::make_BattleNpc( model, name, player.getPos() ); - - auto pZone = player.getCurrentZone(); - pBNpc->setCurrentZone( pZone ); - pZone->pushActor( pBNpc ); - - } - else if( subCommand == "sspawn" ) - { - int32_t model, name, count, distCoefficient, i; - - sscanf( params.c_str(), "%d %d %d %d", &model, &name, &count, &distCoefficient ); - - for ( i = 0; i < count; i++ ) - { - Common::FFXIVARR_POSITION3 posC = player.getPos(); - std::mt19937 gen( rand() * 1000 ); - std::uniform_int_distribution< int > dis( distCoefficient * -1, distCoefficient ); - - posC.x += dis( gen ); - posC.z += dis( gen ); - - Entity::BattleNpcPtr pBNpc( new Entity::BattleNpc( model, name, posC ) ); - - auto pZone = player.getCurrentZone(); - pBNpc->setCurrentZone( pZone ); - pZone->pushActor( pBNpc ); - - } - - } else if( subCommand == "op" ) { // temporary research packet @@ -399,33 +368,6 @@ void Core::DebugCommandHandler::add( char * data, Entity::Player& player, boost: auto pPe = Network::Packets::make_GamePacket( opcode, 0x30, player.getId(), player.getId() ); player.queuePacket( pPe ); } - else if( subCommand == "eventnpc-self" ) - { - int32_t id; - - sscanf( params.c_str(), "%d", &id ); - - Network::Packets::ZoneChannelPacket< Network::Packets::Server::FFXIVIpcNpcSpawn > spawnPacket( player.getId() ); - spawnPacket.data().type = 3; - spawnPacket.data().pos = player.getPos(); - spawnPacket.data().rotation = player.getRotation(); - spawnPacket.data().bNPCBase = id; - spawnPacket.data().bNPCName = id; - spawnPacket.data().targetId = player.getId(); - player.queuePacket( spawnPacket ); - } - else if( subCommand == "eventnpc" ) - { - int32_t id; - - sscanf( params.c_str(), "%d", &id ); - - auto pENpc = Entity::make_EventNpc( id, player.getPos(), player.getRotation() ); - - auto pZone = player.getCurrentZone(); - pENpc->setCurrentZone( pZone ); - pZone->pushActor( pENpc ); - } else if( subCommand == "actrl" ) { @@ -507,7 +449,7 @@ void Core::DebugCommandHandler::get( char * data, Entity::Player& player, boost: std::to_string( player.getPos().x ) + "\n" + std::to_string( player.getPos().y ) + "\n" + std::to_string( player.getPos().z ) + "\n" + - std::to_string( player.getRotation() ) + "\nMapId: " + + std::to_string( player.getRot() ) + "\nMapId: " + std::to_string( map_id ) + "\nZoneID: " + std::to_string(player.getCurrentZone()->getTerritoryId() ) + "\n" ); } @@ -612,7 +554,7 @@ void Core::DebugCommandHandler::nudge( char * data, Entity::Player& player, boos } else { - float angle = player.getRotation() + ( PI / 2 ); + float angle = player.getRot() + ( PI / 2 ); pos.x -= offset * cos( angle ); pos.z += offset * sin( angle ); player.sendNotice( "nudge: Placing forward " + std::to_string( offset ) + " yalms" ); @@ -624,23 +566,18 @@ void Core::DebugCommandHandler::nudge( char * data, Entity::Player& player, boos setActorPosPacket.data().x = player.getPos().x; setActorPosPacket.data().y = player.getPos().y; setActorPosPacket.data().z = player.getPos().z; - setActorPosPacket.data().r16 = Math::Util::floatToUInt16Rot( player.getRotation() ); + setActorPosPacket.data().r16 = Math::Util::floatToUInt16Rot( player.getRot() ); player.queuePacket( setActorPosPacket ); } } void Core::DebugCommandHandler::serverInfo( char * data, Entity::Player& player, boost::shared_ptr< DebugCommand > command ) { - player.sendDebug( "SapphireServer " + Version::VERSION + "\nRev: " + Version::GIT_HASH ); + player.sendDebug( "SapphireZone " + Version::VERSION + "\nRev: " + Version::GIT_HASH ); player.sendDebug( "Compiled: " __DATE__ " " __TIME__ ); player.sendDebug( "Sessions: " + std::to_string( g_framework.getServerZone().getSessionCount() ) ); } -void Core::DebugCommandHandler::unlockCharacter( char* data, Entity::Player& player, boost::shared_ptr< DebugCommand > command ) -{ - player.unlock(); -} - void Core::DebugCommandHandler::script( char* data, Entity::Player &player, boost::shared_ptr< DebugCommand > command ) { std::string subCommand; @@ -776,51 +713,58 @@ void Core::DebugCommandHandler::instance( char* data, Entity::Player &player, bo } else if( subCommand == "set" ) { - uint32_t instanceId; uint32_t index; uint32_t value; - sscanf( params.c_str(), "%d %d %d", &instanceId, &index, &value ); + sscanf( params.c_str(), "%d %d", &index, &value ); - auto pInstance = g_framework.getTerritoryMgr().getInstanceZonePtr( instanceId ); - if( !pInstance ) + + auto instance = boost::dynamic_pointer_cast< InstanceContent >( player.getCurrentZone() ); + if( !instance ) return; - auto instance = boost::dynamic_pointer_cast< InstanceContent >( pInstance ); instance->setVar( static_cast< uint8_t >( index ), static_cast< uint8_t >( value ) ); } - else if( subCommand == "objupdate" ) - { - uint32_t objId; - - sscanf( params.c_str(), "%d", &objId ); - - auto instance = boost::dynamic_pointer_cast< InstanceContent >( player.getCurrentZone() ); - if( !instance ) - return; - - auto obj = instance->getInstanceObject( objId ); - if( !obj ) - return; - - instance->updateInstanceObj( obj ); - } else if( subCommand == "objstate" ) { - uint32_t objId; + char objName[128]; uint8_t state; - sscanf( params.c_str(), "%d %hhu", &objId, &state ); + sscanf( params.c_str(), "%s %hhu", objName, &state ); auto instance = boost::dynamic_pointer_cast< InstanceContent >( player.getCurrentZone() ); if( !instance ) return; - auto obj = instance->getInstanceObject( objId ); + auto obj = instance->getEObjByName( objName ); if( !obj ) return; obj->setState( state ); } + else if( subCommand == "seq" ) + { + uint8_t seq; + + sscanf( params.c_str(), "%hhu", &seq ); + + auto instance = boost::dynamic_pointer_cast< InstanceContent >( player.getCurrentZone() ); + if( !instance ) + return; + + instance->setSequence( seq ); + } + else if( subCommand == "branch" ) + { + uint8_t branch; + + sscanf( params.c_str(), "%hhu", &branch ); + + auto instance = boost::dynamic_pointer_cast< InstanceContent >( player.getCurrentZone() ); + if( !instance ) + return; + + instance->setBranch( branch ); + } else if( subCommand == "festival" ) { uint32_t festivalId; diff --git a/src/servers/sapphire_zone/Event/Director.cpp b/src/servers/sapphire_zone/Event/Director.cpp index a201a5ab..9dee7702 100644 --- a/src/servers/sapphire_zone/Event/Director.cpp +++ b/src/servers/sapphire_zone/Event/Director.cpp @@ -168,3 +168,13 @@ void Core::Event::Director::setDirectorUI8JH( uint8_t value ) { m_unionData.ui8lh.UI8JH = value; } + +void Core::Event::Director::setDirectorBranch( uint8_t value ) +{ + m_branch = value; +} + +void Core::Event::Director::setDirectorSequence( uint8_t value ) +{ + m_sequence = value; +} diff --git a/src/servers/sapphire_zone/Event/Director.h b/src/servers/sapphire_zone/Event/Director.h index f5840390..1693d47b 100644 --- a/src/servers/sapphire_zone/Event/Director.h +++ b/src/servers/sapphire_zone/Event/Director.h @@ -72,6 +72,9 @@ public: void setDirectorUI8JL( uint8_t value ); void setDirectorUI8JH( uint8_t value ); + void setDirectorSequence( uint8_t value ); + void setDirectorBranch( uint8_t value ); + private: /*! Id of the content of the director */ uint16_t m_contentId; diff --git a/src/servers/sapphire_zone/Event/EventHandler.h b/src/servers/sapphire_zone/Event/EventHandler.h index e02434c6..42490462 100644 --- a/src/servers/sapphire_zone/Event/EventHandler.h +++ b/src/servers/sapphire_zone/Event/EventHandler.h @@ -61,6 +61,7 @@ namespace Core { SpecialShop = 0x001B, BahamutGuide = 0x001C, FcTalk = 0x001F, + ICDirector = 0x8003, }; using SceneReturnCallback = std::function< void( Entity::Player&, uint32_t, uint16_t, uint16_t, uint16_t ) > ; diff --git a/src/servers/sapphire_zone/Event/EventHelper.cpp b/src/servers/sapphire_zone/Event/EventHelper.cpp index c5dc910b..0be7640f 100644 --- a/src/servers/sapphire_zone/Event/EventHelper.cpp +++ b/src/servers/sapphire_zone/Event/EventHelper.cpp @@ -1,13 +1,14 @@ #include #include - #include "Framework.h" #include "EventHelper.h" #include "EventHandler.h" - +#include +#include extern Core::Framework g_framework; + using namespace Core::Common; std::string Core::Event::getEventName( uint32_t eventId ) @@ -54,6 +55,15 @@ std::string Core::Event::getEventName( uint32_t eventId ) return "Aetheryte"; return "Aethernet"; } + case Event::EventHandler::EventHandlerType::ICDirector: + { + auto contentInfo = g_framework.getExdDataGen().get< Core::Data::InstanceContent >( eventId & 0xFFFF ); + std::string name = contentInfo->name; + + name.erase( boost::remove_if( name, boost::is_any_of( "★_ '()[]-\x1a\x1\x2\x1f\x1\x3.:" ) ), name.end() ); + name[0] = toupper( name[0] ); + return name; + } case Event::EventHandler::EventHandlerType::Warp: { diff --git a/src/servers/sapphire_zone/Forwards.h b/src/servers/sapphire_zone/Forwards.h index 3ffa511e..9537191c 100644 --- a/src/servers/sapphire_zone/Forwards.h +++ b/src/servers/sapphire_zone/Forwards.h @@ -34,13 +34,10 @@ namespace Core namespace Entity { - TYPE_FORWARD( GameObject ); TYPE_FORWARD( Actor ); + TYPE_FORWARD( Chara ); TYPE_FORWARD( Player ); - TYPE_FORWARD( BattleNpc ); - TYPE_FORWARD( EventNpc ); - TYPE_FORWARD( BattleNpcTemplate ); - TYPE_FORWARD( InstanceObject ); + TYPE_FORWARD( EventObject ); } namespace Event diff --git a/src/servers/sapphire_zone/Inventory/Inventory.cpp b/src/servers/sapphire_zone/Inventory/Inventory.cpp index ae21fe5f..fff194bc 100644 --- a/src/servers/sapphire_zone/Inventory/Inventory.cpp +++ b/src/servers/sapphire_zone/Inventory/Inventory.cpp @@ -720,7 +720,7 @@ bool Core::Inventory::load() while( bagRes->next() ) { uint16_t storageId = bagRes->getUInt16( 1 ); - for( uint32_t i = 1; i <= 25; i++ ) + for( uint32_t i = 1; i <= 35; i++ ) { uint64_t uItemId = bagRes->getUInt64( i + 1 ); if( uItemId == 0 ) @@ -890,7 +890,7 @@ uint8_t Core::Inventory::getFreeSlotsInBags() uint8_t slots = 0; for( uint8_t container : { 0, 1, 2, 3 } ) { - slots += 25 - m_inventoryMap[container]->getEntryCount(); + slots += 34 - m_inventoryMap[container]->getEntryCount(); } return slots; } diff --git a/src/servers/sapphire_zone/Inventory/ItemContainer.cpp b/src/servers/sapphire_zone/Inventory/ItemContainer.cpp index ec30780c..a86c0ce1 100644 --- a/src/servers/sapphire_zone/Inventory/ItemContainer.cpp +++ b/src/servers/sapphire_zone/Inventory/ItemContainer.cpp @@ -13,7 +13,7 @@ extern Core::Framework g_framework; Core::ItemContainer::ItemContainer( uint16_t locationId ) : m_id( locationId ), - m_size( 25 ) + m_size( 35 ) { } diff --git a/src/servers/sapphire_zone/Math/CalcBattle.cpp b/src/servers/sapphire_zone/Math/CalcBattle.cpp index 6c94db6c..86a70fe6 100644 --- a/src/servers/sapphire_zone/Math/CalcBattle.cpp +++ b/src/servers/sapphire_zone/Math/CalcBattle.cpp @@ -3,7 +3,8 @@ #include #include -#include "Actor/Actor.h" +#include "Actor/Chara.h" + #include "Actor/Player.h" #include "CalcBattle.h" diff --git a/src/servers/sapphire_zone/Math/CalcBattle.h b/src/servers/sapphire_zone/Math/CalcBattle.h index 8c43107f..af4ee014 100644 --- a/src/servers/sapphire_zone/Math/CalcBattle.h +++ b/src/servers/sapphire_zone/Math/CalcBattle.h @@ -2,7 +2,7 @@ #define _CALCBATTLE_H #include -#include "Actor/Actor.h" +#include "Forwards.h" using namespace Core::Entity; diff --git a/src/servers/sapphire_zone/Math/CalcStats.cpp b/src/servers/sapphire_zone/Math/CalcStats.cpp index 12c4042e..e7a127c1 100644 --- a/src/servers/sapphire_zone/Math/CalcStats.cpp +++ b/src/servers/sapphire_zone/Math/CalcStats.cpp @@ -3,7 +3,8 @@ #include #include -#include "Actor/Actor.h" +#include "Actor/Chara.h" + #include "Actor/Player.h" #include "CalcStats.h" diff --git a/src/servers/sapphire_zone/Math/CalcStats.h b/src/servers/sapphire_zone/Math/CalcStats.h index 2bf17da5..c1d5df63 100644 --- a/src/servers/sapphire_zone/Math/CalcStats.h +++ b/src/servers/sapphire_zone/Math/CalcStats.h @@ -2,7 +2,7 @@ #define _CALCSTATS_H #include -#include "Actor/Actor.h" +#include "Forwards.h" using namespace Core::Entity; diff --git a/src/servers/sapphire_zone/Network/Handlers/ActionHandler.cpp b/src/servers/sapphire_zone/Network/Handlers/ActionHandler.cpp index 789da330..46d2175c 100644 --- a/src/servers/sapphire_zone/Network/Handlers/ActionHandler.cpp +++ b/src/servers/sapphire_zone/Network/Handlers/ActionHandler.cpp @@ -45,6 +45,65 @@ using namespace Core::Common; using namespace Core::Network::Packets; using namespace Core::Network::Packets::Server; +enum ClientTrigger +{ + ToggleSeathe = 0x01, + ToggleAutoAttack = 0x02, + ChangeTarget = 0x03, + + Dismount = 0x65, + + RemoveStatusEffect = 0x68, + CastCancel = 0x69, + + Return = 0xC8, // return dead / accept raise + FinishZoning = 0xC9, + Teleport = 0xCA, + + MarkPlayer = 0x12D, // Mark player, visible to party only + SetTitle = 0x12E, + TitleList = 0x12F, + + UpdatedSeenHowTos = 0x133, + AllotAttribute = 0x135, + + ClearWaymarks = 0x13A, + + HuntingLogDetails = 0x194, + + Timers = 0x1AB, + + DyeItem = 0x1B5, + + RequestChocoboInventory = 0x1C4, + + Emote = 0x1F4, + PersistantEmoteCancel = 0x1F7, + PoseChange = 0x1F9, + PoseReapply = 0x1FA, + PoseCancel = 0x1FB, + + AchievementCrit = 0x202, + AchievementComp = 0x203, + AchievementCatChat = 0x206, + + + DirectorInitFinish = 0x321, + + SomeDirectorEvent = 0x328, // unsure what exactly triggers it, starts director when returning to instance though + + EnterTerritoryEventFinished = 0x330, + + AchievementCritReq = 0x3E8, + AchievementList = 0x3E9, + + CompanionAction = 0x6A4, + CompanionSetBarding = 0x6A5, + CompanionActionUnlock = 0x6A6, + + OpenPerformInstrumentUI = 0x71C, +}; + void Core::Network::GameConnection::actionHandler( const Packets::GamePacket& inPacket, Entity::Player& player ) { @@ -67,13 +126,13 @@ void Core::Network::GameConnection::actionHandler( const Packets::GamePacket& in switch( commandId ) { - case 0x01: // Toggle sheathe + case ClientTrigger::ToggleSeathe: // Toggle sheathe { if ( param11 == 1 ) - player.setStance( Entity::Actor::Stance::Active ); + player.setStance( Entity::Chara::Stance::Active ); else { - player.setStance( Entity::Actor::Stance::Passive ); + player.setStance( Entity::Chara::Stance::Passive ); player.setAutoattack( false ); } @@ -81,12 +140,12 @@ void Core::Network::GameConnection::actionHandler( const Packets::GamePacket& in break; } - case 0x02: // Toggle auto-attack + case ClientTrigger::ToggleAutoAttack: // Toggle auto-attack { if ( param11 == 1 ) { player.setAutoattack( true ); - player.setStance( Entity::Actor::Stance::Active ); + player.setStance( Entity::Chara::Stance::Active ); } else player.setAutoattack( false ); @@ -95,58 +154,75 @@ void Core::Network::GameConnection::actionHandler( const Packets::GamePacket& in break; } - case 0x03: // Change target + case ClientTrigger::ChangeTarget: // Change target { uint64_t targetId = inPacket.getValAt< uint64_t >( 0x24 ); player.changeTarget( targetId ); break; } - case 0x65: + case ClientTrigger::Dismount: { player.dismount(); break; } - case 0x68: // Remove status (clicking it off) + case ClientTrigger::RemoveStatusEffect: // Remove status (clicking it off) { // todo: check if status can be removed by client from exd player.removeSingleStatusEffectById( static_cast< uint32_t >( param1 ) ); break; } - case 0x69: // Cancel cast + case ClientTrigger::CastCancel: // Cancel cast { if( player.getCurrentAction() ) player.getCurrentAction()->setInterrupted(); break; } - case 0x12E: // Set player title + case ClientTrigger::MarkPlayer: // Mark player + { + break; + } + case ClientTrigger::SetTitle: // Set player title { player.setTitle( static_cast< uint16_t >( param1 ) ); break; } - case 0x12F: // Get title list + case ClientTrigger::TitleList: // Get title list { - ZoneChannelPacket< FFXIVIpcPlayerTitleList > titleListPacket( player.getId() ); - memcpy( titleListPacket.data().titleList, player.getTitleList(), sizeof( titleListPacket.data().titleList ) ); - - player.queuePacket( titleListPacket ); + player.sendTitleList(); break; } - case 0x133: // Update howtos seen + case ClientTrigger::UpdatedSeenHowTos: // Update howtos seen { - uint32_t howToId = static_cast< uint32_t >( param1 ); + uint32_t howToId = param11; player.updateHowtosSeen( howToId ); break; } - case 0x1F4: // emote + case ClientTrigger::Emote: // emote { uint64_t targetId = player.getTargetId(); uint32_t emoteId = inPacket.getValAt< uint32_t >( 0x24 ); - player.sendToInRangeSet( ActorControlPacket144( player.getId(), Emote, emoteId, 0, 0, 0, targetId ) ); + player.emote( emoteId, targetId ); break; } - case 0xC8: // return dead + case ClientTrigger::PersistantEmoteCancel: // cancel persistant emote + { + break; + } + case ClientTrigger::PoseChange: // change pose + { + break; + } + case ClientTrigger::PoseReapply: // reapply pose + { + break; + } + case ClientTrigger::PoseCancel: // cancel pose + { + break; + } + case ClientTrigger::Return: // return dead / accept raise { switch ( static_cast < ResurrectType >( param1 ) ) { @@ -162,80 +238,37 @@ void Core::Network::GameConnection::actionHandler( const Packets::GamePacket& in } } - case 0xC9: // Finish zoning + case ClientTrigger::FinishZoning: // Finish zoning { - switch( player.getZoningType() ) - { - case ZoneingType::None: - player.sendToInRangeSet( ActorControlPacket143( player.getId(), ZoneIn, 0x01 ), true ); - break; - case ZoneingType::Teleport: - player.sendToInRangeSet( ActorControlPacket143( player.getId(), ZoneIn, 0x01, 0, 0, 110 ), true ); - break; - case ZoneingType::Return: - case ZoneingType::ReturnDead: - { - if( player.getStatus() == Entity::Actor::ActorStatus::Dead ) - { - player.resetHp(); - player.resetMp(); - player.setStatus( Entity::Actor::ActorStatus::Idle ); - - player.sendToInRangeSet( ActorControlPacket143( player.getId(), ZoneIn, 0x01, 0x01, 0, 111 ), true ); - player.sendToInRangeSet( ActorControlPacket142( player.getId(), SetStatus, static_cast< uint8_t >( Entity::Actor::ActorStatus::Idle ) ), true ); - } - else - player.sendToInRangeSet( ActorControlPacket143( player.getId(), ZoneIn, 0x01, 0x00, 0, 111 ), true ); - } - break; - case ZoneingType::FadeIn: - break; - } - - player.setZoningType( Common::ZoneingType::None ); - - player.unsetStateFlag( PlayerStateFlag::BetweenAreas ); - player.unsetStateFlag( PlayerStateFlag::BetweenAreas1 ); + player.finishZoning(); break; } - case 0xCA: // Teleport + case ClientTrigger::Teleport: // Teleport { - // TODO: only register this action if enough gil is in possession - auto targetAetheryte = g_framework.getExdDataGen().get< Core::Data::Aetheryte >( param11 ); - if( targetAetheryte ) - { - auto fromAetheryte = g_framework.getExdDataGen().get< Core::Data::Aetheryte >( g_framework.getExdDataGen().get< Core::Data::TerritoryType >( player.getZoneId() )->aetheryte ); - - // calculate cost - does not apply for favorite points or homepoints neither checks for aether tickets - auto cost = static_cast< uint16_t >( ( sqrt( pow( fromAetheryte->aetherstreamX - targetAetheryte->aetherstreamX, 2 ) + - pow( fromAetheryte->aetherstreamY - targetAetheryte->aetherstreamY, 2 ) ) / 2 ) + 100 ); - - // cap at 999 gil - cost = cost > uint16_t{999} ? uint16_t{999} : cost; - - bool insufficientGil = player.getCurrency( Inventory::CurrencyType::Gil ) < cost; - // todo: figure out what param1 really does - player.queuePacket( ActorControlPacket143( player.getId(), TeleportStart, insufficientGil ? 2 : 0, param11 ) ); - - if( !insufficientGil ) - { - auto pActionTeleport = Action::make_ActionTeleport( player.getAsPlayer(), param11, cost ); - player.setCurrentAction( pActionTeleport ); - } - } + player.teleportQuery( param11 ); break; } - case 0x1B5: // Dye item + case ClientTrigger::DyeItem: // Dye item { break; } - case 0x321: // Director init finish + case ClientTrigger::DirectorInitFinish: // Director init finish { player.getCurrentZone()->onInitDirector( player ); break; } + case ClientTrigger::SomeDirectorEvent: // Director init finish + { + player.getCurrentZone()->onSomeDirectorEvent( player ); + break; + } + case ClientTrigger::EnterTerritoryEventFinished:// this may still be something else. I think i have seen it elsewhere + { + player.setOnEnterEventDone( true ); + break; + } default: { g_framework.getLogger().debug( "[" + std::to_string( m_pSession->getId() ) + "] Unhandled action: " + diff --git a/src/servers/sapphire_zone/Network/Handlers/EventHandlers.cpp b/src/servers/sapphire_zone/Network/Handlers/EventHandlers.cpp index 6a95de0b..e59540eb 100644 --- a/src/servers/sapphire_zone/Network/Handlers/EventHandlers.cpp +++ b/src/servers/sapphire_zone/Network/Handlers/EventHandlers.cpp @@ -21,6 +21,9 @@ #include "Actor/Player.h" #include "Event/EventHelper.h" + +#include "Zone/InstanceContent.h" + #include "Session.h" #include "Forwards.h" #include "Framework.h" @@ -40,7 +43,7 @@ void Core::Network::GameConnection::eventHandlerTalk( const Packets::GamePacket& std::string eventName = "onTalk"; std::string objName = Event::getEventName( eventId ); - player.sendDebug( "Actor: " + + player.sendDebug( "Chara: " + std::to_string( actorId ) + " -> " + std::to_string( Event::mapEventActorToRealActor( static_cast< uint32_t >( actorId ) ) ) + " \neventId: " + @@ -52,8 +55,13 @@ void Core::Network::GameConnection::eventHandlerTalk( const Packets::GamePacket& player.sendDebug( "Calling: " + objName + "." + eventName ); player.eventStart( actorId, eventId, Event::EventHandler::Talk, 0, 0 ); - if( !g_framework.getScriptMgr().onTalk( player, actorId, eventId ) && - eventType == Event::EventHandler::EventHandlerType::Quest ) + + if( auto instance = player.getCurrentInstance() ) + { + instance->onTalk( player, eventId, actorId ); + } + else if( !g_framework.getScriptMgr().onTalk( player, actorId, eventId ) && + eventType == Event::EventHandler::EventHandlerType::Quest ) { auto questInfo = g_framework.getExdDataGen().get< Core::Data::Quest >( eventId ); if( questInfo ) @@ -75,7 +83,7 @@ void Core::Network::GameConnection::eventHandlerEmote( const Packets::GamePacket std::string eventName = "onEmote"; std::string objName = Event::getEventName( eventId ); - player.sendDebug( "Actor: " + + player.sendDebug( "Chara: " + std::to_string( actorId ) + " -> " + std::to_string( Event::mapEventActorToRealActor( static_cast< uint32_t >( actorId ) ) ) + " \neventId: " + @@ -155,9 +163,22 @@ void Core::Network::GameConnection::eventHandlerEnterTerritory( const Packets::G player.sendDebug( "Calling: " + objName + "." + eventName + " - " + std::to_string( eventId ) ); - player.eventStart( player.getId(), eventId, Event::EventHandler::EnterTerritory, 0, player.getZoneId() ); + player.eventStart( player.getId(), eventId, Event::EventHandler::EnterTerritory, 0, player.getZoneId(), 0 ); - g_framework.getScriptMgr().onEnterTerritory( player, eventId, param1, param2 ); + if( auto instance = player.getCurrentInstance() ) + { + // param2 of eventStart + // 0 = default state? + // 1 = restore state? + // (^ Mordred: Nope, i don't think thats it ) + player.eventStart( player.getId(), eventId, Event::EventHandler::EnterTerritory, 0, player.getZoneId(), instance->getDirectorId() & 0xFFFF ); + instance->onEnterTerritory( player, eventId, param1, param2 ); + } + else + { + player.eventStart( player.getId(), eventId, Event::EventHandler::EnterTerritory, 0, player.getZoneId() ); + g_framework.getScriptMgr().onEnterTerritory( player, eventId, param1, param2 ); + } player.checkEvent( eventId ); } diff --git a/src/servers/sapphire_zone/Network/Handlers/GMCommandHandlers.cpp b/src/servers/sapphire_zone/Network/Handlers/GMCommandHandlers.cpp index ccf4ae71..1af6389f 100644 --- a/src/servers/sapphire_zone/Network/Handlers/GMCommandHandlers.cpp +++ b/src/servers/sapphire_zone/Network/Handlers/GMCommandHandlers.cpp @@ -6,6 +6,13 @@ #include #include +#include +#include + +#include "Network/GameConnection.h" + +#include "Session.h" + #include "Zone/TerritoryMgr.h" #include "Zone/Zone.h" #include "Zone/ZonePosition.h" @@ -141,7 +148,7 @@ void Core::Network::GameConnection::gm1Handler( const Packets::GamePacket& inPac player.sendNotice( "Race for " + targetPlayer->getName() + " was set to " + std::to_string( param1 ) ); targetPlayer->spawn( targetPlayer ); auto inRange = targetPlayer->getInRangeActors(); - for ( auto actor : inRange ) + for( auto actor : inRange ) { targetPlayer->despawn( actor->getAsPlayer() ); targetPlayer->spawn( actor->getAsPlayer() ); @@ -154,7 +161,7 @@ void Core::Network::GameConnection::gm1Handler( const Packets::GamePacket& inPac player.sendNotice( "Tribe for " + targetPlayer->getName() + " was set to " + std::to_string( param1 ) ); targetPlayer->spawn( targetPlayer ); auto inRange = targetPlayer->getInRangeActors(); - for ( auto actor : inRange ) + for( auto actor : inRange ) { targetPlayer->despawn( actor->getAsPlayer() ); targetPlayer->spawn( actor->getAsPlayer() ); @@ -167,7 +174,7 @@ void Core::Network::GameConnection::gm1Handler( const Packets::GamePacket& inPac player.sendNotice( "Sex for " + targetPlayer->getName() + " was set to " + std::to_string( param1 ) ); targetPlayer->spawn( targetPlayer ); auto inRange = targetActor->getInRangeActors(); - for ( auto actor : inRange ) + for( auto actor : inRange ) { targetPlayer->despawn( actor->getAsPlayer() ); targetPlayer->spawn( actor->getAsPlayer() ); @@ -182,18 +189,17 @@ void Core::Network::GameConnection::gm1Handler( const Packets::GamePacket& inPac } case GmCommand::Weather: { - targetPlayer->getCurrentZone()->setWeatherOverride( param1 ); + targetPlayer->getCurrentZone()->setWeatherOverride( static_cast< Common::Weather >( param1 ) ); player.sendNotice( "Weather in Zone \"" + targetPlayer->getCurrentZone()->getName() + "\" of " + targetPlayer->getName() + " set in range." ); break; } case GmCommand::Call: { - if ( targetPlayer->getZoneId() != player.getZoneId() ) + if( targetPlayer->getZoneId() != player.getZoneId() ) targetPlayer->setZone( player.getZoneId() ); - targetPlayer->changePosition( player.getPos().x, player.getPos().y, player.getPos().z, - player.getRotation() ); + targetPlayer->changePosition( player.getPos().x, player.getPos().y, player.getPos().z, player.getRot() ); player.sendNotice( "Calling " + targetPlayer->getName() ); break; } @@ -218,7 +224,7 @@ void Core::Network::GameConnection::gm1Handler( const Packets::GamePacket& inPac } case GmCommand::Kill: { - targetActor->takeDamage( 9999999 ); + targetActor->getAsChara()->takeDamage( 9999999 ); player.sendNotice( "Killed " + std::to_string( targetActor->getId() ) ); break; } @@ -268,10 +274,10 @@ void Core::Network::GameConnection::gm1Handler( const Packets::GamePacket& inPac } case GmCommand::Inv: { - if ( targetActor->getInvincibilityType() == Common::InvincibilityType::InvincibilityRefill ) - targetActor->setInvincibilityType( Common::InvincibilityType::InvincibilityNone ); + if( targetActor->getAsChara()->getInvincibilityType() == Common::InvincibilityType::InvincibilityRefill ) + targetActor->getAsChara()->setInvincibilityType( Common::InvincibilityType::InvincibilityNone ); else - targetActor->setInvincibilityType( Common::InvincibilityType::InvincibilityRefill ); + targetActor->getAsChara()->setInvincibilityType( Common::InvincibilityType::InvincibilityRefill ); player.sendNotice( "Invincibility for " + targetPlayer->getName() + " was switched." ); @@ -279,11 +285,11 @@ void Core::Network::GameConnection::gm1Handler( const Packets::GamePacket& inPac } case GmCommand::Orchestrion: { - if ( param1 == 1 ) + if( param1 == 1 ) { - if ( param2 == 0 ) + if( param2 == 0 ) { - for ( uint8_t i = 0; i < 255; i++ ) + for( uint8_t i = 0; i < 255; i++ ) targetActor->getAsPlayer()->learnSong( i, 0 ); player.sendNotice( "All Songs for " + targetPlayer->getName() + @@ -326,7 +332,7 @@ void Core::Network::GameConnection::gm1Handler( const Packets::GamePacket& inPac { uint32_t gil = targetPlayer->getCurrency( 1 ); - if ( gil < param1 ) + if( gil < param1 ) { player.sendUrgent( "Player does not have enough Gil(" + std::to_string( gil ) + ")" ); } @@ -381,11 +387,11 @@ void Core::Network::GameConnection::gm1Handler( const Packets::GamePacket& inPac } case GmCommand::Aetheryte: { - if ( param1 == 0 ) + if( param1 == 0 ) { - if ( param2 == 0 ) + if( param2 == 0 ) { - for ( uint8_t i = 0; i < 255; i++ ) + for( uint8_t i = 0; i < 255; i++ ) targetActor->getAsPlayer()->registerAetheryte( i ); player.sendNotice( "All Aetherytes for " + targetPlayer->getName() + @@ -421,7 +427,7 @@ void Core::Network::GameConnection::gm1Handler( const Packets::GamePacket& inPac player.sendUrgent( "No zone instance found for " + std::to_string( param1 ) ); break; } - targetPlayer->setPosition( targetPlayer->getPos() ); + targetPlayer->setPos( targetPlayer->getPos() ); targetPlayer->performZoning( param1, targetPlayer->getPos(), 0 ); player.sendNotice( targetPlayer->getName() + " was warped to zone " + std::to_string( param1 ) + " (" + pZone->getName() + ")" ); } @@ -429,23 +435,23 @@ void Core::Network::GameConnection::gm1Handler( const Packets::GamePacket& inPac } case GmCommand::TeriInfo: { + auto pCurrentZone = player.getCurrentZone(); player.sendNotice( "ZoneId: " + std::to_string( player.getZoneId() ) + "\nName: " + - player.getCurrentZone()->getName() + "\nInternalName: " + - player.getCurrentZone()->getInternalName() + "\nPopCount: " + - std::to_string( player.getCurrentZone()->getPopCount() ) + - "\nCurrentWeather:" + std::to_string( player.getCurrentZone()->getCurrentWeather() ) + - "\nNextWeather:" + std::to_string( player.getCurrentZone()->getNextWeather() ) ); + pCurrentZone->getName() + "\nInternalName: " + + pCurrentZone->getInternalName() + "\nPopCount: " + + std::to_string( pCurrentZone->getPopCount() ) + + "\nCurrentWeather:" + std::to_string( static_cast< uint8_t >( pCurrentZone->getCurrentWeather() ) ) + + "\nNextWeather:" + std::to_string( static_cast< uint8_t >( pCurrentZone->getNextWeather() ) ) ); break; } case GmCommand::Jump: { auto inRange = player.getInRangeActors(); - for( auto actor : inRange ) - { - player.changePosition( targetActor->getPos().x, targetActor->getPos().y, targetActor->getPos().z, - targetActor->getRotation() ); - } + + player.changePosition( targetActor->getPos().x, targetActor->getPos().y, targetActor->getPos().z, + targetActor->getRot() ); + player.sendNotice( "Jumping to " + targetPlayer->getName() + " in range." ); break; } @@ -468,7 +474,7 @@ void Core::Network::GameConnection::gm2Handler( const Packets::GamePacket& inPac g_framework.getLogger().debug( player.getName() + " used GM2 commandId: " + std::to_string( commandId ) + ", params: " + param1 ); auto targetSession = g_framework.getServerZone().getSession( param1 ); - Core::Entity::ActorPtr targetActor; + Core::Entity::CharaPtr targetActor; if( targetSession != nullptr ) { @@ -498,11 +504,11 @@ void Core::Network::GameConnection::gm2Handler( const Packets::GamePacket& inPac { targetPlayer->resetHp(); targetPlayer->resetMp(); - targetPlayer->setStatus( Entity::Actor::ActorStatus::Idle ); + targetPlayer->setStatus( Entity::Chara::ActorStatus::Idle ); targetPlayer->sendToInRangeSet( ActorControlPacket143( player.getId(), ZoneIn, 0x01, 0x01, 0, 113 ), true ); targetPlayer->sendToInRangeSet( ActorControlPacket142( player.getId(), SetStatus, - static_cast< uint8_t >( Entity::Actor::ActorStatus::Idle ) ), true ); + static_cast< uint8_t >( Entity::Chara::ActorStatus::Idle ) ), true ); player.sendNotice( "Raised " + targetPlayer->getName() ); break; } @@ -513,17 +519,16 @@ void Core::Network::GameConnection::gm2Handler( const Packets::GamePacket& inPac player.setZone( targetPlayer->getZoneId() ); } player.changePosition( targetActor->getPos().x, targetActor->getPos().y, targetActor->getPos().z, - targetActor->getRotation() ); + targetActor->getRot() ); player.sendNotice( "Jumping to " + targetPlayer->getName() ); break; } case GmCommand::Call: { - if ( targetPlayer->getZoneId() != player.getZoneId() ) + if( targetPlayer->getZoneId() != player.getZoneId() ) targetPlayer->setZone( player.getZoneId() ); - targetPlayer->changePosition( player.getPos().x, player.getPos().y, player.getPos().z, - player.getRotation() ); + targetPlayer->changePosition( player.getPos().x, player.getPos().y, player.getPos().z, player.getRot() ); player.sendNotice( "Calling " + targetPlayer->getName() ); break; } diff --git a/src/servers/sapphire_zone/Network/Handlers/PacketHandlers.cpp b/src/servers/sapphire_zone/Network/Handlers/PacketHandlers.cpp index 3d9022d5..87bcd02b 100644 --- a/src/servers/sapphire_zone/Network/Handlers/PacketHandlers.cpp +++ b/src/servers/sapphire_zone/Network/Handlers/PacketHandlers.cpp @@ -7,7 +7,8 @@ #include #include #include - +#include +#include #include "Network/GameConnection.h" #include "Zone/TerritoryMgr.h" @@ -181,13 +182,13 @@ void Core::Network::GameConnection::updatePositionHandler( const Packets::GamePa ( player.getPos().y != inPacket.getValAt< float >( 0x30 ) ) || ( player.getPos().z != inPacket.getValAt< float >( 0x34 ) ) ) bPosChanged = true; - if( !bPosChanged && player.getRotation() == inPacket.getValAt< float >( 0x20 ) ) + if( !bPosChanged && player.getRot() == inPacket.getValAt< float >( 0x20 ) ) return; - player.setRotation( inPacket.getValAt< float >( 0x20 ) ); - player.setPosition( inPacket.getValAt< float >( 0x2c ), - inPacket.getValAt< float >( 0x30 ), - inPacket.getValAt< float >( 0x34 ) ); + player.setRot( inPacket.getValAt< float >( 0x20 ) ); + player.setPos( inPacket.getValAt< float >( 0x2c ), + inPacket.getValAt< float >( 0x30 ), + inPacket.getValAt< float >( 0x34 ) ); if( ( player.getCurrentAction() != nullptr ) && bPosChanged ) player.getCurrentAction()->setInterrupted(); @@ -793,16 +794,14 @@ void Core::Network::GameConnection::tellHandler( const Packets::GamePacket& inPa auto pTargetPlayer = pSession->getPlayer(); - if( pTargetPlayer->hasStateFlag( PlayerStateFlag::BetweenAreas ) || - pTargetPlayer->hasStateFlag( PlayerStateFlag::BetweenAreas1 ) ) + if( pTargetPlayer->hasStateFlag( PlayerStateFlag::BetweenAreas ) ) { // send error for player between areas // TODO: implement me return; } - if( pTargetPlayer->hasStateFlag( PlayerStateFlag::BoundByDuty ) || - pTargetPlayer->hasStateFlag( PlayerStateFlag::BoundByDuty1 ) ) + if( pTargetPlayer->hasStateFlag( PlayerStateFlag::BoundByDuty ) ) { // send error for player bound by duty // TODO: implement me diff --git a/src/servers/sapphire_zone/Network/Handlers/SkillHandler.cpp b/src/servers/sapphire_zone/Network/Handlers/SkillHandler.cpp index 6cfc74dd..e84c47dd 100644 --- a/src/servers/sapphire_zone/Network/Handlers/SkillHandler.cpp +++ b/src/servers/sapphire_zone/Network/Handlers/SkillHandler.cpp @@ -80,11 +80,11 @@ void Core::Network::GameConnection::skillHandler( const Packets::GamePacket& inP if( !player.actionHasCastTime( action ) ) { - g_framework.getScriptMgr().onCastFinish( player, targetActor, action ); + g_framework.getScriptMgr().onCastFinish( player, targetActor->getAsChara(), action ); } else { - auto pActionCast = Action::make_ActionCast( player.getAsPlayer(), targetActor, action ); + auto pActionCast = Action::make_ActionCast( player.getAsPlayer(), targetActor->getAsChara(), action ); player.setCurrentAction( pActionCast ); player.sendDebug( "setCurrentAction()" ); player.getCurrentAction()->onStart(); diff --git a/src/servers/sapphire_zone/Network/PacketWrappers/DirectorPlayScenePacket.h b/src/servers/sapphire_zone/Network/PacketWrappers/DirectorPlayScenePacket.h new file mode 100644 index 00000000..1c37c123 --- /dev/null +++ b/src/servers/sapphire_zone/Network/PacketWrappers/DirectorPlayScenePacket.h @@ -0,0 +1,55 @@ +#ifndef _DIRECTORPLAYSCENE_H +#define _DIRECTORPLAYSCENE_H + +#include +#include "Forwards.h" + +namespace Core { +namespace Network { +namespace Packets { +namespace Server { + +/** +* @brief The packet sent to play an event. +*/ +class DirectorPlayScenePacket : public ZoneChannelPacket< FFXIVIpcDirectorPlayScene > +{ +public: + DirectorPlayScenePacket( uint32_t playerId, + uint64_t actorId, + uint32_t eventId, + uint16_t scene, + uint32_t flags, + uint8_t param3, + uint32_t param4 = 0, + uint32_t param5 = 0 ) : + ZoneChannelPacket< FFXIVIpcDirectorPlayScene >( playerId, playerId ) + { + initialize( actorId, eventId, scene, flags, param3, param4, param5 ); + }; + +private: + void initialize( uint64_t actorId, + uint32_t eventId, + uint16_t scene, + uint32_t flags, + uint8_t param3, + uint32_t param4, + uint32_t param5 ) + { + m_data.actorId = actorId; + m_data.eventId = eventId; + m_data.scene = scene; + m_data.flags = flags; + m_data.param3 = param3; + m_data.param4 = param4; + m_data.param5 = param5; + }; +}; + +} +} +} +} + +#endif /*_EVENTPLAY_H*/ diff --git a/src/servers/sapphire_zone/Network/PacketWrappers/EventStartPacket.h b/src/servers/sapphire_zone/Network/PacketWrappers/EventStartPacket.h index 2109c856..963c138f 100644 --- a/src/servers/sapphire_zone/Network/PacketWrappers/EventStartPacket.h +++ b/src/servers/sapphire_zone/Network/PacketWrappers/EventStartPacket.h @@ -20,10 +20,11 @@ public: uint32_t eventId, uint8_t param1 = 0, uint8_t param2 = 0, - uint32_t param3 = 0 ) : + uint32_t param3 = 0, + uint32_t contentId = 0 ) : ZoneChannelPacket< FFXIVIpcEventStart >( playerId, playerId ) { - initialize( actorId, eventId, param1, param2, param3 ); + initialize( actorId, eventId, param1, param2, param3, contentId ); }; private: @@ -31,13 +32,15 @@ private: uint32_t eventId, uint8_t param1, uint8_t param2, - uint32_t param3 ) + uint32_t param3, + uint32_t contentId ) { m_data.actorId = actorId; m_data.eventId = eventId; m_data.param1 = param1; m_data.param2 = param2; m_data.param3 = param3; + m_data.contentId = contentId; }; }; diff --git a/src/servers/sapphire_zone/Network/PacketWrappers/MoveActorPacket.h b/src/servers/sapphire_zone/Network/PacketWrappers/MoveActorPacket.h index 1771d801..2ffa31d2 100644 --- a/src/servers/sapphire_zone/Network/PacketWrappers/MoveActorPacket.h +++ b/src/servers/sapphire_zone/Network/PacketWrappers/MoveActorPacket.h @@ -21,17 +21,17 @@ class MoveActorPacket : public ZoneChannelPacket< FFXIVIpcActorMove > { public: - MoveActorPacket( Entity::Actor& actor, uint8_t unk1, uint8_t unk2, uint8_t unk3, uint16_t unk4 ) : + MoveActorPacket( Entity::Chara& actor, uint8_t unk1, uint8_t unk2, uint8_t unk3, uint16_t unk4 ) : ZoneChannelPacket< FFXIVIpcActorMove >( actor.getId(), actor.getId() ) { initialize( actor, unk1, unk2, unk3, unk4 ); }; private: - void initialize( Entity::Actor& actor, uint8_t unk1, uint8_t unk2, uint8_t unk3, uint16_t unk4 ) + void initialize( Entity::Chara& actor, uint8_t unk1, uint8_t unk2, uint8_t unk3, uint16_t unk4 ) { - m_data.rotation = Math::Util::floatToUInt8Rot( actor.getRotation() ); + m_data.rotation = Math::Util::floatToUInt8Rot( actor.getRot() ); m_data.unknown_1 = unk1; m_data.unknown_2 = unk2; m_data.unknown_3 = unk3; diff --git a/src/servers/sapphire_zone/Network/PacketWrappers/PlayerSpawnPacket.h b/src/servers/sapphire_zone/Network/PacketWrappers/PlayerSpawnPacket.h index 9c367aa9..06ecc139 100644 --- a/src/servers/sapphire_zone/Network/PacketWrappers/PlayerSpawnPacket.h +++ b/src/servers/sapphire_zone/Network/PacketWrappers/PlayerSpawnPacket.h @@ -64,7 +64,7 @@ namespace Server { m_data.pos.x = player.getPos().x; m_data.pos.y = player.getPos().y; m_data.pos.z = player.getPos().z; - m_data.rotation = Math::Util::floatToUInt16Rot( player.getRotation() ); + m_data.rotation = Math::Util::floatToUInt16Rot( player.getRot() ); m_data.title = player.getTitle(); @@ -76,7 +76,7 @@ namespace Server { //m_data.u23 = 0x04; //m_data.u24 = 256; m_data.state = static_cast< uint8_t >( player.getStatus() ); - m_data.type = 1; + m_data.modelType = player.getModelType(); if( target.getId() == player.getId() ) { m_data.spawnIndex = 0x00; @@ -84,28 +84,31 @@ namespace Server { else { m_data.spawnIndex = target.getSpawnIdForActorId( player.getId() ); + + if( !target.isActorSpawnIdValid( m_data.spawnIndex ) ) + return; } // 0x20 == spawn hidden to be displayed by the spawneffect control m_data.displayFlags = player.getStance(); if( player.getZoningType() != Common::ZoneingType::None ) { - m_data.displayFlags |= Entity::Actor::DisplayFlags::Invisible; + m_data.displayFlags |= Entity::Chara::DisplayFlags::Invisible; } if( player.getEquipDisplayFlags() & Core::Common::EquipDisplayFlags::HideHead ) { - m_data.displayFlags |= Entity::Actor::DisplayFlags::HideHead; + m_data.displayFlags |= Entity::Chara::DisplayFlags::HideHead; } if( player.getEquipDisplayFlags() & Core::Common::EquipDisplayFlags::HideWeapon ) { - m_data.displayFlags |= Entity::Actor::DisplayFlags::HideWeapon; + m_data.displayFlags |= Entity::Chara::DisplayFlags::HideWeapon; } if( player.getEquipDisplayFlags() & Core::Common::EquipDisplayFlags::Visor ) { - m_data.displayFlags |= Entity::Actor::DisplayFlags::Visor; + m_data.displayFlags |= Entity::Chara::DisplayFlags::Visor; } m_data.currentMount = player.getCurrentMount(); diff --git a/src/servers/sapphire_zone/Network/PacketWrappers/PlayerStateFlagsPacket.h b/src/servers/sapphire_zone/Network/PacketWrappers/PlayerStateFlagsPacket.h index 920a4a80..ac1bf5a5 100644 --- a/src/servers/sapphire_zone/Network/PacketWrappers/PlayerStateFlagsPacket.h +++ b/src/servers/sapphire_zone/Network/PacketWrappers/PlayerStateFlagsPacket.h @@ -26,8 +26,8 @@ public: PlayerStateFlagsPacket( Entity::Player& player, std::vector< Common::PlayerStateFlag > flags ) : ZoneChannelPacket< FFXIVIpcPlayerStateFlags >( player.getId(), player.getId() ) { - uint8_t newFlags[7]; - memset( newFlags, 0, 7 ); + uint8_t newFlags[12]; + memset( newFlags, 0, 12 ); for( auto& flag : flags ) { @@ -46,7 +46,7 @@ public: private: void initialize( const uint8_t* flags ) { - memcpy( m_data.flags, flags, 7 ); + memcpy( m_data.flags, flags, 12 ); }; }; diff --git a/src/servers/sapphire_zone/Network/PacketWrappers/UpdateHpMpTpPacket.h b/src/servers/sapphire_zone/Network/PacketWrappers/UpdateHpMpTpPacket.h index e880a4da..64952593 100644 --- a/src/servers/sapphire_zone/Network/PacketWrappers/UpdateHpMpTpPacket.h +++ b/src/servers/sapphire_zone/Network/PacketWrappers/UpdateHpMpTpPacket.h @@ -2,7 +2,7 @@ #define _UPDATEHPMPTP_H #include -#include +#include #include "Forwards.h" namespace Core { @@ -17,14 +17,14 @@ class UpdateHpMpTpPacket : public ZoneChannelPacket< FFXIVIpcUpdateHpMpTp > { public: - UpdateHpMpTpPacket( Entity::Actor& actor ) : + UpdateHpMpTpPacket( Entity::Chara& actor ) : ZoneChannelPacket< FFXIVIpcUpdateHpMpTp >( actor.getId(), actor.getId() ) { initialize( actor ); }; private: - void initialize( Entity::Actor& actor ) + void initialize( Entity::Chara& actor ) { m_data.hp = actor.getHp(); m_data.mp = actor.getMp(); diff --git a/src/servers/sapphire_zone/Script/NativeScriptApi.h b/src/servers/sapphire_zone/Script/NativeScriptApi.h index 893dc6cd..43edd68e 100644 --- a/src/servers/sapphire_zone/Script/NativeScriptApi.h +++ b/src/servers/sapphire_zone/Script/NativeScriptApi.h @@ -4,11 +4,7 @@ #include #include #include - -#include -#include -#include -#include +#include "Forwards.h" #ifdef _MSC_VER #define EXPORT __declspec( dllexport ) @@ -53,14 +49,14 @@ public: ScriptObject( effectId, typeid( StatusEffectScript ).hash_code() ) { } - virtual void onTick( Entity::Actor& actor ) { } - virtual void onApply( Entity::Actor& actor ) { } - virtual void onRemove( Entity::Actor& actor ) { } - virtual void onExpire( Entity::Actor& actor ) { } - virtual void onPlayerCollision( Entity::Actor& actor, Entity::Actor& actorHit ) { } - virtual void onPlayerFinishCast( Entity::Actor& actor ) { } - virtual void onPlayerDamaged( Entity::Actor& actor ) { } - virtual void onPlayerDeath( Entity::Actor& actor ) { } + virtual void onTick( Entity::Chara& actor ) { } + virtual void onApply( Entity::Chara& actor ) { } + virtual void onRemove( Entity::Chara& actor ) { } + virtual void onExpire( Entity::Chara& actor ) { } + virtual void onPlayerCollision( Entity::Chara& actor, Entity::Chara& actorHit ) { } + virtual void onPlayerFinishCast( Entity::Chara& actor ) { } + virtual void onPlayerDamaged( Entity::Chara& actor ) { } + virtual void onPlayerDeath( Entity::Chara& actor ) { } }; @@ -71,9 +67,9 @@ public: ScriptObject( abilityId, typeid( ActionScript ).hash_code() ) { } - virtual void onStart( Entity::Actor& sourceActor, Entity::Actor& targetActor ) { } - virtual void onCastFinish( Entity::Player& player, Entity::Actor& targetActor ) { } - virtual void onInterrupt( Entity::Actor& sourceActor/*, Core::Entity::Actor targetActor*/ ) { } + virtual void onStart( Entity::Chara& sourceActor, Entity::Chara& targetActor ) { } + virtual void onCastFinish( Entity::Player& player, Entity::Chara& targetActor ) { } + virtual void onInterrupt( Entity::Chara& sourceActor/*, Core::Entity::Chara targetActor*/ ) { } }; @@ -87,7 +83,7 @@ public: virtual void onTalk( uint32_t eventId, Entity::Player& player, uint64_t actorId ) { } virtual void onNpcKill( uint32_t npcId, Entity::Player& player ) { } virtual void onEmote( uint64_t actorId, uint32_t eventId, uint32_t emoteId, Entity::Player& player ) { } - virtual void onEnterZone( Entity::Player& player, uint32_t eventId, uint16_t param1, uint16_t param2 ) { } + virtual void onEnterTerritory( Entity::Player& player, uint32_t eventId, uint16_t param1, uint16_t param2 ) { } virtual void onWithinRange( Entity::Player& player, uint32_t eventId, uint32_t param1, float x, float y, float z ) { } virtual void onOutsideRange( Entity::Player& player, uint32_t eventId, uint32_t param1, float x, float y, float z ) { } virtual void onEventItem( Entity::Player& player, uint32_t eventItemId, uint32_t eventId, uint32_t castTime, uint64_t targetId ) { } @@ -117,11 +113,12 @@ class InstanceContentScript : public ScriptObject { public: explicit InstanceContentScript( uint32_t instanceContentId ) : - ScriptObject( instanceContentId, typeid( InstanceContentScript ).hash_code() ) + ScriptObject( uint32_t{ 0x8003 } << 16 | instanceContentId, typeid( InstanceContentScript ).hash_code() ) { } - virtual void onInit( InstanceContent& instance ) { } - virtual void onUpdate( InstanceContent& instance, uint32_t currTime ) { } + virtual void onInit( InstanceContentPtr instance ) { } + virtual void onUpdate( InstanceContentPtr instance, uint32_t currTime ) { } + virtual void onEnterTerritory( Entity::Player& player, uint32_t eventId, uint16_t param1, uint16_t param2 ) { } }; -#endif \ No newline at end of file +#endif diff --git a/src/servers/sapphire_zone/Script/NativeScriptMgr.cpp b/src/servers/sapphire_zone/Script/NativeScriptMgr.cpp index 1cbf66e4..173d03ae 100644 --- a/src/servers/sapphire_zone/Script/NativeScriptMgr.cpp +++ b/src/servers/sapphire_zone/Script/NativeScriptMgr.cpp @@ -1,4 +1,7 @@ #include "NativeScriptMgr.h" +#include + +#include namespace Core { namespace Scripting { diff --git a/src/servers/sapphire_zone/Script/NativeScriptMgr.h b/src/servers/sapphire_zone/Script/NativeScriptMgr.h index dccb693a..9445bb56 100644 --- a/src/servers/sapphire_zone/Script/NativeScriptMgr.h +++ b/src/servers/sapphire_zone/Script/NativeScriptMgr.h @@ -7,9 +7,6 @@ #include #include -#include - -#include #include "ScriptLoader.h" diff --git a/src/servers/sapphire_zone/Script/ScriptLoader.h b/src/servers/sapphire_zone/Script/ScriptLoader.h index 98334bf1..adcc0ef1 100644 --- a/src/servers/sapphire_zone/Script/ScriptLoader.h +++ b/src/servers/sapphire_zone/Script/ScriptLoader.h @@ -10,7 +10,8 @@ #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN -#include + +#include typedef HMODULE ModuleHandle; #else #include diff --git a/src/servers/sapphire_zone/Script/ScriptMgr.cpp b/src/servers/sapphire_zone/Script/ScriptMgr.cpp index 69981957..cf146040 100644 --- a/src/servers/sapphire_zone/Script/ScriptMgr.cpp +++ b/src/servers/sapphire_zone/Script/ScriptMgr.cpp @@ -9,10 +9,9 @@ #include #include "Zone/Zone.h" - +#include "Zone/InstanceContent.h" #include "Actor/Player.h" -#include "Actor/BattleNpc.h" - +#include "ServerZone.h" #include "Event/EventHandler.h" #include "Event/EventHelper.h" @@ -142,12 +141,6 @@ void Core::Scripting::ScriptMgr::onPlayerFirstEnterWorld( Entity::Player& player // } } -bool Core::Scripting::ScriptMgr::registerBnpcTemplate( std::string templateName, uint32_t bnpcBaseId, - uint32_t bnpcNameId, uint32_t modelId, std::string aiName ) -{ - return g_framework.getServerZone().registerBnpcTemplate( templateName, bnpcBaseId, bnpcNameId, modelId, aiName ); -} - bool Core::Scripting::ScriptMgr::onTalk( Entity::Player& player, uint64_t actorId, uint32_t eventId ) { @@ -176,7 +169,7 @@ bool Core::Scripting::ScriptMgr::onEnterTerritory( Entity::Player& player, uint3 auto script = m_nativeScriptMgr->getScript< EventScript >( eventId ); if( !script ) return false; - script->onEnterZone( player, eventId, param1, param2 ); + script->onEnterTerritory( player, eventId, param1, param2 ); return true; } @@ -278,7 +271,7 @@ bool Core::Scripting::ScriptMgr::onMobKill( Entity::Player& player, uint16_t nam return true; } -bool Core::Scripting::ScriptMgr::onCastFinish( Entity::Player& player, Entity::ActorPtr pTarget, uint32_t actionId ) +bool Core::Scripting::ScriptMgr::onCastFinish( Entity::Player& player, Entity::CharaPtr pTarget, uint32_t actionId ) { auto script = m_nativeScriptMgr->getScript< ActionScript >( actionId ); @@ -287,7 +280,7 @@ bool Core::Scripting::ScriptMgr::onCastFinish( Entity::Player& player, Entity::A return true; } -bool Core::Scripting::ScriptMgr::onStatusReceive( Entity::ActorPtr pActor, uint32_t effectId ) +bool Core::Scripting::ScriptMgr::onStatusReceive( Entity::CharaPtr pActor, uint32_t effectId ) { auto script = m_nativeScriptMgr->getScript< StatusEffectScript >( effectId ); @@ -303,30 +296,30 @@ bool Core::Scripting::ScriptMgr::onStatusReceive( Entity::ActorPtr pActor, uint3 return false; } -bool Core::Scripting::ScriptMgr::onStatusTick( Entity::ActorPtr pActor, Core::StatusEffect::StatusEffect& effect ) +bool Core::Scripting::ScriptMgr::onStatusTick( Entity::CharaPtr pChara, Core::StatusEffect::StatusEffect& effect ) { auto script = m_nativeScriptMgr->getScript< StatusEffectScript >( effect.getId() ); if( script ) { - if( pActor->isPlayer() ) - pActor->getAsPlayer()->sendDebug( "Calling status tick for statusid: " + std::to_string( effect.getId() ) ); + if( pChara->isPlayer() ) + pChara->getAsPlayer()->sendDebug( "Calling status tick for statusid: " + std::to_string( effect.getId() ) ); - script->onTick( *pActor ); + script->onTick( *pChara ); return true; } return false; } -bool Core::Scripting::ScriptMgr::onStatusTimeOut( Entity::ActorPtr pActor, uint32_t effectId ) +bool Core::Scripting::ScriptMgr::onStatusTimeOut( Entity::CharaPtr pChara, uint32_t effectId ) { auto script = m_nativeScriptMgr->getScript< StatusEffectScript >( effectId ); if( script ) { - if( pActor->isPlayer() ) - pActor->getAsPlayer()->sendDebug( "Calling status timeout for statusid: " + std::to_string( effectId ) ); + if( pChara->isPlayer() ) + pChara->getAsPlayer()->sendDebug( "Calling status timeout for statusid: " + std::to_string( effectId ) ); - script->onExpire( *pActor ); + script->onExpire( *pChara ); return true; } @@ -345,9 +338,9 @@ bool Core::Scripting::ScriptMgr::onZoneInit( ZonePtr pZone ) return false; } -bool Core::Scripting::ScriptMgr::onInstanceInit( InstanceContent& instance ) +bool Core::Scripting::ScriptMgr::onInstanceInit( InstanceContentPtr instance ) { - auto script = m_nativeScriptMgr->getScript< InstanceContentScript >( instance.getInstanceContentId() ); + auto script = m_nativeScriptMgr->getScript< InstanceContentScript >( instance->getDirectorId() ); if( script ) { script->onInit( instance ); @@ -357,9 +350,10 @@ bool Core::Scripting::ScriptMgr::onInstanceInit( InstanceContent& instance ) return false; } -bool Core::Scripting::ScriptMgr::onInstanceUpdate( InstanceContent& instance, uint32_t currTime ) +bool Core::Scripting::ScriptMgr::onInstanceUpdate( InstanceContentPtr instance, uint32_t currTime ) { - auto script = m_nativeScriptMgr->getScript< InstanceContentScript >( instance.getInstanceContentId() ); + auto script = m_nativeScriptMgr->getScript< InstanceContentScript >( instance->getDirectorId() ); + if( script ) { script->onUpdate( instance, currTime ); @@ -369,6 +363,18 @@ bool Core::Scripting::ScriptMgr::onInstanceUpdate( InstanceContent& instance, ui return false; } +bool Core::Scripting::ScriptMgr::onInstanceEnterTerritory( InstanceContentPtr instance, Entity::Player& player, uint32_t eventId, uint16_t param1, uint16_t param2 ) +{ + auto script = m_nativeScriptMgr->getScript< InstanceContentScript >( instance->getDirectorId() ); + if( script ) + { + script->onEnterTerritory( player, eventId, param1, param2 ); + return true; + } + + return false; +} + Scripting::NativeScriptMgr& Core::Scripting::ScriptMgr::getNativeScriptHandler() { return *m_nativeScriptMgr; diff --git a/src/servers/sapphire_zone/Script/ScriptMgr.h b/src/servers/sapphire_zone/Script/ScriptMgr.h index 03e0284b..e80b1e1b 100644 --- a/src/servers/sapphire_zone/Script/ScriptMgr.h +++ b/src/servers/sapphire_zone/Script/ScriptMgr.h @@ -37,8 +37,6 @@ namespace Core void onPlayerFirstEnterWorld( Entity::Player& player ); - static bool registerBnpcTemplate( std::string templateName, uint32_t bnpcBaseId, uint32_t bnpcNameId, uint32_t modelId, std::string aiName ); - bool onTalk( Entity::Player& player, uint64_t actorId, uint32_t eventId ); bool onEnterTerritory( Entity::Player& player, uint32_t eventId, uint16_t param1, uint16_t param2 ); bool onWithinRange( Entity::Player& player, uint32_t eventId, uint32_t param1, float x, float y, float z ); @@ -48,19 +46,20 @@ namespace Core bool onMobKill( Entity::Player& player, uint16_t nameId ); - bool onCastFinish( Entity::Player& pPlayer, Entity::ActorPtr pTarget, uint32_t actionId ); + bool onCastFinish( Entity::Player& pPlayer, Entity::CharaPtr pTarget, uint32_t actionId ); - bool onStatusReceive( Entity::ActorPtr pActor, uint32_t effectId ); - bool onStatusTick( Entity::ActorPtr pActor, Core::StatusEffect::StatusEffect& effect ); - bool onStatusTimeOut( Entity::ActorPtr pActor, uint32_t effectId ); + bool onStatusReceive( Entity::CharaPtr pActor, uint32_t effectId ); + bool onStatusTick( Entity::CharaPtr pActor, Core::StatusEffect::StatusEffect& effect ); + bool onStatusTimeOut( Entity::CharaPtr pActor, uint32_t effectId ); bool onZoneInit( ZonePtr pZone ); bool onEventHandlerReturn( Entity::Player& player, uint32_t eventId, uint16_t subEvent, uint16_t param1, uint16_t param2, uint16_t param3 ); bool onEventHandlerTradeReturn( Entity::Player& player, uint32_t eventId, uint16_t subEvent, uint16_t param, uint32_t catalogId ); - bool onInstanceInit( InstanceContent& instance ); - bool onInstanceUpdate( InstanceContent& instance, uint32_t currTime ); + bool onInstanceInit( InstanceContentPtr instance ); + bool onInstanceUpdate( InstanceContentPtr instance, uint32_t currTime ); + bool onInstanceEnterTerritory( InstanceContentPtr instance, Entity::Player& player, uint32_t eventId, uint16_t param1, uint16_t param2 ); void loadDir( const std::string& dirname, std::set &files, const std::string& ext ); diff --git a/src/servers/sapphire_zone/Script/Scripts/CMakeLists.txt b/src/servers/sapphire_zone/Script/Scripts/CMakeLists.txt index 03c76b3c..419d7884 100644 --- a/src/servers/sapphire_zone/Script/Scripts/CMakeLists.txt +++ b/src/servers/sapphire_zone/Script/Scripts/CMakeLists.txt @@ -39,8 +39,13 @@ foreach(_scriptDir ${children}) endif() endforeach() - add_library("script_${_name}" MODULE "${SCRIPT_BUILD_FILES}" "${SCRIPT_INCLUDE_FILES}" "${_scriptDir}/ScriptLoader.cpp") + add_library("script_${_name}" MODULE ${SCRIPT_BUILD_FILES} "${SCRIPT_INCLUDE_FILES}" "${_scriptDir}/ScriptLoader.cpp") target_link_libraries("script_${_name}" sapphire_zone) + cotire("script_${_name}") + + if(MSVC) + target_link_libraries("script_${_name}" ${Boost_LIBRARIES}) + endif() if(MSVC) set_target_properties("script_${_name}" PROPERTIES diff --git a/src/servers/sapphire_zone/Script/Scripts/ScriptObject.h b/src/servers/sapphire_zone/Script/Scripts/ScriptObject.h index 17da1520..a2ad2f9d 100644 --- a/src/servers/sapphire_zone/Script/Scripts/ScriptObject.h +++ b/src/servers/sapphire_zone/Script/Scripts/ScriptObject.h @@ -1,11 +1,8 @@ #ifndef SAPPHIRE_SCRIPTOBJECT_H #define SAPPHIRE_SCRIPTOBJECT_H -#include