Compare commits

..

No commits in common. "base" and "map_exit" have entirely different histories.

3157 changed files with 131627 additions and 144267 deletions

View file

@ -1,7 +1,7 @@
---
# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
# We base things of WebKit + Allman braces
# Compatible with clang-format-22 (enforced by tools/run_clang_format.py)
# Compatible with clang-format-20
Language: Cpp
# Access modifiers
@ -28,7 +28,8 @@ AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: Never
AllowShortLambdasOnASingleLine: None
AllowShortLoopsOnASingleLine: false
LambdaBodyIndentation: Signature
# Note: LambdaBodyIndentation not available in clang-format-20
# Template declarations
AlwaysBreakTemplateDeclarations: Yes
@ -70,8 +71,6 @@ BinPackParameters: false
# Namespaces
CompactNamespaces: false
NamespaceIndentation: None
WrapNamespaceBodyWithEmptyLines: Always
SeparateDefinitionBlocks: Always
# Braced lists and initializers
Cpp11BracedListStyle: false
@ -90,7 +89,8 @@ MaxEmptyLinesToKeep: 1
# Pointer and reference alignment
PointerAlignment: Left
ReferenceAlignment: Pointer
QualifierAlignment: Left # West Const
# Note: QualifierAlignment not available in clang-format-20, use manual const placement
# Comments
ReflowComments: Always

2
.gitattributes vendored
View file

@ -12,8 +12,6 @@
*.h text eol=lf
*.sql text eol=lf
*.sh text eol=lf
*.yaml text eol=lf
*.yml text eol=lf
# Retaining CRLF line ends in the documentation dir (for now..)
*.txt text eol=crlf

View file

@ -41,7 +41,7 @@ jobs:
- name: Install Dependencies
run: |
sudo apt-get update
sudo apt-get install -y software-properties-common cmake libmariadb-dev-compat libluajit-5.1-dev libzmq3-dev zlib1g-dev libssl-dev binutils-dev libzstd-dev libdwarf-dev
sudo apt-get install -y software-properties-common cmake libmariadb-dev-compat libluajit-5.1-dev libzmq3-dev zlib1g-dev libssl-dev binutils-dev libzstd-dev
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL

View file

@ -119,7 +119,7 @@ jobs:
- name: Run xi_test
id: xi_test
continue-on-error: true
run: docker exec test-server /bin/bash -c './xi_test --keep-going --retry 5 --output log/tests.ctrf.json'
run: docker exec test-server /bin/bash -c './xi_test --keep-going --output log/tests.ctrf.json'
- name: Publish Test Report
if: ${{ !cancelled() }}

View file

@ -196,50 +196,31 @@ jobs:
id: changed
run: |
mapfile -t changed_files < changed-files.txt
yaml_files=()
other_files=()
data_files=()
for f in "${changed_files[@]}"; do
if [[ $f == data/*.yaml || $f == data/*.yml ]]; then
yaml_files+=("$f")
elif [[ $f == data/* ]]; then
other_files+=("$f")
if [[ $f == data/* ]]; then
data_files+=("$f")
fi
done
echo "yaml=${yaml_files[*]}" >> "$GITHUB_OUTPUT"
echo "other=${other_files[*]}" >> "$GITHUB_OUTPUT"
echo "Changed YAML: ${yaml_files[*]}"
echo "Changed other: ${other_files[*]}"
echo "files=${data_files[*]}" >> "$GITHUB_OUTPUT"
echo "Changed data files: ${data_files[*]}"
- uses: actions/checkout@v6
if: steps.changed.outputs.yaml != '' || steps.changed.outputs.other != ''
- name: Setup Python
if: steps.changed.outputs.yaml != ''
uses: actions/setup-python@v6
with:
python-version: "3.14"
- name: Install Python dependencies
if: steps.changed.outputs.yaml != ''
run: python -m pip install pyyaml ruamel.yaml
- name: Check YAML formatting
if: steps.changed.outputs.yaml != ''
run: python tools/yaml/format.py ${{ steps.changed.outputs.yaml }} --check
if: steps.changed.outputs.files != ''
- name: Cache prettier results
if: steps.changed.outputs.other != ''
if: steps.changed.outputs.files != ''
uses: actions/cache@v5
with:
path: /tmp/prettier-cache
key: prettier-cache-${{ hashFiles('data/**/*.json') }}
key: prettier-cache-${{ hashFiles('data/**/*.yaml', 'data/**/*.json') }}
restore-keys: prettier-cache-
- name: Run prettier
if: steps.changed.outputs.other != ''
if: steps.changed.outputs.files != ''
run: |
set +e
FAILED=$(npx --yes prettier@3 --cache --cache-strategy content --cache-location /tmp/prettier-cache --list-different ${{ steps.changed.outputs.other }})
FAILED=$(npx --yes prettier@3 --cache --cache-strategy content --cache-location /tmp/prettier-cache --list-different ${{ steps.changed.outputs.files }})
set -e
if [ -z "$FAILED" ]; then
echo "All files use Prettier code style!"
@ -316,7 +297,7 @@ jobs:
- runner: macos-26
test_modules: false
multi_process: false
xi_test: true
xi_test: false
startup_checks: true
- runner: ubuntu-26.04
test_modules: false

View file

@ -99,7 +99,7 @@ jobs:
- name: Install dependencies (macOS)
if: ${{ runner.os == 'macOS' && env.SKIP_BUILD != 'true' }}
run: |
brew install --quiet \
brew update && brew install --quiet \
mariadb \
zeromq \
luajit \
@ -108,7 +108,7 @@ jobs:
- name: Setup LLVM Clang (macOS)
if: ${{ runner.os == 'macOS' && startsWith(inputs.compiler, 'clang') && env.SKIP_BUILD != 'true' }}
run: |
brew install --quiet \
brew update && brew install --quiet \
llvm@${{ inputs.compiler_version }} \
lld@${{ inputs.compiler_version }}
@ -128,7 +128,7 @@ jobs:
- name: Setup GCC (macOS)
if: ${{ runner.os == 'macOS' && startsWith(inputs.compiler, 'gcc') && env.SKIP_BUILD != 'true' }}
run: |
brew install --quiet gcc@${{ inputs.compiler_version }}
brew update && brew install --quiet gcc@${{ inputs.compiler_version }}
GCC_PREFIX=$(brew --prefix gcc@${{ inputs.compiler_version }})
echo "PATH=$GCC_PREFIX/bin:$PATH" >> $GITHUB_ENV
echo "CC=$GCC_PREFIX/bin/gcc-${{ inputs.compiler_version }}" >> $GITHUB_ENV
@ -139,7 +139,6 @@ jobs:
run: |
sudo apt-get update && sudo apt-get install --assume-yes --no-install-recommends --quiet \
binutils-dev \
libdwarf-dev \
libluajit-5.1-dev \
libmariadb-dev-compat \
libssl-dev \
@ -243,15 +242,6 @@ jobs:
xi_*
tracy.tar.gz
- name: Remove exes from build cache
if: ${{ inputs.save_cache && env.SKIP_BUILD != 'true' && steps.restore_build.outputs.cache-hit != 'true' }}
run: |
rm -f build/src/login/xi_connect*
rm -f build/src/map/xi_map*
rm -f build/src/search/xi_search*
rm -f build/src/test/xi_test*
rm -f build/src/world/xi_world*
- name: Cache build
if: ${{ inputs.save_cache && env.SKIP_BUILD != 'true' && steps.restore_build.outputs.cache-hit != 'true' }}
uses: actions/cache/save@v5

View file

@ -80,7 +80,6 @@ jobs:
sudo apt-get update && sudo apt-get install --assume-yes --no-install-recommends --quiet \
binutils \
git \
libdwarf1 \
libmariadb-dev-compat \
libzmq5 \
lua5.1 \
@ -168,7 +167,7 @@ jobs:
if: ${{ inputs.xi_test }}
id: xi_test
continue-on-error: true
run: ./xi_test --keep-going --retry 5 --output tests.ctrf.json
run: ./xi_test --keep-going --output tests.ctrf.json
- name: Publish Test Report
if: ${{ !cancelled() && inputs.xi_test }}

View file

@ -101,7 +101,7 @@ jobs:
build_type: Release
test_modules: false
multi_process: true
xi_test: true
xi_test: ${{ !startsWith(matrix.runner, 'macos') }}
startup_checks: true
Docker_Test:

View file

@ -1,10 +1,6 @@
*.codegen.json
*.codegen.lua
# YAML is formatted by tools/yaml/format.py
*.yaml
*.yml
build/
build*/
cmake-*/

View file

@ -2,5 +2,14 @@
"tabWidth": 2,
"useTabs": false,
"printWidth": 120,
"endOfLine": "lf"
"endOfLine": "lf",
"overrides": [
{
"files": ["**/*.yaml", "**/*.yml"],
"options": {
"singleQuote": false,
"bracketSpacing": true
}
}
]
}

View file

@ -13,7 +13,6 @@
"jeff-hykin.better-cpp-syntax",
"bierner.github-markdown-preview",
"esbenp.prettier-vscode",
"redhat.vscode-yaml",
"jkillian.custom-local-formatters"
"redhat.vscode-yaml"
]
}

View file

@ -356,19 +356,12 @@
"editor.formatOnSave": true
},
"[yaml]": {
"editor.defaultFormatter": "jkillian.custom-local-formatters",
"editor.formatOnSave": true
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"yaml.format.enable": false,
"customLocalFormatters.formatters": [
{
"command": "python tools/yaml/format.py --stdin",
"languages": ["yaml"]
}
],
"files.associations": {
"algorithm": "cpp",
"any": "cpp",

View file

@ -4,35 +4,15 @@ cmake_minimum_required(VERSION 3.25)
cmake_policy(SET CMP0141 NEW)
set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT Embedded)
# On macOS, pin the deployment target to the host OS version (must happen before
# project()). Without this, some compilers infer it from the Darwin kernel version
# using the pre-macOS-26 numbering scheme (e.g. Homebrew LLVM 20 maps Darwin 25 to
# "macOS 16.0" instead of 26), and every link against a Homebrew dylib stamped with
# the real version warns:
# ld: warning: building for macOS-16.0, but linking with dylib built for newer version 26.0
# Overridable as usual via -DCMAKE_OSX_DEPLOYMENT_TARGET or MACOSX_DEPLOYMENT_TARGET.
if(CMAKE_HOST_APPLE AND "${CMAKE_OSX_DEPLOYMENT_TARGET}" STREQUAL "" AND "$ENV{MACOSX_DEPLOYMENT_TARGET}" STREQUAL "")
execute_process(
COMMAND sw_vers -productVersion
OUTPUT_VARIABLE host_macos_version
OUTPUT_STRIP_TRAILING_WHITESPACE
)
string(REGEX MATCH "^[0-9]+" host_macos_major "${host_macos_version}")
if(host_macos_major)
set(CMAKE_OSX_DEPLOYMENT_TARGET "${host_macos_major}.0")
endif()
endif()
project(server C CXX)
# https://cmake.org/cmake/help/latest/policy/CMP0069.html
cmake_policy(SET CMP0069 NEW)
set(CMAKE_POLICY_DEFAULT_CMP0069 NEW)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_SCAN_FOR_MODULES OFF)
set(LINKER_LANGUAGE CXX)
set(USE_FOLDERS ON)
@ -139,32 +119,22 @@ endif()
# Link this 'library' to set the c++ standard / compile-time options requested
add_library(project_options INTERFACE)
target_compile_features(project_options INTERFACE cxx_std_23)
# Windows-only preprocessor defines for our first-party code ONLY.
if(WIN32)
target_compile_definitions(project_options INTERFACE
NOMINMAX
WIN32_LEAN_AND_MEAN
_CRT_SECURE_NO_WARNINGS
)
endif()
target_compile_features(project_options INTERFACE cxx_std_20)
# Link this 'library' to use the warnings specified in CompilerWarnings.cmake
add_library(project_warnings INTERFACE)
set_project_warnings(project_warnings)
add_compile_definitions(
# Globally define SOL_ALL_SAFETIES_ON so sol can be included anywhere.
SOL_ALL_SAFETIES_ON=1
# If SOL_NO_CHECK_NUMBER_PRECISION is defined, turns off number precision and integer
# precision fitting when pushing numbers into sol.
SOL_NO_CHECK_NUMBER_PRECISION=1
SOL_DEFAULT_PASS_ON_ERROR=1
SOL_PRINT_ERRORS=0
SPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_DEBUG
XI_CMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}"
XI_BUILD_TYPE="$<CONFIG>"
# Globally define SOL_ALL_SAFETIES_ON so sol can be included anywhere
# If SOL_NO_CHECK_NUMBER_PRECISION is defined, turns off number precision and integer
# precision fitting when pushing numbers into sol
# add_compile_definitions() comes with CMake 3.12
add_definitions(
-DSOL_ALL_SAFETIES_ON=1
-DSOL_NO_CHECK_NUMBER_PRECISION=1
-DSOL_DEFAULT_PASS_ON_ERROR=1
-DSOL_PRINT_ERRORS=0
-DSPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_DEBUG
)
# For external libs

View file

@ -81,7 +81,6 @@ function(set_project_warnings project_name)
CLANG_WARNINGS
-Wno-nan-infinity-disabled # TODO: fmt triggers this, a combination of the fast-math flag and `isfinite`
-Wunused-private-field # warn on unused private fields
-Wno-nontrivial-memcall # recastnavigation memsets non-trivially-copyable types (e.g. dtMeshTile)
)
endif()

View file

@ -1,12 +1,12 @@
message(STATUS "CMAKE_SOURCE_DIR: ${CMAKE_SOURCE_DIR}")
if(${CMAKE_SOURCE_DIR} MATCHES " +")
set(STRIPPED_PATH "")
STRING(REGEX REPLACE " +" "_" STRIPPED_PATH "${CMAKE_SOURCE_DIR}")
set(STRIPPED_PATH "")
STRING(REGEX REPLACE " +" "_" STRIPPED_PATH "${CMAKE_SOURCE_DIR}")
message(STATUS
"Current path: ${CMAKE_SOURCE_DIR}\n"
"Suggested path: ${STRIPPED_PATH}\n"
"Your path contains spaces, this is not recommended.")
message(STATUS
"Current path: ${CMAKE_SOURCE_DIR}\n"
"Suggested path: ${STRIPPED_PATH}\n"
"Your path contains spaces, this is not recommended.")
endif()
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
@ -33,15 +33,3 @@ set(libpath "lib${platform_suffix}")
if(WIN32)
set(CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH OFF)
endif()
if(APPLE)
# The modern Apple linker (ld_prime, Xcode 15+) warns when the same library
# appears more than once on the link line. CMake's dependency propagation
# legitimately repeats static libs (historically required by classic ld64),
# so the warning is pure noise: silence it where the linker supports the flag.
include(CheckLinkerFlag)
check_linker_flag(CXX "-Wl,-no_warn_duplicate_libraries" LINKER_SUPPORTS_NO_WARN_DUPLICATE_LIBRARIES)
if(LINKER_SUPPORTS_NO_WARN_DUPLICATE_LIBRARIES)
add_link_options("-Wl,-no_warn_duplicate_libraries")
endif()
endif()

View file

@ -51,6 +51,8 @@ if(MSVC)
list(APPEND FLAGS_AND_DEFINES
-D_CONSOLE
-D_MBCS
-DNOMINMAX
-D_CRT_SECURE_NO_WARNINGS
-D_CRT_NONSTDC_NO_DEPRECATE
# TODO: This is being overwritten by /Ob0
# /Ob2 # Inline Function Expansion
@ -76,7 +78,7 @@ if(MSVC)
)
endif()
link_libraries(ws2_32 dbghelp shlwapi winmm shell32 user32)
link_libraries(WS2_32 dbghelp Shlwapi)
endif()
if(UNIX)
@ -115,25 +117,6 @@ function(set_target_output_directory target)
COMMAND_EXPAND_LISTS
VERBATIM)
endif()
if(APPLE)
# Under LTO the linker merges codegen objects into temp files and deletes after linking.
# We need to preserve those for the next step!
# -object_path_lto persists those codegen objects to a real, per-target directory so
# dsymutil can gather their DWARF into the .dSYM.
if(CMAKE_INTERPROCEDURAL_OPTIMIZATION)
set(lto_object_dir "${CMAKE_BINARY_DIR}/lto-objects/${target}")
file(MAKE_DIRECTORY "${lto_object_dir}")
target_link_options(${target} PRIVATE "-Wl,-object_path_lto,${lto_object_dir}")
endif()
# dsymutil consolidates the DWARF into a self-contained .dSYM that travels with the
# binary, and atos picks it up automatically.
add_custom_command(TARGET ${target} POST_BUILD
COMMAND "$<$<CONFIG:Debug,RelWithDebInfo>:dsymutil;$<TARGET_FILE:${target}>;-o;${CMAKE_SOURCE_DIR}/$<TARGET_FILE_NAME:${target}>.dSYM>"
COMMAND_EXPAND_LISTS
VERBATIM)
endif()
endfunction()
function(disable_lto target)

View file

@ -1,16 +0,0 @@
# yaml-language-server: $schema=../schemas/enums/_enum.schema.json
meta:
cpp:
underlying: uint8_t
lua:
table: xi.allegiance
values:
mob: 0
player: 1
san_doria: 2
bastok: 3
windurst: 4
wyverns: 5
griffons: 6

View file

@ -1,53 +0,0 @@
# yaml-language-server: $schema=../schemas/enums/_enum.schema.json
meta:
cpp:
underlying: uint8_t
lua:
table: xi.animation
values:
none: 0
attack: 1
despawn: 2
death: 3
event: 4
chocobo: 5
fishing: 6
open_door: 8
close_door: 9
elevator_up: 10
elevator_down: 11
fishing_npc: 32
healing: 33
fishing_fish: 38
fishing_caught: 39
fishing_rod_break: 40
fishing_line_break: 41
fishing_monster: 42
fishing_stop: 43
synth: 44
sit: 47
ranged: 48
fishing_start: 50
new_fishing_start: 56
new_fishing_fish: 57
new_fishing_caught: 58
new_fishing_rod_break: 59
new_fishing_line_break: 60
new_fishing_monster: 61
new_fishing_stop: 62
# 63 through 73 are used with /sitchair
sitchair_0: 63
sitchair_1: 64
sitchair_2: 65
sitchair_3: 66
sitchair_4: 67
sitchair_5: 68
sitchair_6: 69
sitchair_7: 70
sitchair_8: 71
sitchair_9: 72
sitchair_10: 73
mount: 85
trust: 90 # trust NPC spawn-in animation

View file

@ -1,15 +0,0 @@
# yaml-language-server: $schema=../schemas/enums/_enum.schema.json
meta:
cpp:
underlying: uint8_t
lua:
table: xi.attackType
values:
none: 0
physical: 1
magical: 2
ranged: 3
breath: 4
special: 5

View file

@ -1,17 +0,0 @@
# yaml-language-server: $schema=../schemas/enums/_enum.schema.json
meta:
flags: true
cpp:
underlying: uint16_t
lua:
table: xi.behavior
values:
none: 0x000
no_despawn: 0x001 # mob does not despawn on death
standback: 0x002 # mob will standback forever
raisable: 0x004 # mob can be raised via Raise spells
no_assist: 0x008 # mob can not be targeted by helpful magic from players (cure, protect, etc)
aggro_ambush: 0x200 # mob aggroes by ambush
no_turn: 0x400 # mob does not turn to face target

View file

@ -1,12 +0,0 @@
# yaml-language-server: $schema=../schemas/enums/_enum.schema.json
meta:
cpp:
underlying: uint8_t
lua:
table: xi.claimType
values:
exclusive: 0 # Regular exclusive claim behavior. Only one entity and related group can attack.
non_exclusive: 1 # Regular claim behavior but multiple unrelated entities can attack and compete for claim. Rewards distributed to last claiming entity.
unclaimable: 2 # Mob cannot be claimed. Multiple unrelated entities can attack. Rewards will not be distributed.

View file

@ -7,17 +7,17 @@ meta:
table: xi.damageType
values:
none: 0
piercing: 1
slashing: 2
blunt: 3
none: 0
piercing: 1
slashing: 2
blunt: 3
hand_to_hand: 4
elemental: 5
fire: 6
ice: 7
wind: 8
earth: 9
thunder: 10
water: 11
light: 12
dark: 13
elemental: 5
fire: 6
ice: 7
wind: 8
earth: 9
thunder: 10
water: 11
light: 12
dark: 13

View file

@ -1,21 +0,0 @@
# yaml-language-server: $schema=../schemas/enums/_enum.schema.json
meta:
flags: true
cpp:
underlying: uint16_t
lua:
table: xi.detects
values:
none: 0x000
sight: 0x001
hearing: 0x002
sight_and_hearing: 0x003
lowhp: 0x004
none1: 0x008
none2: 0x010
magic: 0x020
weaponskill: 0x040
jobability: 0x080
scent: 0x100

View file

@ -7,26 +7,26 @@ meta:
table: xi.ecosystem
values:
unclassified: 0
amorph: 1
aquan: 2
arcana: 3
unclassified: 0
amorph: 1
aquan: 2
arcana: 3
archaic_machine: 4
beast: 5
beastmen: 6
bird: 7
demon: 8
dragon: 9
elemental: 10
empty: 11
humanoid: 12
lizard: 13
luminian: 14
luminion: 15
plantoid: 16
supreme_beings: 17
undead: 18
vermin: 19
voragean: 20
structures: 21
weapons: 22
beast: 5
beastmen: 6
bird: 7
demon: 8
dragon: 9
elemental: 10
empty: 11
humanoid: 12
lizard: 13
luminian: 14
luminion: 15
plantoid: 16
supreme_beings: 17
undead: 18
vermin: 19
voragean: 20
structures: 21
weapons: 22

View file

@ -7,9 +7,9 @@ meta:
table: xi.effectOverwrite
values:
equal_higher: 0 # only overwrite if equal or higher (tier, power)
higher: 1 # only overwrite if higher (tier, power)
never: 2 # never overwrite
always: 3 # always overwrite no matter
equal_higher: 0 # only overwrite if equal or higher (tier, power)
higher: 1 # only overwrite if higher (tier, power)
never: 2 # never overwrite
always: 3 # always overwrite no matter
ignore_duplicate: 4 # ignore dupes
tier_higher: 5 # only overwrite if tier is higher (regardless of power)
tier_higher: 5 # only overwrite if tier is higher (regardless of power)

View file

@ -7,12 +7,12 @@ meta:
table: xi.element
values:
none: 0
fire: 1
ice: 2
wind: 3
earth: 4
none: 0
fire: 1
ice: 2
wind: 3
earth: 4
thunder: 5
water: 6
light: 7
dark: 8
water: 6
light: 7
dark: 8

View file

@ -1,21 +0,0 @@
# yaml-language-server: $schema=../schemas/enums/_enum.schema.json
meta:
flags: true
cpp:
underlying: uint32_t
lua:
table: xi.entityFlags
values:
none: 0x000
info_icon: 0x001 # (I) Icon next to name
# TODO: Flags 0x002, 0x004 and 0x008 do different things for different entities.
# : It isn't one-size-fits-all, and different combinations may do different things.
# : It'll need to be researched more.
# alt_appearance: 0x002
hide_name: 0x008
call_for_help: 0x020
hide_model: 0x080
hide_hp: 0x100
untargetable: 0x800

View file

@ -1,29 +0,0 @@
# yaml-language-server: $schema=../schemas/enums/_enum.schema.json
meta:
flags: true
cpp:
underlying: uint32_t
lua:
table: xi.immunity
values:
none: 0x00000000
addle: 0x00000001
gravity: 0x00000002
bind: 0x00000004
stun: 0x00000008
silence: 0x00000010
paralyze: 0x00000020
blind: 0x00000040
slow: 0x00000080
poison: 0x00000100
elegy: 0x00000200
requiem: 0x00000400
light_sleep: 0x00000800
dark_sleep: 0x00001000
aspir: 0x00002000
terror: 0x00004000
dispel: 0x00008000
petrify: 0x00010000
plague: 0x00020000

View file

@ -1,34 +0,0 @@
# yaml-language-server: $schema=../schemas/enums/_enum.schema.json
meta:
cpp:
underlying: uint8_t
case: screaming
lua:
table: xi.job
values:
none: 0
war: 1
mnk: 2
whm: 3
blm: 4
rdm: 5
thf: 6
pld: 7
drk: 8
bst: 9
brd: 10
rng: 11
sam: 12
nin: 13
drg: 14
smn: 15
blu: 16
cor: 17
pup: 18
dnc: 19
sch: 20
geo: 21
run: 22
mon: 23 # NOTE: MON is not a full job

View file

@ -7,69 +7,67 @@ meta:
table: xi.latent
values:
hp_under_percent: 0 # hp less than or equal to % - PARAM: HP PERCENT
hp_over_percent: 1 # hp more than % - PARAM: HP PERCENT
hp_under_tp_under_100: 2 # hp less than or equal to %, tp under 100 - PARAM: HP PERCENT
hp_over_tp_under_100: 3 # hp more than %, tp over 100 - PARAM: HP PERCENT
mp_under_percent: 4 # mp less than or equal to % - PARAM: MP PERCENT
mp_under: 5 # mp less than # - PARAM: MP #
tp_under: 6 # tp under # and during WS - PARAM: TP VALUE
tp_over: 7 # tp over # - PARAM: TP VALUE
subjob: 8 # subjob - PARAM: JOBTYPE
pet_id: 9 # pettype - PARAM: PETID
weapon_drawn: 10 # weapon drawn
weapon_sheathed: 11 # weapon sheathed
signet_bonus: 12 # While in conquest region and engaged to an even match or less target
status_effect_active: 13 # status effect on player - PARAM: EFFECTID
no_food_active: 14 # no food effects active on player
party_members: 15 # party size # - PARAM: # OF MEMBERS
party_members_in_zone: 16 # party size # and members in zone - PARAM: # OF MEMBERS
sanction_regen_bonus: 17 # While in besieged region and HP is less than PARAM%
hp_under_percent: 0 # hp less than or equal to % - PARAM: HP PERCENT
hp_over_percent: 1 # hp more than % - PARAM: HP PERCENT
hp_under_tp_under_100: 2 # hp less than or equal to %, tp under 100 - PARAM: HP PERCENT
hp_over_tp_under_100: 3 # hp more than %, tp over 100 - PARAM: HP PERCENT
mp_under_percent: 4 # mp less than or equal to % - PARAM: MP PERCENT
mp_under: 5 # mp less than # - PARAM: MP #
tp_under: 6 # tp under # and during WS - PARAM: TP VALUE
tp_over: 7 # tp over # - PARAM: TP VALUE
subjob: 8 # subjob - PARAM: JOBTYPE
pet_id: 9 # pettype - PARAM: PETID
weapon_drawn: 10 # weapon drawn
weapon_sheathed: 11 # weapon sheathed
signet_bonus: 12 # While in conquest region and engaged to an even match or less target
status_effect_active: 13 # status effect on player - PARAM: EFFECTID
no_food_active: 14 # no food effects active on player
party_members: 15 # party size # - PARAM: # OF MEMBERS
party_members_in_zone: 16 # party size # and members in zone - PARAM: # OF MEMBERS
sanction_regen_bonus: 17 # While in besieged region and HP is less than PARAM%
sanction_refresh_bonus: 18 # While in besieged region and MP is less than PARAM%
sigil_regen_bonus: 19 # While in campaign region and HP is less than PARAM%
sigil_refresh_bonus: 20 # While in campaign region and MP is less than PARAM%
avatar_in_party: 21 # party has a specific avatar - PARAM: same as globals/pets.lua (21 for any avatar)
job_in_party: 22 # party has job - PARAM: JOBTYPE
zone: 23 # in zone - PARAM: zoneid
synth_trainee: 24 # synth skill under 40 + no support: PARAM: 48: FISH, 49: WOOD, 50: SMITH, 51: GOLDSMITH, 52: CLOTH, 53: LEATHER, 54: BONE, 55: ALCHEMY, 56: COOKING
song_roll_active: 25 # any song or roll active
time_of_day: 26 # PARAM: 0: DAYTIME 1: NIGHTTIME 2: DUSK-DAWN
hour_of_day: 27 # PARAM: 1: NEW DAY, 2: DAWN, 3: DAY, 4: DUSK, 5: EVENING, 6: DEAD OF NIGHT
firesday: 28
earthsday: 29
watersday: 30
windsday: 31
darksday: 32
iceday: 34
lightningsday: 35
lightsday: 36
moon_phase: 37 # PARAM: 0: New Moon, 1: Waxing Crescent, 2: First Quarter, 3: Waxing Gibbous, 4: Full Moon, 5: Waning Gibbous, 6: Last Quarter, 7: Waning Crescent
job_multiple: 38 # PARAM: 0: ODD, 2: EVEN, 3-X: DIVISOR
job_multiple_at_night: 39 # PARAM: 0: ODD, 2: EVEN, 3-X: DIVISOR
equipped_in_slot: 40 # When item is equipped in the specified slot (e.g. Dweomer Knife, Erlking's Sword, etc.) PARAM: slotID
during_ws: 41 # During WS
weather_condition: 42 # See Weather.h for WEATHER enum
weapon_drawn_hp_under: 43 # PARAM: HP PERCENT
nation_citizen: 44 # Triggered by player being citizen of nation matching param: 0 San d'Oria, 1 Bastok, 2 Windurst
mp_under_visible_gear: 45 # mp less than or equal to %, calculated using MP bonuses from visible gear only
hp_over_visible_gear: 46 # hp more than or equal to %, calculated using HP bonuses from visible gear only
weapon_broken: 47
in_dynamis: 48
food_active: 49 # food effect (foodId) active - PARAM: FOOD ITEMID
job_level_below: 50 # PARAM: level
job_level_above: 51 # PARAM: level
weather_element: 52 # PARAM: 0: NONE, 1: FIRE, 2: ICE, 3: WIND 4: EARTH, 5: THUNDER, 6: WATER, 7: LIGHT, 8: DARK
nation_control: 53 # checks if player region is under nation's control - PARAM: 0: Under own nation's control, 1: Outside own nation's control
zone_home_nation: 54 # in zone and citizen of nation (aketons)
mp_over: 55 # mp greater than # - PARAM: MP #
weapon_drawn_mp_over: 56 # while weapon is drawn and mp greater than # - PARAM: MP #
eleven_roll_active: 57 # corsair roll of 11 active
in_assault: 58 # is in an Instance battle in a TOAU zone
vs_ecosystem: 59 # Vs. Specific Ecosystem ID (e.g. Vs. Plantoid: Accuracy+3)
vs_species: 60 # Vs. Specific Species ID (e.g. Vs. Korrigan: Accuracy+3)
vs_family: 61 # Vs. Specific Family ID (e.g. Vs. Mandragora: Accuracy+3)
mainjob: 62 # mainjob - PARAM: JOBTYPE
in_adoulin: 63
in_garrison: 64 # while in an active Garrison
sanction_food_bonus: 65 # While in besieged region
sigil_food_bonus: 66 # While in campaign region
sigil_regen_bonus: 19 # While in campaign region and HP is less than PARAM%
sigil_refresh_bonus: 20 # While in campaign region and MP is less than PARAM%
avatar_in_party: 21 # party has a specific avatar - PARAM: same as globals/pets.lua (21 for any avatar)
job_in_party: 22 # party has job - PARAM: JOBTYPE
zone: 23 # in zone - PARAM: zoneid
synth_trainee: 24 # synth skill under 40 + no support: PARAM: 48: FISH, 49: WOOD, 50: SMITH, 51: GOLDSMITH, 52: CLOTH, 53: LEATHER, 54: BONE, 55: ALCHEMY, 56: COOKING
song_roll_active: 25 # any song or roll active
time_of_day: 26 # PARAM: 0: DAYTIME 1: NIGHTTIME 2: DUSK-DAWN
hour_of_day: 27 # PARAM: 1: NEW DAY, 2: DAWN, 3: DAY, 4: DUSK, 5: EVENING, 6: DEAD OF NIGHT
firesday: 28
earthsday: 29
watersday: 30
windsday: 31
darksday: 32
iceday: 34
lightningsday: 35
lightsday: 36
moon_phase: 37 # PARAM: 0: New Moon, 1: Waxing Crescent, 2: First Quarter, 3: Waxing Gibbous, 4: Full Moon, 5: Waning Gibbous, 6: Last Quarter, 7: Waning Crescent
job_multiple: 38 # PARAM: 0: ODD, 2: EVEN, 3-X: DIVISOR
job_multiple_at_night: 39 # PARAM: 0: ODD, 2: EVEN, 3-X: DIVISOR
equipped_in_slot: 40 # When item is equipped in the specified slot (e.g. Dweomer Knife, Erlking's Sword, etc.) PARAM: slotID
during_ws: 41 # During WS
weather_condition: 42 # See Weather.h for WEATHER enum
weapon_drawn_hp_under: 43 # PARAM: HP PERCENT
nation_citizen: 44 # Triggered by player being citizen of nation matching param: 0 San d'Oria, 1 Bastok, 2 Windurst
mp_under_visible_gear: 45 # mp less than or equal to %, calculated using MP bonuses from visible gear only
hp_over_visible_gear: 46 # hp more than or equal to %, calculated using HP bonuses from visible gear only
weapon_broken: 47
in_dynamis: 48
food_active: 49 # food effect (foodId) active - PARAM: FOOD ITEMID
job_level_below: 50 # PARAM: level
job_level_above: 51 # PARAM: level
weather_element: 52 # PARAM: 0: NONE, 1: FIRE, 2: ICE, 3: WIND 4: EARTH, 5: THUNDER, 6: WATER, 7: LIGHT, 8: DARK
nation_control: 53 # checks if player region is under nation's control - PARAM: 0: Under own nation's control, 1: Outside own nation's control
zone_home_nation: 54 # in zone and citizen of nation (aketons)
mp_over: 55 # mp greater than # - PARAM: MP #
weapon_drawn_mp_over: 56 # while weapon is drawn and mp greater than # - PARAM: MP #
eleven_roll_active: 57 # corsair roll of 11 active
in_assault: 58 # is in an Instance battle in a TOAU zone
vs_ecosystem: 59 # Vs. Specific Ecosystem ID (e.g. Vs. Plantoid: Accuracy+3)
vs_species: 60 # Vs. Specific Species ID (e.g. Vs. Korrigan: Accuracy+3)
vs_family: 61 # Vs. Specific Family ID (e.g. Vs. Mandragora: Accuracy+3)
mainjob: 62 # mainjob - PARAM: JOBTYPE
in_adoulin: 63
in_garrison: 64 # while in an active Garrison

View file

@ -1,107 +0,0 @@
# yaml-language-server: $schema=../schemas/enums/_enum.schema.json
meta:
cpp:
underlying: uint16_t
lua:
table: xi.mobMod
values:
none: 0
gil_min: 1 # minimum gil drop -- spawn mod only
gil_max: 2 # maximum gil drop -- spawn mod only
mp_base: 3 # Give mob mp. Used for mobs that are not mages, wyverns, avatars
sight_range: 4 # sight range
sound_range: 5 # sound range
buff_chance: 6 # % chance to buff (combat only)
ga_chance: 7 # % chance to use -ga spell
heal_chance: 8 # % chance to use heal
hp_heal_chance: 9 # can cast cures below this HP %
sublink: 10 # Sub link group. Enables mobs from different families to link if they share a SUBLINK value.
link_radius: 11 # link radius
sees_through_illusion: 12 # Mob can see through the Illusion effect that grants effects similar to Sneak & Invisible without this mod and allows aggro (see Viscious Liquid in mamook)
severe_spell_chance: 13 # % chance to use a severe spell like death or impact
skill_list: 14 # uses given mob skill list
mug_gil: 15 # amount gil carried for mugging
detection: 16 # Overrides mob family's detection method. In order to set to override to none an unused bit must be set such as xi::Detects::None1.
no_despawn: 17 # do not despawn when too far from spawn. Gob Diggers have this.
var: 18 # temp var for whatever. Gets cleared on spawn
can_shield_block: 19 # toggle shield use for mobs without physical shields (trusts)
no_h2h_penalty: 20 # Disables H2H penalty in base damage calculation when set to non-zero
pet_spell_list: 21 # set pet spell list
na_chance: 22 # % chance to cast -na
immunity: 23 # immune to set status effects. This only works from the db, not scripts
gradual_rage: 24 # (!) TODO: NOT YET IMPLEMENTED -- gradually rages
build_resist: 25 # (!) TODO: NOT YET IMPLEMENTED -- builds resistance to given effects
superlink: 26 # super link group. Only use this in scripts! Must be defined in onMobInitialize if relevant mobs are different families
spell_list: 27 # set spell list
exp_bonus: 28 # bonus exp (bonus / 100) negative values reduce exp.
assist: 29 # mobs will assist me
special_skill: 30 # give special skill
roam_distance: 31 # distance allowed to roam from spawn
dont_roam_home: 32 # Allow mobs to roam any distance from spawn. Useful for mobs with scripted roaming behavior.
special_cool: 33 # cool down for special
magic_cool: 34 # cool down for magic
standback_cool: 35 # cool down time for standing back (casting spell while not in attack range)
roam_cool: 36 # cool down time in seconds after roaming
always_aggro: 37 # aggro regardless of level. Spheroids
no_drops: 38 # If set monster cannot drop any items, not even seals.
share_pos: 39 # share a pos with another mob (eald'narche exoplates)
teleport_cd: 40 # cooldown for teleport abilities (tarutaru AA, angra mainyu, eald'narche)
teleport_start: 41 # mobskill ID to begin teleport
teleport_end: 42 # mobskill ID to end teleport
teleport_type: 43 # teleport type - 1: on cooldown, 2 - to close distance
dual_wield: 44 # enables a mob to use their offhand in attacks
add_effect: 45 # enables additional effect script to process on mobs attacks
auto_spikes: 46 # enables additional effect script to process when mob is attacked
spawn_leash: 47 # forces a mob to not move farther from its spawn than its leash distance
share_target: 48 # mob always targets same target as ID in this var
check_as_nm: 49 # If set , mob will check as a NM
roam_reset_facing: 50 # Resume facing the default spawn rotation after roaming home.
roam_turns: 51 # Maximum amount of turns during a roam
roam_rate: 52 # Roaming frequency. roam_cool - rand(roam_cool / (roam_rate / 10))
behavior: 53 # Add behaviors to mob
gil_bonus: 54 # Allow mob to drop gil. Multiplier to gil dropped by mob (bonus / 100) * total
idle_despawn: 55 # Time (in seconds) to despawn after being idle
hp_standback: 56 # mob will always standback with hp % higher to value
magic_delay: 57 # Amount of seconds mob waits before casting first spell
special_delay: 58 # Amount of seconds mob waits before using first special
base_damage_modifier: 59 # Add a flat modifer mob a mob's base damage. This is subject to multiplication by MOBMOD_BASE_DAMAGE_MULTIPLIER.
spawn_animationsub: 60 # reset animationsub to this on spawn
hp_scale: 61 # Scale the mobs max HP. ( hp_scale / 100 ) * maxhp
no_standback: 62 # Mob will never standback
attack_skill_list: 63 # skill list to use in place of regular attacks
charmable: 64 # mob is charmable
no_move: 65 # Mob will not be able to move
multi_hit: 66 # Mob will have as many swings as defined.
no_aggro: 67 # If set, mob cannot aggro until unset.
alli_hate: 68 # Range around target to add alliance member to enmity list.
no_link: 69 # If set, mob cannot link until unset.
no_rest: 70 # Mob cannot regain hp (e.g. re-burrowing antlions during ENM).
leader: 71 # Used for mob following. Positive number is how many followers a leader has. A negative number is the distance from this mob ID to the leader ID.
magic_range: 72 # magic aggro range
target_distance_offset: 73 # Adjusts how close a mob will move to it's target. 12 = 1.2 yalm. Positive values to go closer, negative farther.
one_way_linking: 74 # Will link with other mobs in its party (typically the same mob family) while roaming, but will not let others link with it once engaged
can_parry: 75 # Check if a mob is allowed to have parry rank (Rank Value 1-5)
no_widescan: 76 # Disables widescan for a specific mob
trust_distance: 77 # TRUSTS ONLY: Set movement type/distance. See trust.lua for details.
standback_range: 78 # Applies a specific standback range for the mob
cannot_guard: 79 # Check if the mob does not guard(despite being a MNK or PUP mob)
skip_allegiance_check: 80 # Skip the allegiance check for valid target (allows for example a mob to cast a TARGET_ENEMY spell on itself)
ability_response: 81 # Mob can respond to player ability use with onPlayerAbilityUse()
run_speed_mult: 82 # Multiplier for the speed of a mob while running (generally when the target is out of range) 100 = 1.00x
claim_type: 83 # Changes the claim behavior of the mob. See xi::ClaimType enum.
no_spell_cost: 84 # Mob does not use MP when casting spells
astral_pet_offset: 85 # If non-zero, defines the offset from main mob's ID for astral flow (if zero, will assume offset of 2)
base_damage_multiplier: 86 # Multiplies the mob's base damage. Example: 150 = x1.5. MOBMOD_DAMAGE_OFFSET/MOBMOD_RANGED_DAMAGE_OFFSET are not subject to multiplication.
damage_offset: 87 # Adds or subtracts the mob's base damage offset.
ranged_damage_offset: 88 # Adds or subtracts the mob's ranged base damage offset.
avatar_petid: 89 # A value from xi.petId to select model/ability from when owner uses astral flow
avatar_astral_delay: 90 # Number of milliseconds to delay AF after avatar spawn
h2h_single_swing: 91 # Mob will have only one swing per attack even as MNK with H2H skill
aoe_hit_all: 92 # Mob AoE can hit any player regardless of enmity
ranged_attack_range: 93 # Max range for ranged auto attacks. Mob will move closer if target is beyond this range.
follow_leash_range: 94 # Distance the leader can walk before their followers start moving. Applied to followers.
follow_stop_range: 95 # Distance the followers attempt to stop at once their leader stops moving. Applied to followers.
trust_shield_size: 96 # TRUSTS ONLY: Set the size of the mob's shield. 3 = Default size, only used for trusts that use shields.
bodyguard: 97 # Charmed mob defends its master similar to an avatar. (Maiden's Virelai)

View file

@ -1,17 +0,0 @@
# yaml-language-server: $schema=../schemas/enums/_enum.schema.json
meta:
flags: true
cpp:
underlying: uint8_t
lua:
table: xi.mobType
values:
normal: 0x00
unused: 0x01 # available for use
notorious: 0x02
fished: 0x04
called: 0x08
battlefield: 0x10
event: 0x20

File diff suppressed because it is too large Load diff

View file

@ -1,14 +0,0 @@
# yaml-language-server: $schema=../schemas/enums/_enum.schema.json
meta:
flags: true
cpp:
underlying: uint8_t
lua:
table: xi.nameVis
values:
none: 0x00
icon: 0x01 # (I) Icon next to name
hide_name: 0x08
ghost_phase: 0x80

View file

@ -1,23 +0,0 @@
# yaml-language-server: $schema=../schemas/enums/_enum.schema.json
meta:
flags: true
cpp:
underlying: uint16_t
lua:
table: xi.roamFlag
values:
none: 0x000
none0: 0x001
none1: 0x002
none2: 0x004
none3: 0x008
none4: 0x010
none5: 0x020
worm: 0x040 # pop up and down when moving
ambush: 0x080 # stays hidden until someone comes close (antlion)
scripted: 0x100 # calls lua method for roaming logic
ignore: 0x200 # ignore all hate, except linking hate
stealth: 0x400 # stays name hidden and untargetable until someone comes close (chigoe)
follow: 0x800 # follows a player when sighted for a little while

View file

@ -1,65 +0,0 @@
# yaml-language-server: $schema=../schemas/enums/_enum.schema.json
meta:
cpp:
underlying: uint8_t
lua:
table: xi.skill
values:
# Combat Skills
none: 0
hand_to_hand: 1
dagger: 2
sword: 3
great_sword: 4
axe: 5
great_axe: 6
scythe: 7
polearm: 8
katana: 9
great_katana: 10
club: 11
staff: 12
# 13-21 unused
automaton_melee: 22
automaton_ranged: 23
automaton_magic: 24
archery: 25
marksmanship: 26
throwing: 27
# Defensive Skills
guard: 28
evasion: 29
shield: 30
parry: 31
# Magic Skills
divine_magic: 32
healing_magic: 33
enhancing_magic: 34
enfeebling_magic: 35
elemental_magic: 36
dark_magic: 37
summoning_magic: 38
ninjutsu: 39
singing: 40
string_instrument: 41
wind_instrument: 42
blue_magic: 43
geomancy: 44
handbell: 45
# 46-47 unused
# Crafting Skills
fishing: 48
woodworking: 49
smithing: 50
goldsmithing: 51
clothcraft: 52
leathercraft: 53
bonecraft: 54
alchemy: 55
cooking: 56
synergy: 57
# Other Skills
rid: 58
dig: 59

View file

@ -1,11 +0,0 @@
# yaml-language-server: $schema=../schemas/enums/_enum.schema.json
meta:
cpp:
underlying: uint8_t
lua:
table: xi.spawnAnimation
values:
normal: 0
special: 1

View file

@ -1,19 +0,0 @@
# yaml-language-server: $schema=../schemas/enums/_enum.schema.json
meta:
flags: true
cpp:
underlying: uint8_t
lua:
table: xi.spawnType
values:
normal: 0x00 # 00:00-24:00
at_night: 0x01 # 20:00-04:00
at_evening: 0x02 # 18:00-06:00
weather: 0x04
fog: 0x08 # 02:00-07:00
moonphase: 0x10
lottery: 0x20
windowed: 0x40
scripted: 0x80 # scripted spawn

View file

@ -1,17 +0,0 @@
# yaml-language-server: $schema=../schemas/enums/_enum.schema.json
meta:
cpp:
underlying: uint8_t
lua:
table: xi.status
values:
normal: 0
update: 1
disappear: 2
invisible: 3
status_4: 4
cutscene_only: 6
status_18: 18
shutdown: 20

View file

@ -8,35 +8,35 @@ meta:
table: xi.effectFlag
values:
none: 0x00000000
dispelable: 0x00000001
erasable: 0x00000002
attack: 0x00000004
empathy: 0x00000008
damage: 0x00000010
death: 0x00000020
magic_begin: 0x00000040
magic_end: 0x00000080
on_zone: 0x00000100
none: 0x00000000
dispelable: 0x00000001
erasable: 0x00000002
attack: 0x00000004
empathy: 0x00000008
damage: 0x00000010
death: 0x00000020
magic_begin: 0x00000040
magic_end: 0x00000080
on_zone: 0x00000100
no_loss_message: 0x00000200
invisible: 0x00000400
detectable: 0x00000800
no_rest: 0x00001000
prevent_action: 0x00002000
waltzable: 0x00004000
food: 0x00008000
song: 0x00010000
roll: 0x00020000
synth_support: 0x00040000
confrontation: 0x00080000
logout: 0x00100000
bloodpact: 0x00200000
on_jobchange: 0x00400000
no_cancel: 0x00800000
influence: 0x01000000
offline_tick: 0x02000000
aura: 0x04000000
hide_timer: 0x08000000
on_zone_pathos: 0x10000000
invisible: 0x00000400
detectable: 0x00000800
no_rest: 0x00001000
prevent_action: 0x00002000
waltzable: 0x00004000
food: 0x00008000
song: 0x00010000
roll: 0x00020000
synth_support: 0x00040000
confrontation: 0x00080000
logout: 0x00100000
bloodpact: 0x00200000
on_jobchange: 0x00400000
no_cancel: 0x00800000
influence: 0x01000000
offline_tick: 0x02000000
aura: 0x04000000
hide_timer: 0x08000000
on_zone_pathos: 0x10000000
always_expiring: 0x20000000
on_attack: 0x40000000
on_attack: 0x40000000

View file

@ -1,29 +0,0 @@
# yaml-language-server: $schema=../schemas/enums/_enum.schema.json
meta:
cpp:
underlying: uint16_t
lua:
table: xi.weather
values:
none: 0
sunshine: 1
clouds: 2
fog: 3
hot_spell: 4
heat_wave: 5
rain: 6
squall: 7
dust_storm: 8
sand_storm: 9
wind: 10
gales: 11
snow: 12
blizzards: 13
thunder: 14
thunderstorms: 15
auroras: 16
stellar_glare: 17
gloom: 18
darkness: 19

View file

@ -1,26 +0,0 @@
# yaml-language-server: $schema=../schemas/enums/_enum.schema.json
meta:
flags: true
cpp:
underlying: uint16_t
lua:
table: xi.zoneMisc
values:
none: 0x0000 # Able to be used in any area
escape: 0x0001 # Ability to use Escape Spell
fellow: 0x0002 # Ability to summon Fellow NPC
mount: 0x0004 # Ability to use Chocobos and mounts
mazurka: 0x0008 # Ability to use Mazurka Spell
tractor: 0x0010 # Ability to use Tractor Spell
mogmenu: 0x0020 # Ability to communicate with Nomad Moogle (menu access mog house)
costume: 0x0040 # Ability to use a Costumes
pet: 0x0080 # Ability to summon Pets
treasure: 0x0100 # Presence in the global zone TreasurePool
auction_house: 0x0200 # Ability to use the auction house
yell: 0x0400 # Send and receive /yell commands
trust: 0x0800 # Ability to summon Trust NPC
los_player_block: 0x1000 # Players can't use magic/JAs through walls if this is set
los_off: 0x2000 # Zone should not have LoS checks
assist: 0x4000 # Send and receive /assiste, /assistj commands

View file

@ -1,20 +0,0 @@
# yaml-language-server: $schema=../schemas/enums/_enum.schema.json
meta:
flags: true
cpp:
underlying: uint16_t
lua:
table: xi.zoneType
values:
unknown: 0x0000
city: 0x0001
outdoors: 0x0002
dungeon: 0x0004
signet: 0x0008
sanction: 0x0010
sigil: 0x0020
ionis: 0x0040
dynamis: 0x0080
instanced: 0x0100

File diff suppressed because it is too large Load diff

View file

@ -12,7 +12,6 @@ apk --update-cache add \
bash \
binutils \
git \
libdwarf \
lua5.1-dev \
luajit \
mariadb-client \
@ -66,15 +65,13 @@ apk --update-cache add \
ccache \
cmake \
g++ \
libdwarf-dev \
linux-headers \
luajit-dev \
make \
mariadb-dev \
ninja-build \
ninja-is-really-ninja \
openssl-dev \
python3-dev \
samurai \
zeromq-dev \
zlib-dev \
zstd-dev

View file

@ -19,7 +19,6 @@ apt-get update && apt-get install --assume-yes --no-install-recommends --quiet \
binutils \
ca-certificates \
git \
libdwarf1 \
libzmq5 \
lua5.1 \
luajit \
@ -75,14 +74,12 @@ apt-get update && apt-get install --assume-yes --no-install-recommends --quiet \
ccache \
cmake \
g++-$GCC_VERSION \
libdwarf-dev \
libluajit-5.1-dev \
libmariadb-dev-compat \
libssl-dev \
libzmq3-dev \
make \
ninja-build \
pkg-config \
python3-dev \
python3-venv \
zlib1g-dev \

View file

@ -3,7 +3,6 @@
# add_subdirectory(zmq) # Handled globally
# add_subdirectory(openssl) # Handled globally
add_subdirectory(concurrentqueue)
add_subdirectory(SPSCQueue)
add_subdirectory(sol)
# CPM Modules
@ -299,48 +298,10 @@ CPMAddPackage(
SYSTEM ON
) # defines: unordered_dense::unordered_dense (aliased to FlatHashMap, see src/common/flat_hash_map.h)
# TODO: std::move_only_function lands in C++23, but libc++ (macOS) has not implemented
# : it yet. Remove this once all compilers for all platforms implement.
CPMAddPackage(
NAME function2
GITHUB_REPOSITORY Naios/function2
GIT_TAG 4.2.4
SYSTEM ON
) # defines: function2 (aliased to Fn, see src/common/types/fn.h)
# How cpptrace turns a crash address into function + file:line, per platform:
set(CPPTRACE_SYMBOL_OPTIONS "")
if(APPLE)
# macOS binaries carry a "debug map" pointing at the .o files rather than embedded DWARF;
# atos resolves against the staged .dSYM (see set_target_output_directory) and is fast.
list(APPEND CPPTRACE_SYMBOL_OPTIONS
"CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF OFF"
"CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE ON"
)
elseif(UNIX)
# Linux: resolve in-process with libdwarf.
list(APPEND CPPTRACE_SYMBOL_OPTIONS
"CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE OFF"
"CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF ON"
"CPPTRACE_USE_EXTERNAL_LIBDWARF ON"
"CPPTRACE_FIND_LIBDWARF_WITH_PKGCONFIG ON"
)
endif()
CPMAddPackage(
NAME cpptrace
GITHUB_REPOSITORY jeremy-rifkin/cpptrace
GIT_TAG v1.0.4
SYSTEM ON
OPTIONS
${CPPTRACE_SYMBOL_OPTIONS}
) # defines: cpptrace::cpptrace
set(SHARED_EXTERNAL_LIBS
fmt::fmt
spdlog
concurrentqueue
spscqueue
mariadbclient
sol2_single
argparse
@ -352,15 +313,25 @@ set(SHARED_EXTERNAL_LIBS
magic_enum
utf8cpp
unordered_dense::unordered_dense
function2
cpptrace::cpptrace
)
if(APPLE)
if(WIN32)
# add wepoll for epoll support on windows
add_subdirectory(wepoll)
list(APPEND SHARED_EXTERNAL_LIBS
wepoll
)
# backwards needs these to add to linker
elseif(APPLE)
# MacOS has bfd built in clang
list(APPEND SHARED_EXTERNAL_LIBS
dl
)
elseif(UNIX)
# Linux needs bfd as it's not native like MacOS
# bfd may link zstd
list(APPEND SHARED_EXTERNAL_LIBS
bfd
dl

View file

@ -1,3 +0,0 @@
add_library(spscqueue INTERFACE)
target_sources(spscqueue INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include/rigtorp/SPSCQueue.h)
target_include_directories(spscqueue SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include/)

View file

@ -1,237 +0,0 @@
/*
Copyright (c) 2020 Erik Rigtorp <erik@rigtorp.se>
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.
*/
#pragma once
#include <atomic>
#include <cassert>
#include <cstddef>
#include <memory> // std::allocator
#include <new> // std::hardware_destructive_interference_size
#include <stdexcept>
#include <type_traits> // std::enable_if, std::is_*_constructible
#ifdef __has_cpp_attribute
#if __has_cpp_attribute(nodiscard)
#define RIGTORP_NODISCARD [[nodiscard]]
#endif
#endif
#ifndef RIGTORP_NODISCARD
#define RIGTORP_NODISCARD
#endif
namespace rigtorp {
template <typename T, typename Allocator = std::allocator<T>> class SPSCQueue {
#if defined(__cpp_if_constexpr) && defined(__cpp_lib_void_t)
template <typename Alloc2, typename = void>
struct has_allocate_at_least : std::false_type {};
template <typename Alloc2>
struct has_allocate_at_least<
Alloc2, std::void_t<typename Alloc2::value_type,
decltype(std::declval<Alloc2 &>().allocate_at_least(
size_t{}))>> : std::true_type {};
#endif
public:
explicit SPSCQueue(const size_t capacity,
const Allocator &allocator = Allocator())
: capacity_(capacity), allocator_(allocator) {
// The queue needs at least one element
if (capacity_ < 1) {
capacity_ = 1;
}
capacity_++; // Needs one slack element
// Prevent overflowing size_t
if (capacity_ > SIZE_MAX - 2 * kPadding) {
capacity_ = SIZE_MAX - 2 * kPadding;
}
#if defined(__cpp_if_constexpr) && defined(__cpp_lib_void_t)
if constexpr (has_allocate_at_least<Allocator>::value) {
auto res = allocator_.allocate_at_least(capacity_ + 2 * kPadding);
slots_ = res.ptr;
capacity_ = res.count - 2 * kPadding;
} else {
slots_ = std::allocator_traits<Allocator>::allocate(
allocator_, capacity_ + 2 * kPadding);
}
#else
slots_ = std::allocator_traits<Allocator>::allocate(
allocator_, capacity_ + 2 * kPadding);
#endif
static_assert(alignof(SPSCQueue<T>) == kCacheLineSize, "");
static_assert(sizeof(SPSCQueue<T>) >= 3 * kCacheLineSize, "");
assert(reinterpret_cast<char *>(&readIdx_) -
reinterpret_cast<char *>(&writeIdx_) >=
static_cast<std::ptrdiff_t>(kCacheLineSize));
}
~SPSCQueue() {
while (front()) {
pop();
}
std::allocator_traits<Allocator>::deallocate(allocator_, slots_,
capacity_ + 2 * kPadding);
}
// non-copyable and non-movable
SPSCQueue(const SPSCQueue &) = delete;
SPSCQueue &operator=(const SPSCQueue &) = delete;
template <typename... Args>
void emplace(Args &&...args) noexcept(
std::is_nothrow_constructible<T, Args &&...>::value) {
static_assert(std::is_constructible<T, Args &&...>::value,
"T must be constructible with Args&&...");
auto const writeIdx = writeIdx_.load(std::memory_order_relaxed);
auto nextWriteIdx = writeIdx + 1;
if (nextWriteIdx == capacity_) {
nextWriteIdx = 0;
}
while (nextWriteIdx == readIdxCache_) {
readIdxCache_ = readIdx_.load(std::memory_order_acquire);
}
new (&slots_[writeIdx + kPadding]) T(std::forward<Args>(args)...);
writeIdx_.store(nextWriteIdx, std::memory_order_release);
}
template <typename... Args>
RIGTORP_NODISCARD bool try_emplace(Args &&...args) noexcept(
std::is_nothrow_constructible<T, Args &&...>::value) {
static_assert(std::is_constructible<T, Args &&...>::value,
"T must be constructible with Args&&...");
auto const writeIdx = writeIdx_.load(std::memory_order_relaxed);
auto nextWriteIdx = writeIdx + 1;
if (nextWriteIdx == capacity_) {
nextWriteIdx = 0;
}
if (nextWriteIdx == readIdxCache_) {
readIdxCache_ = readIdx_.load(std::memory_order_acquire);
if (nextWriteIdx == readIdxCache_) {
return false;
}
}
new (&slots_[writeIdx + kPadding]) T(std::forward<Args>(args)...);
writeIdx_.store(nextWriteIdx, std::memory_order_release);
return true;
}
void push(const T &v) noexcept(std::is_nothrow_copy_constructible<T>::value) {
static_assert(std::is_copy_constructible<T>::value,
"T must be copy constructible");
emplace(v);
}
template <typename P, typename = typename std::enable_if<
std::is_constructible<T, P &&>::value>::type>
void push(P &&v) noexcept(std::is_nothrow_constructible<T, P &&>::value) {
emplace(std::forward<P>(v));
}
RIGTORP_NODISCARD bool
try_push(const T &v) noexcept(std::is_nothrow_copy_constructible<T>::value) {
static_assert(std::is_copy_constructible<T>::value,
"T must be copy constructible");
return try_emplace(v);
}
template <typename P, typename = typename std::enable_if<
std::is_constructible<T, P &&>::value>::type>
RIGTORP_NODISCARD bool
try_push(P &&v) noexcept(std::is_nothrow_constructible<T, P &&>::value) {
return try_emplace(std::forward<P>(v));
}
RIGTORP_NODISCARD T *front() noexcept {
auto const readIdx = readIdx_.load(std::memory_order_relaxed);
if (readIdx == writeIdxCache_) {
writeIdxCache_ = writeIdx_.load(std::memory_order_acquire);
if (writeIdxCache_ == readIdx) {
return nullptr;
}
}
return &slots_[readIdx + kPadding];
}
void pop() noexcept {
static_assert(std::is_nothrow_destructible<T>::value,
"T must be nothrow destructible");
auto const readIdx = readIdx_.load(std::memory_order_relaxed);
assert(writeIdx_.load(std::memory_order_acquire) != readIdx &&
"Can only call pop() after front() has returned a non-nullptr");
slots_[readIdx + kPadding].~T();
auto nextReadIdx = readIdx + 1;
if (nextReadIdx == capacity_) {
nextReadIdx = 0;
}
readIdx_.store(nextReadIdx, std::memory_order_release);
}
RIGTORP_NODISCARD size_t size() const noexcept {
std::ptrdiff_t diff = writeIdx_.load(std::memory_order_acquire) -
readIdx_.load(std::memory_order_acquire);
if (diff < 0) {
diff += capacity_;
}
return static_cast<size_t>(diff);
}
RIGTORP_NODISCARD bool empty() const noexcept {
return writeIdx_.load(std::memory_order_acquire) ==
readIdx_.load(std::memory_order_acquire);
}
RIGTORP_NODISCARD size_t capacity() const noexcept { return capacity_ - 1; }
private:
#ifdef __cpp_lib_hardware_interference_size
static constexpr size_t kCacheLineSize =
std::hardware_destructive_interference_size;
#else
static constexpr size_t kCacheLineSize = 64;
#endif
// Padding to avoid false sharing between slots_ and adjacent allocations
static constexpr size_t kPadding = (kCacheLineSize - 1) / sizeof(T) + 1;
private:
size_t capacity_;
T *slots_;
#if defined(__has_cpp_attribute) && __has_cpp_attribute(no_unique_address)
Allocator allocator_ [[no_unique_address]];
#else
Allocator allocator_;
#endif
// Align to cache line size in order to avoid false sharing
// readIdxCache_ and writeIdxCache_ is used to reduce the amount of cache
// coherency traffic
alignas(kCacheLineSize) std::atomic<size_t> writeIdx_ = {0};
alignas(kCacheLineSize) size_t readIdxCache_ = 0;
alignas(kCacheLineSize) std::atomic<size_t> readIdx_ = {0};
alignas(kCacheLineSize) size_t writeIdxCache_ = 0;
};
} // namespace rigtorp

21
ext/backward/LICENSE.txt Normal file
View file

@ -0,0 +1,21 @@
Copyright 2013 Google Inc. All Rights Reserved.
The MIT License (MIT)
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.

42
ext/backward/backward.cpp Normal file
View file

@ -0,0 +1,42 @@
// Pick your poison.
//
// On GNU/Linux, you have few choices to get the most out of your stack trace.
//
// By default you get:
// - object filename
// - function name
//
// In order to add:
// - source filename
// - line and column numbers
// - source code snippet (assuming the file is accessible)
// Install one of the following libraries then uncomment one of the macro (or
// better, add the detection of the lib and the macro definition in your build
// system)
// - apt-get install libdw-dev ...
// - g++/clang++ -ldw ...
// #define BACKWARD_HAS_DW 1
// - apt-get install binutils-dev ...
// - g++/clang++ -lbfd ...
// #define BACKWARD_HAS_BFD 1
// - apt-get install libdwarf-dev ...
// - g++/clang++ -ldwarf ...
// #define BACKWARD_HAS_DWARF 1
// Regardless of the library you choose to read the debug information,
// for potentially more detailed stack traces you can use libunwind
// - apt-get install libunwind-dev
// - g++/clang++ -lunwind
// #define BACKWARD_HAS_LIBUNWIND 1
#include "backward.hpp"
namespace backward {
backward::SignalHandling sh;
} // namespace backward

4514
ext/backward/backward.hpp Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,9 @@
set(SOURCES
wepoll.c
wepoll.h
)
add_library(wepoll STATIC ${SOURCES})
target_include_directories(wepoll SYSTEM PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
)

28
ext/wepoll/LICENSE Normal file
View file

@ -0,0 +1,28 @@
wepoll - epoll for Windows
https://github.com/piscisaureus/wepoll
Copyright 2012-2020, Bert Belder <bertbelder@gmail.com>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

202
ext/wepoll/README.md Normal file
View file

@ -0,0 +1,202 @@
# wepoll - epoll for windows
[![][ci status badge]][ci status link]
This library implements the [epoll][man epoll] API for Windows
applications. It is fast and scalable, and it closely resembles the API
and behavior of Linux' epoll.
## Rationale
Unlike Linux, OS X, and many other operating systems, Windows doesn't
have a good API for receiving socket state notifications. It only
supports the `select` and `WSAPoll` APIs, but they
[don't scale][select scale] and suffer from
[other issues][wsapoll broken].
Using I/O completion ports isn't always practical when software is
designed to be cross-platform. Wepoll offers an alternative that is
much closer to a drop-in replacement for software that was designed
to run on Linux.
## Features
* Can poll 100000s of sockets efficiently.
* Fully thread-safe.
* Multiple threads can poll the same epoll port.
* Sockets can be added to multiple epoll sets.
* All epoll events (`EPOLLIN`, `EPOLLOUT`, `EPOLLPRI`, `EPOLLRDHUP`)
are supported.
* Level-triggered and one-shot (`EPOLLONESTHOT`) modes are supported
* Trivial to embed: you need [only two files][dist].
## Limitations
* Only works with sockets.
* Edge-triggered (`EPOLLET`) mode isn't supported.
## How to use
The library is [distributed][dist] as a single source file
([wepoll.c][wepoll.c]) and a single header file ([wepoll.h][wepoll.h]).<br>
Compile the .c file as part of your project, and include the header wherever
needed.
## Compatibility
* Requires Windows Vista or higher.
* Can be compiled with recent versions of MSVC, Clang, and GCC.
## API
### General remarks
* The epoll port is a `HANDLE`, not a file descriptor.
* All functions set both `errno` and `GetLastError()` on failure.
* For more extensive documentation, see the [epoll(7) man page][man epoll],
and the per-function man pages that are linked below.
### epoll_create/epoll_create1
```c
HANDLE epoll_create(int size);
HANDLE epoll_create1(int flags);
```
* Create a new epoll instance (port).
* `size` is ignored but most be greater than zero.
* `flags` must be zero as there are no supported flags.
* Returns `NULL` on failure.
* [Linux man page][man epoll_create]
### epoll_close
```c
int epoll_close(HANDLE ephnd);
```
* Close an epoll port.
* Do not attempt to close the epoll port with `close()`,
`CloseHandle()` or `closesocket()`.
### epoll_ctl
```c
int epoll_ctl(HANDLE ephnd,
int op,
SOCKET sock,
struct epoll_event* event);
```
* Control which socket events are monitored by an epoll port.
* `ephnd` must be a HANDLE created by
[`epoll_create()`](#epoll_createepoll_create1) or
[`epoll_create1()`](#epoll_createepoll_create1).
* `op` must be one of `EPOLL_CTL_ADD`, `EPOLL_CTL_MOD`, `EPOLL_CTL_DEL`.
* `sock` must be a valid socket created by [`socket()`][msdn socket],
[`WSASocket()`][msdn wsasocket], or [`accept()`][msdn accept].
* `event` should be a pointer to a [`struct epoll_event`](#struct-epoll_event).<br>
If `op` is `EPOLL_CTL_DEL` then the `event` parameter is ignored, and it
may be `NULL`.
* Returns 0 on success, -1 on failure.
* It is recommended to always explicitly remove a socket from its epoll
set using `EPOLL_CTL_DEL` *before* closing it.<br>
As on Linux, closed sockets are automatically removed from the epoll set, but
wepoll may not be able to detect that a socket was closed until the next call
to [`epoll_wait()`](#epoll_wait).
* [Linux man page][man epoll_ctl]
### epoll_wait
```c
int epoll_wait(HANDLE ephnd,
struct epoll_event* events,
int maxevents,
int timeout);
```
* Receive socket events from an epoll port.
* `events` should point to a caller-allocated array of
[`epoll_event`](#struct-epoll_event) structs, which will receive the
reported events.
* `maxevents` is the maximum number of events that will be written to the
`events` array, and must be greater than zero.
* `timeout` specifies whether to block when no events are immediately available.
- `<0` block indefinitely
- `0` report any events that are already waiting, but don't block
- `≥1` block for at most N milliseconds
* Return value:
- `-1` an error occurred
- `0` timed out without any events to report
- `≥1` the number of events stored in the `events` buffer
* [Linux man page][man epoll_wait]
### struct epoll_event
```c
typedef union epoll_data {
void* ptr;
int fd;
uint32_t u32;
uint64_t u64;
SOCKET sock; /* Windows specific */
HANDLE hnd; /* Windows specific */
} epoll_data_t;
```
```c
struct epoll_event {
uint32_t events; /* Epoll events and flags */
epoll_data_t data; /* User data variable */
};
```
* The `events` field is a bit mask containing the events being
monitored/reported, and optional flags.<br>
Flags are accepted by [`epoll_ctl()`](#epoll_ctl), but they are not reported
back by [`epoll_wait()`](#epoll_wait).
* The `data` field can be used to associate application-specific information
with a socket; its value will be returned unmodified by
[`epoll_wait()`](#epoll_wait).
* [Linux man page][man epoll_ctl]
| Event | Description |
|---------------|----------------------------------------------------------------------|
| `EPOLLIN` | incoming data available, or incoming connection ready to be accepted |
| `EPOLLOUT` | ready to send data, or outgoing connection successfully established |
| `EPOLLRDHUP` | remote peer initiated graceful socket shutdown |
| `EPOLLPRI` | out-of-band data available for reading |
| `EPOLLERR` | socket error<sup>1</sup> |
| `EPOLLHUP` | socket hang-up<sup>1</sup> |
| `EPOLLRDNORM` | same as `EPOLLIN` |
| `EPOLLRDBAND` | same as `EPOLLPRI` |
| `EPOLLWRNORM` | same as `EPOLLOUT` |
| `EPOLLWRBAND` | same as `EPOLLOUT` |
| `EPOLLMSG` | never reported |
| Flag | Description |
|------------------|---------------------------|
| `EPOLLONESHOT` | report event(s) only once |
| `EPOLLET` | not supported by wepoll |
| `EPOLLEXCLUSIVE` | not supported by wepoll |
| `EPOLLWAKEUP` | not supported by wepoll |
<sup>1</sup>: the `EPOLLERR` and `EPOLLHUP` events may always be reported by
[`epoll_wait()`](#epoll_wait), regardless of the event mask that was passed to
[`epoll_ctl()`](#epoll_ctl).
[ci status badge]: https://ci.appveyor.com/api/projects/status/github/piscisaureus/wepoll?branch=master&svg=true
[ci status link]: https://ci.appveyor.com/project/piscisaureus/wepoll/branch/master
[dist]: https://github.com/piscisaureus/wepoll/tree/dist
[man epoll]: http://man7.org/linux/man-pages/man7/epoll.7.html
[man epoll_create]: http://man7.org/linux/man-pages/man2/epoll_create.2.html
[man epoll_ctl]: http://man7.org/linux/man-pages/man2/epoll_ctl.2.html
[man epoll_wait]: http://man7.org/linux/man-pages/man2/epoll_wait.2.html
[msdn accept]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms737526(v=vs.85).aspx
[msdn socket]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms740506(v=vs.85).aspx
[msdn wsasocket]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms742212(v=vs.85).aspx
[select scale]: https://daniel.haxx.se/docs/poll-vs-select.html
[wsapoll broken]: https://daniel.haxx.se/blog/2012/10/10/wsapoll-is-broken/
[wepoll.c]: https://github.com/piscisaureus/wepoll/blob/dist/wepoll.c
[wepoll.h]: https://github.com/piscisaureus/wepoll/blob/dist/wepoll.h

2253
ext/wepoll/wepoll.c Normal file

File diff suppressed because it is too large Load diff

113
ext/wepoll/wepoll.h Normal file
View file

@ -0,0 +1,113 @@
/*
* wepoll - epoll for Windows
* https://github.com/piscisaureus/wepoll
*
* Copyright 2012-2020, Bert Belder <bertbelder@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef WEPOLL_H_
#define WEPOLL_H_
#ifndef WEPOLL_EXPORT
#define WEPOLL_EXPORT
#endif
#include <stdint.h>
enum EPOLL_EVENTS {
EPOLLIN = (int) (1U << 0),
EPOLLPRI = (int) (1U << 1),
EPOLLOUT = (int) (1U << 2),
EPOLLERR = (int) (1U << 3),
EPOLLHUP = (int) (1U << 4),
EPOLLRDNORM = (int) (1U << 6),
EPOLLRDBAND = (int) (1U << 7),
EPOLLWRNORM = (int) (1U << 8),
EPOLLWRBAND = (int) (1U << 9),
EPOLLMSG = (int) (1U << 10), /* Never reported. */
EPOLLRDHUP = (int) (1U << 13),
EPOLLONESHOT = (int) (1U << 31)
};
#define EPOLLIN (1U << 0)
#define EPOLLPRI (1U << 1)
#define EPOLLOUT (1U << 2)
#define EPOLLERR (1U << 3)
#define EPOLLHUP (1U << 4)
#define EPOLLRDNORM (1U << 6)
#define EPOLLRDBAND (1U << 7)
#define EPOLLWRNORM (1U << 8)
#define EPOLLWRBAND (1U << 9)
#define EPOLLMSG (1U << 10)
#define EPOLLRDHUP (1U << 13)
#define EPOLLONESHOT (1U << 31)
#define EPOLL_CTL_ADD 1
#define EPOLL_CTL_MOD 2
#define EPOLL_CTL_DEL 3
typedef void* HANDLE;
typedef uintptr_t SOCKET;
typedef union epoll_data {
void* ptr;
int fd;
uint32_t u32;
uint64_t u64;
SOCKET sock; /* Windows specific */
HANDLE hnd; /* Windows specific */
} epoll_data_t;
struct epoll_event {
uint32_t events; /* Epoll events and flags */
epoll_data_t data; /* User data variable */
};
#ifdef __cplusplus
extern "C" {
#endif
WEPOLL_EXPORT HANDLE epoll_create(int size);
WEPOLL_EXPORT HANDLE epoll_create1(int flags);
WEPOLL_EXPORT int epoll_close(HANDLE ephnd);
WEPOLL_EXPORT int epoll_ctl(HANDLE ephnd,
int op,
SOCKET sock,
struct epoll_event* event);
WEPOLL_EXPORT int epoll_wait(HANDLE ephnd,
struct epoll_event* events,
int maxevents,
int timeout);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* WEPOLL_H_ */

View file

@ -89,7 +89,7 @@ hnmSystem:addOverride('xi.zones.Dragons_Aery.Zone.onInitialize', function(zone)
-- First-time setup.
if hnmPopTime == 0 then
hnmPopTime = currentTime + math.randomInt(1, 48) * 1800
hnmPopTime = currentTime + math.random(1, 48) * 1800
SetServerVariable('[HNM]Fafnir', hnmPopTime) -- Save pop time.
end
@ -99,7 +99,7 @@ hnmSystem:addOverride('xi.zones.Dragons_Aery.Zone.onInitialize', function(zone)
if
hnmKillCount > 3 and
(math.randomInt(1, 5) == 3 or hnmKillCount > 6)
(math.random(1, 5) == 3 or hnmKillCount > 6)
then
monster = dragonsAeryID.mob.NIDHOGG
end
@ -122,7 +122,7 @@ hnmSystem:addOverride('xi.zones.Dragons_Aery.mobs.Fafnir.onMobDespawn', function
super(mob)
-- Server Variable work.
local randomPopTime = 75600 + math.randomInt(0, 6) * 1800
local randomPopTime = 75600 + math.random(0, 6) * 1800
local hnmKillCount = GetServerVariable('[HNM]Fafnir_C') + 1
SetServerVariable('[HNM]Fafnir', GetSystemTime() + randomPopTime) -- Save next pop time.
@ -133,7 +133,7 @@ hnmSystem:addOverride('xi.zones.Dragons_Aery.mobs.Fafnir.onMobDespawn', function
if
hnmKillCount > 3 and
(math.randomInt(1, 5) == 3 or hnmKillCount > 6)
(math.random(1, 5) == 3 or hnmKillCount > 6)
then
monster = dragonsAeryID.mob.NIDHOGG
end
@ -146,7 +146,7 @@ hnmSystem:addOverride('xi.zones.Dragons_Aery.mobs.Nidhogg.onMobDespawn', functio
super(mob)
-- Server Variable work.
local randomPopTime = 75600 + math.randomInt(0, 6) * 1800
local randomPopTime = 75600 + math.random(0, 6) * 1800
SetServerVariable('[HNM]Fafnir', GetSystemTime() + randomPopTime) -- Save next pop time.
SetServerVariable('[HNM]Fafnir_C', 0) -- Save kill count.
@ -222,7 +222,7 @@ hnmSystem:addOverride('xi.zones.Valley_of_Sorrows.Zone.onInitialize', function(z
-- First-time setup.
if hnmPopTime == 0 then
hnmPopTime = currentTime + math.randomInt(1, 48) * 1800
hnmPopTime = currentTime + math.random(1, 48) * 1800
SetServerVariable('[HNM]Adamantoise', hnmPopTime) -- Save pop time.
end
@ -232,7 +232,7 @@ hnmSystem:addOverride('xi.zones.Valley_of_Sorrows.Zone.onInitialize', function(z
if
hnmKillCount > 3 and
(math.randomInt(1, 5) == 3 or hnmKillCount > 6)
(math.random(1, 5) == 3 or hnmKillCount > 6)
then
monster = valleySorrowsID.mob.ASPIDOCHELONE
end
@ -255,7 +255,7 @@ hnmSystem:addOverride('xi.zones.Valley_of_Sorrows.mobs.Adamantoise.onMobDespawn'
super(mob)
-- Server Variable work.
local randomPopTime = 75600 + math.randomInt(0, 6) * 1800
local randomPopTime = 75600 + math.random(0, 6) * 1800
local hnmKillCount = GetServerVariable('[HNM]Adamantoise_C') + 1
SetServerVariable('[HNM]Adamantoise', GetSystemTime() + randomPopTime) -- Save next pop time.
@ -266,7 +266,7 @@ hnmSystem:addOverride('xi.zones.Valley_of_Sorrows.mobs.Adamantoise.onMobDespawn'
if
hnmKillCount > 3 and
(math.randomInt(1, 5) == 3 or hnmKillCount > 6)
(math.random(1, 5) == 3 or hnmKillCount > 6)
then
monster = valleySorrowsID.mob.ASPIDOCHELONE
end
@ -279,7 +279,7 @@ hnmSystem:addOverride('xi.zones.Valley_of_Sorrows.mobs.Aspidochelone.onMobDespaw
super(mob)
-- Server Variable work.
local randomPopTime = 75600 + math.randomInt(0, 6) * 1800
local randomPopTime = 75600 + math.random(0, 6) * 1800
SetServerVariable('[HNM]Adamantoise', GetSystemTime() + randomPopTime) -- Save next pop time.
SetServerVariable('[HNM]Adamantoise_C', 0) -- Save kill count.
@ -355,7 +355,7 @@ hnmSystem:addOverride('xi.zones.Behemoths_Dominion.Zone.onInitialize', function(
-- First-time setup.
if hnmPopTime == 0 then
hnmPopTime = currentTime + math.randomInt(1, 48) * 1800
hnmPopTime = currentTime + math.random(1, 48) * 1800
SetServerVariable('[HNM]Behemoth', hnmPopTime) -- Save pop time.
end
@ -365,7 +365,7 @@ hnmSystem:addOverride('xi.zones.Behemoths_Dominion.Zone.onInitialize', function(
if
hnmKillCount > 3 and
(math.randomInt(1, 5) == 3 or hnmKillCount > 6)
(math.random(1, 5) == 3 or hnmKillCount > 6)
then
monster = behemothDomID.mob.KING_BEHEMOTH
end
@ -388,7 +388,7 @@ hnmSystem:addOverride('xi.zones.Behemoths_Dominion.mobs.Behemoth.onMobDespawn',
super(mob)
-- Server Variable work.
local randomPopTime = 75600 + math.randomInt(0, 6) * 1800
local randomPopTime = 75600 + math.random(0, 6) * 1800
local hnmKillCount = GetServerVariable('[HNM]Behemoth_C') + 1
SetServerVariable('[HNM]Behemoth', GetSystemTime() + randomPopTime) -- Save next pop time.
@ -399,7 +399,7 @@ hnmSystem:addOverride('xi.zones.Behemoths_Dominion.mobs.Behemoth.onMobDespawn',
if
hnmKillCount > 3 and
(math.randomInt(1, 5) == 3 or hnmKillCount > 6)
(math.random(1, 5) == 3 or hnmKillCount > 6)
then
monster = behemothDomID.mob.KING_BEHEMOTH
end
@ -412,7 +412,7 @@ hnmSystem:addOverride('xi.zones.Behemoths_Dominion.mobs.King_Behemoth.onMobDespa
super(mob)
-- Server Variable work.
local randomPopTime = 75600 + math.randomInt(0, 6) * 1800
local randomPopTime = 75600 + math.random(0, 6) * 1800
SetServerVariable('[HNM]Behemoth', GetSystemTime() + randomPopTime) -- Save next pop time.
SetServerVariable('[HNM]Behemoth_C', 0) -- Save kill count.

View file

@ -1,19 +0,0 @@
-----------------------------------
-- NIN/DRG/SAM Quest Mobs Original Difficulty Module
-- Reverts the nerfs to the NIN/DRG/SAM quest mobs in the May 10, 2011 version update
-- Source: https://forum.square-enix.com/ffxi/threads/7257
-----------------------------------
-- Level: set all six spawns to level 32 (min == max => fixed level)
UPDATE mob_spawn_points SET minLevel = 32, maxLevel = 32 WHERE mobid = 17486187; -- Korroloka Leech (Korroloka Tunnel)
UPDATE mob_spawn_points SET minLevel = 32, maxLevel = 32 WHERE mobid = 17486188; -- Korroloka Leech (Korroloka Tunnel)
UPDATE mob_spawn_points SET minLevel = 32, maxLevel = 32 WHERE mobid = 17486189; -- Korroloka Leech (Korroloka Tunnel)
UPDATE mob_spawn_points SET minLevel = 32, maxLevel = 32 WHERE mobid = 17350928; -- Cyranuce M Cutauleon (Ghelsba Outpost)
UPDATE mob_spawn_points SET minLevel = 32, maxLevel = 32 WHERE mobid = 17219999; -- Forger (Konschtat Highlands)
UPDATE mob_spawn_points SET minLevel = 32, maxLevel = 32 WHERE mobid = 17272838; -- Guardian Treant (The Sanctuary of Zi'Tah)
-- HP: per-group override, keyed by (groupid, zoneid)
UPDATE mob_groups SET HP = 900 WHERE groupid = 28 AND zoneid = 173; -- Korroloka Leech x3 (Korroloka Tunnel)
UPDATE mob_groups SET HP = 2700 WHERE groupid = 26 AND zoneid = 140; -- Cyranuce M Cutauleon (Ghelsba Outpost)
UPDATE mob_groups SET HP = 2000 WHERE groupid = 33 AND zoneid = 108; -- Forger (Konschtat Highlands)
UPDATE mob_groups SET HP = 2000 WHERE groupid = 5 AND zoneid = 121; -- Guardian Treant (The Sanctuary of Zi'Tah)

View file

@ -69,8 +69,7 @@ UPDATE merits SET value = 20 WHERE name = 'arcane_circle_recast';
UPDATE abilities SET recastTime = 300 WHERE name = 'weapon_bash';
-- Weapon Bash merit: Revert value to 10 seconds per level
-- Note: merit is named weapon_bash_effect (provides both recast reduction and effect)
UPDATE merits SET value = 10 WHERE name = 'weapon_bash_effect';
UPDATE merits SET value = 10 WHERE name = 'weapon_bash_recast';
-- Dark Seal: Revert recast from 5 to 15 minutes
-- Source: https://www.bg-wiki.com/ffxi/Version_Update_(03/26/2012)
@ -252,14 +251,6 @@ UPDATE abilities SET recastTime = 900 WHERE name = 'deep_breathing';
-- Deep Breathing merit: Revert value to 150 seconds per level
UPDATE merits SET value = 150 WHERE name = 'deep_breathing';
-----------------------------------
-- Summoner
-----------------------------------
-- Summoning Magic Casting Time Merit: Repurposed to Spirit MP cost merit. Revert merit value from 5 to 1 per level.
-- Source: https://forum.square-enix.com/ffxi/threads/22099-March-27-2012-%28JST%29-Version-Update
UPDATE merits SET value = 1 WHERE name = 'summoning_magic_cast_time';
-----------------------------------
-- Corsair
-----------------------------------

View file

@ -45,7 +45,7 @@ class AHPaginationModule : public CPPModule
return false;
}
if (PChar->m_GMlevel == 0 && !PChar->loc.zone->CanUseMisc(xi::ZoneMisc::AuctionHouse))
if (PChar->m_GMlevel == 0 && !PChar->loc.zone->CanUseMisc(MISC_AH))
{
ShowWarning("[AH PAGES] %s is trying to use the auction house in a disallowed zone [%s]", PChar->getName(), PChar->loc.zone->getName());
return true;

View file

@ -35,7 +35,7 @@ hnmSystem:addOverride('xi.zones.Dragons_Aery.Zone.onInitialize', function(zone)
-- First-time setup.
if hnmPopTime == 0 then
hnmPopTime = currentTime + math.randomInt(1, 48) * 1800
hnmPopTime = currentTime + math.random(1, 48) * 1800
SetServerVariable('[HNM]Fafnir', hnmPopTime) -- Save pop time.
end
@ -54,7 +54,7 @@ hnmSystem:addOverride('xi.zones.Dragons_Aery.mobs.Fafnir.onMobDespawn', function
super(mob)
-- Server Variable work.
local randomPopTime = 75600 + math.randomInt(0, 6) * 1800
local randomPopTime = 75600 + math.random(0, 6) * 1800
SetServerVariable('[HNM]Fafnir', GetSystemTime() + randomPopTime) -- Save next pop time.
@ -85,7 +85,7 @@ hnmSystem:addOverride('xi.zones.Valley_of_Sorrows.Zone.onInitialize', function(z
-- First-time setup.
if hnmPopTime == 0 then
hnmPopTime = currentTime + math.randomInt(1, 48) * 1800
hnmPopTime = currentTime + math.random(1, 48) * 1800
SetServerVariable('[HNM]Adamantoise', hnmPopTime) -- Save pop time.
end
@ -104,7 +104,7 @@ hnmSystem:addOverride('xi.zones.Valley_of_Sorrows.mobs.Adamantoise.onMobDespawn'
super(mob)
-- Server Variable work.
local randomPopTime = 75600 + math.randomInt(0, 6) * 1800
local randomPopTime = 75600 + math.random(0, 6) * 1800
SetServerVariable('[HNM]Adamantoise', GetSystemTime() + randomPopTime) -- Save next pop time.
@ -135,7 +135,7 @@ hnmSystem:addOverride('xi.zones.Behemoths_Dominion.Zone.onInitialize', function(
-- First-time setup.
if hnmPopTime == 0 then
hnmPopTime = currentTime + math.randomInt(1, 48) * 1800
hnmPopTime = currentTime + math.random(1, 48) * 1800
SetServerVariable('[HNM]Behemoth', hnmPopTime) -- Save pop time.
end
@ -154,7 +154,7 @@ hnmSystem:addOverride('xi.zones.Behemoths_Dominion.mobs.Behemoth.onMobDespawn',
super(mob)
-- Server Variable work.
local randomPopTime = 75600 + math.randomInt(0, 6) * 1800
local randomPopTime = 75600 + math.random(0, 6) * 1800
SetServerVariable('[HNM]Behemoth', GetSystemTime() + randomPopTime) -- Save next pop time.

View file

@ -0,0 +1,20 @@
-----------------------------------
-- Module to make 'Mom the Adventurer?' quest missable.
-----------------------------------
require('modules/module_utils')
-----------------------------------
local m = Module:new('missable_mom_the_adventurer')
m:addOverride('xi.server.onServerStart', function()
super()
xi.module.modifyInteractionEntry('scripts/quests/bastok/Mom_the_Adventurer', function(quest)
quest.sections[1].check = function(player, status, vars)
return status ~= xi.questStatus.QUEST_ACCEPTED and
player:getFameLevel(xi.fameArea.BASTOK) < 2 and
vars.Prog == 0
end
end)
end)
return m

View file

@ -1,92 +0,0 @@
-----------------------------------
-- Mog House Exit Home Point Prompt Module
-----------------------------------
-- Restores the prompt to set your home point when exiting the Mog House after changing jobs.
-----------------------------------
require('modules/module_utils')
require('scripts/globals/moghouse')
-----------------------------------
local m = Module:new('moghouse_exit_homepoint')
-- Variable used for this module to store the job snapshot in the player's character variables.
local jobSnapshot = 'moghouseExitJobSnapshot'
-- The current event ID for the Mog House exit prompt. May change with future updates. If this module ever breaks, check here first.
local exitEventId = 30004
-- Zones to prompt players to set their home point upon exiting the Mog House.
local exitZones =
{
['Southern_San_dOria' ] = xi.zone.SOUTHERN_SAN_DORIA,
['Northern_San_dOria' ] = xi.zone.NORTHERN_SAN_DORIA,
['Port_San_dOria' ] = xi.zone.PORT_SAN_DORIA,
['Bastok_Mines' ] = xi.zone.BASTOK_MINES,
['Bastok_Markets' ] = xi.zone.BASTOK_MARKETS,
['Port_Bastok' ] = xi.zone.PORT_BASTOK,
['Windurst_Waters' ] = xi.zone.WINDURST_WATERS,
['Windurst_Walls' ] = xi.zone.WINDURST_WALLS,
['Port_Windurst' ] = xi.zone.PORT_WINDURST,
['Windurst_Woods' ] = xi.zone.WINDURST_WOODS,
['RuLude_Gardens' ] = xi.zone.RULUDE_GARDENS,
['Upper_Jeuno' ] = xi.zone.UPPER_JEUNO,
['Lower_Jeuno' ] = xi.zone.LOWER_JEUNO,
['Port_Jeuno' ] = xi.zone.PORT_JEUNO,
['Al_Zahbi' ] = xi.zone.AL_ZAHBI,
['Aht_Urhgan_Whitegate' ] = xi.zone.AHT_URHGAN_WHITEGATE,
['Southern_San_dOria_[S]'] = xi.zone.SOUTHERN_SAN_DORIA_S,
['Bastok_Markets_[S]' ] = xi.zone.BASTOK_MARKETS_S,
['Windurst_Waters_[S]' ] = xi.zone.WINDURST_WATERS_S,
}
local promptZoneIds = {}
for _, zoneId in pairs(exitZones) do
promptZoneIds[zoneId] = true
end
local currentJob = function(player)
return player:getMainJob() * 100 + player:getSubJob()
end
m:addOverride('xi.moghouse.onMoghouseZoneEvent', function(player, prevZone)
-- Create a snapshot of the players current job when entering the mog house.
if player:inMogHouse() then
player:setCharVar(jobSnapshot, currentJob(player))
return super(player, prevZone)
end
local moghouseExitPos = player:getXPos() == 0 and player:getYPos() == 0 and player:getZPos() == 0
local baseCsId = super(player, prevZone)
local storedJob = player:getCharVar(jobSnapshot)
if moghouseExitPos and storedJob > 0 then
player:setCharVar(jobSnapshot, 0)
if
baseCsId == -1 and
promptZoneIds[player:getZoneID()] and
storedJob ~= currentJob(player)
then
return exitEventId
end
end
return baseCsId
end)
for zoneName in pairs(exitZones) do
m:addOverride(string.format('xi.zones.%s.Zone.onEventFinish', zoneName), function(player, csid, option, npc)
if csid == exitEventId then
if option == 0 then
player:setHomePoint()
player:messageSpecial(zones[player:getZoneID()].text.HOMEPOINT_SET)
end
return
end
super(player, csid, option, npc)
end)
end
return m

View file

@ -20,7 +20,7 @@ local nmsToPersist =
'Behemoths_Dominion',
'Behemoth',
function()
return 75600 + math.randomInt(0, 6) * 1800
return 75600 + math.random(0, 6) * 1800
end
},
}

View file

@ -1,297 +0,0 @@
-----------------------------------
-- Reverts blood pacts to no longer have a duration based on summoning magic.
-- Source: https://www.bg-wiki.com/ffxi/Version_Update_(09/08/2010)
-----------------------------------
require('modules/module_utils')
-----------------------------------
local moduleName = 'abyssea_avatar_bloodpacts'
if xi.module.isContentEnabled('ABYSSEA') then
return { name = moduleName }
end
local m = Module:new(moduleName)
-----------------------------------
-- Shining Ruby
-----------------------------------
m:addOverride('xi.actions.abilities.pets.shining_ruby.onPetAbility', function(target, pet, petskill, summoner, action)
local duration = 180
xi.job_utils.summoner.onUseBloodPact(target, petskill, summoner, action)
target:delStatusEffect(xi.effect.SHINING_RUBY)
target:addStatusEffect(xi.effect.SHINING_RUBY, { power = 1, duration = duration, origin = pet })
if target:getID() == action:getPrimaryTargetID() then
petskill:setMsg(xi.msg.basic.SKILL_GAIN_EFFECT_2)
else
petskill:setMsg(xi.msg.basic.JA_GAIN_EFFECT)
end
return xi.effect.SHINING_RUBY
end)
-----------------------------------
-- Hastega
-----------------------------------
m:addOverride('xi.actions.abilities.pets.hastega.onPetAbility', function(target, pet, petskill, summoner, action)
local duration = 180
xi.job_utils.summoner.onUseBloodPact(target, petskill, summoner, action)
-- Reverts Garuda's Hastega to use 102/1024 or 9.96%
local typeEffect = xi.effect.HASTE
if target:addStatusEffect(typeEffect, { power = 996, duration = duration, origin = pet }) then
if target:getID() == action:getPrimaryTargetID() then
petskill:setMsg(xi.msg.basic.SKILL_GAIN_EFFECT_2)
else
petskill:setMsg(xi.msg.basic.JA_GAIN_EFFECT)
end
else
petskill:setMsg(xi.msg.basic.JA_NO_EFFECT_2)
return
end
return typeEffect
end)
-----------------------------------
-- Crimson Howl
-----------------------------------
m:addOverride('xi.actions.abilities.pets.crimson_howl.onPetAbility', function(target, pet, petskill, summoner, action)
local duration = 60
xi.job_utils.summoner.onUseBloodPact(target, petskill, summoner, action)
local typeEffect = xi.effect.WARCRY
if target:addStatusEffect(typeEffect, { power = 9, duration = duration, origin = pet }) then
if target:getID() == action:getPrimaryTargetID() then
petskill:setMsg(xi.msg.basic.SKILL_GAIN_EFFECT_2)
else
petskill:setMsg(xi.msg.basic.JA_GAIN_EFFECT)
end
else
petskill:setMsg(xi.msg.basic.JA_NO_EFFECT_2)
return
end
return typeEffect
end)
-----------------------------------
-- Frost Armor
-----------------------------------
m:addOverride('xi.actions.abilities.pets.frost_armor.onPetAbility', function(target, pet, petskill, summoner, action)
local duration = 180
xi.job_utils.summoner.onUseBloodPact(target, petskill, summoner, action)
local typeEffect = xi.effect.ICE_SPIKES
target:delStatusEffect(typeEffect)
if target:addStatusEffect(typeEffect, { power = 15, duration = duration, origin = pet }) then
if target:getID() == action:getPrimaryTargetID() then
petskill:setMsg(xi.msg.basic.SKILL_GAIN_EFFECT_2)
else
petskill:setMsg(xi.msg.basic.JA_GAIN_EFFECT)
end
else
petskill:setMsg(xi.msg.basic.JA_NO_EFFECT_2)
return
end
return typeEffect
end)
-----------------------------------
-- Rolling Thunder
-----------------------------------
m:addOverride('xi.actions.abilities.pets.rolling_thunder.onPetAbility', function(target, pet, petskill, summoner, action)
local duration = 120
xi.job_utils.summoner.onUseBloodPact(target, petskill, summoner, action)
local magicskill = xi.data.skillLevel.getSkillCap(target:getMainLvl(), xi.skillRank.A_PLUS)
local potency = 3 + 6 * magicskill / 100
if magicskill > 200 then
potency = 5 + 5 * magicskill / 100
end
xi.mobskills.mobBuffMove(target, xi.effect.ENTHUNDER, potency, 0, duration)
if target:getID() == action:getPrimaryTargetID() then
petskill:setMsg(xi.msg.basic.JA_RECEIVES_EFFECT_2)
else
petskill:setMsg(xi.msg.basic.JA_RECEIVES_EFFECT)
end
return xi.effect.ENTHUNDER
end)
-----------------------------------
-- Lightning Armor
-----------------------------------
m:addOverride('xi.actions.abilities.pets.lightning_armor.onPetAbility', function(target, pet, petskill, summoner, action)
local duration = 180
xi.job_utils.summoner.onUseBloodPact(target, petskill, summoner, action)
local typeEffect = xi.effect.SHOCK_SPIKES
target:delStatusEffect(typeEffect)
if target:addStatusEffect(typeEffect, { power = 15, duration = duration, origin = pet }) then
if target:getID() == action:getPrimaryTargetID() then
petskill:setMsg(xi.msg.basic.SKILL_GAIN_EFFECT_2)
else
petskill:setMsg(xi.msg.basic.JA_GAIN_EFFECT)
end
else
petskill:setMsg(xi.msg.basic.JA_NO_EFFECT_2)
return
end
return typeEffect
end)
-----------------------------------
-- Ecliptic Growl
-----------------------------------
m:addOverride('xi.actions.abilities.pets.ecliptic_growl.onPetAbility', function(target, pet, petskill, summoner, action)
local duration = 180
xi.job_utils.summoner.onUseBloodPact(target, petskill, summoner, action)
local moonCycle = getVanadielMoonCycle()
local cycleBuffs =
{
[xi.moonCycle.NEW_MOON] = 1,
[xi.moonCycle.LESSER_WAXING_CRESCENT] = 2,
[xi.moonCycle.GREATER_WAXING_CRESCENT] = 3,
[xi.moonCycle.FIRST_QUARTER] = 4,
[xi.moonCycle.LESSER_WAXING_GIBBOUS] = 5,
[xi.moonCycle.GREATER_WAXING_GIBBOUS] = 6,
[xi.moonCycle.FULL_MOON] = 7,
[xi.moonCycle.GREATER_WANING_GIBBOUS] = 6,
[xi.moonCycle.LESSER_WANING_GIBBOUS] = 5,
[xi.moonCycle.THIRD_QUARTER] = 4,
[xi.moonCycle.GREATER_WANING_CRESCENT] = 3,
[xi.moonCycle.LESSER_WANING_CRESCENT] = 2,
}
local buffValue = cycleBuffs[moonCycle]
target:delStatusEffect(xi.effect.STR_BOOST)
target:delStatusEffect(xi.effect.DEX_BOOST)
target:delStatusEffect(xi.effect.VIT_BOOST)
target:delStatusEffect(xi.effect.AGI_BOOST)
target:delStatusEffect(xi.effect.MND_BOOST)
target:delStatusEffect(xi.effect.CHR_BOOST)
target:addStatusEffect(xi.effect.STR_BOOST, { power = buffValue, duration = duration, origin = pet })
target:addStatusEffect(xi.effect.DEX_BOOST, { power = buffValue, duration = duration, origin = pet })
target:addStatusEffect(xi.effect.VIT_BOOST, { power = buffValue, duration = duration, origin = pet })
target:addStatusEffect(xi.effect.AGI_BOOST, { power = 8 - buffValue, duration = duration, origin = pet })
target:addStatusEffect(xi.effect.INT_BOOST, { power = 8 - buffValue, duration = duration, origin = pet })
target:addStatusEffect(xi.effect.MND_BOOST, { power = 8 - buffValue, duration = duration, origin = pet })
target:addStatusEffect(xi.effect.CHR_BOOST, { power = 8 - buffValue, duration = duration, origin = pet })
if target:getID() == action:getPrimaryTargetID() then
petskill:setMsg(xi.msg.basic.STATUS_BOOST)
else
petskill:setMsg(xi.msg.basic.STATUS_BOOST_2)
end
return 0
end)
-----------------------------------
-- Ecliptic Howl
-----------------------------------
m:addOverride('xi.actions.abilities.pets.ecliptic_howl.onPetAbility', function(target, pet, petskill, summoner, action)
local duration = 180
xi.job_utils.summoner.onUseBloodPact(target, petskill, summoner, action)
local moonCycle = getVanadielMoonCycle()
local cycleBuffs =
{
[xi.moonCycle.NEW_MOON] = 1,
[xi.moonCycle.LESSER_WAXING_CRESCENT] = 5,
[xi.moonCycle.GREATER_WAXING_CRESCENT] = 9,
[xi.moonCycle.FIRST_QUARTER] = 13,
[xi.moonCycle.LESSER_WAXING_GIBBOUS] = 17,
[xi.moonCycle.GREATER_WAXING_GIBBOUS] = 21,
[xi.moonCycle.FULL_MOON] = 25,
[xi.moonCycle.GREATER_WANING_GIBBOUS] = 21,
[xi.moonCycle.LESSER_WANING_GIBBOUS] = 17,
[xi.moonCycle.THIRD_QUARTER] = 13,
[xi.moonCycle.GREATER_WANING_CRESCENT] = 9,
[xi.moonCycle.LESSER_WANING_CRESCENT] = 5,
}
local buffValue = cycleBuffs[moonCycle]
target:delStatusEffect(xi.effect.ACCURACY_BOOST)
target:delStatusEffect(xi.effect.EVASION_BOOST)
target:addStatusEffect(xi.effect.ACCURACY_BOOST, { power = buffValue, duration = duration, origin = pet })
target:addStatusEffect(xi.effect.EVASION_BOOST, { power = 25 - buffValue, duration = duration, origin = pet })
if target:getID() == action:getPrimaryTargetID() then
petskill:setMsg(xi.msg.basic.ACC_EVA_BOOST)
else
petskill:setMsg(xi.msg.basic.ACC_EVA_BOOST_2)
end
return 0
end)
-----------------------------------
-- Noctoshield
-----------------------------------
m:addOverride('xi.actions.abilities.pets.noctoshield.onPetAbility', function(target, pet, petskill, summoner, action)
xi.job_utils.summoner.onUseBloodPact(target, petskill, summoner, action)
local duration = 180
if target:addStatusEffect(xi.effect.PHALANX, { power = 13, duration = duration, origin = pet }) then
if target:getID() == action:getPrimaryTargetID() then
petskill:setMsg(xi.msg.basic.SKILL_GAIN_EFFECT_2)
else
petskill:setMsg(xi.msg.basic.JA_GAIN_EFFECT)
end
else
petskill:setMsg(xi.msg.basic.JA_NO_EFFECT_2)
return
end
return xi.effect.PHALANX
end)
-----------------------------------
-- Dream Shroud
-----------------------------------
m:addOverride('xi.actions.abilities.pets.dream_shroud.onPetAbility', function(target, pet, petskill, summoner, action)
xi.job_utils.summoner.onUseBloodPact(target, petskill, summoner, action)
local duration = 180
local hour = VanadielHour()
local buffvalue = math.abs(12 - hour) + 1
target:delStatusEffect(xi.effect.MAGIC_ATK_BOOST)
target:delStatusEffect(xi.effect.MAGIC_DEF_BOOST)
target:addStatusEffect(xi.effect.MAGIC_ATK_BOOST, { power = buffvalue, duration = duration, origin = pet })
target:addStatusEffect(xi.effect.MAGIC_DEF_BOOST, { power = 14 - buffvalue, duration = duration, origin = pet })
if target:getID() == action:getPrimaryTargetID() then
petskill:setMsg(xi.msg.basic.JA_RECEIVES_MAB_MDB)
else
petskill:setMsg(xi.msg.basic.JA_RECEIVES_MAB_MDB_2)
end
return 0
end)
return m

View file

@ -0,0 +1,110 @@
-----------------------------------
-- Analyzer (Pre 2012)
-- Reduces damage taken from repeated TP moves by 10%, with an additional 10% per Earth Maneuver.
-- Resets after a new skill is used.
-- Was changed March 27th, 2012 to analyze multiple TP moves based off earth maneuvers, with a 40% damage reduction.
-- NOTE: This module touches both the attachment, and the combat utility. Both are needed for this attachment to function correctly.
-- https://wiki.ffo.jp/html/10746.html
-----------------------------------
require('modules/module_utils')
-----------------------------------
local moduleName = 'era_analyzer'
if xi.module.isContentEnabled('ABYSSEA') then
return { name = moduleName }
end
local m = Module:new(moduleName)
-----------------------------------
-- Attachment Module
-----------------------------------
m:addOverride('xi.actions.abilities.pets.attachments.analyzer.onEquip', function(pet, attachment)
-- Analyzed skills do not carry over between zones, onEquip is called when rebuilding the automaton after zoning.
pet:setLocalVar('analyzedSkill1', 0)
pet:addListener('WEAPONSKILL_TAKE', 'ANALYZER_WEAPONSKILL_TAKE', function(mob, target, skill, tp, action)
local analyzerModifier = target:getMod(xi.mod.AUTO_ANALYZER)
local incomingSkill = skill:getID()
-- If Analyzer modifier is 0, return. (Should be impossible since the attachment wouldn't be equipped, but just in case.)
if analyzerModifier <= 0 then
return
end
local analyzedSkill = target:getLocalVar('analyzedSkill1')
-- If the incoming skill is the one we have already analyzed, return.
if incomingSkill == analyzedSkill then
return
end
-- If the incoming skill is different from the analyzed skill, update the analyzed skill ID.
if incomingSkill ~= analyzedSkill then
target:setLocalVar('analyzedSkill1', incomingSkill)
end
end)
xi.automaton.onAttachmentEquip(pet, attachment)
end)
m:addOverride('xi.actions.abilities.pets.attachments.analyzer.onUnequip', function(pet, attachment)
-- Clear analyzed skill on unequip, and remove the listener.
pet:setLocalVar('analyzedSkill1', 0)
pet:removeListener('ANALYZER_WEAPONSKILL_TAKE')
xi.automaton.onAttachmentUnequip(pet, attachment)
end)
m:addOverride('xi.actions.abilities.pets.attachments.analyzer.onManeuverGain', function(pet, attachment, maneuvers)
end)
m:addOverride('xi.actions.abilities.pets.attachments.analyzer.onManeuverLose', function(pet, attachment, maneuvers)
end)
m:addOverride('xi.actions.abilities.pets.attachments.analyzer.onUpdate', function(pet, attachment, maneuvers)
end)
-----------------------------------
-- Combat Utility Override
-----------------------------------
m:addOverride('utils.handleAutomatonAutoAnalyzer', function(actor, skill, damage)
local analyzerModifier = actor:getMod(xi.mod.AUTO_ANALYZER)
-- If no Analyzer equipped, return unmodified damage.
if analyzerModifier <= 0 then
return damage
end
local automatonId = actor:getID()
-- If we can't track this automaton, return unmodified damage.
if not automatonId then
return damage
end
local automatonMaster = actor:getMaster()
-- If we can't get the master of this automaton, return unmodified damage.
if not automatonMaster then
return damage
end
local incomingSkill = skill:getID()
local analyzedSkill = actor:getLocalVar('analyzedSkill1')
-- If the incoming skill is the same ID as the analyzed skill, apply the Analyzer damage reduction and return.
if incomingSkill == analyzedSkill then
local earthManeuvers = automatonMaster:countEffect(xi.effect.EARTH_MANEUVER)
local damageReduction = 10 + 10 * earthManeuvers
return math.floor(damage * (100 - damageReduction) / 100)
end
-- If we somehow make it here, return unmodified damage.
return damage
end)
return m

View file

@ -0,0 +1,113 @@
-----------------------------------
-- Flame Holder
-- Reverts Flame Holder to its pre-2015 functionality - Where it consumed Fire Maneuvers on skill execution.
-- Flame Holder Bonus increased on August 5th, 2015.
-- Changed to no longer consume Fire Maneuvers on August 6th, 2019.
-- Source : https://wiki.ffo.jp/html/11183.html
-----------------------------------
require('modules/module_utils')
-----------------------------------
local moduleName = 'era_flame_holder'
if xi.module.isContentEnabled('SOA') then
return { name = moduleName }
end
local m = Module:new(moduleName)
local validSkills = set
{
xi.mobSkill.ARCUBALLISTA_AUTOMATON,
xi.mobSkill.ARMOR_PIERCER_AUTOMATON,
xi.mobSkill.ARMOR_SHATTERER_AUTOMATON,
xi.mobSkill.BONE_CRUSHER_AUTOMATON,
xi.mobSkill.CANNIBAL_BLADE_AUTOMATON,
xi.mobSkill.CHIMERA_RIPPER_AUTOMATON,
xi.mobSkill.DAZE_AUTOMATON,
xi.mobSkill.KNOCKOUT_AUTOMATON,
xi.mobSkill.MAGIC_MORTAR_AUTOMATON,
xi.mobSkill.SLAPSTICK_AUTOMATON,
xi.mobSkill.STRING_CLIPPER_AUTOMATON,
xi.mobSkill.STRING_SHREDDER_AUTOMATON,
}
m:addOverride('xi.actions.abilities.pets.attachments.flame_holder.onEquip', function(pet, attachment)
pet:addListener('WEAPONSKILL_STATE_ENTER', 'AUTO_FLAME_HOLDER_START', function(automaton, skillId)
-- Not a valid skill for Flame Holder
if not validSkills[skillId] then
return
end
local master = automaton:getMaster()
if not master then
return
end
-- Fetch the amount of active Fire Maneuvers on weaponskill state entry.
local fireManeuvers = master:countEffect(xi.effect.FIRE_MANEUVER)
-- No Fire Maneuvers
if fireManeuvers == 0 then
return
end
-- Set the WEAPONSKILL_DAMAGE_BASE mod to 125% / 150% / 175% based on the number of Fire Maneuvers active.
local flameHolderAmount = 100 + 25 * fireManeuvers
automaton:setLocalVar('fireManeuvers', fireManeuvers)
automaton:setLocalVar('flameHolderAmount', flameHolderAmount)
automaton:addMod(xi.mod.WEAPONSKILL_DAMAGE_BASE, flameHolderAmount)
end)
pet:addListener('WEAPONSKILL_STATE_EXIT', 'AUTO_FLAME_HOLDER_END', function(automaton, skillId, wasExecuted)
local flameHolderAmount = automaton:getLocalVar('flameHolderAmount')
-- If no Flame Holder bonus is active, do nothing.
if flameHolderAmount == 0 then
return
end
local fireManeuvers = automaton:getLocalVar('fireManeuvers')
local master = automaton:getMaster()
if not master then
return
end
-- Consume all Fire Maneuvers on execution.
for i = 1, fireManeuvers do
master:delStatusEffectSilent(xi.effect.FIRE_MANEUVER)
end
-- Remove the Flame Holder bonus and reset local variables.
automaton:delMod(xi.mod.WEAPONSKILL_DAMAGE_BASE, flameHolderAmount)
automaton:setLocalVar('flameHolderAmount', 0)
automaton:setLocalVar('fireManeuvers', 0)
end)
end)
m:addOverride('xi.actions.abilities.pets.attachments.flame_holder.onUnequip', function(pet, attachment)
local amount = pet:getLocalVar('flameHolderAmount')
-- Should be nearly impossible, but just in case.
if amount ~= 0 then
pet:delMod(xi.mod.WEAPONSKILL_DAMAGE_BASE, amount)
end
pet:setLocalVar('flameHolderAmount', 0)
pet:setLocalVar('fireManeuvers', 0)
pet:removeListener('AUTO_FLAME_HOLDER_START')
pet:removeListener('AUTO_FLAME_HOLDER_END')
end)
m:addOverride('xi.actions.abilities.pets.attachments.flame_holder.onManeuverGain', function(pet, attachment, maneuvers)
end)
m:addOverride('xi.actions.abilities.pets.attachments.flame_holder.onManeuverLose', function(pet, attachment, maneuvers)
end)
m:addOverride('xi.actions.abilities.pets.attachments.flame_holder.onUpdate', function(pet, attachment, maneuvers)
end)
return m

View file

@ -0,0 +1,87 @@
-----------------------------------
-- Ice Maker
-- Reverts Ice Maker to its pre-2015 functionality - Where it consumed Ice Maneuvers on skill execution.
-- Icemaker MAB Coefficients increased on August 5th, 2015.
-- Changed to no longer consume Ice Maneuvers on August 6th, 2019.
-- Source : https://wiki.ffo.jp/html/11198.html
-----------------------------------
require('modules/module_utils')
-----------------------------------
local moduleName = 'era_ice_maker'
if xi.module.isContentEnabled('SOA') then
return { name = moduleName }
end
local m = Module:new(moduleName)
m:addOverride('xi.actions.abilities.pets.attachments.ice_maker.onEquip', function(pet, attachment)
pet:addListener('MAGIC_START', 'AUTO_ICE_MAKER_START', function(automaton, skillId)
local master = automaton:getMaster()
if not master then
return
end
local iceManeuvers = master:countEffect(xi.effect.ICE_MANEUVER)
if iceManeuvers == 0 then
return
end
local iceMakerAmount = 20 * iceManeuvers
automaton:setLocalVar('iceManeuvers', iceManeuvers)
automaton:setLocalVar('iceMakerAmount', iceMakerAmount)
automaton:addMod(xi.mod.AUTO_MAB_COEFFICIENT, iceMakerAmount)
end)
pet:addListener('MAGIC_STATE_EXIT', 'AUTO_ICE_MAKER_END', function(automaton, skillId, wasExecuted)
local iceMakerAmount = automaton:getLocalVar('iceMakerAmount')
-- If no Ice Maker bonus, do nothing.
if iceMakerAmount == 0 then
return
end
local iceManeuvers = automaton:getLocalVar('iceManeuvers')
local master = automaton:getMaster()
if not master then
return
end
-- Consume all Ice Maneuvers on magic state exit.
for i = 1, iceManeuvers do
master:delStatusEffectSilent(xi.effect.ICE_MANEUVER)
end
automaton:delMod(xi.mod.AUTO_MAB_COEFFICIENT, iceMakerAmount)
automaton:setLocalVar('iceMakerAmount', 0)
automaton:setLocalVar('iceManeuvers', 0)
end)
end)
m:addOverride('xi.actions.abilities.pets.attachments.ice_maker.onUnequip', function(pet, attachment)
local amount = pet:getLocalVar('iceMakerAmount')
if amount ~= 0 then
pet:delMod(xi.mod.AUTO_MAB_COEFFICIENT, amount)
end
pet:setLocalVar('iceMakerAmount', 0)
pet:setLocalVar('iceManeuvers', 0)
pet:removeListener('AUTO_ICE_MAKER_START')
pet:removeListener('AUTO_ICE_MAKER_END')
end)
m:addOverride('xi.actions.abilities.pets.attachments.ice_maker.onManeuverGain', function(pet, attachment, maneuvers)
end)
m:addOverride('xi.actions.abilities.pets.attachments.ice_maker.onManeuverLose', function(pet, attachment, maneuvers)
end)
m:addOverride('xi.actions.abilities.pets.attachments.ice_maker.onUpdate', function(pet, attachment, maneuvers)
end)
return m

View file

@ -1,552 +0,0 @@
-----------------------------------
-- Attachment module
-- Recreates attachment effects in the WoTG Era.
-----------------------------------
require('modules/module_utils')
require('scripts/globals/automaton')
-----------------------------------
local moduleName = 'attachments'
if xi.module.isContentEnabled('ABYSSEA') then
return { name = moduleName }
end
local m = Module:new(moduleName)
-- Reduces Enmity boost from Strobe : https://wiki.ffo.jp/html/8610.html
-- Reduces Store TP from Inhibitor : https://wiki.ffo.jp/html/8625.html
-- Changes Armor Plate and Armor Plate II to Defense instead of PDT : https://wiki.ffo.jp/html/9070.html
-- Adds a Ranged Attack Penalty to Drum Magazine. : https://wiki.ffo.jp/html/8882.html
-- Changes Turbo Charger Haste to Gear Haste instead of Magic : https://wiki.ffo.jp/html/8627.html
-- Adds Burden to Tactical Processor : https://wiki.ffo.jp/html/13527.html
-- Reduces scaling from Volt Gun : https://wiki.ffo.jp/html/8752.html
-- Reduces Burden Decay From Heatsink : https://wiki.ffo.jp/html/8629.html
-- Reduces the potency of Steam Jackets Damage Reduction : https://wiki.ffo.jp/html/15352.html
xi.automaton.attachmentModifiers['strobe' ] = { { modifier = xi.mod.ENMITY, values = { 5, 15, 25, 40 }, opticFiber = true }, }
xi.automaton.attachmentModifiers['inhibitor' ] = { { modifier = xi.mod.STORETP, values = { 5, 10, 15, 20 }, opticFiber = true }, }
xi.automaton.attachmentModifiers['armor_plate' ] = { { modifier = xi.mod.DEFP, values = { 10, 15, 20, 25 }, opticFiber = true }, }
xi.automaton.attachmentModifiers['armor_plate_ii' ] = { { modifier = xi.mod.DEFP, values = { 20, 25, 30, 35 }, opticFiber = true }, }
xi.automaton.attachmentModifiers['drum_magazine' ] = { { modifier = xi.mod.AUTO_RANGED_DELAY, values = { 2, 4, 6, 8 }, opticFiber = false },
{ modifier = xi.mod.RACC, values = { -15, -30, -50, -75 }, opticFiber = false }, }
xi.automaton.attachmentModifiers['flame_holder' ] = { { modifier = xi.mod.WEAPONSKILL_DAMAGE_BASE, values = { 0, 125, 150, 175 }, opticFiber = true }, }
xi.automaton.attachmentModifiers['ice_maker' ] = { { modifier = xi.mod.AUTO_MAB_COEFFICIENT, values = { 0, 20, 40, 60 }, opticFiber = true }, }
xi.automaton.attachmentModifiers['turbo_charger' ] = { { modifier = xi.mod.HASTE_GEAR, values = { 500, 1500, 2000, 2500 }, opticFiber = true }, }
xi.automaton.attachmentModifiers['tactical_processor'] = { { modifier = xi.mod.AUTO_DECISION_DELAY, values = { 50, 70, 85, 115 }, opticFiber = false },
{ modifier = xi.mod.OVERLOAD_THRESH, values = { -5, -5, -5, -5 }, opticFiber = false }, }
xi.automaton.attachmentModifiers['volt_gun' ] = { { modifier = xi.mod.VOLT_GUN_POTENCY, values = { 0, 0, 0, 0 }, opticFiber = false }, }
xi.automaton.attachmentModifiers['heatsink' ] = { { modifier = xi.mod.BURDEN_DECAY, values = { 1, 1, 1, 1 }, opticFiber = false }, }
xi.automaton.attachmentModifiers['steam_jacket' ] = { { modifier = xi.mod.AUTO_STEAM_JACKET_REDUCTION, values = { 25, 35, 40, 60 }, opticFiber = true }, }
-- Reduces potency of Auto Repair Kit II and removed level based scaling from Mana Tank : https://wiki.ffo.jp/html/19739.html
xi.automaton.repairKit.data['auto-repair_kit_ii' ] = { id = 196, hpBoost = 2, regenBase = { 0, 2, 3, 4 }, regenMultiplier = { 0, 0.4, 0.6, 0.8 } }
xi.automaton.manaTank.data ['mana_tank' ] = { id = 225, mpBoost = 1, refreshBase = { 0, 1, 2, 3 }, refreshMultiplier = { 0, 0.0, 0.0, 0.0 } }
xi.automaton.manaTank.data ['mana_tank_ii' ] = { id = 228, mpBoost = 2, refreshBase = { 0, 2, 3, 4 }, refreshMultiplier = { 0, 0.0, 0.0, 0.0 } }
-----------------------------------
-- Flame Holder - Reduces Flame Holder Scaling, and consumes all Fire Maneuvers on weaponskill execution. https://wiki.ffo.jp/html/11183.html
-----------------------------------
local validFlameHolderSkills = set
{
xi.mobSkill.ARCUBALLISTA_AUTOMATON,
xi.mobSkill.ARMOR_PIERCER_AUTOMATON,
xi.mobSkill.ARMOR_SHATTERER_AUTOMATON,
xi.mobSkill.BONE_CRUSHER_AUTOMATON,
xi.mobSkill.CANNIBAL_BLADE_AUTOMATON,
xi.mobSkill.CHIMERA_RIPPER_AUTOMATON,
xi.mobSkill.DAZE_AUTOMATON,
xi.mobSkill.KNOCKOUT_AUTOMATON,
xi.mobSkill.MAGIC_MORTAR_AUTOMATON,
xi.mobSkill.SLAPSTICK_AUTOMATON,
xi.mobSkill.STRING_CLIPPER_AUTOMATON,
xi.mobSkill.STRING_SHREDDER_AUTOMATON,
}
m:addOverride('xi.actions.abilities.pets.attachments.flame_holder.onEquip', function(pet, attachment)
pet:addListener('WEAPONSKILL_STATE_EXIT', 'AUTO_FLAME_HOLDER_END', function(automaton, skillId, wasExecuted)
if not validFlameHolderSkills[skillId] then
return
end
if not wasExecuted then
return
end
local master = automaton:getMaster()
if not master then
return
end
-- Consume all Fire Maneuvers on weaponskill execution.
local fireManeuvers = master:countEffect(xi.effect.FIRE_MANEUVER)
for i = 1, fireManeuvers do
master:delStatusEffectSilent(xi.effect.FIRE_MANEUVER)
end
master:updateAttachments()
end)
xi.automaton.onAttachmentEquip(pet, attachment)
end)
m:addOverride('xi.actions.abilities.pets.attachments.flame_holder.onUnequip', function(pet, attachment)
xi.automaton.onAttachmentUnequip(pet, attachment)
pet:removeListener('AUTO_FLAME_HOLDER_END')
end)
-----------------------------------
-- Ice Maker - Reduces Magic Attack Bonus from Ice Maker, and consumes all Ice Maneuvers on magic attack execution. https://wiki.ffo.jp/html/11198.html
-----------------------------------
local validIceMakerSpells = set
{
xi.magic.spell.FIRE,
xi.magic.spell.FIRE_II,
xi.magic.spell.FIRE_III,
xi.magic.spell.FIRE_IV,
xi.magic.spell.FIRE_V,
xi.magic.spell.BLIZZARD,
xi.magic.spell.BLIZZARD_II,
xi.magic.spell.BLIZZARD_III,
xi.magic.spell.BLIZZARD_IV,
xi.magic.spell.BLIZZARD_V,
xi.magic.spell.AERO,
xi.magic.spell.AERO_II,
xi.magic.spell.AERO_III,
xi.magic.spell.AERO_IV,
xi.magic.spell.AERO_V,
xi.magic.spell.STONE,
xi.magic.spell.STONE_II,
xi.magic.spell.STONE_III,
xi.magic.spell.STONE_IV,
xi.magic.spell.STONE_V,
xi.magic.spell.THUNDER,
xi.magic.spell.THUNDER_II,
xi.magic.spell.THUNDER_III,
xi.magic.spell.THUNDER_IV,
xi.magic.spell.THUNDER_V,
xi.magic.spell.WATER,
xi.magic.spell.WATER_II,
xi.magic.spell.WATER_III,
xi.magic.spell.WATER_IV,
xi.magic.spell.WATER_V,
}
m:addOverride('xi.actions.abilities.pets.attachments.ice_maker.onEquip', function(pet, attachment)
pet:addListener('MAGIC_USE', 'AUTO_ICE_MAKER_USE', function(automaton, target, spell, action)
if not validIceMakerSpells[spell:getID()] then
return
end
local master = automaton:getMaster()
if not master then
return
end
local iceManeuvers = master:countEffect(xi.effect.ICE_MANEUVER)
for i = 1, iceManeuvers do
master:delStatusEffectSilent(xi.effect.ICE_MANEUVER)
end
master:updateAttachments()
end)
xi.automaton.onAttachmentEquip(pet, attachment)
end)
m:addOverride('xi.actions.abilities.pets.attachments.ice_maker.onUnequip', function(pet, attachment)
xi.automaton.onAttachmentUnequip(pet, attachment)
pet:removeListener('AUTO_ICE_MAKER_USE')
end)
-----------------------------------
-- Replicator - Reduces amount of absorbs granted by Replicator, and changes them to Blink from Utsusemi. Also consumes Wind Maneuvers. https://wiki.ffo.jp/html/12225.html
-----------------------------------
local shadowTable =
{
[1] = 2,
[2] = 3,
[3] = 4,
}
m:addOverride('xi.actions.abilities.pets.automaton.replicator.onAutomatonAbilityCheck', function(target, automaton, skill)
return 0
end)
m:addOverride('xi.actions.abilities.pets.automaton.replicator.onAutomatonAbility', function(target, automaton, skill, master, action)
local windManeuvers = xi.automaton.getManeuverCount(master, master:countEffect(xi.effect.WIND_MANEUVER))
local shadows = shadowTable[windManeuvers]
automaton:addRecast(xi.recast.ABILITY, skill:getID(), 60)
for i = 1, windManeuvers do
master:delStatusEffectSilent(xi.effect.WIND_MANEUVER)
end
master:updateAttachments()
if
shadows and
target:addStatusEffect(xi.effect.BLINK, { power = shadows, duration = 300, origin = automaton })
then
skill:setMsg(xi.msg.basic.SKILL_GAIN_EFFECT)
else
skill:setMsg(xi.msg.basic.SKILL_NO_EFFECT)
end
return xi.effect.BLINK
end)
-----------------------------------
-- Shock Absorber - Reduces the potency of the stoneskin effect and removes scaling. https://wiki.ffo.jp/html/12927.html
-----------------------------------
m:addOverride('xi.actions.abilities.pets.automaton.shock_absorber.onAutomatonAbilityCheck', function(target, automaton, skill)
return 0
end)
m:addOverride('xi.actions.abilities.pets.automaton.shock_absorber.onAutomatonAbility', function(target, automaton, skill, master, action)
automaton:addRecast(xi.recast.ABILITY, skill:getID(), 180)
if target:addStatusEffect(xi.effect.STONESKIN, { power = 100, duration = 180, origin = automaton, tier = 4 }) then
skill:setMsg(xi.msg.basic.SKILL_GAIN_EFFECT)
else
skill:setMsg(xi.msg.basic.SKILL_NO_EFFECT)
end
return xi.effect.STONESKIN
end)
-----------------------------------
-- Shield Bash - Removes gaurunteed hit chance while Hammermill is equipped. https://wiki.ffo.jp/html/12156.html
-----------------------------------
local shieldBashSlowTable =
{
[1] = { tier = 4, duration = 30 },
[2] = { tier = 5, duration = 50 },
[3] = { tier = 6, duration = 70 },
}
local function applyHammermillSlow(automaton, target, skill, master)
local power = automaton:getMod(xi.mod.AUTO_SHIELD_BASH_SLOW) * 100
if power <= 0 then
return
end
local slowTier = shieldBashSlowTable[master and xi.automaton.getManeuverCount(master, master:countEffect(xi.effect.EARTH_MANEUVER)) or 0]
local params =
{
[1] = { effectId = xi.effect.SLOW, power = power, duration = slowTier.duration, tier = slowTier.tier },
}
xi.combat.action.executeMobskillStatusEffect(automaton, target, skill, params, { messageBypass = true })
end
m:addOverride('xi.actions.abilities.pets.automaton.shield_bash.onAutomatonAbilityCheck', function(target, automaton, skill)
return 0
end)
-----------------------------------
-- Hammermill
-----------------------------------
m:addOverride('xi.actions.abilities.pets.automaton.shield_bash.onAutomatonAbility', function(target, automaton, skill, master, action)
local params = {}
params.baseDamage = automaton:getWeaponDmg()
params.numHits = utils.clamp(1 + xi.automaton.getExtraHits(automaton, 1), 1, 8)
params.fTP = { 1.0, 1.0, 1.0 }
params.attackType = xi.attackType.PHYSICAL
params.damageType = xi.damageType.BLUNT
params.shadowBehavior = params.numHits
local hammermillEquipped = automaton:hasAttachmentSet(xi.item.HAMMERMILL_ATTACHMENT)
if hammermillEquipped then
local shieldBashBonus = 1.0 + automaton:getMod(xi.mod.SHIELD_BASH) / 100
params.fTP =
{
params.fTP[1] * shieldBashBonus,
params.fTP[2] * shieldBashBonus,
params.fTP[3] * shieldBashBonus,
}
end
local info = xi.mobskills.mobPhysicalMove(automaton, target, skill, action, params)
if xi.mobskills.processDamage(automaton, target, skill, action, info) then
target:takeDamage(info.damage, automaton, info.attackType, info.damageType)
xi.mobskills.mobStatusEffectMove(automaton, target, xi.effect.STUN, 1, 0, 6)
if hammermillEquipped then
applyHammermillSlow(automaton, target, skill, master)
end
end
return info.damage
end)
-----------------------------------
-- Analyzer - Caps the amount of skills that can be analyzed to 1. https://wiki.ffo.jp/html/10746.html
-----------------------------------
m:addOverride('xi.actions.abilities.pets.attachments.analyzer.onEquip', function(pet, attachment)
pet:setLocalVar('analyzedSkill1', 0)
pet:addListener('WEAPONSKILL_TAKE', 'ANALYZER_WEAPONSKILL_TAKE', function(mob, target, skill, tp, action)
local analyzerModifier = target:getMod(xi.mod.AUTO_ANALYZER)
local incomingSkill = skill:getID()
if analyzerModifier <= 0 then
return
end
local analyzedSkill = target:getLocalVar('analyzedSkill1')
if incomingSkill == analyzedSkill then
return
end
if incomingSkill ~= analyzedSkill then
target:setLocalVar('analyzedSkill1', incomingSkill)
end
end)
xi.automaton.onAttachmentEquip(pet, attachment)
end)
m:addOverride('xi.actions.abilities.pets.attachments.analyzer.onUnequip', function(pet, attachment)
pet:setLocalVar('analyzedSkill1', 0)
pet:removeListener('ANALYZER_WEAPONSKILL_TAKE')
xi.automaton.onAttachmentUnequip(pet, attachment)
end)
m:addOverride('xi.actions.abilities.pets.attachments.analyzer.onManeuverGain', function(pet, attachment, maneuvers)
end)
m:addOverride('xi.actions.abilities.pets.attachments.analyzer.onManeuverLose', function(pet, attachment, maneuvers)
end)
m:addOverride('xi.actions.abilities.pets.attachments.analyzer.onUpdate', function(pet, attachment, maneuvers)
end)
m:addOverride('utils.handleAutomatonAutoAnalyzer', function(actor, skill, damage)
local analyzerModifier = actor:getMod(xi.mod.AUTO_ANALYZER)
if analyzerModifier <= 0 then
return damage
end
local automatonId = actor:getID()
if not automatonId then
return damage
end
local automatonMaster = actor:getMaster()
if not automatonMaster then
return damage
end
local incomingSkill = skill:getID()
local analyzedSkill = actor:getLocalVar('analyzedSkill1')
if incomingSkill == analyzedSkill then
local earthManeuvers = xi.automaton.getManeuverCount(automatonMaster, automatonMaster:countEffect(xi.effect.EARTH_MANEUVER))
local damageReduction = 10 + 10 * earthManeuvers
return math.floor(damage * (100 - damageReduction) / 100)
end
return damage
end)
-----------------------------------
-- Eraser - Changes Eraser to consume all Maneuvers on activation. https://wiki.ffo.jp/html/5365.html
-----------------------------------
local maneuvers =
{
xi.effect.FIRE_MANEUVER,
xi.effect.ICE_MANEUVER,
xi.effect.WIND_MANEUVER,
xi.effect.EARTH_MANEUVER,
xi.effect.THUNDER_MANEUVER,
xi.effect.WATER_MANEUVER,
xi.effect.LIGHT_MANEUVER,
xi.effect.DARK_MANEUVER,
}
local function removeAllManeuvers(master)
-- Era Eraser consumes all active maneuvers, not just Light Maneuvers.
for _, maneuverId in ipairs(maneuvers) do
for _ = 1, master:countEffect(maneuverId) do
master:delStatusEffectSilent(maneuverId)
end
end
end
local removables =
{
-- Songs
xi.effect.ELEGY,
xi.effect.REQUIEM,
xi.effect.THRENODY,
-- Enfeebling
xi.effect.BLINDNESS,
xi.effect.PARALYSIS,
xi.effect.SILENCE,
xi.effect.POISON,
xi.effect.CURSE_I,
xi.effect.CURSE_II,
xi.effect.DISEASE,
xi.effect.PLAGUE,
xi.effect.WEIGHT,
xi.effect.BIND,
xi.effect.ADDLE,
xi.effect.SLOW,
xi.effect.PETRIFICATION,
-- DoTs
xi.effect.BIO,
xi.effect.DIA,
xi.effect.BURN,
xi.effect.FROST,
xi.effect.CHOKE,
xi.effect.RASP,
xi.effect.SHOCK,
xi.effect.DROWN,
-- Main Stat Downs
xi.effect.STR_DOWN,
xi.effect.DEX_DOWN,
xi.effect.VIT_DOWN,
xi.effect.AGI_DOWN,
xi.effect.INT_DOWN,
xi.effect.MND_DOWN,
xi.effect.CHR_DOWN,
-- Combat Stat Downs
xi.effect.ACCURACY_DOWN,
xi.effect.ATTACK_DOWN,
xi.effect.EVASION_DOWN,
xi.effect.DEFENSE_DOWN,
-- Magic Stat Downs
xi.effect.MAGIC_ACC_DOWN,
xi.effect.MAGIC_ATK_DOWN,
xi.effect.MAGIC_EVASION_DOWN,
xi.effect.MAGIC_DEF_DOWN,
-- HP/MP/TP Stat Downs
xi.effect.MAX_TP_DOWN,
xi.effect.MAX_MP_DOWN,
xi.effect.MAX_HP_DOWN,
}
m:addOverride('xi.actions.abilities.pets.automaton.eraser.onAutomatonAbilityCheck', function(target, automaton, skill)
return 0
end)
m:addOverride('xi.actions.abilities.pets.automaton.eraser.onAutomatonAbility', function(target, automaton, skill, master, action)
automaton:addRecast(xi.recast.ABILITY, skill:getID(), 30)
local lightManeuvers = xi.automaton.getManeuverCount(master, master:countEffect(xi.effect.LIGHT_MANEUVER))
local effectsRemoved = 0
for _, effectId in ipairs(removables) do
if target:hasStatusEffect(effectId) then
target:delStatusEffectSilent(effectId)
effectsRemoved = effectsRemoved + 1
if effectsRemoved >= lightManeuvers then
break
end
end
end
removeAllManeuvers(master)
master:updateAttachments()
if effectsRemoved > 0 then
skill:setMsg(xi.msg.basic.DISAPPEAR_NUM)
else
skill:setMsg(xi.msg.basic.USES)
end
return effectsRemoved
end)
-----------------------------------
-- Economizer - Changes Economizer to consume all Dark Maneuvers on activation. : https://wiki.ffo.jp/html/10435.html
-----------------------------------
local activationThresholds =
{
[0] = 30,
[1] = 40,
[2] = 50,
[3] = 60,
}
m:addOverride('xi.actions.abilities.pets.automaton.economizer.onEquip', function(pet)
pet:addListener('AUTOMATON_ATTACHMENT_CHECK', 'ATTACHMENT_ECONOMIZER', function(automaton, target)
-- If Economizer is still on cooldown, do nothing.
if automaton:hasRecast(xi.recast.ABILITY, xi.mobSkill.ECONOMIZER_AUTOMATON) then
return
end
local master = automaton:getMaster()
if not master then
return
end
local darkManeuvers = master:countEffect(xi.effect.DARK_MANEUVER)
if darkManeuvers == 0 then
return
end
local maxMP = automaton:getMaxMP()
-- If this automaton has no MP, do nothing.
if maxMP == 0 then
return
end
local mpPercent = automaton:getMPP()
local mpThreshold = activationThresholds[darkManeuvers] or 30
-- If the automaton's MP is above the threshold, do nothing.
if mpPercent > mpThreshold then
return
end
automaton:useMobAbility(xi.mobSkill.ECONOMIZER_AUTOMATON, automaton)
end)
end)
m:addOverride('xi.actions.abilities.pets.automaton.economizer.onAutomatonAbility', function(target, automaton, skill, master, action)
automaton:addRecast(xi.recast.ABILITY, skill:getID(), 180)
local darkManeuvers = master:countEffect(xi.effect.DARK_MANEUVER)
local mpRecovered = math.floor(automaton:getMaxMP() * 0.2 * darkManeuvers)
for _ = 1, darkManeuvers do
master:delStatusEffectSilent(xi.effect.DARK_MANEUVER)
end
master:updateAttachments()
skill:setMsg(xi.msg.basic.SKILL_RECOVERS_MP)
return automaton:addMP(mpRecovered)
end)
return m

View file

@ -0,0 +1,129 @@
-----------------------------------
-- Eraser (Pre-2011)
-- Removes up to 3 status effects from the Automaton or its Master based on the number of Light Maneuvers active.
-- Consumes all maneuvers on use.
-- Eraser cannot remove Venom, Death Sentence, Charm or Gradual Petrification.
-- Prioritizes removing effects from the Automaton over the Master.
-- Updated to consume only Light Maneuvers on December 15th, 2011.
-- Updated to consume no maneuvers on March 11th, 2019.
-- https://wiki.ffo.jp/html/5365.html
-----------------------------------
require('modules/module_utils')
-----------------------------------
local moduleName = 'era_eraser'
if xi.module.isContentEnabled('ABYSSEA') then
return { name = moduleName }
end
local m = Module:new(moduleName)
local maneuvers =
{
xi.effect.FIRE_MANEUVER,
xi.effect.ICE_MANEUVER,
xi.effect.WIND_MANEUVER,
xi.effect.EARTH_MANEUVER,
xi.effect.THUNDER_MANEUVER,
xi.effect.WATER_MANEUVER,
xi.effect.LIGHT_MANEUVER,
xi.effect.DARK_MANEUVER,
}
local removables =
{
-- Songs
xi.effect.ELEGY,
xi.effect.REQUIEM,
xi.effect.THRENODY,
-- Enfeebling
xi.effect.BLINDNESS,
xi.effect.PARALYSIS,
xi.effect.SILENCE,
xi.effect.POISON,
xi.effect.CURSE_I,
xi.effect.CURSE_II,
xi.effect.DISEASE,
xi.effect.PLAGUE,
xi.effect.WEIGHT,
xi.effect.BIND,
xi.effect.ADDLE,
xi.effect.SLOW,
xi.effect.PETRIFICATION,
-- DoTs
xi.effect.BIO,
xi.effect.DIA,
xi.effect.BURN,
xi.effect.FROST,
xi.effect.CHOKE,
xi.effect.RASP,
xi.effect.SHOCK,
xi.effect.DROWN,
-- Main Stat Downs
xi.effect.STR_DOWN,
xi.effect.DEX_DOWN,
xi.effect.VIT_DOWN,
xi.effect.AGI_DOWN,
xi.effect.INT_DOWN,
xi.effect.MND_DOWN,
xi.effect.CHR_DOWN,
-- Combat Stat Downs
xi.effect.ACCURACY_DOWN,
xi.effect.ATTACK_DOWN,
xi.effect.EVASION_DOWN,
xi.effect.DEFENSE_DOWN,
-- Magic Stat Downs
xi.effect.MAGIC_ACC_DOWN,
xi.effect.MAGIC_ATK_DOWN,
xi.effect.MAGIC_EVASION_DOWN,
xi.effect.MAGIC_DEF_DOWN,
-- HP/MP/TP Stat Downs
xi.effect.MAX_TP_DOWN,
xi.effect.MAX_MP_DOWN,
xi.effect.MAX_HP_DOWN,
}
m:addOverride('xi.actions.abilities.pets.automaton.eraser.onAutomatonAbilityCheck', function(target, automaton, skill)
return 0
end)
m:addOverride('xi.actions.abilities.pets.automaton.eraser.onAutomatonAbility', function(target, automaton, skill, master, action)
automaton:addRecast(xi.recast.ABILITY, skill:getID(), 30)
local lightManeuvers = master:countEffect(xi.effect.LIGHT_MANEUVER)
local effectsRemoved = 0
for _, effectId in ipairs(removables) do
if target:hasStatusEffect(effectId) then
target:delStatusEffectSilent(effectId)
effectsRemoved = effectsRemoved + 1
if effectsRemoved >= lightManeuvers then
break
end
end
end
for _, maneuverId in ipairs(maneuvers) do
for _ = 1, master:countEffect(maneuverId) do
master:delStatusEffectSilent(maneuverId)
end
end
if effectsRemoved > 0 then
skill:setMsg(xi.msg.basic.DISAPPEAR_NUM)
else
skill:setMsg(xi.msg.basic.USES)
end
return effectsRemoved
end)
return m

View file

@ -0,0 +1,51 @@
-----------------------------------
-- Replicator (Pre-2011)
-- Description : Applies Blink based on Wind Maneuvers when HP is below a certain threshold. Cooldown of 1 minute. Consumes all Wind Maneuvers on use.
-- If Automaton has a Damage Gauge equipped, activation threshold is increased to 75% HP.
-- Amount of images increased on December 15th, 2011.
-- Changed from Blink to Copy Image on August 5th, 2015.
-- Changed to not consume Wind Maneuvers on August 6th, 2019.
-- https://wiki.ffo.jp/html/12225.html
-----------------------------------
require('modules/module_utils')
-----------------------------------
local moduleName = 'era_replicator'
if xi.module.isContentEnabled('ABYSSEA') then
return { name = moduleName }
end
local m = Module:new(moduleName)
local shadowTable =
{
[1] = 2,
[2] = 3,
[3] = 4,
}
m:addOverride('xi.actions.abilities.pets.automaton.replicator.onAutomatonAbilityCheck', function(target, automaton, skill)
return 0
end)
m:addOverride('xi.actions.abilities.pets.automaton.replicator.onAutomatonAbility', function(target, automaton, skill, master, action)
local windManeuvers = master:countEffect(xi.effect.WIND_MANEUVER)
local shadows = shadowTable[windManeuvers]
automaton:addRecast(xi.recast.ABILITY, skill:getID(), 60)
for i = 1, windManeuvers do
master:delStatusEffectSilent(xi.effect.WIND_MANEUVER)
end
if target:addStatusEffect(xi.effect.BLINK, { power = shadows, duration = 300, origin = automaton }) then
skill:setMsg(xi.msg.basic.SKILL_GAIN_EFFECT)
else
skill:setMsg(xi.msg.basic.SKILL_NO_EFFECT)
end
return xi.effect.BLINK
end)
return m

View file

@ -1,37 +0,0 @@
-----------------------------------
-- Reverts Hastega to no longer have a duration based on summoning magic and reduces haste power
-- Source: https://www.bg-wiki.com/ffxi/Version_Update_(04/08/2009)
-----------------------------------
require('modules/module_utils')
-----------------------------------
local moduleName = 'hastega'
if xi.module.isContentEnabled('WOTG') then
return { name = moduleName }
end
local m = Module:new(moduleName)
m:addOverride('xi.actions.abilities.pets.hastega.onPetAbility', function(target, pet, petskill, summoner, action)
local duration = 180
xi.job_utils.summoner.onUseBloodPact(target, petskill, summoner, action)
-- Reverts Garuda's Hastega to use 102/1024 or 9.96%
local typeEffect = xi.effect.HASTE
if target:addStatusEffect(typeEffect, { power = 996, duration = duration, origin = pet }) then
if target:getID() == action:getPrimaryTargetID() then
petskill:setMsg(xi.msg.basic.SKILL_GAIN_EFFECT_2)
else
petskill:setMsg(xi.msg.basic.JA_GAIN_EFFECT)
end
else
petskill:setMsg(xi.msg.basic.JA_NO_EFFECT_2)
return
end
return typeEffect
end)
return m

View file

@ -145,11 +145,10 @@ m:addOverride('xi.actions.weaponskills.namas_arrow.onUseWeaponSkill', function(p
params.overrideVE = 480
params.rangedAccuracyBonus = 100
local damage, criticalHit, tpHits, extraHits = xi.weaponskills.doRangedWeaponskill(player, target, wsID, params, tp, action, primary)
-- Apply aftermath
xi.aftermath.addStatusEffect(player, tp, xi.slot.RANGED, xi.aftermath.type.RELIC)
local damage, criticalHit, tpHits, extraHits = xi.weaponskills.doRangedWeaponskill(player, target, wsID, params, tp, action, primary)
return tpHits, extraHits, criticalHit, damage
end)

View file

@ -1,305 +0,0 @@
-----------------------------------
-- WoTG Era Automaton Weaponskills
-----------------------------------
require('modules/module_utils')
-----------------------------------
local moduleName = 'wotg_automaton'
local m = Module:new(moduleName)
if xi.module.isContentEnabled('ABYSSEA') then
return { name = moduleName }
end
-----------------------------------
-- Arcuballista
-----------------------------------
m:addOverride('xi.actions.abilities.pets.automaton.arcuballista.onAutomatonAbility', function(target, automaton, skill, master, action)
local params = {}
params.baseDamage = xi.automaton.getRangedBaseDamage(automaton)
params.numHits = 1
params.fTP = { 2.5, 3.0, 4.0 }
params.dex_wSC = 0.30
params.accuracyModifier = { 100, 100, 100 }
params.attackType = xi.attackType.RANGED
params.damageType = xi.damageType.PIERCING
params.shadowBehavior = xi.mobskills.shadowBehavior.NUMSHADOWS_1
params.skipParry = true
params.skipGuard = true
params.skipBlock = true
xi.automaton.applyFlameHolder(automaton, params.fTP)
local info = xi.mobskills.mobRangedMove(automaton, target, skill, action, params)
if xi.mobskills.processDamage(automaton, target, skill, action, info) then
target:takeDamage(info.damage, automaton, info.attackType, info.damageType)
end
return info.damage
end)
-----------------------------------
-- Armor Piercer
-----------------------------------
m:addOverride('xi.actions.abilities.pets.automaton.armor_piercer.onAutomatonAbility', function(target, automaton, skill, master, action)
local params = {}
params.baseDamage = xi.automaton.getRangedBaseDamage(automaton)
params.numHits = 1
params.fTP = { 3.0, 3.5, 4.0 }
params.dex_wSC = 0.30
params.ignoreDefense = { 0.5, 0.5, 0.5 }
params.accuracyModifier = { 100, 100, 100 }
params.attackType = xi.attackType.RANGED
params.damageType = xi.damageType.PIERCING
params.shadowBehavior = xi.mobskills.shadowBehavior.NUMSHADOWS_1
params.skipParry = true
params.skipGuard = true
params.skipBlock = true
xi.automaton.applyFlameHolder(automaton, params.fTP)
local info = xi.mobskills.mobRangedMove(automaton, target, skill, action, params)
if xi.mobskills.processDamage(automaton, target, skill, action, info) then
target:takeDamage(info.damage, automaton, info.attackType, info.damageType)
end
return info.damage
end)
-----------------------------------
-- Bone Crusher
-----------------------------------
m:addOverride('xi.actions.abilities.pets.automaton.bone_crusher.onAutomatonAbility', function(target, automaton, skill, master, action)
local params = {}
params.baseDamage = automaton:getWeaponDmg()
params.numHits = utils.clamp(3 + xi.automaton.getExtraHits(automaton, 3), 1, 8)
params.fTP = { 1.5, 1.5, 1.5 }
params.vit_wSC = 0.30
params.attackType = xi.attackType.PHYSICAL
params.damageType = xi.damageType.BLUNT
params.shadowBehavior = params.numHits
if target:isUndead() then
params.fTP = { 2.5, 2.5, 2.5 }
end
xi.automaton.applyFlameHolder(automaton, params.fTP)
local info = xi.mobskills.mobPhysicalMove(automaton, target, skill, action, params)
if xi.mobskills.processDamage(automaton, target, skill, action, info) then
target:takeDamage(info.damage, automaton, info.attackType, info.damageType)
xi.mobskills.mobStatusEffectMove(automaton, target, xi.effect.STUN, 1, 0, 4)
end
return info.damage
end)
-----------------------------------
-- Cannibal Blade
-----------------------------------
m:addOverride('xi.actions.abilities.pets.automaton.cannibal_blade.onAutomatonAbility', function(target, automaton, skill, master, action)
local params = {}
params.baseDamage = automaton:getSkillLevel(xi.skill.AUTOMATON_MELEE) / 9.2
params.numHits = 1
params.fTP = { 11.0, 12.5, 14.0 }
params.attackType = xi.attackType.PHYSICAL
params.damageType = xi.damageType.SLASHING
params.shadowBehavior = xi.mobskills.shadowBehavior.NUMSHADOWS_1
params.guaranteedFirstHit = true
params.skipFSTR = true
params.skipPDIF = true
xi.automaton.applyFlameHolder(automaton, params.fTP)
local info = xi.mobskills.mobPhysicalMove(automaton, target, skill, action, params)
if xi.mobskills.processDamage(automaton, target, skill, action, info) then
target:takeDamage(info.damage, automaton, info.attackType, info.damageType)
if not target:isUndead() then
automaton:addHP(info.damage)
end
end
-- Does not return TP.
automaton:setTP(0)
return info.damage
end)
-----------------------------------
-- Chimera Ripper
-----------------------------------
m:addOverride('xi.actions.abilities.pets.automaton.chimera_ripper.onAutomatonAbility', function(target, automaton, skill, master, action)
local params = {}
params.baseDamage = automaton:getWeaponDmg()
params.numHits = utils.clamp(1 + xi.automaton.getExtraHits(automaton, 1), 1, 8)
params.fTP = { 2.0, 2.5, 3.0 }
params.str_wSC = 0.30
params.accuracyModifier = { 100, 100, 100 }
params.attackType = xi.attackType.PHYSICAL
params.damageType = xi.damageType.SLASHING
params.shadowBehavior = params.numHits
xi.automaton.applyFlameHolder(automaton, params.fTP)
local info = xi.mobskills.mobPhysicalMove(automaton, target, skill, action, params)
if xi.mobskills.processDamage(automaton, target, skill, action, info) then
target:takeDamage(info.damage, automaton, info.attackType, info.damageType)
end
return info.damage
end)
-----------------------------------
-- Daze
-----------------------------------
m:addOverride('xi.actions.abilities.pets.automaton.daze.onAutomatonAbility', function(target, automaton, skill, master, action)
local params = {}
params.baseDamage = xi.automaton.getRangedBaseDamage(automaton)
params.numHits = 1
params.fTP = { 5.0, 5.5, 6.0 }
params.dex_wSC = 0.30
params.accuracyModifier = { 150, 150, 150 }
params.attackType = xi.attackType.RANGED
params.damageType = xi.damageType.PIERCING
params.shadowBehavior = xi.mobskills.shadowBehavior.NUMSHADOWS_1
params.skipParry = true
params.skipGuard = true
params.skipBlock = true
xi.automaton.applyFlameHolder(automaton, params.fTP)
local info = xi.mobskills.mobRangedMove(automaton, target, skill, action, params)
if xi.mobskills.processDamage(automaton, target, skill, action, info) then
target:takeDamage(info.damage, automaton, info.attackType, info.damageType)
xi.mobskills.mobStatusEffectMove(automaton, target, xi.effect.STUN, 1, 0, 4)
end
return info.damage
end)
-----------------------------------
-- Knockout
-----------------------------------
m:addOverride('xi.actions.abilities.pets.automaton.knockout.onAutomatonAbility', function(target, automaton, skill, master, action)
local params = {}
params.numHits = utils.clamp(1 + xi.automaton.getExtraHits(automaton, 1), 1, 8)
params.fTP = { 4.0, 4.5, 5.0 }
params.agi_wSC = 0.40
params.accuracyModifier = { 50, 50, 50 }
params.attackType = xi.attackType.PHYSICAL
params.damageType = xi.damageType.BLUNT
params.shadowBehavior = params.numHits
xi.automaton.applyFlameHolder(automaton, params.fTP)
local info = xi.mobskills.mobPhysicalMove(automaton, target, skill, action, params)
if xi.mobskills.processDamage(automaton, target, skill, action, info) then
target:takeDamage(info.damage, automaton, info.attackType, info.damageType)
xi.mobskills.mobStatusEffectMove(automaton, target, xi.effect.EVASION_DOWN, 20, 0, 30)
end
return info.damage
end)
-----------------------------------
-- Magic Mortar
-----------------------------------
m:addOverride('xi.actions.abilities.pets.automaton.magic_mortar.onAutomatonAbility', function(target, automaton, skill, master, action)
local params = {}
params.baseDamage = automaton:getMaxHP() - automaton:getHP()
params.fTP = { 0.50, 0.75, 1.00 }
params.element = xi.element.NONE
params.attackType = xi.attackType.MAGICAL
params.damageType = xi.damageType.ELEMENTAL
params.shadowBehavior = xi.mobskills.shadowBehavior.IGNORE_SHADOWS
-- Flame Holder multiplies the base damage of Magic Mortar. Gives a 25% boost at 3 Fire Maneuvers.
local flameHolderModifier = 1.0 + (automaton:getMod(xi.mod.WEAPONSKILL_DAMAGE_BASE) - 100) / 1000
if flameHolderModifier > 1.0 then
params.baseDamage = math.floor(params.baseDamage * flameHolderModifier)
end
local info = xi.mobskills.mobMagicalMove(automaton, target, skill, action, params)
if xi.mobskills.processDamage(automaton, target, skill, action, info) then
target:takeDamage(info.damage, automaton, info.attackType, info.damageType)
end
return info.damage
end)
-----------------------------------
-- Slapstick
-----------------------------------
m:addOverride('xi.actions.abilities.pets.automaton.slapstick.onAutomatonAbility', function(target, automaton, skill, master, action)
local params = {}
params.numHits = utils.clamp(3 + xi.automaton.getExtraHits(automaton, 3), 1, 8)
params.fTP = { 1.0, 1.0, 1.0 }
params.str_wSC = 0.20
params.dex_wSC = 0.20
params.accuracyModifier = { 0, 30, 50 }
params.attackType = xi.attackType.PHYSICAL
params.damageType = xi.damageType.BLUNT
params.shadowBehavior = params.numHits
xi.automaton.applyFlameHolder(automaton, params.fTP)
local info = xi.mobskills.mobPhysicalMove(automaton, target, skill, action, params)
if xi.mobskills.processDamage(automaton, target, skill, action, info) then
target:takeDamage(info.damage, automaton, info.attackType, info.damageType)
end
return info.damage
end)
-----------------------------------
-- String Clipper
-----------------------------------
m:addOverride('xi.actions.abilities.pets.automaton.string_clipper.onAutomatonAbility', function(target, automaton, skill, master, action)
local params = {}
params.baseDamage = automaton:getWeaponDmg()
params.numHits = utils.clamp(2 + xi.automaton.getExtraHits(automaton, 2), 1, 8)
params.fTP = { 2.0, 2.0, 2.0 }
params.str_wSC = 0.15
params.dex_wSC = 0.15
params.attackMultiplier = { 1.5, 1.5, 1.5 }
params.accuracyModifier = { 0, 50, 100 }
params.attackType = xi.attackType.PHYSICAL
params.damageType = xi.damageType.SLASHING
params.shadowBehavior = params.numHits
xi.automaton.applyFlameHolder(automaton, params.fTP)
local info = xi.mobskills.mobPhysicalMove(automaton, target, skill, action, params)
if xi.mobskills.processDamage(automaton, target, skill, action, info) then
target:takeDamage(info.damage, automaton, info.attackType, info.damageType)
end
return info.damage
end)
return m

View file

@ -37,7 +37,7 @@ m:addOverride('xi.actions.weaponskills.smash_axe.onUseWeaponSkill', function(pla
local damage, criticalHit, tpHits, extraHits = xi.weaponskills.doPhysicalWeaponskill(player, target, wsID, params, tp, action, primary, taChar)
-- Handle status effect
if math.randomInt(1, 100) <= xi.weaponskills.fTP(tp, { 50, 75, 100 }) then
if math.random(1, 100) <= xi.weaponskills.fTP(tp, { 50, 75, 100 }) then
local effectId = xi.effect.STUN
local actionElement = xi.element.THUNDER
local power = 1
@ -63,7 +63,7 @@ m:addOverride('xi.actions.weaponskills.gale_axe.onUseWeaponSkill', function(play
local damage, criticalHit, tpHits, extraHits = xi.weaponskills.doPhysicalWeaponskill(player, target, wsID, params, tp, action, primary, taChar)
-- Handle status effect
if math.randomInt(1, 100) <= xi.weaponskills.fTP(tp, { 50, 75, 100 }) then
if math.random(1, 100) <= xi.weaponskills.fTP(tp, { 50, 75, 100 }) then
local effectId = xi.effect.CHOKE
local actionElement = xi.element.WIND
local power = 5

View file

@ -56,7 +56,7 @@ m:addOverride('xi.actions.weaponskills.brainshaker.onUseWeaponSkill', function(p
local damage, criticalHit, tpHits, extraHits = xi.weaponskills.doPhysicalWeaponskill(player, target, wsID, params, tp, action, primary, taChar)
-- Handle status effect
if math.randomInt(1, 100) <= xi.weaponskills.fTP(tp, { 50, 75, 100 }) then
if math.random(1, 100) <= xi.weaponskills.fTP(tp, { 50, 75, 100 }) then
local effectId = xi.effect.STUN
local actionElement = xi.element.THUNDER
local power = 1
@ -106,7 +106,7 @@ m:addOverride('xi.actions.weaponskills.skullbreaker.onUseWeaponSkill', function(
local damage, criticalHit, tpHits, extraHits = xi.weaponskills.doPhysicalWeaponskill(player, target, wsID, params, tp, action, primary, taChar)
-- Handle status effect
if math.randomInt(1, 100) <= xi.weaponskills.fTP(tp, { 50, 75, 100 }) then
if math.random(1, 100) <= xi.weaponskills.fTP(tp, { 50, 75, 100 }) then
local effectId = xi.effect.INT_DOWN
local actionElement = xi.element.FIRE
local power = 10

View file

@ -63,7 +63,7 @@ m:addOverride('xi.actions.weaponskills.shadowstitch.onUseWeaponSkill', function(
local damage, criticalHit, tpHits, extraHits = xi.weaponskills.doPhysicalWeaponskill(player, target, wsID, params, tp, action, primary, taChar)
-- Handle status effect
if math.randomInt(1, 100) <= xi.weaponskills.fTP(tp, { 50, 75, 100 }) then
if math.random(1, 100) <= xi.weaponskills.fTP(tp, { 50, 75, 100 }) then
local effectId = xi.effect.BIND
local actionElement = xi.element.ICE
local power = 1
@ -270,7 +270,7 @@ m:addOverride('xi.actions.weaponskills.mordant_rime.onUseWeaponSkill', function(
local damage, criticalHit, tpHits, extraHits = xi.weaponskills.doPhysicalWeaponskill(player, target, wsID, params, tp, action, primary, taChar)
-- Handle status effect
if math.randomInt(1, 100) <= xi.weaponskills.fTP(tp, { 50, 75, 100 }) then
if math.random(1, 100) <= xi.weaponskills.fTP(tp, { 50, 75, 100 }) then
local effectId = xi.effect.WEIGHT
local actionElement = xi.element.WIND
local power = 25

View file

@ -37,7 +37,7 @@ m:addOverride('xi.actions.weaponskills.tachi_hobaku.onUseWeaponSkill', function(
local damage, criticalHit, tpHits, extraHits = xi.weaponskills.doPhysicalWeaponskill(player, target, wsID, params, tp, action, primary, taChar)
-- Handle status effect
if math.randomInt(1, 100) <= xi.weaponskills.fTP(tp, { 50, 75, 100 }) then
if math.random(1, 100) <= xi.weaponskills.fTP(tp, { 50, 75, 100 }) then
local effectId = xi.effect.STUN
local actionElement = xi.element.THUNDER
local power = 1

View file

@ -92,7 +92,7 @@ m:addOverride('xi.actions.weaponskills.shockwave.onUseWeaponSkill', function(pla
local power = 1
local skillType = xi.skill.GREAT_SWORD
local resist = xi.combat.magicHitRate.calculateResistRate(player, target, 0, skillType, 0, actionElement, 0, effectId, 0)
local duration = math.floor((math.randomInt(0, 30) + tp * 0.01) * resist)
local duration = math.floor((math.random(0, 30) + tp * 0.01) * resist)
xi.weaponskills.handleWeaponskillEffect(player, target, effectId, actionElement, damage, power, duration)
return tpHits, extraHits, criticalHit, damage

View file

@ -37,7 +37,7 @@ m:addOverride('xi.actions.weaponskills.shoulder_tackle.onUseWeaponSkill', functi
local damage, criticalHit, tpHits, extraHits = xi.weaponskills.doPhysicalWeaponskill(player, target, wsID, params, tp, action, primary, taChar)
if math.randomInt(1, 100) <= xi.weaponskills.fTP(tp, { 50, 75, 100 }) then
if math.random(1, 100) <= xi.weaponskills.fTP(tp, { 50, 75, 100 }) then
local effectId = xi.effect.STUN
local actionElement = xi.element.THUNDER
local power = 1

View file

@ -138,11 +138,9 @@ m:addOverride('xi.actions.weaponskills.coronach.onUseWeaponSkill', function(play
params.overrideVE = 240
params.rangedAccuracyBonus = 100
xi.aftermath.addStatusEffect(player, tp, xi.slot.MAIN, xi.aftermath.type.RELIC)
local damage, criticalHit, tpHits, extraHits = xi.weaponskills.doRangedWeaponskill(player, target, wsID, params, tp, action, primary)
-- Apply aftermath
xi.aftermath.addStatusEffect(player, tp, xi.slot.RANGED, xi.aftermath.type.RELIC)
return tpHits, extraHits, criticalHit, damage
end)
@ -158,8 +156,7 @@ m:addOverride('xi.actions.weaponskills.trueflight.onUseWeaponSkill', function(pl
params.includemab = true
params.dStat = xi.mod.AGI
-- Apply aftermath
xi.aftermath.addStatusEffect(player, tp, xi.slot.RANGED, xi.aftermath.type.MYTHIC)
xi.aftermath.addStatusEffect(player, tp, xi.slot.MAIN, xi.aftermath.type.MYTHIC)
local damage, criticalHit, tpHits, extraHits = xi.weaponskills.doMagicWeaponskill(player, target, wsID, params, tp, action, primary)
return tpHits, extraHits, criticalHit, damage
@ -177,8 +174,7 @@ m:addOverride('xi.actions.weaponskills.leaden_salute.onUseWeaponSkill', function
params.includemab = true
params.dStat = xi.mod.AGI
-- Apply aftermath
xi.aftermath.addStatusEffect(player, tp, xi.slot.RANGED, xi.aftermath.type.MYTHIC)
xi.aftermath.addStatusEffect(player, tp, xi.slot.MAIN, xi.aftermath.type.MYTHIC)
local damage, criticalHit, tpHits, extraHits = xi.weaponskills.doMagicWeaponskill(player, target, wsID, params, tp, action, primary)
return tpHits, extraHits, criticalHit, damage

View file

@ -69,7 +69,7 @@ m:addOverride('xi.actions.weaponskills.leg_sweep.onUseWeaponSkill', function(pla
local damage, criticalHit, tpHits, extraHits = xi.weaponskills.doPhysicalWeaponskill(player, target, wsID, params, tp, action, primary, taChar)
-- Handle status effect
if math.randomInt(1, 100) <= xi.weaponskills.fTP(tp, { 33, 66, 100 }) then
if math.random(1, 100) <= xi.weaponskills.fTP(tp, { 33, 66, 100 }) then
local effectId = xi.effect.STUN
local actionElement = xi.element.THUNDER
local power = 1

View file

@ -182,7 +182,7 @@ m:addOverride('xi.actions.weaponskills.catastrophe.onUseWeaponSkill', function(p
-- Handle HP Drain
if not target:isUndead() then
local drain = math.floor(damage * math.randomInt(30, 70) / 100)
local drain = math.floor(damage * math.random(30, 70) / 100)
drain = utils.clamp(drain, 0, targetHP)

View file

@ -70,7 +70,7 @@ m:addOverride('xi.actions.weaponskills.starburst.onUseWeaponSkill', function(pla
params.ele = xi.element.LIGHT
params.dStat = xi.mod.INT
if math.randomInt(1, 100) <= 50 then
if math.random(1, 100) <= 50 then
params.ele = xi.element.DARK
end
@ -89,7 +89,7 @@ m:addOverride('xi.actions.weaponskills.sunburst.onUseWeaponSkill', function(play
params.ele = xi.element.LIGHT
params.dStat = xi.mod.INT
if math.randomInt(1, 100) <= 50 then
if math.random(1, 100) <= 50 then
params.ele = xi.element.DARK
end

View file

@ -72,7 +72,7 @@ m:addOverride('xi.actions.weaponskills.flat_blade.onUseWeaponSkill', function(pl
local damage, criticalHit, tpHits, extraHits = xi.weaponskills.doPhysicalWeaponskill(player, target, wsID, params, tp, action, primary, taChar)
-- Handle status effect
if math.randomInt(1, 100) <= xi.weaponskills.fTP(tp, { 50, 75, 100 }) then
if math.random(1, 100) <= xi.weaponskills.fTP(tp, { 50, 75, 100 }) then
local effectId = xi.effect.STUN
local actionElement = xi.element.THUNDER
local power = 1
@ -166,8 +166,8 @@ m:addOverride('xi.actions.weaponskills.spirits_within.onUseWeaponSkill', functio
local damage = dmg
damage = math.floor(damage * xi.combat.damage.calculateDamageAdjustment(target, false, false, false, true))
damage = math.floor(damage * xi.spells.damage.calculateAbsorption(target, xi.element.NONE, false, false, false, true))
damage = math.floor(damage * xi.spells.damage.calculateNullification(target, xi.element.NONE, false, false, false, true))
damage = math.floor(damage * xi.spells.damage.calculateAbsorption(target, xi.element.NONE, false))
damage = math.floor(damage * xi.spells.damage.calculateNullification(target, xi.element.NONE, false, true))
damage = math.floor(target:handleSevereDamage(damage, false))
if damage > 0 then
@ -298,19 +298,21 @@ m:addOverride('xi.actions.weaponskills.atonement.onUseWeaponSkill', function(pla
return calcParams.tpHitsLanded, calcParams.extraHitsLanded, calcParams.criticalHit, damage
end
-- Calculate damage based on target's CE and VE, clamped to the global damage cap.
damage = utils.clamp(target:getCE(player) * cePercent + target:getVE(player) * vePercent, 0, globalDamageCap)
-- Regular damage formula
local dmg = target:getCE(player) * cePercent + target:getVE(player) * vePercent
-- This is here to account for damage adjustments needed because it is breath damage.
damage = dmg
damage = math.floor(damage * xi.combat.damage.calculateDamageAdjustment(target, false, false, false, true))
damage = math.floor(damage * xi.spells.damage.calculateAbsorption(target, xi.element.NONE, false, false, false, true))
damage = math.floor(damage * xi.spells.damage.calculateNullification(target, xi.element.NONE, false, false, false, true))
damage = math.floor(damage * xi.spells.damage.calculateAbsorption(target, xi.element.NONE, false))
damage = math.floor(damage * xi.spells.damage.calculateNullification(target, xi.element.NONE, false, true))
damage = math.floor(target:handleSevereDamage(damage, false))
if player:getMod(xi.mod.WEAPONSKILL_DAMAGE_BASE + wsID) > 0 then
damage = damage * (100 + player:getMod(xi.mod.WEAPONSKILL_DAMAGE_BASE + wsID)) / 100
end
damage = utils.clamp(damage, 0, globalDamageCap)
calcParams.finalDmg = damage
-- If one or more hits land, Atonement always counts as landing both hits.
@ -352,7 +354,7 @@ m:addOverride('xi.actions.weaponskills.death_blossom.onUseWeaponSkill', function
local damage, criticalHit, tpHits, extraHits = xi.weaponskills.doPhysicalWeaponskill(player, target, wsID, params, tp, action, primary, taChar)
-- Handle status effect
if math.randomInt(1, 100) <= xi.weaponskills.fTP(tp, { 50, 75, 100 }) then
if math.random(1, 100) <= xi.weaponskills.fTP(tp, { 50, 75, 100 }) then
local effectId = xi.effect.MAGIC_EVASION_DOWN
local actionElement = xi.element.THUNDER
local power = 10

View file

@ -1,24 +0,0 @@
-----------------------------------
-- Pre-SoA Magic Burst Module
-- Restores the pre-2015 magic burst multiplier: 1.25 + 0.05 per skillchain step.
-- https://forum.square-enix.com/ffxi/threads/46531
-----------------------------------
require('modules/module_utils')
-----------------------------------
local moduleName = 'era_magic_burst'
if xi.module.isContentEnabled('SOA') then
return { name = moduleName }
end
local m = Module:new(moduleName)
m:addOverride('xi.spells.damage.calculateIfMagicBurst', function(caster, target, spellElement, magicBurstTier)
if spellElement <= xi.element.NONE then
return 1
end
return 1.25 + 0.05 * utils.clamp(magicBurstTier, 1, 5)
end)
return m

View file

@ -1,132 +0,0 @@
-----------------------------------
-- Era Skillchain Module
-- Adds a resistance check back to skillchain damage calculations.
-- https://wiki.ffo.jp/html/32936.html
-----------------------------------
require('modules/module_utils')
-----------------------------------
local moduleName = 'era_skillchain'
if xi.module.isContentEnabled('SOA') then
return { name = moduleName }
end
local m = Module:new(moduleName)
local chainMultipliers =
{
[1] = { 0.50, 0.60, 0.70, 0.80, 0.90, 1.00 }, -- Level 1
[2] = { 0.60, 0.75, 1.00, 1.25, 1.50, 1.75 }, -- Level 2
[3] = { 1.00, 1.50, 1.75, 2.00, 2.25, 2.50 }, -- Level 3
[4] = { 1.50, 1.80, 2.10, 2.40, 2.70, 3.00 }, -- Level 4 "Radiance/Umbra"
}
local function getSkillchainElementToUse(target, skillchainType)
-- Build skillchain available elements table.
local elementTable = {}
for i = xi.element.FIRE, xi.element.DARK do
if xi.data.element.skillchainElementTable[i][skillchainType] > 0 then
table.insert(elementTable, #elementTable + 1, i)
end
end
-- Early return: Single elemental SC. No need to continue.
if #elementTable == 1 then
return elementTable[1]
end
-- Get lowest resistance rank value.
local lowestResRank = 11
local lowestElement = xi.element.FIRE
for j = #elementTable, 1, -1 do
local resRankValue = target:getMod(xi.data.element.getElementalResistanceRankModifier(elementTable[j]))
if resRankValue <= lowestResRank then
lowestResRank = resRankValue
lowestElement = elementTable[j]
end
end
return lowestElement
end
m:addOverride('xi.combat.skillchain.calculateSkillchainDamage', function(actor, target, baseDamage)
local skillchainEffect = target:getStatusEffect(xi.effect.SKILLCHAIN)
if not skillchainEffect then
return 0
end
local skillchainType = skillchainEffect:getPower()
if skillchainType == 0 then
return 0
end
local skillchainLevel = skillchainEffect:getTier()
if skillchainLevel < 1 or skillchainLevel > 4 then
return 0
end
local skillchainCount = skillchainEffect:getSubPower()
if skillchainCount < 1 or skillchainCount > 6 then
return 0
end
local skillchainElement = getSkillchainElementToUse(target, skillchainType)
if not skillchainElement then
return 0
end
if xi.spells.damage.calculateNullification(target, skillchainElement, false, true, false, false) == 0 then
return 0
end
-- Calculate resist, base damage and multipliers.
local finalDamage = math.abs(baseDamage) -- Damage from skillchain, no matter if absorbed or not.
local levelMultiplier = chainMultipliers[skillchainLevel][skillchainCount]
local bonusMultiplier = 1 + actor:getMod(xi.mod.SKILLCHAINBONUS) / 100
local damageMultiplier = 1 + actor:getMod(xi.mod.SKILLCHAINDMG) / 10000
local dayWeatherMultiplier = xi.spells.damage.calculateDayAndWeather(actor, skillchainElement, false)
local staffMultiplier = xi.spells.damage.calculateElementalStaffBonus(actor, skillchainElement)
local affinityMultiplier = xi.spells.damage.calculateElementalAffinityBonus(actor, skillchainElement)
local resistRate = xi.combat.magicHitRate.calculateResistRate(actor, target, 0, 0, xi.skillRank.A_PLUS, skillchainElement, 0, 0, 0)
local magicTakenMultiplier = xi.combat.damage.calculateDamageAdjustment(target, false, true, false, false)
-- Unconfirmed order.
local inninMultiplier = 1 + actor:getMerit(xi.merit.INNIN_EFFECT) / 100
local sengikoriMultiplier = 1 + target:getMod(xi.mod.SENGIKORI_SC_DMG_DEBUFF) / 100
local absorptionMultiplier = xi.spells.damage.calculateAbsorption(target, skillchainElement, false, true, false, false)
-- Apply multipliers in order and floor after each step.
finalDamage = math.floor(finalDamage * levelMultiplier)
finalDamage = math.floor(finalDamage * bonusMultiplier) + actor:getMod(xi.mod.MAGIC_DAMAGE)
finalDamage = math.floor(finalDamage * damageMultiplier)
finalDamage = math.floor(finalDamage * dayWeatherMultiplier)
finalDamage = math.floor(finalDamage * staffMultiplier)
finalDamage = math.floor(finalDamage * affinityMultiplier)
finalDamage = math.floor(finalDamage * resistRate)
finalDamage = math.floor(finalDamage * magicTakenMultiplier)
finalDamage = math.floor(finalDamage * inninMultiplier)
finalDamage = math.floor(finalDamage * sengikoriMultiplier)
finalDamage = math.floor(finalDamage * absorptionMultiplier)
-- Handle (reset) Sengikori.
target:setMod(xi.mod.SENGIKORI_SC_DMG_DEBUFF, 0)
-- Handle other damage alterations.
if finalDamage > 0 then
finalDamage = utils.clamp(utils.handlePhalanx(target, finalDamage), 0, 99999)
finalDamage = utils.clamp(utils.handleOneForAll(target, finalDamage), 0, 99999)
finalDamage = utils.clamp(utils.handleStoneskin(target, finalDamage), 0, 99999)
finalDamage = target:checkDamageCap(finalDamage)
target:takeDamage(finalDamage, actor, xi.attackType.SPECIAL, xi.damageType.ELEMENTAL + skillchainElement)
-- Handle absorbption.
else
target:addHP(-finalDamage)
end
return finalDamage
end)
return m

View file

@ -1,25 +0,0 @@
-----------------------------------
-- Era Signet Duration Module - Reduces duration of Signet effects by 3 hours.
-- Changed May 10th, 2011 : https://wiki.ffo.jp/html/564.html
-----------------------------------
require('modules/module_utils')
-----------------------------------
local moduleName = 'era_signet_duration'
if xi.module.isContentEnabled('ABYSSEA') then
return { name = moduleName }
end
local m = Module:new(moduleName)
m:addOverride('xi.conquest.bestowSignet', function(player, pNation, pRank, mOffset)
super(player, pNation, pRank, mOffset)
local signet = player:getStatusEffect(xi.effect.SIGNET)
if signet then
signet:setDuration(signet:getDuration() - 3 * 3600 * 1000)
end
end)
return m

View file

@ -1,26 +0,0 @@
-----------------------------------
-- Module: Elemental Spirit Perpetuation Cost (level restriction)
-- Reverts perpetuation cost which is recalculated on level restriction if a summoned elemental spirit is present.
-- Source: https://forum.square-enix.com/ffxi/threads/22099-March-27-2012-%28JST%29-Version-Update
-----------------------------------
require('modules/module_utils')
-----------------------------------
local moduleName = 'era_effect_level_restriction'
if xi.module.isContentEnabled('ABYSSEA') then
return { name = moduleName }
end
local m = Module:new(moduleName)
m:addOverride('xi.effects.level_restriction.onEffectGain', function(target, effect)
super(target, effect)
xi.job_utils.summoner.applySpiritPerpetuationCost(target)
end)
m:addOverride('xi.effects.level_restriction.onEffectLose', function(target, effect)
super(target, effect)
xi.job_utils.summoner.applySpiritPerpetuationCost(target)
end)
return m

View file

@ -1,108 +0,0 @@
-----------------------------------
-- Automaton Utility Module
-----------------------------------
-- https://wiki.ffo.jp/html/19739.html
-- https://wiki.ffo.jp/html/32936.html
-----------------------------------
require('modules/module_utils')
require('scripts/globals/automaton')
require('scripts/globals/pets/automaton')
-----------------------------------
local moduleName = 'automaton_global'
if not xi.module.isContentEnabled('WOTG') then
return { name = moduleName }
end
local m = Module:new(moduleName)
local maneuverList =
{
[xi.jobAbility.FIRE_MANEUVER ] = { effect = xi.effect.FIRE_MANEUVER, element = xi.element.FIRE, stat = xi.mod.STR },
[xi.jobAbility.ICE_MANEUVER ] = { effect = xi.effect.ICE_MANEUVER, element = xi.element.ICE, stat = xi.mod.INT },
[xi.jobAbility.WIND_MANEUVER ] = { effect = xi.effect.WIND_MANEUVER, element = xi.element.WIND, stat = xi.mod.AGI },
[xi.jobAbility.EARTH_MANEUVER ] = { effect = xi.effect.EARTH_MANEUVER, element = xi.element.EARTH, stat = xi.mod.VIT },
[xi.jobAbility.THUNDER_MANEUVER] = { effect = xi.effect.THUNDER_MANEUVER, element = xi.element.THUNDER, stat = xi.mod.DEX },
[xi.jobAbility.WATER_MANEUVER ] = { effect = xi.effect.WATER_MANEUVER, element = xi.element.WATER, stat = xi.mod.MND },
[xi.jobAbility.LIGHT_MANEUVER ] = { effect = xi.effect.LIGHT_MANEUVER, element = xi.element.LIGHT, stat = xi.mod.CHR },
[xi.jobAbility.DARK_MANEUVER ] = { effect = xi.effect.DARK_MANEUVER, element = xi.element.DARK, stat = nil },
}
local function getAddBurdenValue(player, maneuverElement, maneuverStat)
-- Dark Maneuvers
if maneuverElement == xi.element.DARK then
local frameEquipped = player:getAutomatonFrame()
if
frameEquipped == xi.automaton.frame.VALOREDGE or
frameEquipped == xi.automaton.frame.SHARPSHOT
then
return 8
end
return 14
else
-- Fire, Ice, Wind, Earth, Lightning, Water, Light Maneuvers
local statDifference = player:getStat(maneuverStat) - player:getPet():getStat(maneuverStat)
if statDifference >= 4 then
return 14
elseif statDifference >= 0 then
return 19 - statDifference
else
return 20
end
end
end
-- Remove Overdrive returning 3 maneuvers while active.
m:addOverride('xi.automaton.getManeuverCount', function(master, maneuvers)
if not master then
return 0
end
return math.min(maneuvers, 3)
end)
-- Remove level scaling maneuver stat bonus.
m:addOverride('xi.automaton.onUseManeuver', function(player, target, ability, action)
local pet = player:getPet()
if not pet then
return
end
local maneuverInfo = maneuverList[ability:getID()]
local element = maneuverInfo.element - 1
local burdenValue = getAddBurdenValue(player, maneuverInfo.element, maneuverInfo.stat)
local overload = target:addBurden(element, burdenValue)
if
overload ~= 0 and
(player:getMod(xi.mod.PREVENT_OVERLOAD) > 0 or pet:getMod(xi.mod.PREVENT_OVERLOAD) > 0) and
player:delStatusEffectSilent(xi.effect.WATER_MANEUVER)
then
overload = 0
end
action:messageID(player:getID(), xi.msg.basic.AUTO_OVERLOAD_CHANCE)
if overload ~= 0 then
target:removeAllManeuvers()
target:addStatusEffect(xi.effect.OVERLOAD, { duration = overload, origin = player })
pet:addStatusEffect(xi.effect.OVERLOAD, { duration = overload, origin = pet })
action:messageID(player:getID(), xi.msg.basic.AUTO_OVERLOADED)
else
if target:getActiveManeuverCount() == 3 then
target:removeOldestManeuver()
end
local maneuverBonus = 1 + target:getMod(xi.mod.MANEUVER_BONUS)
target:addStatusEffect(maneuverInfo.effect, { power = maneuverBonus, duration = utils.clamp(pet:getLocalVar('MANEUVER_DURATION'), 60, 300), origin = player })
end
return target:getOverloadChance(element)
end)
return m

View file

@ -209,7 +209,7 @@ if not xi.module.isContentEnabled('ABYSSEA') then
local snakeEye = caster:getStatusEffect(xi.effect.SNAKE_EYE)
if snakeEye then
if roll >= 5 and math.randomInt(1, 100) < snakeEye:getPower() then
if roll >= 5 and math.random(1, 100) < snakeEye:getPower() then
roll = 11
else
roll = roll + 1
@ -217,7 +217,7 @@ if not xi.module.isContentEnabled('ABYSSEA') then
caster:delStatusEffect(xi.effect.SNAKE_EYE)
else
roll = roll + math.randomInt(1, 6)
roll = roll + math.random(1, 6)
end
if roll >= 12 then -- bust
@ -251,9 +251,8 @@ if not xi.module.isContentEnabled('ABYSSEA') then
-- Snake Eye: Revert to cooldown reduction from merit points, remove bonus free XI effect.
-- Source: https://www.bg-wiki.com/ffxi/Version_Update_(05/15/2012)
-- Merit value is set to 150s/level by modules/abyssea/sql/job_adjustments.sql
m:addOverride('xi.job_utils.corsair.useSnakeEye', function(player, action)
local recastReduction = player:getMerit(xi.merit.SNAKE_EYE) - 150
local recastReduction = (player:getMerit(xi.merit.SNAKE_EYE) / 10) * 60
action:setRecast(action:getRecast() - recastReduction)
player:addStatusEffect(xi.effect.SNAKE_EYE, { power = 0, duration = 60, origin = player })
@ -295,8 +294,7 @@ if not xi.module.isContentEnabled('ABYSSEA') then
player:delStatusEffect(selected:getEffectType())
player:delStatusEffectSilent(xi.effect.DOUBLE_UP_CHANCE)
-- Merit value is set to 150s/level by modules/abyssea/sql/job_adjustments.sql
local recastReduction = player:getMerit(xi.merit.FOLD) - 150
local recastReduction = (player:getMerit(xi.merit.FOLD) / 10) * 60
action:setRecast(action:getRecast() - recastReduction)
end
end)

View file

@ -99,7 +99,7 @@ if not xi.module.isContentEnabled('ROV') then
local drainamount = 0
if wyvern:getHP() ~= wyvern:getMaxHP() then
drainamount = (math.randomInt(25, 35) / 100) * playerHP
drainamount = (math.random(25, 35) / 100) * playerHP
drainamount = drainamount * (1 - (0.01 * player:getJobPointLevel(xi.jp.SPIRIT_LINK_EFFECT)))
end
@ -376,7 +376,7 @@ if not xi.module.isContentEnabled('ABYSSEA') then
local drainAmount = 0
if wyvern:getHP() ~= wyvern:getMaxHP() then
drainAmount = (math.randomInt(25, 35) / 100) * playerHP
drainAmount = (math.random(25, 35) / 100) * playerHP
drainAmount = drainAmount * (1 - (0.01 * player:getJobPointLevel(xi.jp.SPIRIT_LINK_EFFECT)))
end

View file

@ -22,7 +22,7 @@ if not xi.module.isContentEnabled('SOA') then
-- Source: https://forum.square-enix.com/ffxi/threads/44592-Oct-7-2014-%28JST%29-Version-Update
m:addOverride('xi.job_utils.ninja.useSange', function(player, target, ability, action)
local meritReduction = player:getMerit(xi.merit.SANGE) - 150
action:setRecast(math.max(0, action:getRecast() - meritReduction))
ability:setRecast(math.max(0, ability:getRecast() - meritReduction))
-- Apply Sange effect (shadows are consumed when the ranged attack fires)
player:addStatusEffect(xi.effect.SANGE, { duration = 60, origin = player })

View file

@ -15,6 +15,7 @@ if not xi.module.isContentEnabled('ROV') then
local defense = player:getMainLvl() == 75 and 23 or 21
-- Apply STONESKIN effect but display as RAMPART icon
-- TODO: subType 2 not yet implemented for magical only stoneskin
target:addStatusEffect(xi.effect.STONESKIN, { power = defense, duration = duration, origin = player, icon = xi.effect.RAMPART, subType = 2, subPower = stoneskinHP })
return xi.effect.RAMPART
@ -23,7 +24,10 @@ if not xi.module.isContentEnabled('ROV') then
-- Stoneskin onEffectGain: Add defense buff when displayed as RAMPART
m:addOverride('xi.effects.stoneskin.onEffectGain', function(target, effect)
if effect:getIcon() == xi.effect.RAMPART then
effect:addMod(xi.mod.STONESKIN, effect:getSubPower())
effect:addMod(xi.mod.DEF, effect:getPower())
else
effect:addMod(xi.mod.STONESKIN, effect:getPower())
end
end)
end
@ -67,8 +71,7 @@ if not xi.module.isContentEnabled('ABYSSEA') then
local recastReduction = player:getMerit(xi.merit.FEALTY) - 150
action:setRecast(action:getRecast() - recastReduction)
-- Divide by merit value (150s in pre-Abyssea) to recover merit rank count for gear scaling
local enhFealty = (player:getMerit(xi.merit.FEALTY) / 150) * player:getMod(xi.mod.ENHANCES_FEALTY)
local enhFealty = (player:getMerit(xi.merit.FEALTY) / 5) * player:getMod(xi.mod.ENHANCES_FEALTY)
local duration = 60 + enhFealty
player:addStatusEffect(xi.effect.FEALTY, { power = 1, duration = duration, origin = player })
@ -97,12 +100,12 @@ if not xi.module.isContentEnabled('ABYSSEA') then
then
local resistanceRate = xi.combat.magicHitRate.calculateResistRate(player, target, 0, 0, xi.skillRank.A_PLUS, xi.element.THUNDER, xi.mod.INT, xi.effect.STUN, 0)
if xi.data.statusEffect.isResistRateSuccessfull(xi.effect.STUN, resistanceRate, 0) then
target:addStatusEffect(xi.effect.STUN, { power = 1, duration = math.randomInt(2, 8) * resistanceRate, origin = player })
target:addStatusEffect(xi.effect.STUN, { power = 1, duration = math.random(2, 8) * resistanceRate, origin = player })
end
end
-- Randomize damage
local randomizer = 1 + (math.randomInt(1, 5) / 100)
local randomizer = 1 + (math.random(1, 5) / 100)
damage = damage * randomizer
damage = utils.handleStoneskin(target, damage)

View file

@ -1,142 +0,0 @@
-----------------------------------
-- Module: Puppetmaster Job Adjustments
-----------------------------------
require('modules/module_utils')
-----------------------------------
local moduleName = 'era_job_utils_puppetmaster'
local m = Module:new(moduleName)
if xi.module.isContentEnabled('ABYSSEA') then
return { name = moduleName }
end
-- Overdrive: Revert duration from 180 to 60 seconds : https://wiki.ffo.jp/html/954.html
m:addOverride('xi.job_utils.puppetmaster.onAbilityUseOverdrive', function(player, target, ability, action)
local pet = player:getPet()
player:addStatusEffect(xi.effect.OVERDRIVE, { duration = 60 + player:getMod(xi.mod.OVERDRIVE_BONUS_DURATION), origin = player })
if pet then
pet:addStatusEffect(xi.effect.OVERDRIVE, { duration = 60 + player:getMod(xi.mod.OVERDRIVE_BONUS_DURATION), origin = pet })
action:ID(player:getID(), pet:getID())
end
return xi.effect.OVERDRIVE
end)
-- Repair: Revert recast to a flat 180 seconds : https://wiki.ffo.jp/html/954.html
m:addOverride('xi.job_utils.puppetmaster.onAbilityCheckRepair', function(player, target, ability)
local msg, param = super(player, target, ability)
if msg == 0 then
ability:setRecast(180)
end
return msg, param
end)
local removableEffects =
{
-- Songs
xi.effect.ELEGY,
xi.effect.REQUIEM,
xi.effect.THRENODY,
-- Enfeebling
xi.effect.BLINDNESS,
xi.effect.PARALYSIS,
xi.effect.SILENCE,
xi.effect.CURSE_I,
xi.effect.CURSE_II,
xi.effect.DISEASE,
xi.effect.PLAGUE,
xi.effect.WEIGHT,
xi.effect.BIND,
xi.effect.ADDLE,
xi.effect.SLOW,
xi.effect.PETRIFICATION,
-- DoTs
xi.effect.BIO,
xi.effect.DIA,
xi.effect.POISON,
xi.effect.BURN,
xi.effect.FROST,
xi.effect.CHOKE,
xi.effect.RASP,
xi.effect.SHOCK,
xi.effect.DROWN,
-- Main Stat Downs
xi.effect.STR_DOWN,
xi.effect.DEX_DOWN,
xi.effect.VIT_DOWN,
xi.effect.AGI_DOWN,
xi.effect.INT_DOWN,
xi.effect.MND_DOWN,
xi.effect.CHR_DOWN,
-- Combat Stat Downs
xi.effect.ACCURACY_DOWN,
xi.effect.ATTACK_DOWN,
xi.effect.EVASION_DOWN,
xi.effect.DEFENSE_DOWN,
-- Magic Stat Downs
xi.effect.MAGIC_ACC_DOWN,
xi.effect.MAGIC_ATK_DOWN,
xi.effect.MAGIC_EVASION_DOWN,
xi.effect.MAGIC_DEF_DOWN,
-- HP/MP/TP Stat Downs
xi.effect.MAX_TP_DOWN,
xi.effect.MAX_MP_DOWN,
xi.effect.MAX_HP_DOWN
}
local function removeStatusEffects(pet, amountToRemove)
local effectsRemoved = 0
for _, effectId in ipairs(removableEffects) do
if effectsRemoved >= amountToRemove then
break
end
if pet:delStatusEffect(effectId) then
effectsRemoved = effectsRemoved + 1
end
end
return effectsRemoved
end
-- Repair: Remove the initial burst heal, leaving only the Regen and status removal effects
m:addOverride('xi.job_utils.puppetmaster.onAbilityUseRepair', function(player, target, ability, action)
local pet = player:getPet()
if not pet then
return
end
-- Self-cast ability but reports on pet
action:ID(player:getID(), pet:getID())
local oilEquipped = xi.job_utils.puppetmaster.oilData[player:getEquipID(xi.slot.AMMO)]
local regenAmount = oilEquipped.regen
local regenTime = oilEquipped.duration
removeStatusEffects(pet, player:getMod(xi.mod.REPAIR_EFFECT))
local bonus = 1 + player:getMerit(xi.merit.REPAIR_EFFECT) / 100 + player:getMod(xi.mod.REPAIR_POTENCY) / 100
regenAmount = regenAmount * bonus
pet:wakeUp()
pet:delStatusEffect(xi.effect.REGEN)
pet:addStatusEffect(xi.effect.REGEN, { power = regenAmount, duration = regenTime, origin = player, tick = 3 }) -- 3 = tick, each 3 seconds.
player:removeAmmo(1)
ability:setMsg(xi.msg.basic.USES_JA)
end)
return m

Some files were not shown because too many files have changed in this diff Show more