From b7599fa6cc268d28cb2a3db174dcf9dbc259e107 Mon Sep 17 00:00:00 2001 From: Adrienne Walker Date: Thu, 21 Dec 2023 15:21:18 -0800 Subject: [PATCH] docs: update all markdown files to direct to OP (#6063) --- .github/workflows/README.md | 15 +- CODE_OF_CONDUCT.md | 77 +- CONTRIBUTING.md | 2 + docs/CactbotCustomization.md | 515 +---- docs/FAQ-Troubleshooting.md | 319 +-- docs/Headmarkers.md | 207 +- docs/LogGuide.md | 2770 +-------------------------- docs/MemorySignatures.md | 396 +--- docs/OopsyraidsyGuide.md | 318 +-- docs/PatchUpdateChecklist.md | 279 +-- docs/RaidbossGuide.md | 1129 +---------- docs/RemoteCactbot.md | 152 +- docs/TimelineGuide.md | 1260 +----------- docs/ko-KR/CactbotCustomization.md | 585 +----- docs/ko-KR/README.md | 511 +---- docs/zh-CN/CactbotCustomization.md | 361 +--- docs/zh-CN/MemorySignatures.md | 232 +-- docs/zh-CN/README.md | 419 +--- docs/zh-CN/RaidbossGuide.md | 617 +----- docs/zh-CN/TimelineGuide.md | 1115 +---------- docs/zh-TW/CactbotCustomization.md | 318 +-- ui/raidboss/emulator/Readme.md | 48 +- ui/raidboss/skins/dorgrin/Readme.md | 30 +- util/README.md | 18 +- 24 files changed, 71 insertions(+), 11622 deletions(-) diff --git a/.github/workflows/README.md b/.github/workflows/README.md index b8ec90e1b5..bb01386854 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -1,14 +1,5 @@ -# Workflows +# Permanently Moved to OverlayPlugin/cactbot -Replacing [Travis CI](https://travis-ci.org/) as this project's primary CI/CD tool, -[GitHub Actions](https://help.github.com/en/actions) uses workflows to perform operations -when specific events within the repository occur. -Workflows are specified in YAML and can utilize shared components -from other GitHub Actions to form expansive pipelines. +See: [.github/workflows/README.md](https://github.com/OverlayPlugin/cactbot/blob/main/.github/workflows/README.md) -These workflows should appear within pull requests as status indicators -that denote "pending”, “success”, “failure”, or “error", -and should contain detail links to view the workflows and their individual steps therein. -Additionally, these workflow runs should be visible by clicking -the [Actions](https://github.com/quisquous/cactbot/actions) tab -in the repository's menu at the top of the repository's main page. + diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 62acda1ff5..c9c2ab06d7 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,76 +1,5 @@ -# Contributor Covenant Code of Conduct +# Permanently Moved to OverlayPlugin/cactbot -## Our Pledge +See: [CODE_OF_CONDUCT.md](https://github.com/OverlayPlugin/cactbot/blob/main/CODE_OF_CONDUCT.md) -In the interest of fostering an open and welcoming environment, we as -contributors and maintainers pledge to making participation in our project and -our community a harassment-free experience for everyone, regardless of age, body -size, disability, ethnicity, sex characteristics, gender identity and expression, -level of experience, education, socio-economic status, nationality, personal -appearance, race, religion, or sexual identity and orientation. - -## Our Standards - -Examples of behavior that contributes to creating a positive environment -include: - -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members - -Examples of unacceptable behavior by participants include: - -* The use of sexualized language or imagery and unwelcome sexual attention or - advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic - address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Our Responsibilities - -Project maintainers are responsible for clarifying the standards of acceptable -behavior and are expected to take appropriate and fair corrective action in -response to any instances of unacceptable behavior. - -Project maintainers have the right and responsibility to remove, edit, or -reject comments, commits, code, wiki edits, issues, and other contributions -that are not aligned to this Code of Conduct, or to ban temporarily or -permanently any contributor for other behaviors that they deem inappropriate, -threatening, offensive, or harmful. - -## Scope - -This Code of Conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. Examples of -representing a project or community include using an official project e-mail -address, posting via an official social media account, or acting as an appointed -representative at an online or offline event. Representation of a project may be -further defined and clarified by project maintainers. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at cactbot@quisquo.us. All -complaints will be reviewed and investigated and will result in a response that -is deemed necessary and appropriate to the circumstances. The project team is -obligated to maintain confidentiality with regard to the reporter of an incident. -Further details of specific enforcement policies may be posted separately. - -Project maintainers who do not follow or enforce the Code of Conduct in good -faith may face temporary or permanent repercussions as determined by other -members of the project's leadership. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, -available at - -[homepage]: https://www.contributor-covenant.org - -For answers to common questions about this code of conduct, see - + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dfc3c47f61..ec7964c563 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,3 +1,5 @@ # Permanently Moved to OverlayPlugin/cactbot +See: [CONTRIBUTING.md](https://github.com/OverlayPlugin/cactbot/blob/main/CONTRIBUTING.md) + diff --git a/docs/CactbotCustomization.md b/docs/CactbotCustomization.md index 657fc0b666..eea74e3ea0 100644 --- a/docs/CactbotCustomization.md +++ b/docs/CactbotCustomization.md @@ -1,514 +1,5 @@ -# Cactbot Customization +# Permanently Moved to OverlayPlugin/cactbot -🌎 [**English**] [[简体中文](./zh-CN/CactbotCustomization.md)] [[繁體中文](./zh-TW/CactbotCustomization.md)] [[한국어](./ko-KR/CactbotCustomization.md)] +See: [docs/CactbotCustomization.md](https://github.com/OverlayPlugin/cactbot/blob/main/docs/CactbotCustomization.md) -- [Using the cactbot UI](#using-the-cactbot-ui) -- [Changing Trigger Text with the cactbot UI](#changing-trigger-text-with-the-cactbot-ui) -- [User Directory Overview](#user-directory-overview) -- [Setting Your User Directory](#setting-your-user-directory) -- [Customizing Appearance](#customizing-appearance) -- [Overriding Raidboss Triggers](#overriding-raidboss-triggers) - - [Example 1: changing the output text](#example-1-changing-the-output-text) - - [Example 2: making provoke work for all jobs](#example-2-making-provoke-work-for-all-jobs) - - [Example 3: adding custom triggers](#example-3-adding-custom-triggers) -- [Overriding Raidboss Timelines](#overriding-raidboss-timelines) -- [Customizing Behavior](#customizing-behavior) -- [Debugging User Files](#debugging-user-files) - - [Check the OverlayPlugin log for errors](#check-the-overlayplugin-log-for-errors) - - [Check if your file is loaded](#check-if-your-file-is-loaded) - - [Check if your user file has errors](#check-if-your-user-file-has-errors) - -## Using the cactbot UI - -The best way to customize cactbot is to use the cactbot configuration UI. -This is under -ACT -> Plugins -> OverlayPlugin.dll -> Cactbot. - -This has options for things like: - -- setting triggers to tts -- disabling triggers -- changing the output text of triggers -- changing triggers to say a player's job instead of name -- changing your cactbot language -- volume settings -- getting rid of that cheese icon - -It is not possible to configure everything you might want -through the cactbot configuration UI. -However, it is the easiest place to start with. -Over time, more options will be added there. - -These options are stored in your -`%APPDATA%\Advanced Combat Tracker\Config\RainbowMage.OverlayPlugin.config.json` -file. -You should not need to edit that file directly. - -## Changing Trigger Text with the cactbot UI - -In the cactbot configuration UI, under -ACT -> Plugins -> OverlayPlugin.dll -> Cactbot -> Raidboss, -there are individual trigger listings. -You can use these listings to change various exposed configuration settings per trigger. - -Settings with a bell (🔔) next to their name are trigger outputs that you can override. -For example, maybe there's an 🔔onTarget field whose text is `Tank Buster on ${player}`. -This is the string that will get played on screen (or via tts) when there is a tank buster on some person. -`${player}` here is a parameter that will be set dynamically by the trigger. -Anything that looks like `${param}` is such a dynamic parameter. - -You could change this to say `${player} is going to die!` instead. -Or, maybe you don't care who it's on, and you can edit the text to `Buster` to be brief. -If you want to undo your overriding, just clear the text. - -There are some limitations to this overriding. -You cannot change the logic. -You cannot make `tts` to say something different than the `alarmText` in most cases. -You cannot add additional parameters. -If you want to do any of these more complicated overrides, -then you will want to look at the [Overriding Raidboss Triggers](#overriding-raidboss-triggers) section. - -Any parameter that refers to a player (usually called `${player}` but not always) -can also be further modified in each individual output string: - -- `${player.job}`: job abbreviation, e.g. WHM -- `${player.jobFull}`: job full name, e.g. White Mage -- `${player.role}`: role, e.g. support -- `${player.name}`: player's full name, e.g. Tini Poutini -- `${player.nick}`: player's nickname / first name, e.g. Tini -- `${player.id}`: a player's id (for testing purposes), e.g. 1000485F - -If there are bugs or a player is not in your party or you use an invalid suffix, -it may fall back to a default nickname just so something can be printed. - -By default, `${player}` implies `${player.nick}`, -however you can set this default with the "Default Player Label" option -in the cactbot config UI under the raidboss section. - -## User Directory Overview - -If the cactbot UI doesn't have the option you are looking for, -then you may need to consider user file overrides. -At this point, you are writing JavaScript and CSS, -and so you might need a little bit of programming savvy. - -The general philosophy of cactbot is that -any user configuration should only go in files in the user directory. -This will prevent your changes from being overwritten during future cactbot updates. -Additionally, modifying cactbot files directly from a cactbot release -will not work properly without running extra build steps. - -All cactbot UI modules can load user settings from the [user/](../user/) directory. -The `raidboss` module loads `user/raidboss.js` and `user/raidboss.css`, -as well as any `.js` and `.css` files inside the `user/raidboss/` directory -with any filenames in any number of subfolders. -(Timeline `.txt` files must be directly in the same folder as the `.js` that refers to them.) -These user-defined files are included after cactbot's files and can override its settings. - -Similarly, the `oopsyraidsy` module loads `user/oopsyraidsy.js` and `user/oopsyraidsy.css`, -as well as all `.js` and `.css` files inside the `user/oopsyraidsy/` directory. -And so on, for each module by name. - -cactbot loads files in subdirectories (alphabetically) before loading files in outer directories. -This is so that `user/raidboss.js` will always be loaded last -and can override anything that is set inside a file inside of `user/raidboss/`. -For example, `user/alphascape/some_file.js` will load before `user/mystatic/some_file.js`, -which will both load before `user/raidboss.js`. -The same ordering applies to `.css` files. - -In this documentation, any reference to "user-defined js file" applies to both of these. -There is no difference between `user/raidboss.js` and `user/raidboss/some_file.js`, -other than the order in which they load. -Similarly, "user-defined css file" means both `user/radar.css` and `user/radar/some_file.css`. -Subdirectories in the user folder are intended to -make it easier to share triggers and customizations with others. - -You can get more information about the loading order -by looking at the [debug messages](#check-if-your-file-is-loaded) -when developer mode is turned on. - -The `user/` directory already includes some example configuration files, -which you can rename and use. -For example the [user/raidboss-example.js](../user/raidboss-example.js) file -can be renamed to `user/raidboss.js` -and edited to change the behavior of the `raidboss` module. - -After making any changes to these files, -pressing the "Reload overlay" button -for the appropriate overlay in ACT's OverlayPlugin settings will apply the changes. - -## Setting Your User Directory - -The cactbot user directory can be set via the cactbot configuration UI: -ACT -> Plugins -> OverlayPlugin.dll -> Cactbot -> Cactbot user directory. -Click the `Choose Directory` button and select a folder on disk. - -If you haven't selected one, -it will try to select one based on where you have installed cactbot on disk. - -Ideally, you should select the `cactbot/user` folder from your cactbot installation. -This is often in `%APPDATA%\Advanced Combat Tracker\Plugins\cactbot-version\cactbot\user`. -[This folder](../user) has example customization files. - -## Customizing Appearance - -A user-defined css file can change positions, sizes, colors, etc. for components of -the UI module. See the `ui//.css` to find the selectors you can modify. - -For example in [ui/raidboss/raidboss.css](../ui/raidboss/raidboss.css), -you see the `#popup-text-container` and `#timeline-container` -which can be changed via `user/raidboss.css` to different positions as desired. -You can use `user/raidboss.css` or a `.css` file in `user/raidboss/` to add additional styling. - -The size and color of info text alerts can also be changed -by making a CSS rule for the `.info-text` class such as below: - -```css -.info-text { - font-size: 200%; - color: rgb(50, 100, 50); -} -``` - -You can think about the CSS in the user file as being appended to the end -of any built-in cactbot CSS file. -Therefore, you need to keep in mind [CSS specificity rules](https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity), -possibly adding `!important` to force your rule to override. -Additionally, you may need to unset properties by setting them to `auto`. - -The best way to debug CSS issues is to use [Chrome DevTools](https://developers.google.com/web/tools/chrome-devtools). -You can open DevTools for an overlay by going to -ACT -> Plugins -> OverlayPlugin.dll -> your overlay -> Open DevTools. - -**Note**: some things are hard or impossible to customize, such as the timeline bars. -This is because they use custom elements, -and they don't expose a lot of knobs to tune. -If you have particular things you want to change about the timeline bars that you can't, -please feel free to submit a [github issue](https://github.com/quisquous/cactbot/issues/new/choose). - -**Warning**: cactbot makes no guarantees about preserving CSS backwards compatability. -Future changes to cactbot may rearrange elements, -change element names and classes, -or change styling entirely. -In general, you are on your own if you want to style cactbot with CSS. - -## Overriding Raidboss Triggers - -You can use a user-defined js file -(e.g. `user/raidboss.js` or any `.js` file under `user/raidboss/`) -to override how triggers behave. -You can change the text that they output, -what jobs they run for, -and how long they stay on screen, -and anything else. - -You can see readable JavaScript versions of all of the cactbot triggers -in this branch: -This is the preferred reference to use for viewing, copying, and pasting. -Triggers in the main branch -or shipped in a cactbot release are often in unreadable bundles -or are TypeScript which is not supported in user folders. - -In your user-defined js file for raidboss, -there is an `Options.Triggers` list that contains a list of trigger sets. -You can use this to append new triggers and -modify existing triggers. -If a user file contains a trigger with the same id as any previous trigger -(including triggers in cactbot), then that trigger will override it. - -If you are going to modify triggers, -it is worth reading the [trigger guide](RaidbossGuide.md) -to understand what the various fields of each trigger means. - -In general, the pattern to follow is to add a block of code -to your user-defined js file (e.g. `user/raidboss.js`) that looks like this: - -```javascript -Options.Triggers.push({ - // Find the ZoneId from the top of the file, - // e.g. ZoneId.MatchAll (for all zones) or ZoneId.TheBozjanSouthernFront. - zoneId: ZoneId.PutTheZoneFromTheTopOfTheFileHere, - triggers: [ - { - // This is where you put the trigger object. - // e.g. id or netRegex or infoText - }, - ], -}); -``` - -The easiest approach to modify triggers is to copy and paste the block of code -above for each trigger. -Modify the `zoneId` line to have the zone id for the zone you care about, -usually from the top of the cactbot trigger file. -[This file](../resources/zone_id.ts) has a list of all the zone ids. -If you specify one incorrectly, you will get a warning in the OverlayPlugin log window. -Then, [copy the trigger text](https://github.com/quisquous/cactbot/tree/triggers) into this block. -Edit as needed. -Repeat for all the triggers you want to modify. -Reload your raidboss overlay to apply your changes. - -**Note**: This method completely removes the original trigger, -and so do not delete any logic when making edits. -Also, this is JavaScript, and so it still needs to be valid JavaScript. -If you are not a programmer, be extra careful with what and how you edit. - -### Example 1: changing the output text - -Let's say hypothetically that you are doing UCOB -and your group decides that they are going to do "fire out" first -instead of "fire in" first like cactbot calls it by default. -Additionally, you *also* want to have the tts say something different for this trigger. -You keep forgetting to get out, so you want it to repeat a few times. - -If you only wanted to change the `infoText`, -you could do this via [Changing Trigger Text with the cactbot UI](#changing-trigger-text-with-the-cactbot-ui). - -One way to adjust this is to edit the trigger output for this trigger. -You can find the original fireball #1 trigger in -[ui/raidboss/data/04-sb/ultimate/unending_coil_ultimate.js](https://github.com/quisquous/cactbot/blob/triggers/04-sb/ultimate/unending_coil_ultimate.js#:~:text=UCU%20Nael%20Fireball%201). - -This chunk of code is what you would paste into the bottom of your user-defined js file. - -```javascript -Options.Triggers.push({ - zoneId: ZoneId.TheUnendingCoilOfBahamutUltimate, - triggers: [ - { - id: 'UCU Nael Fireball 1', - netRegex: NetRegexes.ability({ source: 'Ragnarok', id: '26B8', capture: false }), - delaySeconds: 35, - suppressSeconds: 99999, - // The infoText is what appears on screen in green. - infoText: { - en: 'Fire OUT', - }, - tts: { - en: 'out out out out out', - }, - run: function(data) { - data.naelFireballCount = 1; - }, - }, - ], -}); -``` - -This edit also removed languages other than English. - -### Example 2: making provoke work for all jobs - -Currently, provoke only works for players in your alliance and not for all jobs. -This example shows how to make it work for all players. -The provoke trigger can be found in -[ui/raidboss/data/00-misc/general.js](https://github.com/quisquous/cactbot/blob/triggers/00-misc/general.js#:~:text=General%20Provoke). - -Here is a modified version with a different `condition` function. -Because this shares the same `General Provoke` id with the built-in cactbot trigger, -it will override the built-in version. - -This chunk of code is what you would paste into the bottom of your user-defined js file. - -```javascript -Options.Triggers.push({ - zoneId: ZoneId.MatchAll, - triggers: [ - { - id: 'General Provoke', - netRegex: NetRegexes.ability({ id: '1D6D' }), - condition: function(data, matches) { - // I want to see all provokes, even they are not in the party, - // or I am not a tank. - return true; - }, - infoText: (data, matches, output) => { - return output.text!({ player: data.party.member(matches.source) }); - }, - outputStrings: { - text: { - en: 'Provoke: ${player}', - de: 'Herausforderung: ${player}', - fr: 'Provocation: ${player}', - ja: '挑発: ${player}', - cn: '挑衅: ${player}', - ko: '도발: ${player}', - }, - }, - }, - ], -}); -``` - -You could also just delete the `condition` function entirely here, -as triggers without conditions will always run when their regex matches. - -### Example 3: adding custom triggers - -You can also use this same pattern to add your own custom triggers. - -Here's an example of a custom trigger that prints "Get out!!!", -one second after you receive an effect called "Forked Lightning". - -```javascript -Options.Triggers.push({ - zoneId: ZoneId.MatchAll, - triggers: [ - { - // This id is made up, and is not overriding a cactbot trigger. - id: 'Personal Forked Lightning', - regex: Regexes.gainsEffect({ effect: 'Forked Lightning' }), - condition: (data, matches) => { return matches.target === data.me; }, - delaySeconds: 1, - alertText: 'Get out!!!', - }, - - // ... other triggers here, if you want - ], -}); -``` - -Your best resources for learning how to write cactbot triggers -is the [trigger guide](RaidbossGuide.md) -and also reading through existing triggers in [ui/raidboss/data](../ui/raidboss/data). - -## Overriding Raidboss Timelines - -Some customization of timelines can be done via the [cactbot config UI](#using-the-cactbot-ui). -This UI allows you to hide/rename existing timeline entries -or add custom entries at particular times. - -This section is for when you need to do more than that, and want to replace a timeline entirely. -Completely overriding a raidboss timeline is similar to [overriding a trigger](#overriding-raidboss-triggers). - -The steps to override a timeline are: - -1) Copy the timeline text file out of cactbot and into your user folder - - For example, you could copy - [ui/raidboss/data/05-shb/ultimate/the_epic_of_alexander.txt](../ui/raidboss/data/05-shb/ultimate/the_epic_of_alexander.txt) - to `user/the_epic_of_alexander.txt`. - -1) Add a section to your user/raidboss.js file to override this. - - Like adding a trigger, you add a section with the `zoneId`, - along with `overrideTimelineFile: true`, - and a `timelineFile` with the name of the text file. - - ```javascript - Options.Triggers.push({ - zoneId: ZoneId.TheEpicOfAlexanderUltimate, - overrideTimelineFile: true, - timelineFile: 'the_epic_of_alexander.txt', - }); - ``` - - In this case, this assumes that you have followed step 1 - and there is a `user/the_epic_of_alexander.txt` file. - - By setting `overrideTimelineFile: true`, - it tells cactbot to replace the built-in timeline entirely - with any new timeline that you add. - -1) Edit your new timeline file in your user folder as needed - - Refer to the [timeline guide](TimelineGuide.md) for more documentation on the timeline format. - -**Note**: Editing timelines is a bit risky, -as there may be timeline triggers that refer to specific timeline text. -For instance, in TEA, there are timeline triggers for `Fluid Swing` and `Propeller Wind`, etc. -If these names are changed or removed, then the timeline triggers will also be broken. - -## Customizing Behavior - -This section discusses other kinds of customizations you can make to cactbot modules. -There are some variables that are not in the config UI and are also not triggers. - -Each cactbot module has an `Options` variable that controls various options. -The options that can be changed are documented in the `Options` section -at the top of each `ui//.js` file. - -For example in [ui/raidboss/raidboss.js](../ui/raidboss/raidboss.js), -you see the `PlayerNicks` option which allows you to give people nicknames -when their names are called out - -```javascript -Options.PlayerNicks = { - // 'Firstname Lastname': 'Nickname', - 'Banana Nana': 'Nana', - 'The Great\'one': 'Joe', // The Great'one => Joe, needs a backslash for the apostrophe - 'Viewing Cutscene': 'Cut', - // etc, with more nicknames -}; -``` - -You can override text-to-speech callouts globally via - -```javascript -Options.TransformTts = (text) => { - return text.replace('a', 'b'); -}; -``` - -**Warning**: files inside of your user directory will silently overwrite settings -that were set from the cactbot configuration UI. -This can be confusing, -so it's generally preferable to let the config tool set everything you can, -and only use user files in order to set things that the config tool does not -provide access to. - -## Global Trigger File Imports - -User files are `eval`'d in JavaScript, -and thus cannot `import` in the same way that built-in trigger files do. -User javascript files have access to the following globals: - -- [Conditions](../resources/conditions.ts) -- [ContentType](../resources/content_type.ts) -- [NetRegexes](../resources/netregexes.ts) -- [Regexes](../resources/regexes.ts) -- [Responses](../resources/responses.ts) -- [Outputs](../resources/outputs.ts) -- [Util](../resources/util.ts) -- [ZoneId](../resources/zone_id.ts) -- [ZoneInfo](../resources/zone_info.ts) - -## Debugging User Files - -### Check the OverlayPlugin log for errors - -The OverlayPlugin log is scrolling window of text that can be found by going to -ACT -> Plugins -> OverlayPlugin.dll, -and looking at the bottom of the window. - -If there are errors, they will appear here. - -### Check if your file is loaded - -First, turn on debug mode for raidboss. -Go to the cactbot configuration UI, -enable `Show developer options` and reload the page. -Then, enable `Enable debug mode` under Raidboss, and reload again. - -When raidboss debug mode is on, -it will print more information to the OverlayPlugin log. -It will list lines for each local user file it loads: -`[10/19/2020 6:18:27 PM] Info: raidbossy: BrowserConsole: local user file: C:\Users\tinipoutini\cactbot\user\raidboss.js` - -Verify that your user file is loaded at all. - -The order that the filenames are printed is the order in which they are loaded. - -### Check if your user file has errors - -User files are JavaScript, and so if you write incorrect JavaScript, there will be errors -and your user file will be skipped and it will not load. -Check the OverlayPlugin log for errors when loading. - -Here's an example: - -```log -[10/19/2020 6:18:27 PM] Info: raidbossy: BrowserConsole: local user file: C:\Users\tinipoutini\cactbot\user\raidboss.js (Source: file:///C:/Users/tinipoutini/cactbot/resources/user_config.ts, Line: 83) -[10/19/2020 6:18:27 PM] Info: raidbossy: BrowserConsole: *** ERROR IN USER FILE *** (Source: file:///C:/Users/tinipoutini/cactbot/resources/user_config.ts, Line: 95) -[10/19/2020 6:18:27 PM] Info: raidbossy: BrowserConsole: SyntaxError: Unexpected token : - at loadUser (file:///C:/Users/tinipoutini/cactbot/resources/user_config.ts:92:28) (Source: file:///C:/Users/tinipoutini/cactbot/resources/user_config.ts, Line: 96) -``` + diff --git a/docs/FAQ-Troubleshooting.md b/docs/FAQ-Troubleshooting.md index c942593818..e7aea9718f 100644 --- a/docs/FAQ-Troubleshooting.md +++ b/docs/FAQ-Troubleshooting.md @@ -1,318 +1,5 @@ -# Troubleshooting FAQ +# Permanently Moved to OverlayPlugin/cactbot -- [General FFXIV ACT Troubleshooting](#general-ffxiv-act-troubleshooting) -- [Installation Testing](#installation-testing) - - [Verify That Your Plugins Are in the Right Order](#verify-that-your-plugins-are-in-the-correct-order) - - [Verify Your Language Settings Match](#verify-your-language-settings-match) - - [Verify That You Have Added the Overlays You Want](#verify-that-you-have-added-the-overlays-you-want) - - [Summerford Farms Raidboss Test](#summerford-farms-raidboss-test) - - [Summerford Farms Provoke Test](#summerford-farms-provoke-test) -- [New Content Isn't Working](#new-content-isnt-working) - - [Do Your Cactbot Plugin and Overlay Paths Match](#do-your-cactbot-plugin-and-overlay-paths-match) - - [Has This Content Been Added to Cactbot](#has-this-content-been-added-to-cactbot) -- [Fixing Chat Log Problems](#fixing-chat-log-problems) - - [Check Game Chat Log Filters](#check-game-chat-log-filters) - - [Check FFXIV Plugin Filters](#check-ffxiv-plugin-filters) - - [Check Dalamud Plugins](#check-dalamud-plugins) - - [Check Your Network Log](#check-your-network-log) -- [Fixing Network Data Problems](#fixing-network-data-problems) - - [Has the Game Just Updated?](#has-the-game-just-updated) - - [Test Game Connection](#test-game-connection) - - [Cactbot Stops Working Randomly](#cactbot-stops-working-randomly) -- [Problems During Cactbot Installation](#problems-during-cactbot-installation) -- [My TTS Isn't Working](#my-tts-isnt-working) -- [Places to Get Help](#places-to-get-help) -- [How to Find a Network Log](#how-to-find-a-network-log) +See: [docs/FAQ-Troubleshooting.md](https://github.com/OverlayPlugin/cactbot/blob/main/docs/FAQ-Troubleshooting.md) -## General FFXIV ACT Troubleshooting - -[This guide](https://github.com/OverlayPlugin/docs/blob/main/faq/README.md) -is an excellent FAQ for common FFXIV ACT Plugin and OverlayPlugin issues. - -If you get an error that it can't find **FFXIV_ACT_Plugin.dll**, make sure the **FFXIV_ACT_Plugin.dll** is in the `%APPDATA%\Advanced Combat Tracker\Plugins` folder. -You can install **FFXIV_ACT_Plugin.dll** from the ACT Startup Wizard. - -You should also verify that your OverlayPlugin is at least version 0.18.2. -Look at ACT -> Plugins -> Plugin Listing -> OverlayPlugin.dll. -Is the version 0.18.2 or earlier? -If so, follow - -## Installation Testing - -First, follow the [installation guide](../README.md#installing) for cactbot from the main readme. -Once done, check the following steps. - -### Verify That Your Plugins Are in the Right Order - -Open ACT -> Plugins -> Plugin Listing. -You should have `FFXIV_ACT_Plugin.dll`, `OverlayPlugin.dll`, and `CactbotOverlay.dll` in that order. - -See: [this section](../README.md#plugin-load-order) for more information about load order. - -### Verify Your Language Settings Match - -Verify that the game language matches the FFXIV ACT Plugin language. -You can check the plugin language via ACT -> Plugins -> FFXIV ACT Plugin -> Language. -(Note: this may not be true for Chinese users.) - -Raidboss should pick up the plugin language automatically. -It does not matter which cactbot display, alerts, or timeline language you have set -as these are all for display purposes. - -### Verify That You Have Added the Overlays You Want - -It is not enough to just add the cactbot plugin, -you also need to add an overlay for the parts of cactbot you want. -Commonly, most people add raidboss for alerts and timelines. - -If you are not sure which overlays you have, -go to ACT -> Plugins -> OverlayPlugin.dll -> General and click "Copy Tech Support Info to Clipboard". -Paste this somewhere (like notepad) to look at. -It should list the overlays you think exist (e.g. raidboss for alerts and timelines). - -One important note is that raidboss has three versions: - -- combined (alerts and timelines) -- timeline only (just timeline, no alerts) -- alerts only (just alerts, no timeline) - -A common use case is to add one overlay for timeline only and one overlay for alerts only. -If you are seeing timelines but not alerts or vice versa, -make sure you have the overlays you need. - -See: [this section](../README.md#adding-overlays) for how to add cactbot overlays. - -### Summerford Farms Raidboss Test - -This test verifies that basic timelines and triggers are working -and your overlay has been added correctly. - -To do a basic test of raidboss, -teleport to Summerford Farms. -Do a `/countdown 5`. - -When the countdown ends, -a timeline should appear on your screen -and several alert messages should appear. -You can teleport to stop the timeline. - -If this does not work, -see the [Fixing Chat Log Problems](#fixing-chat-log-problems) section. - -### Summerford Farms Provoke Test - -A second test to do for network data is to switch to a tank job. -Teleport to Summerford Farms again. -Use Provoke on a Striking Dummy. -You should see an alert trigger when you do this. - -If this does not work, -see the [Fixing Network Data Problems](#fixing-network-data-problems) section. - -## New Content Isn't Working - -A common complaint that people have is that older content works in cactbot but not newer. -There are a number of reasons this could be the case: - -### Do Your Cactbot Plugin and Overlay Paths Match - -Check the cactbot plugin path in ACT -> Plugins -> Plugin Listing -> CactbotOverlay.dll -> Plugin Info -> FileName. -Then check the overlay URL via ACT -> Plugins -> OverlayPlugin.dll -> (name of a cactbot overlay, e.g. raidboss) -> URL. -Make sure the paths are the same. - -If the plugin has a path including `cactbot/cactbot/` and the overlay has a path like `cactbot/cactbot-0.19.2/` -then you should remove and re-add your cactbot overlay. -You can do this via ACT -> Plugins -> OverlayPlugin.dll -> (name of the overlay to remove) -> Remove (button at the bottom). -Then hit New at the bottom to re-add it and select the same overlay type that it was. - -Note: This will not remove your settings. Also, this should be a "one time" fix and in the future this won't happen again. - -### Has This Content Been Added to Cactbot - -It takes time and effort to add support for new content to cactbot. -If it just came out in the last week or few, -it's possible that there is not support for it yet. - -Check for the latest list of supported content. -It is possible that support for content has been added to cactbot but not released. -You can check the release notes for more details. - -Additionally, if you are not playing in English, -make sure you check that ACT -> Plugins -> FFXIV ACT Plugin -> Parser Options is set to the language of your client. -Content that has just been added to cactbot may not have been translated yet -and so may not work immediately in non-English languages. -This is usually fixed by the next release. - -## Fixing Chat Log Problems - -The chat log is currently used by cactbot for a number of things. - -The most common things are broken when the chat log has an issue is: - -- zones sealing/unsealing to start dungeon timelines -- UCOB Nael dialog -- countdown messages (for the jobs countdown timer and Summerford Farms test) - -### Check Game Chat Log Filters - -FFXIV doesn't send chat messages to you that you have turned off due to a filter. -In game, go to System -> Character Configuration -> Log Window Settings -> Log Filters. -Pick a log number. It does not have to be the General log, -and could be some log that you don't ever look at. - -Verify that these categories are enabled for at least one log: - -- Announcements - - System Messages - - Echo - - NPC Dialogue (Announcements) - -### Check FFXIV Plugin Filters - -The FFXIV plugin has a filter as well that is sometimes enabled. -Go to ACT -> Plugins -> FFXIV ACT Plugin. -Verify that `Hide Chat Log (for privacy)` is not clicked. - -### Check Dalamud Plugins - -There are a number of quicklauncher/Dalamud plugins that can mess with chat output. -If you are still having issues, try launching the game without quicklauncher. -If this fixes the issue, then one of your Dalamud plugins is at fault. -Figure out which one, and disable it or configure it differently. - -### Check Your Network Log - -One final check here is to look at the network log file from ACT. -You can find your network log [with these steps](#how-to-find-a-network-log). - -In the network log, -you can see if the lines that cactbot is looking for appears in the file -or have been modified by Dalamud plugins. -Chat log lines start with `00`. - -If you need an example to try, -queue into Susano normal (The Pool of Tribute trial, level 63) unsynced. -Open ACT. Do a `/countdown 5`, pull the boss, and then wipe. - -In your network log, you should be able to find something like each of the following lines (not exact): - -```text -00|2023-11-29T11:55:14.0000000-08:00|00B9||Battle commencing in 5 seconds! (Your Name)|0cb8cd44ec6d6723 -00|2023-11-29T11:46:08.0000000-08:00|0044|Susano|Let the revels begin!|8cfb1aec4563d935 -``` - -## Fixing Network Data Problems - -### Has the Game Just Updated? - -If the game has patched recently, -you need to wait for the ACT FFXIV Plugin to update before anything will work. -Even if cactbot has done a release, this is not enough. - -### Test Game Connection - -Verify your firewall rules by going to ACT -> Plugins -> FFXIV ACT Plugin -> Test Game Connection. -You should get a notification that says something like -`Success: All FFXIV memory signatures detected successfully, and Network data is available.`. - -If you do not get this message, ask in the [FFXIV ACT discord](#places-to-get-help) for help. - -### Verify DPS Parsing - -Even if the game connection works, that doesn't mean everything is correct. -If dps parsing isn't working, then cactbot triggers will likely not work either. - -Verify dps parsing works. -Open ACT. -Go hit a striking dummy for 10 seconds. -If you have a dps overlay, numbers should appear. -Regardless of a dps overlay, if you go to ACT -> Main and click on the encounter, -then you should see a dps chart. - -If you don't see dps charts, -make sure you have enabled Deucalion via -ACT -> Plugins -> FFXIV ACT Plugin -> Inject and use Deucalion for network data. - -### Cactbot Stops Working Randomly - -If cactbot and dps parsing works properly and then stops, -then you should enable Deucalion via -ACT -> Plugins -> FFXIV ACT Plugin -> Inject and use Deucalion for network data. - -Clicking this option will also allow you to start ACT after the game has started. - -## Problems During Cactbot Installation - -If you get an error in the OverlayPlugin console similar to `System.MissingMethodException: Method not found: '!!0[] System.Array.Empty()` then you have installed the wrong .NET framework version. Please install the [.NET Framework](https://www.microsoft.com/net/download/framework) version 4.6.1 or above. - -If you get an error that says `Plugin Load Failure` and `The downloaded file did not contain a plugin that could be loaded`, -there could be several potential issues. - -- Make sure you have [installed OverlayPlugin](https://github.com/quisquous/cactbot#install-overlayplugin). -- Check your OverlayPlugin version in **Plugins** -> **Plugin Listing** -> **OverlayPlugin.dll**. -If this is not the same version as [this release](https://github.com/OverlayPlugin/OverlayPlugin/releases/latest), -then remove it and re-follow the [installation instructions](https://github.com/quisquous/cactbot#install-overlayplugin). -- Make sure you are running x64 ACT (`Advanced Combat Tracker.exe`) and not x86 ACT (`ACTx86.exe`). -- Finally, make sure you have reloaded ACT once you have installed OverlayPlugin. - -If you get an error similar to -`Invalid Plugin: This assembly does not have a class that implements ACT's plugin interface, or scanning the assembly threw an error.` -or -`Load Error: Method 'LoadConfig' in type 'CactbotEventSource' etc etc does not have an implementation` -then you should make sure that `CactbotOverlay.dll` is listed after `OverlayPlugin.dll` in -**Plugins** -> **Plugin Listing**. - -## My TTS Isn't Working - -Cactbot uses ACT for Text to Speech (TTS). -If your TTS isn't working, then ACT is likely not set up properly. -If you are not using some custom plugin like yukkuri etc, -the default ACT TTS settings are in ACT -> Options -> Sound Settings -> Text to Speech. - -If you want to test TTS with cactbot, you can use the [test overlay](#test-module). -If you add this overlay and then in game type `/echo tts:thing to say` it will use TTS to play it. - -If this does not work, verify that you see the echo line in game. -See: [Fixing Chat Log Problems](#fixing-chat-log-problems). - -## Places to Get Help - -- ask in the [FFXIV ACT discord](https://discord.gg/ahFKcmx) #troubleshooting channel -- open a [github issue](https://github.com/quisquous/cactbot/issues) - -It is highly recommended that you ask in the FFXIV ACT discord first for most troubleshooting issues. -You should also read the #troubleshooting channel pins first. -Pasting OverlayPlugin's tech support info is also a lot of help in tracking down issues. -Go to ACT -> Plugins -> OverlayPlugin.dll -> General and click "Copy Tech Support Info to Clipboard" to find it. - -## How to Find a Network Log - -If you are having issues with triggers or timelines, -it can be useful to attach a network log from ACT -so that the [network logs](LogGuide.md#network-log-lines) -can be replayed and investigated for errors. - -To find your network logs, go to the **Plugins** tab in ACT, -click the **FFXIV Settings** button, -and then click **Open FFXIV Log Folder**. - -![image](images/troubleshooting_openlogfolder.png) - -This will open up a folder window with files in it. -Select a file named something like **Network_etc.log**. -The files are named with your FFXIV ACT Plugin version and the date. - -![image](images/troubleshooting_networklog.png) - -These files are often large, so zip them up first. - -You can attach these to github issues directly. -Alternatively, find some file hosting site, -upload the files there, -and then attach the link. - -If you want to split a log to only include a particular fight, -you can use the [log splitter](https://quisquous.github.io/cactbot/util/logtools/splitter.html) -by dragging a network log file to the page and selecting the fights you want. - -If you are trying to debug something, it is usually better to not split the log. + diff --git a/docs/Headmarkers.md b/docs/Headmarkers.md index 3d3ad96215..ba58d0b501 100644 --- a/docs/Headmarkers.md +++ b/docs/Headmarkers.md @@ -1,206 +1,5 @@ -# Headmarker Guide +# Permanently Moved to OverlayPlugin/cactbot -This is a guide to finding the real ids for "headmarkers", -i.e. the visual that appears above your player's head when you need to spread, stack, etc. +See: [docs/Headmarkers.md](https://github.com/OverlayPlugin/cactbot/blob/main/docs/Headmarkers.md) -cactbot calls them "headmarkers", -e.g. `NetRegexes.headMarker()`. -The FFXIV parsing plugin calls them "TargetIcon", -e.g. `[21:40:13.596] TargetIcon 1B:4002ADD7:Arcane Cylinder:0000:0000:00B5:0000:0000:0000`. -FFXIV itself calls them "Lockon", -e.g. . - -This guide will call them "headmarkers" from here on out. - -## History and Motivation - -During TEA, -Square Enix changed headmarkers to have a per-instance offset in certain content. -This now happens in all ultimates, extremes, and savage raids. -Once the content is no longer current, -sometimes the offsets are then removed. - -This same thing happened with ability ids as well during o4s. -However, the FFXIV parsing plugin and fflogs cannot function without real ability ids, -and so the parsing plugin takes care of this itself. -Headmarkers are not parsing related, -and so headmarker offsets are left to downstream developers to fix themselves. - -The offset is sent to the client when zoning into the instance, -and so is the same for every pull while in the same instance. - -## Handling this in cactbot - -cactbot chooses to handle this by trying to figure out the true headmarker ids. -This is so that it's easy to find different content using the same id, -e.g. limit cut starting at `004F` (usually, but not always). -It's also easier to double check that the values are correct. -Ideally, when finding headmarkers, please leave a comment with the full avfx path. - -cactbot handles offsets by looking for headmarkers, -recording the first id it finds, -and then comparing to the expected first headmarker to calculate the offset. - -### No Offsets - -If the encounter does not have headmarker offsets, -please use headmarker ids directly in triggers, -e.g. [p9n](https://github.com/quisquous/cactbot/blob/7b904e35c7d678013d229080c858f19d35510ac1/ui/raidboss/data/06-ew/raid/p9n.ts#L33-L38). - -### Same first headmarker - -Most of the time, -if the encounter does have headmarker offsets, -the first headmarker id will always be the same. - -Most trigger sets do something like [p11s](https://github.com/quisquous/cactbot/blob/3ca3589/ui/raidboss/data/06-ew/raid/p11s.ts). - -There's a helper function to set the headmarker offset if it's not found, -and return the true headmarker id. - -```typescript -// Helper functions. -const firstHeadmarker = parseInt(headmarkers.dike, 16); - -const getHeadmarkerId = (data: Data, matches: NetMatches['HeadMarker']) => { - if (data.decOffset === undefined) - data.decOffset = parseInt(matches.id, 16) - firstHeadmarker; - return (parseInt(matches.id, 16) - data.decOffset).toString(16).toUpperCase().padStart(4, '0'); -}; -``` - -There's usually also a trigger at the top of the file to always try to set the offset. -In the past there's been bugs where `getHeadmarkerId` has been used in `condition` functions. -(See the P9S defamation example below for how the `getHeadmarkerId` call might be skipped.) - -```typescript - { - id: 'P11S Headmarker Tracker', - type: 'HeadMarker', - netRegex: {}, - condition: (data) => data.decOffset === undefined, - // Unconditionally set the first headmarker here so that future triggers are conditional. - run: (data, matches) => getHeadmarkerId(data, matches), - }, -``` - -Then, any later headmarker trigger has to match all headmarker lines, -and use `getHeadmarkerId` to figure out the correct id. -It's definitely a little bit cumbersome and inefficient, but that's what we got. - -```typescript - { - id: 'P9S Defamation', - type: 'HeadMarker', - netRegex: {}, - condition: (data, matches) => { - return data.me === matches.target && - getHeadmarkerId(data, matches) === headmarkers.defamation; - }, - alarmText: (_data, _matches, output) => output.defamation!(), - // etc -``` - -### Different first headmarker - -In rare cases, the first headmarker is not consistent. - -As cactbot resets all trigger info (including recorded headmarker offset) on wipe, -any trigger file must handle the first headmarker from any door boss and final boss simultaneously. - -For example, [P12S](https://github.com/quisquous/cactbot/blob/4700770/ui/raidboss/data/06-ew/raid/p12s.ts#L159-L179) -has a door boss with two different first headmarkers (bottom left / bottom right wing) -and a final boss with one first headmarker. - -See that file for how that can be solved. - -## Lockon table - -All visual effects are avfx game data files and are referenced by the `Lockon` table. - -You can `exd Lockon` from a local copy of [SaintCoinach](https://github.com/xivapi/SaintCoinach), -or alternatively you can browse the Lockon table online here: - -Headmarkers are 2 byte hex values, -and the `key` field in the `Lockon` is the decimal representation of that hex value. - -Here is some code from: - -```typescript -const headmarkers = { - // vfx/lockon/eff/tank_lockonae_0m_5s_01t.avfx - dualityOfDeath: '01D4', - // vfx/lockon/eff/m0361trg_a1t.avfx (through m0361trg_a8t) - limitCut1: '004F', - limitCut2: '0050', - limitCut3: '0051', - limitCut4: '0052', - limitCut5: '0053', - limitCut6: '0054', - limitCut7: '0055', - limitCut8: '0056', - // vfx/lockon/eff/r1fz_skywl_s9x.avfx - defamation: '014A', - // vfx/lockon/eff/n5r9_lockon_bht_c0g.avfx - cometMarker: '01B3', -} as const; -``` - -The comet marker (that appears on one of the two unbroken meteors during Charybdis in P9S) is `01B3`. -`0x01B3` = 435 in decimal. -In the [LockOn table](https://github.com/xivapi/ffxiv-datamining/blob/master/csv/Lockon.csv#L439), -you can find that key 435 has the string `n5r9_lockon_bht_c0g`. - -This corresponds to the game asset `vfx/lockon/eff/n5r9_lockon_bht_c0g.avfx`. -My understanding is that all headmarker effects have this `avfx` extension -and are in the `vfx/lockon/eff/` game directory. - -## How to test headmarkers in game - -In order to verify that you have the correct headmarker id, -you can use VFXEditor. - -Install [FFXIVQuickLauncher](https://github.com/goatcorp/FFXIVQuickLauncher). - -Install the [VFXEditor plugin](https://github.com/0ceal0t/Dalamud-VFXEditor). - -Type `/vfxedit` to start. - -![VFXEditor Initial](images/vfxeditor_initial.png) - -Pick a skill to replace that's easy to do in game, like Cure 1. - -![VFXEditor Replace](images/vfxeditor_replace.png) - -Now, type a vfx to replace it with. - -![VFXEditor Loaded](images/vfxeditor_loaded.png) - -Finally, cast cure and observe the different animation. - -![VFXEditor Result](images/vfxeditor_result.png) - -Headmarkers on mobs (see: p12s wings, this meteor marker) may have odd positions and rotations. - -## How to Find True Ids - -Mostly this is about sleuthing it out. - -Here's some suggestions on things to consider: - -- look for new Lockon entries that weren't there in previous patches -- check if normal mode ids still apply (e.g. p12s wings) -- check if the same ids from previous encounters apply (e.g. limit cut ids) -- read names in the Lockon table and try to make connections, e.g. `m0515_turning_right01c` is the orange clockwise laser rotation - -## Future Work - -If somebody tracked down the network data and game code to find the actual offset and math, -then OverlayPlugin could emit a custom log line for the offset -or custom log lines with adjusted headmarker ids. - -Barring that, it's also possible that OverlayPlugin could handle all of the -"first headmarker" tracking itself per zone in C# code and emit custom log lines. - -Finally, it'd be nice if somebody could figure out how to automatically extract avfx into gifs -and then we could have an online library of headmarker ids mapped to visuals. + diff --git a/docs/LogGuide.md b/docs/LogGuide.md index 61b7d4e656..8956f5c627 100644 --- a/docs/LogGuide.md +++ b/docs/LogGuide.md @@ -1,2769 +1,5 @@ - - - -# Log Lines and Triggers +# Permanently Moved to OverlayPlugin/cactbot -This is intended to be a comprehensive guide to log lines -for folks who want to write ACT triggers for ff14. +See: [docs/LogGuide.md](https://github.com/OverlayPlugin/cactbot/blob/main/docs/LogGuide.md) -This guide was last updated for: - -- [FF14](https://na.finalfantasyxiv.com/lodestone/special/patchnote_log/) Patch 6.4 -- [FFXIV Plugin](https://github.com/ravahn/FFXIV_ACT_Plugin/releases) Patch 2.6.8.9 -- [OverlayPlugin](https://github.com/OverlayPlugin/OverlayPlugin/releases) Patch 0.19.20 - -## TOC - - -- [Data Flow](#data-flow) - - [Viewing logs after a fight](#viewing-logs-after-a-fight) - - [Importing an old fight](#importing-an-old-fight) - - [Importing into ffxivmon](#importing-into-ffxivmon) -- [Glossary of Terms](#glossary-of-terms) - - [Network Data](#network-data) - - [Network Log Lines](#network-log-lines) - - [Parsed Log Lines](#parsed-log-lines) - - [Game Log Lines](#game-log-lines) - - [Object/Actor/Entity/Mob/Combatant](#objectactorentitymobcombatant) - - [Object ID](#object-id) - - [Ability ID](#ability-id) - - [Instance Content ID](#instance-content-id) -- [FFXIV Plugin Log Lines](#ffxiv-plugin-log-lines) - - [Line 00 (0x00): LogLine](#line-00-0x00-logline) - - [Structure](#structure) - - [Regexes](#regexes) - - [Examples](#examples) - - [Don't Write Triggers Against Game Log Lines](#dont-write-triggers-against-game-log-lines) - - [Line 01 (0x01): ChangeZone](#line-01-0x01-changezone) - - [Structure](#structure-1) - - [Regexes](#regexes-1) - - [Examples](#examples-1) - - [Line 02 (0x02): ChangePrimaryPlayer](#line-02-0x02-changeprimaryplayer) - - [Structure](#structure-2) - - [Regexes](#regexes-2) - - [Examples](#examples-2) - - [Line 03 (0x03): AddCombatant](#line-03-0x03-addcombatant) - - [Structure](#structure-3) - - [Regexes](#regexes-3) - - [Examples](#examples-3) - - [Line 04 (0x04): RemoveCombatant](#line-04-0x04-removecombatant) - - [Structure](#structure-4) - - [Regexes](#regexes-4) - - [Examples](#examples-4) - - [Line 11 (0x0B): PartyList](#line-11-0x0b-partylist) - - [Structure](#structure-5) - - [Regexes](#regexes-5) - - [Examples](#examples-5) - - [Line 12 (0x0C): PlayerStats](#line-12-0x0c-playerstats) - - [Structure](#structure-6) - - [Regexes](#regexes-6) - - [Examples](#examples-6) - - [Line 20 (0x14): NetworkStartsCasting](#line-20-0x14-networkstartscasting) - - [Structure](#structure-7) - - [Regexes](#regexes-7) - - [Examples](#examples-7) - - [Line 21 (0x15): NetworkAbility](#line-21-0x15-networkability) - - [Structure](#structure-8) - - [Regexes](#regexes-8) - - [Examples](#examples-8) - - [Ability Flags](#ability-flags) - - [Ability Damage](#ability-damage) - - [Special Case Shifts](#special-case-shifts) - - [Ability Examples](#ability-examples) - - [Line 22 (0x16): NetworkAOEAbility](#line-22-0x16-networkaoeability) - - [Line 23 (0x17): NetworkCancelAbility](#line-23-0x17-networkcancelability) - - [Structure](#structure-9) - - [Regexes](#regexes-9) - - [Examples](#examples-9) - - [Line 24 (0x18): NetworkDoT](#line-24-0x18-networkdot) - - [Structure](#structure-10) - - [Regexes](#regexes-10) - - [Examples](#examples-10) - - [Line 25 (0x19): NetworkDeath](#line-25-0x19-networkdeath) - - [Structure](#structure-11) - - [Regexes](#regexes-11) - - [Examples](#examples-11) - - [Line 26 (0x1A): NetworkBuff](#line-26-0x1a-networkbuff) - - [Structure](#structure-12) - - [Regexes](#regexes-12) - - [Examples](#examples-12) - - [Line 27 (0x1B): NetworkTargetIcon (Head Marker)](#line-27-0x1b-networktargeticon-head-marker) - - [Structure](#structure-13) - - [Regexes](#regexes-13) - - [Examples](#examples-13) - - [Head Marker IDs](#head-marker-ids) - - [Line 28 (0x1C): NetworkRaidMarker (Floor Marker)](#line-28-0x1c-networkraidmarker-floor-marker) - - [Structure](#structure-14) - - [Regexes](#regexes-14) - - [Examples](#examples-14) - - [Combatant Marker Codes](#combatant-marker-codes) - - [Line 29 (0x1D): NetworkTargetMarker (Player Marker)](#line-29-0x1d-networktargetmarker-player-marker) - - [Structure](#structure-15) - - [Regexes](#regexes-15) - - [Examples](#examples-15) - - [Floor Marker Codes](#floor-marker-codes) - - [Line 30 (0x1E): NetworkBuffRemove](#line-30-0x1e-networkbuffremove) - - [Structure](#structure-16) - - [Regexes](#regexes-16) - - [Examples](#examples-16) - - [Line 31 (0x1F): NetworkGauge](#line-31-0x1f-networkgauge) - - [Structure](#structure-17) - - [Regexes](#regexes-17) - - [Examples](#examples-17) - - [Line 32 (0x20): NetworkWorld](#line-32-0x20-networkworld) - - [Line 33 (0x21): Network6D (Actor Control)](#line-33-0x21-network6d-actor-control) - - [Structure](#structure-18) - - [Regexes](#regexes-18) - - [Examples](#examples-18) - - [Line 34 (0x22): NetworkNameToggle](#line-34-0x22-networknametoggle) - - [Structure](#structure-19) - - [Regexes](#regexes-19) - - [Examples](#examples-19) - - [Line 35 (0x23): NetworkTether](#line-35-0x23-networktether) - - [Structure](#structure-20) - - [Regexes](#regexes-20) - - [Examples](#examples-20) - - [Line 36 (0x24): LimitBreak](#line-36-0x24-limitbreak) - - [Structure](#structure-21) - - [Regexes](#regexes-21) - - [Examples](#examples-21) - - [Line 37 (0x25): NetworkActionSync](#line-37-0x25-networkactionsync) - - [Structure](#structure-22) - - [Regexes](#regexes-22) - - [Examples](#examples-22) - - [Line 38 (0x26): NetworkStatusEffects](#line-38-0x26-networkstatuseffects) - - [Structure](#structure-23) - - [Regexes](#regexes-23) - - [Examples](#examples-23) - - [Line 39 (0x27): NetworkUpdateHP](#line-39-0x27-networkupdatehp) - - [Structure](#structure-24) - - [Regexes](#regexes-24) - - [Examples](#examples-24) - - [Line 40 (0x28): Map](#line-40-0x28-map) - - [Structure](#structure-25) - - [Regexes](#regexes-25) - - [Examples](#examples-25) - - [Line 41 (0x29): SystemLogMessage](#line-41-0x29-systemlogmessage) - - [Structure](#structure-26) - - [Regexes](#regexes-26) - - [Examples](#examples-26) - - [Line 42 (0x2A): StatusList3](#line-42-0x2a-statuslist3) - - [Structure](#structure-27) - - [Regexes](#regexes-27) - - [Examples](#examples-27) - - [Line 251 (0xFB): Debug](#line-251-0xfb-debug) - - [Line 252 (0xFC): PacketDump](#line-252-0xfc-packetdump) - - [Line 253 (0xFD): Version](#line-253-0xfd-version) - - [Line 254 (0xFE): Error](#line-254-0xfe-error) -- [OverlayPlugin Log Lines](#overlayplugin-log-lines) - - [Line 256 (0x100): LineRegistration](#line-256-0x100-lineregistration) - - [Structure](#structure-28) - - [Regexes](#regexes-28) - - [Examples](#examples-28) - - [Line 257 (0x101): MapEffect](#line-257-0x101-mapeffect) - - [Structure](#structure-29) - - [Regexes](#regexes-29) - - [Examples](#examples-29) - - [Line 258 (0x102): FateDirector](#line-258-0x102-fatedirector) - - [Structure](#structure-30) - - [Regexes](#regexes-30) - - [Examples](#examples-30) - - [Line 259 (0x103): CEDirector](#line-259-0x103-cedirector) - - [Structure](#structure-31) - - [Regexes](#regexes-31) - - [Examples](#examples-31) - - [Line 260 (0x104): InCombat](#line-260-0x104-incombat) - - [Structure](#structure-32) - - [Regexes](#regexes-32) - - [Examples](#examples-32) - - [Line 261 (0x105): CombatantMemory](#line-261-0x105-combatantmemory) - - [Structure](#structure-33) - - [Regexes](#regexes-33) - - [Examples](#examples-33) - - [Line 262 (0x106): RSVData](#line-262-0x106-rsvdata) - - [Structure](#structure-34) - - [Regexes](#regexes-34) - - [Examples](#examples-34) - - [Line 263 (0x107): StartsUsingExtra](#line-263-0x107-startsusingextra) - - [Structure](#structure-35) - - [Regexes](#regexes-35) - - [Examples](#examples-35) - - [Line 264 (0x108): AbilityExtra](#line-264-0x108-abilityextra) - - [Structure](#structure-36) - - [Regexes](#regexes-36) - - [Examples](#examples-36) - - -## Data Flow - -![Alt text](https://g.gravizo.com/source/data_flow?https%3A%2F%2Fraw.githubusercontent.com%2Fquisquous%2Fcactbot%2Fmain%2Fdocs%2FLogGuide.md) - -
- -data_flow - digraph G { - size ="4,4"; - ff14 [label="ff14 servers"] - ff14 -> ACT [label="network data"] - network [label="network log files"] - ACT [label="ACT + ffxiv plugin",shape=box,penwidth=3] - ACT -> network [label="write to disk"] - fflogs - network -> fflogs [label="upload"] - network -> ffxivmon [label="import"] - network -> ACT [label="import"] - network -> util [label="process"] - util [label="cactbot util scripts"] - plugins [label="triggers, ACT plugins"] - ACT -> plugins [label="parsed log lines"] - ACT -> plugins [label="network log lines"] - } -data_flow -
- -### Viewing logs after a fight - -If you have ACT open during a fight, then it will generate logs. -These logs will be trimmed to the start and end of combat. - -To see the parsed version of these logs, click on the **Main** tab, -expand the zone you care about, -right click on the encounter you want, -then select **View Logs**. - -![view logs screenshot](images/logguide_viewlogs.png) - -The **All** entry includes all the encounters in a zone and cannot be viewed. -You must view individual encounters. - -The window that pops up has the parsed log lines that triggers can be made against. -This is one way to search through and find the text that you want to make a trigger for. - -### Importing an old fight - -Sometimes you have to close ACT, but you want to look at old fights. -Or, somebody else sends you a log, and you want to make triggers from it. - -To do this, click the **Import/Export** tab, -click on **Import a Log File**, -click on **Select File...** -select the **Network_plugin_date.log** log file, -(where `plugin` and `date` are the FFXIV plugin version and day) -and finally click the **YOU** button. - -![import screenshot](images/logguide_import.png) - -This will create encounters whose [logs you can view](#viewing-logs-after-a-fight). - -### Importing into ffxivmon - -If you want to dig into the network data itself, ffxivmon is a great tool. - -To create a log file suitable for ffxivmon, -first turn on the **(DEBUG) Dump all Network Data to logfile** setting in ACT. - -![dump network data screenshot](images/logguide_dumpnetworkdata.png) - -Then, run an encounter in game with ACT running. -Once you're done, import that network log file into ffxivmon. - -![ffxivmon import screenshot](images/logguide_ffxivmon_import.png) - -Now, you can walk through and investigate the network data directly. - -![ffxivmon screenshot](images/logguide_ffxivmon.png) - -## Glossary of Terms - -### Network Data - -This is the raw packet dump sent from ff14 servers to your computer. -This data is processed both by the game itself as well as by the ffxiv plugin to -produce network log lines. - -![network data screenshot](images/logguide_networkdata.png) - -Folks writing triggers generally do not have to worry about raw packet data and -so this document does not focus very much on this type of data. - -### Network Log Lines - -These represent the lines that the ffxiv plugin writes to disk in -**Network_22009_20210801.log** files in your log directory. -These lines are still processed and filtered by the ffxiv plugin, -and are (mostly) not raw network data. - -Here are some example network log lines: - -```log -21|2019-05-31T21:14:56.8980000-07:00|10532971|Tini Poutini|DF9|Fire IV|40002F21|Zombie Brobinyak|150003|3B9D4002|1C|DF98000|0|0|0|0|0|0|0|0|0|0|0|0|104815|348652|12000|12000|1000|1000|-767.7882|156.939|-672.0446|26285|28784|13920|15480|1000|1000|-771.8156|157.1111|-671.3281||8eaa0245ad01981b69fc1af04ea8f9a1 -30|2019-05-31T20:02:41.4560000-07:00|6b4|Boost|0.00|1069C23F|Potato Chippy|1069C23F|Potato Chippy|00|3394|3394||4f7b1fa11ec7a2746a8c46379481267c -20|2019-05-31T20:02:41.4660000-07:00|105E3321|Tater Tot|2C9D|Peculiar Light|105E3321|Tater Tot||c375d8a2d1cf48efceccb136584ed250 -``` - -Data on network log lines is separated by vertical braces, i.e. `|`. -Network log lines also contain the hash of that line at the end. -The log line type itself is in decimal, e.g. aoe abilities are on lines that begin with `22|`. -The equivalent [parsed log line](#parsed-log-lines) would be written as the hex type `0x16`, i.e. `NetworkAOEAbility`. - -The ffxiv plugin does not write the parsed log lines that plugins interact with -to disk. - -The network log lines are used by some tools, such as: - -- fflogs uploader -- ffxivmon -- cactbot make_timeline utility - -In the past, -cactbot used to use [parsed log lines](#parsed-log-lines) for all triggers -but has switched to using network log lines instead -as they have more information. -Timelines still use parsed log lines for syncing (for now). - -If you [import a network log file into ACT](#importing-an-old-fight), -then you can view the parsed log lines for in the fight. - -### Parsed Log Lines - -These are the log lines that come out of the ffxiv plugin at runtime and are -also exposed to plugins for triggers. -These are what the [View Logs](#viewing-logs-after-a-fight) option in ACT shows. -Most cactbot triggers used network log lines, -but ACT custom triggers use parsed log lines. - -Data in parsed log lines is separated by colons, i.e. `:`. -The log line type is in hex. - -Here is an example: - -```log -[21:16:44.288] 15:10532971:Potato Chippy:9C:Scathe:40001299:Striking Dummy:750003:90D0000:1C:9C8000:0:0:0:0:0:0:0:0:0:0:0:0:2778:2778:0:0:1000:1000:-653.9767:-807.7275:31.99997:26945:28784:6720:15480:1000:1000:-631.5208:-818.5244:31.95173: -``` - -Parsed log lines lines always start with the time in square brackets. -This time is formatted to be in your local time zone. -The time is followed with a hex value (in this case 0x15) that indicates the type of the line it is. - -The rest of the data in the line needs to be interpreted based on what type it is. -See the following sections that describe each line. - -### Game Log Lines - -A game log line is a specific type of log line with type `00`. -These log lines also appear directly in your chat windows in game, -possibly in the Battle Log tab. -Try to [avoid writing triggers](#dont-write-triggers-against-game-log-lines) using these lines. - -See: [Line 00](#line00) for examples. - -### Object/Actor/Entity/Mob/Combatant - -These are all words used synonymously in this document to refer to an object -in the game that can use abilities and has stats. -This could be the player, Bahamut, Eos, a Striking Dummy. - -### Object ID - -Object ids are 4 byte identifiers used for all types of objects. - -Player ids always start with the byte `10`, -e.g. `1069C23F` or `10532971`. - -Enemy and pet ids always start with the byte `40`, -e.g. `4000A848` or `4000A962`. - -For `NetworkAOEAbility` lines that don't affect anybody, e.g. a Titan landslide that somehow nobody stands in, -this is represented as hitting the id `E0000000` (and a blank name). - -One thing to note is that in most raids, -there are many mobs in the scene with the same name. -For example, in t13, there are about twenty Bahamut Prime mobs in the zone, -most of which are invisible. -You can often differentiate these by HP values (see [AddCombatant](#line03) log lines). -Often these invisible mobs are used as the damaging actors, -which is why in UWU Titan Phase, both Garuda and Titan use Rock Throw to put people in jails. - -### Ability ID - -Although ff14 differentiates between abilities and spells, -this document uses these words interchangeably. -All actions taken by a player or an enemy are "abilities" and have a unique 4 byte id. - -You can use xivapi.com to look up a particular action, as sometimes these are -listed as "Unknown" from the ffxiv plugin if it hasn't updated yet. -For example, Fire IV has the ability id 0xDF9 = 3577, -so this link will give you more information about it: - - -This works for both players and enemies, abilities and spells. - -### Instance Content ID - -Some lines like the [actor control line](#line33) and [SystemLogMessage](#line41) -have a field called `instance`. -This field is a four byte field with two parts, -The first two bytes are the update type (e.g. `8003` is the update type for instanced content, -and `8004` is the same content but with trusts). -The second two bytes are the `InstanceContentType`, -from the [InstanceContent table](https://github.com/xivapi/ffxiv-datamining/blob/master/csv/InstanceContent.csv). - -For example, if `instance` is `80034E6C` then `0x4E6C` is the `InstanceContentType`. -`0x4E6C` is 20076 in decimal, and corresponds to Diamond Weapon (Savage): . - -## FFXIV Plugin Log Lines - - - -### Line 00 (0x00): LogLine - -These are what this document calls "game log lines". -Because these are not often used for triggers -(other than `0839` and `0044` messages), -the full set of LogTypes is not well-documented. - -(Pull requests welcome!) - - - -#### Structure - -```log -Network Log Line Structure: -00|[timestamp]|[code]|[name]|[line] - -Parsed Log Line Structure: -[timestamp] ChatLog 00:[code]:[name]:[line] -``` - -#### Regexes - -```log -Network Log Line Regex: -^(?00)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\| - -Parsed Log Line Regex: -(?^.{14}) ChatLog (?00):(?[^:]*):(?[^:]*):(?[^:]*)(?:$|:) -``` - -#### Examples - -```log -Network Log Line Examples: -00|2021-04-26T14:12:30.0000000-04:00|0839||You change to warrior.|d8c450105ea12854e26eb687579564df -00|2021-04-26T16:57:41.0000000-04:00|0840||You can now summon the antelope stag mount.|caa3526e9f127887766e9211e87e0e8f -00|2021-04-26T14:17:11.0000000-04:00|0B3A||You defeat the embodiment.|ef3b7b7f1e980f2c08e903edd51c70c7 -00|2021-04-26T14:12:30.0000000-04:00|302B||The gravity node uses Forked Lightning.|45d50c5f5322adf787db2bd00d85493d -00|2021-04-26T14:12:30.0000000-04:00|322A||The attack misses.|f9f57724eb396a6a94232e9159175e8c -00|2021-07-05T18:01:21.0000000-04:00|0044|Tsukuyomi|Oh...it's going to be a long night.|1a81d186fd4d19255f2e01a1694c7607 - -Parsed Log Line Examples: -[14:12:30.000] ChatLog 00:0839::You change to warrior. -[16:57:41.000] ChatLog 00:0840::You can now summon the antelope stag mount. -[14:17:11.000] ChatLog 00:0B3A::You defeat the embodiment. -[14:12:30.000] ChatLog 00:302B::The gravity node uses Forked Lightning. -[14:12:30.000] ChatLog 00:322A::The attack misses. -[18:01:21.000] ChatLog 00:0044:Tsukuyomi:Oh...it's going to be a long night. -``` - - - -#### Don't Write Triggers Against Game Log Lines - -There are a number of reasons to avoid basing triggers on game log lines: - -- can show up later than parsed log lines (often up to half a second) -- inconsistent text (gains effect vs suffers effect, begins casting vs readies, you vs player name) -- often vague (the attack misses) -- can change spelling at the whim of SquareEnix - -Instead, the recommendation is to base your triggers on log lines that are not type `0x00`. -Prefer using [NetworkBuff](#line26) line instead of "suffers the effect" game log lines. -Prefer using the [NetworkStartsCasting](#line20) "starts using" line instead of the "readies" or "begins casting" game log lines. - -At the moment, there are some cases where you must use game log lines, -such as sealing and unsealing of zones, or boss rp text for phase transitions. - -Note: -There are examples where [NetworkStartsCasting](#line20) lines show up -after the corresponding `00` "readies" line, -but it is on the order of tens of milliseconds -and does not consistently show up first. -[NetworkAbility](#line21) lines always seem to show up before the `00` "uses" lines. - - - -### Line 01 (0x01): ChangeZone - -This message is sent when first logging in and whenever the zone is changed. - - - -#### Structure - -```log -Network Log Line Structure: -01|[timestamp]|[id]|[name] - -Parsed Log Line Structure: -[timestamp] Territory 01:[id]:[name] -``` - -#### Regexes - -```log -Network Log Line Regex: -^(?01)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\| - -Parsed Log Line Regex: -(?^.{14}) Territory (?01):(?[^:]*):(?[^:]*)(?:$|:) -``` - -#### Examples - -```log -Network Log Line Examples: -01|2021-04-26T14:13:17.9930000-04:00|326|Kugane Ohashi|b9f401c0aa0b8bc454b239b201abc1b8 -01|2021-04-26T14:22:04.5490000-04:00|31F|Alphascape (V2.0)|8299b97fa36500118fc3a174ed208fe4 - -Parsed Log Line Examples: -[14:13:17.993] Territory 01:326:Kugane Ohashi -[14:22:04.549] Territory 01:31F:Alphascape (V2.0) -``` - - - - - -### Line 02 (0x02): ChangePrimaryPlayer - -This redundant message follows every [ChangeZone](#line01) message to indicate the name of the player. - - - -#### Structure - -```log -Network Log Line Structure: -02|[timestamp]|[id]|[name] - -Parsed Log Line Structure: -[timestamp] ChangePrimaryPlayer 02:[id]:[name] -``` - -#### Regexes - -```log -Network Log Line Regex: -^(?02)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\| - -Parsed Log Line Regex: -(?^.{14}) ChangePrimaryPlayer (?02):(?[^:]*):(?[^:]*)(?:$|:) -``` - -#### Examples - -```log -Network Log Line Examples: -02|2021-04-26T14:11:31.0200000-04:00|10FF0001|Tini Poutini|5b0a5800460045f29db38676e0c3f79a -02|2021-04-26T14:13:17.9930000-04:00|10FF0002|Potato Chippy|34b657d75218545f5a49970cce218ce6 - -Parsed Log Line Examples: -[14:11:31.020] ChangePrimaryPlayer 02:10FF0001:Tini Poutini -[14:13:17.993] ChangePrimaryPlayer 02:10FF0002:Potato Chippy -``` - - - - - -### Line 03 (0x03): AddCombatant - -This message is sent when a new object is added to the scene or -becomes close enough to the player that they can view its actions. - - - -#### Structure - -```log -Network Log Line Structure: -03|[timestamp]|[id]|[name]|[job]|[level]|[ownerId]|[worldId]|[world]|[npcNameId]|[npcBaseId]|[currentHp]|[hp]|[currentMp]|[mp]|[?]|[?]|[x]|[y]|[z]|[heading] - -Parsed Log Line Structure: -[timestamp] AddCombatant 03:[id]:[name]:[job]:[level]:[ownerId]:[worldId]:[world]:[npcNameId]:[npcBaseId]:[currentHp]:[hp]:[currentMp]:[mp]:[?]:[?]:[x]:[y]:[z]:[heading] -``` - -#### Regexes - -```log -Network Log Line Regex: -^(?03)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?:[^|]*\|){2}(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\| - -Parsed Log Line Regex: -(?^.{14}) AddCombatant (?03):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*)(?::[^:]*){2}:(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*)(?:$|:) -``` - -#### Examples - -```log -Network Log Line Examples: -03|2021-06-16T20:46:38.5450000-07:00|10FF0001|Tini Poutini|24|46|0000|28|Jenova|0|0|30460|30460|10000|10000|0|0|-0.76|15.896|0|-3.141593|c0e6f1c201e7285884fb6bf107c533ee -03|2021-06-16T21:35:11.3060000-07:00|4000B364|Catastrophe|00|46|0000|00||5631|6358|57250|57250|0|10000|0|0|0|0|0|-4.792213E-05|9c22c852e1995ed63ff4b71c09b7d1a7 -03|2021-06-16T21:35:11.3060000-07:00|4000B363|Catastrophe|00|46|0000|00||5631|6358|57250|57250|0|10000|0|0|0|0|0|-4.792213E-05|9438b02195d9b785e07383bc84b2bf37 -03|2021-06-16T21:35:11.3060000-07:00|4000B362|Catastrophe|00|46|0000|00||5631|7305|13165210|13165210|10000|10000|0|0|0|-15|0|-4.792213E-05|1c4bc8f27640fab6897dc90c02bba79d -03|2021-06-16T21:35:11.4020000-07:00|4000B365|Catastrophe|00|46|0000|00||5631|6358|57250|57250|0|10000|0|0|0|0|0|-4.792213E-05|8b3f6cf1939428dd9ab0a319aba44910 -03|2021-06-16T21:35:11.4020000-07:00|4000B36a|Catastrophe|00|46|0000|00||5631|6358|57250|57250|0|10000|0|0|0|0|0|-4.792213E-05|b3b3b4f926bcadd8b6ef008232d58922 - -Parsed Log Line Examples: -[20:46:38.545] AddCombatant 03:10FF0001:Tini Poutini:24:46:0000:28:Jenova:0:0:30460:30460:10000:10000:0:0:-0.76:15.896:0:-3.141593 -[21:35:11.306] AddCombatant 03:4000B364:Catastrophe:00:46:0000:00::5631:6358:57250:57250:0:10000:0:0:0:0:0:-4.792213E-05 -[21:35:11.306] AddCombatant 03:4000B363:Catastrophe:00:46:0000:00::5631:6358:57250:57250:0:10000:0:0:0:0:0:-4.792213E-05 -[21:35:11.306] AddCombatant 03:4000B362:Catastrophe:00:46:0000:00::5631:7305:13165210:13165210:10000:10000:0:0:0:-15:0:-4.792213E-05 -[21:35:11.402] AddCombatant 03:4000B365:Catastrophe:00:46:0000:00::5631:6358:57250:57250:0:10000:0:0:0:0:0:-4.792213E-05 -[21:35:11.402] AddCombatant 03:4000B36a:Catastrophe:00:46:0000:00::5631:6358:57250:57250:0:10000:0:0:0:0:0:-4.792213E-05 -``` - - - -This combatant may be invisible and fake. The real ones have more HP. -For example, at the start of Deltascape V2.0 you will see messages like the -latter 5 examples above. - -In heavy zones (e.g. Eureka), combatants may be culled if there are too many -things nearby. -Usually other players are culled first, but mobs can be as well. -Eureka NMs (and S ranks) solve this by having a flag on them -that allows them to be seen via AddCombatant message from anywhere in the zone, -which is why it is possible to write triggers for when these pop. - - - -### Line 04 (0x04): RemoveCombatant - -This message is sent when an object is removed from the scene, either because -the player has moved too far away from it, it has died, or the player has -changed zones. - - - -#### Structure - -```log -Network Log Line Structure: -04|[timestamp]|[id]|[name]|[job]|[level]|[owner]|[?]|[world]|[npcNameId]|[npcBaseId]|[?]|[hp]|[?]|[?]|[?]|[?]|[x]|[y]|[z]|[heading] - -Parsed Log Line Structure: -[timestamp] RemoveCombatant 04:[id]:[name]:[job]:[level]:[owner]:[?]:[world]:[npcNameId]:[npcBaseId]:[?]:[hp]:[?]:[?]:[?]:[?]:[x]:[y]:[z]:[heading] -``` - -#### Regexes - -```log -Network Log Line Regex: -^(?04)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?:[^|]*\|)(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?:[^|]*\|)(?[^|]*)\|(?:[^|]*\|){4}(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\| - -Parsed Log Line Regex: -(?^.{14}) RemoveCombatant (?04):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):[^:]*:(?[^:]*):(?[^:]*):(?[^:]*):[^:]*:(?[^:]*)(?::[^:]*){4}:(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*)(?:$|:) -``` - -#### Examples - -```log -Network Log Line Examples: -04|2021-07-23T23:01:27.5480000-07:00|10FF0001|Tini Poutini|05|1E|0000|35|Jenova|0|0|816|816|10000|10000|0|0|-66.24337|-292.0904|20.06466|1.789943|4fbfc851937873eacf94f1f69e0e2ba9 -04|2021-06-16T21:37:36.0740000-07:00|4000B39C|Petrosphere|00|46|0000|00||6712|7308|0|57250|0|10000|0|0|-16.00671|-0.01531982|0|1.53875|980552ad636f06249f1b5c7a6e675aad - -Parsed Log Line Examples: -[23:01:27.548] RemoveCombatant 04:10FF0001:Tini Poutini:05:1E:0000:35:Jenova:0:0:816:816:10000:10000:0:0:-66.24337:-292.0904:20.06466:1.789943 -[21:37:36.074] RemoveCombatant 04:4000B39C:Petrosphere:00:46:0000:00::6712:7308:0:57250:0:10000:0:0:-16.00671:-0.01531982:0:1.53875 -``` - - - - - -### Line 11 (0x0B): PartyList - -This line represents the players currently in the party, and is sent whenever the party makeup changes. - - - -#### Structure - -```log -Network Log Line Structure: -11|[timestamp]|[partyCount]|[id0]|[id1]|[id2]|[id3]|[id4]|[id5]|[id6]|[id7]|[id8]|[id9]|[id10]|[id11]|[id12]|[id13]|[id14]|[id15]|[id16]|[id17]|[id18]|[id19]|[id20]|[id21]|[id22]|[id23] - -Parsed Log Line Structure: -[timestamp] PartyList 0B:[partyCount]:[id0]:[id1]:[id2]:[id3]:[id4]:[id5]:[id6]:[id7]:[id8]:[id9]:[id10]:[id11]:[id12]:[id13]:[id14]:[id15]:[id16]:[id17]:[id18]:[id19]:[id20]:[id21]:[id22]:[id23] -``` - -#### Regexes - -```log -Network Log Line Regex: -^(?11)\|(?[^|]*)\|(?[^|]*)\| - -Parsed Log Line Regex: -(?^.{14}) PartyList (?0B):(?[^:]*)(?:$|:) -``` - -#### Examples - -```log -Network Log Line Examples: -11|2021-06-16T20:46:38.5450000-07:00|8|10FF0002|10FF0003|10FF0004|10FF0001|10FF0005|10FF0006|10FF0007|10FF0008| -11|2021-06-16T21:47:56.7170000-07:00|4|10FF0002|10FF0001|10FF0003|10FF0004| - -Parsed Log Line Examples: -[20:46:38.545] PartyList 0B:8:10FF0002:10FF0003:10FF0004:10FF0001:10FF0005:10FF0006:10FF0007:10FF0008 -[21:47:56.717] PartyList 0B:4:10FF0002:10FF0001:10FF0003:10FF0004 -``` - - - - - -### Line 12 (0x0C): PlayerStats - -This message is sent whenever your player's stats change and upon entering a new zone/instance. - - - -#### Structure - -```log -Network Log Line Structure: -12|[timestamp]|[job]|[strength]|[dexterity]|[vitality]|[intelligence]|[mind]|[piety]|[attackPower]|[directHit]|[criticalHit]|[attackMagicPotency]|[healMagicPotency]|[determination]|[skillSpeed]|[spellSpeed]|[?]|[tenacity]|[localContentId] - -Parsed Log Line Structure: -[timestamp] PlayerStats 0C:[job]:[strength]:[dexterity]:[vitality]:[intelligence]:[mind]:[piety]:[attackPower]:[directHit]:[criticalHit]:[attackMagicPotency]:[healMagicPotency]:[determination]:[skillSpeed]:[spellSpeed]:[?]:[tenacity]:[localContentId] -``` - -#### Regexes - -```log -Network Log Line Regex: -^(?12)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?:[^|]*\|)(?[^|]*)\|(?[^|]*)\| - -Parsed Log Line Regex: -(?^.{14}) PlayerStats (?0C):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):[^:]*:(?[^:]*):(?[^:]*)(?:$|:) -``` - -#### Examples - -```log -Network Log Line Examples: -12|2021-04-26T14:30:07.4910000-04:00|21|5456|326|6259|135|186|340|5456|380|3863|135|186|2628|1530|380|0|1260|4000174AE14AB6|3c03ce9ee4afccfaae74695376047054 -12|2021-04-26T14:31:25.5080000-04:00|24|189|360|5610|356|5549|1431|189|1340|3651|5549|5549|1661|380|1547|0|380|4000174AE14AB6|53b98d383806c5a29dfe33720f514288 -12|2021-08-06T10:29:35.3400000-04:00|38|308|4272|4443|288|271|340|4272|1210|2655|288|271|2002|1192|380|0|380|4000174AE14AB6|4ce3eac3dbd0eb1d6e0044425d9e091d - -Parsed Log Line Examples: -[14:30:07.491] PlayerStats 0C:21:5456:326:6259:135:186:340:5456:380:3863:135:186:2628:1530:380:0:1260:4000174AE14AB6 -[14:31:25.508] PlayerStats 0C:24:189:360:5610:356:5549:1431:189:1340:3651:5549:5549:1661:380:1547:0:380:4000174AE14AB6 -[10:29:35.340] PlayerStats 0C:38:308:4272:4443:288:271:340:4272:1210:2655:288:271:2002:1192:380:0:380:4000174AE14AB6 -``` - - - - - -### Line 20 (0x14): NetworkStartsCasting - -For abilities with cast bars, -this is the log line that specifies that a player or a monster has started casting an ability. -This precedes a [NetworkAbility](#line21), -[NetworkAOEAbility](#line22), -or [NetworkCancelAbility](#line23) -where it uses the ability or is interrupted. - - - -#### Structure - -```log -Network Log Line Structure: -20|[timestamp]|[sourceId]|[source]|[id]|[ability]|[targetId]|[target]|[castTime]|[x]|[y]|[z]|[heading] - -Parsed Log Line Structure: -[timestamp] StartsCasting 14:[sourceId]:[source]:[id]:[ability]:[targetId]:[target]:[castTime]:[x]:[y]:[z]:[heading] -``` - -#### Regexes - -```log -Network Log Line Regex: -^(?20)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\| - -Parsed Log Line Regex: -(?^.{14}) StartsCasting (?14):(?[^:]*):(?[^:]*):(?[^:]*):(?(?:[^:]|: )*?):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*)(?:$|:) -``` - -#### Examples - -```log -Network Log Line Examples: -20|2021-07-27T12:47:23.1740000-04:00|40024FC4|The Manipulator|F63|Carnage|40024FC4|The Manipulator|4.70|-0.01531982|-13.86256|10.59466|-4.792213E-05|488abf3044202807c62fa32c2e36ee81 -20|2021-07-27T12:48:33.5420000-04:00|10FF0001|Tini Poutini|DF0|Stone III|40024FC4|The Manipulator|2.35|-0.06491255|-9.72675|10.54466|-3.141591|2a24845eab5ed48d4f043f7b6269ef70 -20|2021-07-27T12:48:36.0460000-04:00|10FF0002|Potato Chippy|BA|Succor|10FF0002|Potato Chippy|1.93|-0.7477417|-5.416992|10.54466|2.604979|99a70e6f12f3fcb012e59b3f098fd69b -20|2021-07-27T12:48:29.7830000-04:00|40024FD0|The Manipulator|13BE|Judgment Nisi|10FF0001|Tini Poutini|3.20|8.055649|-17.03842|10.58736|-4.792213E-05|bc1c3d72782de2199bfa90637dbfa9b8 -20|2021-07-27T12:48:36.1310000-04:00|40024FCE|The Manipulator|13D0|Seed Of The Sky|E0000000||2.70|8.055649|-17.03842|10.58736|-4.792213E-05|5377da9551e7ca470709dc08e996bb75 - -Parsed Log Line Examples: -[12:47:23.174] StartsCasting 14:40024FC4:The Manipulator:F63:Carnage:40024FC4:The Manipulator:4.70:-0.01531982:-13.86256:10.59466:-4.792213E-05 -[12:48:33.542] StartsCasting 14:10FF0001:Tini Poutini:DF0:Stone III:40024FC4:The Manipulator:2.35:-0.06491255:-9.72675:10.54466:-3.141591 -[12:48:36.046] StartsCasting 14:10FF0002:Potato Chippy:BA:Succor:10FF0002:Potato Chippy:1.93:-0.7477417:-5.416992:10.54466:2.604979 -[12:48:29.783] StartsCasting 14:40024FD0:The Manipulator:13BE:Judgment Nisi:10FF0001:Tini Poutini:3.20:8.055649:-17.03842:10.58736:-4.792213E-05 -[12:48:36.131] StartsCasting 14:40024FCE:The Manipulator:13D0:Seed Of The Sky:E0000000::2.70:8.055649:-17.03842:10.58736:-4.792213E-05 -``` - - - -These lines are usually (but not always) associated with game log lines that either look like -`00:282B:Shinryu readies Earthen Fury.` -or `00:302b:The proto-chimera begins casting The Ram's Voice.` - - - -### Line 21 (0x15): NetworkAbility - -This is an ability that ends up hitting a single target (possibly the caster's self). -The reason this is worded as "ends up hitting" is that some AOE abilities may only hit a single target, -in which case they still result in this type - -For example, in ucob, if Firehorn's fireball in nael phase hits the whole group, it will be a `22/0x16` type. -If one person runs the fireball out and it only hits them, then it is type `21/0x15` because there's only one target. -If your trigger includes the message type, -it is usually best to write your parsed log line regex `1[56]` -and your network log line regex as `2[12]` -to include both possibilities. - -Ground AOEs that don't hit anybody are considered [NetworkAOEAbility](#line22) lines. - - - -#### Structure - -```log -Network Log Line Structure: -21|[timestamp]|[sourceId]|[source]|[id]|[ability]|[targetId]|[target]|[flags]|[damage]|[?]|[?]|[?]|[?]|[?]|[?]|[?]|[?]|[?]|[?]|[?]|[?]|[?]|[?]|[targetCurrentHp]|[targetMaxHp]|[targetCurrentMp]|[targetMaxMp]|[?]|[?]|[targetX]|[targetY]|[targetZ]|[targetHeading]|[currentHp]|[maxHp]|[currentMp]|[maxMp]|[?]|[?]|[x]|[y]|[z]|[heading]|[sequence]|[targetIndex]|[targetCount] - -Parsed Log Line Structure: -[timestamp] ActionEffect 15:[sourceId]:[source]:[id]:[ability]:[targetId]:[target]:[flags]:[damage]:[?]:[?]:[?]:[?]:[?]:[?]:[?]:[?]:[?]:[?]:[?]:[?]:[?]:[?]:[targetCurrentHp]:[targetMaxHp]:[targetCurrentMp]:[targetMaxMp]:[?]:[?]:[targetX]:[targetY]:[targetZ]:[targetHeading]:[currentHp]:[maxHp]:[currentMp]:[maxMp]:[?]:[?]:[x]:[y]:[z]:[heading]:[sequence]:[targetIndex]:[targetCount] -``` - -#### Regexes - -```log -Network Log Line Regex: -^(?2[12])\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?:[^|]*\|){14}(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?:[^|]*\|){2}(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?:[^|]*\|){2}(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\| - -Parsed Log Line Regex: -(?^.{14}) (?:ActionEffect|AOEActionEffect) (?(?:15|16)):(?[^:]*):(?[^:]*):(?[^:]*):(?(?:[^:]|: )*?):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*)(?::[^:]*){14}:(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*)(?::[^:]*){2}:(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*)(?::[^:]*){2}:(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*)(?:$|:) -``` - -#### Examples - -```log -Network Log Line Examples: -21|2021-07-27T12:48:22.4630000-04:00|40024FD1|Steam Bit|F67|Aetherochemical Laser|10FF0001|Tini Poutini|750003|4620000|1B|F678000|0|0|0|0|0|0|0|0|0|0|0|0|36022|36022|5200|10000|0|1000|1.846313|-12.31409|10.60608|-2.264526|16000|16000|8840|10000|0|1000|-9.079163|-14.02307|18.7095|1.416605|0000DE1F|0|5d60825d70bb46d7fcc8fc0339849e8e -21|2021-07-27T12:46:22.9530000-04:00|10FF0002|Potato Chippy|07|Attack|40024FC5|Right Foreleg|710003|3910000|0|0|0|0|0|0|0|0|0|0|0|0|0|0|378341|380640|8840|10000|0|1000|-6.37015|-7.477235|10.54466|0.02791069|26396|26396|10000|10000|0|1000|-5.443688|-1.163282|10.54466|-2.9113|0000DB6E|0|58206bdd1d0bd8d70f27f3fb2523912b -21|2021-07-27T12:46:21.5820000-04:00|10FF0001|Tini Poutini|03|Sprint|10FF0001|Tini Poutini|1E00000E|320000|0|0|0|0|0|0|0|0|0|0|0|0|0|0|19053|26706|10000|10000|0|1000|-1.210526|17.15058|10.69944|-2.88047|19053|26706|10000|10000|0|1000|-1.210526|17.15058|10.69944|-2.88047|0000DB68|0|29301d52854712315e0951abff146adc -21|2021-07-27T12:47:28.4670000-04:00|40025026|Steam Bit|F6F|Laser Absorption|40024FC4|The Manipulator|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|685814|872320|8840|10000|0|1000|-0.01531982|-13.86256|10.59466|-4.792213E-05|16000|16000|8840|10000|0|1000|0|22.5|10.64999|-3.141593|0000DCEC|0|0f3be60aec05333aae73a042edb7edb4 -21|2021-07-27T12:48:39.1260000-04:00|40024FCE|The Manipulator|13D0|Seed Of The Sky|E0000000||0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|||||||||||16000|16000|8840|10000|0|1000|8.055649|-17.03842|10.58736|-4.792213E-05|0000DE92|0|ca5594611cf4ca4e276f64f2cfba5ffa - -Parsed Log Line Examples: -[12:48:22.463] ActionEffect 15:40024FD1:Steam Bit:F67:Aetherochemical Laser:10FF0001:Tini Poutini:750003:4620000:1B:F678000:0:0:0:0:0:0:0:0:0:0:0:0:36022:36022:5200:10000:0:1000:1.846313:-12.31409:10.60608:-2.264526:16000:16000:8840:10000:0:1000:-9.079163:-14.02307:18.7095:1.416605:0000DE1F:0 -[12:46:22.953] ActionEffect 15:10FF0002:Potato Chippy:07:Attack:40024FC5:Right Foreleg:710003:3910000:0:0:0:0:0:0:0:0:0:0:0:0:0:0:378341:380640:8840:10000:0:1000:-6.37015:-7.477235:10.54466:0.02791069:26396:26396:10000:10000:0:1000:-5.443688:-1.163282:10.54466:-2.9113:0000DB6E:0 -[12:46:21.582] ActionEffect 15:10FF0001:Tini Poutini:03:Sprint:10FF0001:Tini Poutini:1E00000E:320000:0:0:0:0:0:0:0:0:0:0:0:0:0:0:19053:26706:10000:10000:0:1000:-1.210526:17.15058:10.69944:-2.88047:19053:26706:10000:10000:0:1000:-1.210526:17.15058:10.69944:-2.88047:0000DB68:0 -[12:47:28.467] ActionEffect 15:40025026:Steam Bit:F6F:Laser Absorption:40024FC4:The Manipulator:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:685814:872320:8840:10000:0:1000:-0.01531982:-13.86256:10.59466:-4.792213E-05:16000:16000:8840:10000:0:1000:0:22.5:10.64999:-3.141593:0000DCEC:0 -[12:48:39.126] ActionEffect 15:40024FCE:The Manipulator:13D0:Seed Of The Sky:E0000000::0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:::::::::::16000:16000:8840:10000:0:1000:8.055649:-17.03842:10.58736:-4.792213E-05:0000DE92:0 -``` - - - -Index | Example | Explanation ---- | --- | --- -0 | 15 | type id (in hex) -1 | 10532971 | caster object id -2 | Tini Poutini | caster name -3 | 07 | ability id -4 | Attack | ability name -5 | 40001299 | target object id -6 | Striking Dummy | target name -7 | 710003 | [flags](#ability-flags) -8 | 9420000 | [damage](#ability-damage) -9-22 | 0 | ??? (see: [special case shifts](#special-case-shifts)) -23 | 2778 | target current hp -24 | 2778 | target max hp -25 | 0 | target current mp -26 | 0 | target max mp -27 | 1000 | target current tp -28 | 1000 | target max tp -29 | -653.9767 | target x position -30 | -807.7275 | target y position -31 | 31.99997 | target z position -32 | 66480 | caster current hp -33 | 74095 | caster max hp -34 | 4560 | caster current mp -35 | 4560 | caster max mp -36 | 1000 | caster current tp -37 | 1000 | caster max tp -38 | -653.0394 | caster x position -39 | -807.9677 | caster y position -40 | 31.99997 | caster z position - -Network ability lines are a combination of raw network data -(e.g. the `710003` flags and the `9420000` damage) -and frequently sampled data from memory -(e.g. the `66480` current hp value and `-653.0394` x position). - -This means that there's a number of caveats going on to handling all the data in these lines. The raw network data is subject to change over time from ff14 servers. Also, the data from memory may be slightly stale and out of date - -#### Ability Flags - -Damage bitmasks: - -- 0x01 = dodge -- 0x03 = damage done -- 0x05 = blocked damage -- 0x06 = parried damage -- 0x33 = instant death -- 0x2000 = crit damage -- 0x4000 = direct hit damage -- 0x6000 = crit direct hit damage - -Heal bitmasks: - -- 0x000004 = heal -- 0x200004 = crit heal - -Other bitmasks appear on particular abilities, and can indicate whether bane -missed or hit recipients. However, these all appear ability-specific. - -Some of these flags also indicate whether the ability is part of a combo or not -and whether the positional was hit. -However, these values do not seem to be consistent between jobs. - -For example, the flags for successful rear trick attack are `1971.003`. -The `.` here represents 2, 4, or 6 as the trick may crit, dh, both, or neither. -The flags for a missed trick attack positional are `714.003`. - -If you care about specific ability flags, you likely have to do this research yourself. -Please send pull requests to this document so it can be shared! - -#### Ability Damage - -Damage bitmasks: - 0x10000 = hallowed or bolide, no damage (this can be blocked too) - 0x4000 = "a lot" of damage - -The damage value in an ability usage is not the literal damage, because that would be too easy. - -The formula to get from the damage value in the ability log line to the actual damage value is the following. - -First, left-extend zeroes to 4 bytes (8 chars), e.g. 2934001 => 02934001, or 1000 => 00001000. - -The first two bytes (4 chars) are the damage. - -Unless, if there is an 0x00004000 mask, then this implies "a lot" of damage. -In this case, consider the bytes as ABCD, where C is 0x40. -The total damage is calculated as D A (B-D) as three bytes together interpreted -as an integer. - -For example, `424E400F` becomes `0F 42 (4E - OF = 3F)` => `0F 42 3F` => 999999 - -#### Special Case Shifts - -It is not clear what this represents, but sometimes the flags is replaced by -one (or more) pairs of values. - -The most likely case is that if flags is `3F`, -then the flags and damage are in index 9 and 10 instead of 7 and 8, respectively. -In other words, when you see flags being a particular value, -you need to shift everything over two to find the real flags. -See the example below. -It is also to be noted that this value has slowly increased over time and was -`3C` back in 2017. - -The other shift is that plenary indulgence lists the number of stacks in the flags as `113`, `213`, or `313` respectively. -These are always followed by `4C3`. -Therefore, these should also be shifted over two to find the real flags. - -#### Ability Examples - -1) 18216 damage from Grand Cross Alpha (basic damage) -`16:40001333:Neo Exdeath:242B:Grand Cross Alpha:1048638C:Tater Tot:750003:47280000:1C:80242B:0:0:0:0:0:0:0:0:0:0:0:0:36906:41241:5160:5160:880:1000:0.009226365:-7.81128:-1.192093E-07:16043015:17702272:12000:12000:1000:1000:-0.01531982:-19.02808:0:` - -2) 82538 damage from Hyperdrive (0x4000 extra damage mask) -`15:40024FBA:Kefka:28E8:Hyperdrive:106C1DBA:Okonomi Yaki:750003:426B4001:1C:28E88000:0:0:0:0:0:0:0:0:0:0:0:0:35811:62464:4560:4560:940:1000:-0.1586061:-5.753153:0:30098906:31559062:12000:12000:1000:1000:0.3508911:0.4425049:2.384186E-07:` - -3) 22109 damage from Grand Cross Omega (:3F:0: shift) -`16:40001333:Neo Exdeath:242D:Grand Cross Omega:1048638C:Tater Tot:3F:0:750003:565D0000:1C:80242D:0:0:0:0:0:0:0:0:0:0:41241:41241:5160:5160:670:1000:-0.3251641:6.526299:1.192093E-07:7560944:17702272:12000:12000:1000:1000:0:19:2.384186E-07:` - -4) 15732 crit heal from 3 confession stack Plenary Indulgence (:?13:4C3: shift) -`16:10647D2F:Tako Yaki:1D09:Plenary Indulgence:106DD019:Okonomi Yaki:313:4C3:10004:3D74:0:0:0:0:0:0:0:0:0:0:0:0:7124:40265:14400:9192:1000:1000:-10.78815:11.94781:0:11343:40029:19652:16451:1000:1000:6.336648:7.710004:0:` - -5) instant death twister -`16:40004D5D:Twintania:26AB:Twister:10573FDC:Tini Poutini:33:0:1C:26AB8000:0:0:0:0:0:0:0:0:0:0:0:0:43985:43985:5760:5760:910:1000:-8.42179:9.49251:-1.192093E-07:57250:57250:0:0:1000:1000:-8.565645:10.20959:0:` - -6) zero damage targetless aoe (E0000000 target) -`16:103AAEE4:Potato Chippy:B1:Miasma II:E0000000::0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0::::::::::19400:40287:17649:17633:1000:1000:-0.656189:-3.799561:-5.960464E-08:` - - - -### Line 22 (0x16): NetworkAOEAbility - -This is an ability usage in game that ends up hitting multiple actors or no actors at all. - -See: [NetworkAbility](#line21) for a discussion of the difference between `NetworkAbility` and `NetworkAOEAbility`. - - - -### Line 23 (0x17): NetworkCancelAbility - -For abilities with cast bars, this is the log line that specifies that the cast was cancelled either due to movement or an interrupt and it won't go off. - - - -#### Structure - -```log -Network Log Line Structure: -23|[timestamp]|[sourceId]|[source]|[id]|[name]|[reason] - -Parsed Log Line Structure: -[timestamp] CancelAction 17:[sourceId]:[source]:[id]:[name]:[reason] -``` - -#### Regexes - -```log -Network Log Line Regex: -^(?23)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\| - -Parsed Log Line Regex: -(?^.{14}) CancelAction (?17):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*)(?:$|:) -``` - -#### Examples - -```log -Network Log Line Examples: -23|2021-07-27T13:04:38.7790000-04:00|10FF0002|Potato Chippy|408D|Veraero II|Cancelled|dbce3801c08020cb8ae7da9102034131 -23|2021-07-27T13:04:39.0930000-04:00|40000132|Garm|D10|The Dragon's Voice|Interrupted|bd936fde66bab0e8cf2874ebd75df77c -23|2021-07-27T13:04:39.1370000-04:00|4000012F||D52|Unknown_D52|Cancelled|8a15bad31745426d65cc13b8e0d50005 - -Parsed Log Line Examples: -[13:04:38.779] CancelAction 17:10FF0002:Potato Chippy:408D:Veraero II:Cancelled -[13:04:39.093] CancelAction 17:40000132:Garm:D10:The Dragon's Voice:Interrupted -[13:04:39.137] CancelAction 17:4000012F::D52:Unknown_D52:Cancelled -``` - - - - - -### Line 24 (0x18): NetworkDoT - -HoT (heal over time) and DoT (damage over time) amounts. -These are the aggregated quantities of damage for every hot or dot on that target -from a given source. - -The reason why there is such a discrepancy between ACT and fflogs about dots -is that ff14 does not return the exact tick amounts for every active dot. -Instead, if a boss has 20 dots applied to it, -then it returns the total tick amount for all of these dots. -Parsers are left to estimate what the individual dot amounts are. - -The `damageType` field is a number id that corresponds to the `AttackType` table. - - - -#### Structure - -```log -Network Log Line Structure: -24|[timestamp]|[id]|[name]|[which]|[effectId]|[damage]|[currentHp]|[maxHp]|[currentMp]|[maxMp]|[?]|[?]|[x]|[y]|[z]|[heading]|[sourceId]|[source]|[damageType]|[sourceCurrentHp]|[sourceMaxHp]|[sourceCurrentMp]|[sourceMaxMp]|[?]|[?]|[sourceX]|[sourceY]|[sourceZ]|[sourceHeading] - -Parsed Log Line Structure: -[timestamp] DoTHoT 18:[id]:[name]:[which]:[effectId]:[damage]:[currentHp]:[maxHp]:[currentMp]:[maxMp]:[?]:[?]:[x]:[y]:[z]:[heading]:[sourceId]:[source]:[damageType]:[sourceCurrentHp]:[sourceMaxHp]:[sourceCurrentMp]:[sourceMaxMp]:[?]:[?]:[sourceX]:[sourceY]:[sourceZ]:[sourceHeading] -``` - -#### Regexes - -```log -Network Log Line Regex: -^(?24)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?:[^|]*\|){2}(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?:[^|]*\|){2}(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\| - -Parsed Log Line Regex: -(?^.{14}) DoTHoT (?18):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*)(?::[^:]*){2}:(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*)(?::[^:]*){2}:(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*)(?:$|:) -``` - -#### Examples - -```log -Network Log Line Examples: -24|2021-07-27T12:47:05.5100000-04:00|10FF0002|Potato Chippy|HoT|0|3A1|21194|21194|8964|10000|0|1000|-1.815857|-5.630676|10.55192|2.929996|63d7d7e99108018a1890f367f89eae43 -24|2021-07-27T12:47:05.5990000-04:00|10FF0001|Tini Poutini|HoT|0|3BC|26396|26396|10000|10000|0|1000|-0.1373901|-8.438293|10.54466|3.122609|21b814e6f165bc1cde4a6dc23046ecb0 -24|2021-07-27T12:47:06.9340000-04:00|40024FC4|The Manipulator|DoT|0|B7F|709685|872320|8840|10000|0|1000|-0.01531982|-13.86256|10.59466|-4.792213E-05|ce3fd23ca493a37ab7663b8212044e78 - -Parsed Log Line Examples: -[12:47:05.510] DoTHoT 18:10FF0002:Potato Chippy:HoT:0:3A1:21194:21194:8964:10000:0:1000:-1.815857:-5.630676:10.55192:2.929996 -[12:47:05.599] DoTHoT 18:10FF0001:Tini Poutini:HoT:0:3BC:26396:26396:10000:10000:0:1000:-0.1373901:-8.438293:10.54466:3.122609 -[12:47:06.934] DoTHoT 18:40024FC4:The Manipulator:DoT:0:B7F:709685:872320:8840:10000:0:1000:-0.01531982:-13.86256:10.59466:-4.792213E-05 -``` - - - -Ground effect dots get listed separately. - - - -### Line 25 (0x19): NetworkDeath - -This message corresponds to an actor being defeated and killed. -This usually comes along with a game log message such as `You defeat the worm's heart.` - - - -#### Structure - -```log -Network Log Line Structure: -25|[timestamp]|[targetId]|[target]|[sourceId]|[source] - -Parsed Log Line Structure: -[timestamp] Death 19:[targetId]:[target]:[sourceId]:[source] -``` - -#### Regexes - -```log -Network Log Line Regex: -^(?25)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\| - -Parsed Log Line Regex: -(?^.{14}) Death (?19):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*)(?:$|:) -``` - -#### Examples - -```log -Network Log Line Examples: -25|2021-07-27T13:11:08.6990000-04:00|10FF0002|Potato Chippy|4000016E|Angra Mainyu|fd3760add061a5d2e23f63003cd7101d -25|2021-07-27T13:11:09.4110000-04:00|10FF0001|Tini Poutini|4000016E|Angra Mainyu|933d5e946659aa9cc493079d4f6934b3 -25|2021-07-27T13:11:11.6840000-04:00|4000016E|Angra Mainyu|10FF0002|Potato Chippy|0b79669140c20f9aa92ad5559be75022 -25|2021-07-27T13:13:10.6310000-04:00|400001D1|Queen Scylla|10FF0001|Tini Poutini|8798f2cb87c42fde4601258ae94ffb7f - -Parsed Log Line Examples: -[13:11:08.699] Death 19:10FF0002:Potato Chippy:4000016E:Angra Mainyu -[13:11:09.411] Death 19:10FF0001:Tini Poutini:4000016E:Angra Mainyu -[13:11:11.684] Death 19:4000016E:Angra Mainyu:10FF0002:Potato Chippy -[13:13:10.631] Death 19:400001D1:Queen Scylla:10FF0001:Tini Poutini -``` - - - - - -### Line 26 (0x1A): NetworkBuff - -This message is the "gains effect" message for players and mobs gaining effects whether they are good or bad. - - - -#### Structure - -```log -Network Log Line Structure: -26|[timestamp]|[effectId]|[effect]|[duration]|[sourceId]|[source]|[targetId]|[target]|[count]|[targetMaxHp]|[sourceMaxHp] - -Parsed Log Line Structure: -[timestamp] StatusAdd 1A:[effectId]:[effect]:[duration]:[sourceId]:[source]:[targetId]:[target]:[count]:[targetMaxHp]:[sourceMaxHp] -``` - -#### Regexes - -```log -Network Log Line Regex: -^(?26)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\| - -Parsed Log Line Regex: -(?^.{14}) StatusAdd (?1A):(?[^:]*):(?(?:[^:]|: )*?):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*)(?:$|:) -``` - -#### Examples - -```log -Network Log Line Examples: -26|2021-04-26T14:36:09.4340000-04:00|35|Physical Damage Up|15.00|400009D5|Dark General|400009D5|Dark General|00|48865|48865|cbcfac4df1554b8f59f343f017ebd793 -26|2021-04-26T14:23:38.7560000-04:00|13B|Whispering Dawn|21.00|4000B283|Selene|10FF0002|Potato Chippy|4000016E|00|51893|49487|c7400f0eed1fe9d29834369affc22d3b -26|2021-07-02T21:57:07.9110000-04:00|D2|Doom|9.97|40003D9F||10FF0001|Tini Poutini|00|26396|26396|86ff6bf4cfdd68491274fce1db5677e8 - -Parsed Log Line Examples: -[14:36:09.434] StatusAdd 1A:35:Physical Damage Up:15.00:400009D5:Dark General:400009D5:Dark General:00:48865:48865 -[14:23:38.756] StatusAdd 1A:13B:Whispering Dawn:21.00:4000B283:Selene:10FF0002:Potato Chippy:4000016E:00:51893:49487 -[21:57:07.911] StatusAdd 1A:D2:Doom:9.97:40003D9F::10FF0001:Tini Poutini:00:26396:26396 -``` - - - -The `source` can be blank here (and there will be two spaces like the above example if that's the case). - -This line corresponds to game log lines that look like this: -`00:12af:The worm's heart suffers the effect of Slashing Resistance Down.` -`00:112e:Tini Poutini gains the effect of The Balance.` -`00:08af:You suffer the effect of Burning Chains.` - -Although game log lines differentiate between buffs and debuffs, -this `NetworkBuff` line includes all effect types (both positive and negative). - -You cannot count on the time remaining to be precise. -In rare cases, the time will already have counted down a tiny bit. -This matters for cases such as ucob Nael phase doom debuffs. - - - -### Line 27 (0x1B): NetworkTargetIcon (Head Marker) - - - -#### Structure - -```log -Network Log Line Structure: -27|[timestamp]|[targetId]|[target]|[?]|[?]|[id] - -Parsed Log Line Structure: -[timestamp] TargetIcon 1B:[targetId]:[target]:[?]:[?]:[id] -``` - -#### Regexes - -```log -Network Log Line Regex: -^(?27)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?:[^|]*\|){2}(?[^|]*)\| - -Parsed Log Line Regex: -(?^.{14}) TargetIcon (?1B):(?[^:]*):(?[^:]*)(?::[^:]*){2}:(?[^:]*)(?:$|:) -``` - -#### Examples - -```log -Network Log Line Examples: -27|2021-04-26T14:17:31.6980000-04:00|10FF0001|Tini Poutini|0000|A9B9|0057|0000|0000|0000|4fb326d8899ffbd4cbfeb29bbc3080f8 -27|2021-05-11T13:48:45.3370000-04:00|40000950|Copied Knave|0000|0000|0117|0000|0000|0000|fa2e93fccf397a41aac73a3a38aa7410 - -Parsed Log Line Examples: -[14:17:31.698] TargetIcon 1B:10FF0001:Tini Poutini:0000:A9B9:0057:0000:0000:0000 -[13:48:45.337] TargetIcon 1B:40000950:Copied Knave:0000:0000:0117:0000:0000:0000 -``` - - - -The different headmarker IDs (e.g. `0018` or `001A` in the examples above) -are consistent across fights as far as which marker they *visually* represent. -(Correct *resolution* for the marker mechanic may not be.) -For example, `0039` is the meteor marker in Shinryu EX adds phase and the Baldesion Arsenal Ozma fight. -The fields following `id` always appears to be zero in practice, -although the fields before the `id` infrequently have non-zero values. - -Note: It's unclear when the head markers disappear. -Maybe one of these fields is a duration time? It's not clear what either of these unknown values mean. - -Also, this appears to only be true on later fights. -Turn 5 fireball and conflag headmarkers are actions from Twintania and not `NetworkTargetIcon` lines. -It seems likely this was implemented later and nobody wanted to break old content by updating it to use newer types. - -#### Head Marker IDs - -ID | Name | Sample Locations | Consistent meaning? ---- | --- | --- | --- -000[1-2, 4] | Prey Circle (orange) | o6s, The Burn boss 2 | Yes -0007 | Green Meteor | t9n/s | N/A -0008 | Ghost Meteor | t9n/s | N/A -0009 | Red Meteor | t9n/s | N/A -000A | Yellow Meteor | t9n/s | N/A -000D | Devour Flower | t6n/s, Sohm Al boss 1 | Yes -000E | Prey Circle (blue) | t6n/s, o7s | No -0010 | Teal Crystal | Ultima Weapon Ultimate |N/A -0011 | Heavenly Laser (red) | t8n/s, e1n | No -0017 | Red Pinwheel | Sohm Al boss 2, Susano N/EX, e3n/s | No -0028 | Earth Shaker | Sephirot N/EX, o4s | Yes -001C | Gravity Puddle | e1n | N/A -001E | Prey Sphere (orange) | Dun Scaith boss 3, o7n/s | No -001F | Prey Sphere (blue) | t10 | N/A -003[2-5] | Sword Markers 1-4 | Ravana N/EX, Twinning boss 1 | N/A -0037 | Red Dorito | Weeping City boss 2, Ridorana boss 1 | Yes -0039 | Purple Spread Circle (large) | Ravana N/EX, Shinryu EX | Yes -003E | Stack Marker (bordered) | o8n/s, Dun Scaith | Yes -0046 | Green Pinwheel | Dun Scaith boss 1, o5n/s | Yes -0048 | Stack Marker | Sephirot | Yes -004B | Acceleration Bomb | Weeping City boss 3, Susano N/EX, o4s | Yes -004C | Purple Fire Circle (large) | e2n/s | Yes -0054 | Thunder Tether (orange) | Titania EX | N/A -0057 | Flare | o4n/s, e2n/s | Yes -005C | Prey (dark) | Dun Scaith boss 3/4, Holminster Switch boss 3 | No -005D | Stack Marker (tank--no border) | Dun Scaith boss 4, e4s | Yes -0060 | Orange Spread Circle (small) | Hades N | Yes -0061 | Chain Tether (orange) | The Vault boss 3, Shinryu N/EX | Yes -0064 | Stack Marker (bordered) | o3s, Ridorana boss 3 | Yes -0065 | Spread Bubble | o3s, Byakko EX | N/A -006E | Levinbolt | Susano EX | N/A -0076 | Prey (dark) | Bahamut Ultimate | N/A -0078 | Orange Spread Circle (large) | Akadaemia Anyder | Yes -007B | Scatter (animated Play symbol) | Rabanastre boss 4 | N/A -007C | Turn Away (animated eye symbol) | Rabanastre boss 4 | N/A -007E | Green Crystal | Shinryu N/EX | No -0083 | Sword Meteor (Tsukuyomi) | Tsukuyomi EX | N/A -0087 | Prey Sphere (blue) | Akadaemia Anyder | N/A -008A | Orange Spread Circle (large) | Innocence N/EX, Orbonne boss 3 | Yes -008B | Purple Spread Circle (small) | Ridorana boss 1, Hades N | Yes -008E | Death From Above | o10s | N/A -008F | Death From Below | o10s | N/A -009[1-8] | Fundamental Synergy Square/Circle | o12s | N/A -00A1 | Stack Marker (bordered) | Titania N/EX | Yes -00A9 | Orange Spread Circle (small) | o11n/s, e3n/s | Yes -00AB | Green Poison Circle | Qitana Ravel | N/A -00AC | Reprobation Tether | Innocence EX | N/A -00AE | Blue Pinwheel | Sohm Al boss 2 | N/A -00B9 | Yellow Triangle (spread) | e4s | N/A -00BA | Orange Square (stack) | e4s |N/A -00BB | Blue Square (big spread) | e4s |N/A -00BD | Purple Spread Circle (giant) | TItania N/EX | Yes -00BF | Granite Gaol | e4s | N/A - - - -### Line 28 (0x1C): NetworkRaidMarker (Floor Marker) - -This message indicates a floor waymarker was added or deleted. - - - -#### Structure - -```log -Network Log Line Structure: -28|[timestamp]|[operation]|[waymark]|[id]|[name]|[x]|[y]|[z] - -Parsed Log Line Structure: -[timestamp] WaymarkMarker 1C:[operation]:[waymark]:[id]:[name]:[x]:[y]:[z] -``` - -#### Regexes - -```log -Network Log Line Regex: -^(?28)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\| - -Parsed Log Line Regex: -(?^.{14}) WaymarkMarker (?1C):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*)(?:$|:) -``` - -#### Examples - -```log -Network Log Line Examples: -28|2021-04-26T19:04:39.1920000-04:00|Delete|7|10FF0001|Tini Poutini|0|0|0|b714a8b5b34ea60f8bf9f480508dc427 -28|2021-04-26T19:27:23.5340000-04:00|Add|4|10FF0001|Tini Poutini|76.073|110.588|0|bcf81fb146fe88230333bbfd649eb240 - -Parsed Log Line Examples: -[19:04:39.192] WaymarkMarker 1C:Delete:7:10FF0001:Tini Poutini:0:0:0 -[19:27:23.534] WaymarkMarker 1C:Add:4:10FF0001:Tini Poutini:76.073:110.588:0 -``` - - - -#### Combatant Marker Codes - -| ID | Description | -| --- | ----------- | -| 0 | A | -| 1 | B | -| 2 | C | -| 3 | D | -| 4 | 1 | -| 5 | 2 | -| 6 | 3 | -| 7 | 4 | - - - -### Line 29 (0x1D): NetworkTargetMarker (Player Marker) - -This message indicates a target marker placed above or removed from a combatant's head by a player. - - - -#### Structure - -```log -Network Log Line Structure: -29|[timestamp]|[operation]|[waymark]|[id]|[name]|[targetId]|[targetName] - -Parsed Log Line Structure: -[timestamp] SignMarker 1D:[operation]:[waymark]:[id]:[name]:[targetId]:[targetName] -``` - -#### Regexes - -```log -Network Log Line Regex: -^(?29)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\| - -Parsed Log Line Regex: -(?^.{14}) SignMarker (?1D):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*)(?:$|:) -``` - -#### Examples - -```log -Network Log Line Examples: -29|2021-06-10T20:15:15.1000000-04:00|Delete|0|10FF0001|Tini Poutini|4000641D||50460af5ff3f8ec9ad03e6953d3d1ba9 -29|2021-05-25T22:54:32.5660000-04:00|Add|6|10FF0001|Tini Poutini|10FF0002|Potato Chippy|70a8c8a728d09af83e0a486e8271cc57 - -Parsed Log Line Examples: -[20:15:15.100] SignMarker 1D:Delete:0:10FF0001:Tini Poutini:4000641D: -[22:54:32.566] SignMarker 1D:Add:6:10FF0001:Tini Poutini:10FF0002:Potato Chippy -``` - - - -#### Floor Marker Codes - -| ID | Description | -| --- | ----------- | -| 0 | Hexagon 1 | -| 1 | Hexagon 2 | -| 2 | Hexagon 3 | -| 3 | Hexagon 4 | -| 4 | Hexagon 5 | -| 5 | Chain 1 | -| 6 | Chain 2 | -| 7 | Chain 3 | -| 8 | Ignore 1 | -| 9 | Ignore 2 | -| 10 | Square | -| 11 | Circle | -| 12 | Plus | -| 13 | Triangle | - - - -### Line 30 (0x1E): NetworkBuffRemove - -This is the paired "end" message to the [NetworkBuff](#line26) "begin" message. -This message corresponds to the loss of effects (either positive or negative). - - - -#### Structure - -```log -Network Log Line Structure: -30|[timestamp]|[effectId]|[effect]|[?]|[sourceId]|[source]|[targetId]|[target]|[count] - -Parsed Log Line Structure: -[timestamp] StatusRemove 1E:[effectId]:[effect]:[?]:[sourceId]:[source]:[targetId]:[target]:[count] -``` - -#### Regexes - -```log -Network Log Line Regex: -^(?30)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?:[^|]*\|)(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\| - -Parsed Log Line Regex: -(?^.{14}) StatusRemove (?1E):(?[^:]*):(?(?:[^:]|: )*?):[^:]*:(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*)(?:$|:) -``` - -#### Examples - -```log -Network Log Line Examples: -30|2021-04-26T14:38:09.6990000-04:00|13A|Inferno|0.00|400009FF|Ifrit-Egi|400009FD|Scylla|00|941742|4933|19164478551c91375dc13d0998365130 -30|2021-04-26T14:37:12.8740000-04:00|77B|Summon Order|0.00|400009E8|Eos|400009E8|Eos|01|5810|5810|b1736ae2cf65864623f9779635c361cd -30|2021-04-26T14:23:38.8440000-04:00|BD|Bio II|0.00|10FF0001|Tini Poutini|4000B262|Midgardsormr|00|10851737|51654|e34ec8d3a8db783fe34f152178775804 - -Parsed Log Line Examples: -[14:38:09.699] StatusRemove 1E:13A:Inferno:0.00:400009FF:Ifrit-Egi:400009FD:Scylla:00:941742:4933 -[14:37:12.874] StatusRemove 1E:77B:Summon Order:0.00:400009E8:Eos:400009E8:Eos:01:5810:5810 -[14:23:38.844] StatusRemove 1E:BD:Bio II:0.00:10FF0001:Tini Poutini:4000B262:Midgardsormr:00:10851737:51654 -``` - - - - - -### Line 31 (0x1F): NetworkGauge - -Info about the current player's job gauge. - - - -#### Structure - -```log -Network Log Line Structure: -31|[timestamp]|[id]|[data0]|[data1]|[data2]|[data3] - -Parsed Log Line Structure: -[timestamp] Gauge 1F:[id]:[data0]:[data1]:[data2]:[data3] -``` - -#### Regexes - -```log -Network Log Line Regex: -^(?31)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\| - -Parsed Log Line Regex: -(?^.{14}) Gauge (?1F):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*)(?:$|:) -``` - -#### Examples - -```log -Network Log Line Examples: -31|2019-11-27T23:22:40.6960000-05:00|10FF0001|FA753019|FD37|E9A55201|7F47|f17ea56b26ff020d1c0580207f6f4673 -31|2021-04-28T00:26:19.1320000-04:00|10FF0002|BF000018|10035|40006600|00|f31bf7667388ce9b11bd5dd2626c7b99 - -Parsed Log Line Examples: -[23:22:40.696] Gauge 1F:10FF0001:FA753019:FD37:E9A55201:7F47 -[00:26:19.132] Gauge 1F:10FF0002:BF000018:10035:40006600:00 -``` - - - -Each of the values after the name represents the memory for the job gauge, -interpreted as a 4 byte integer. -To get back to the original memory, zero pad out to 4 bytes, -and then reverse the bytes (because little endian). - -For example, take this line: -`1F:10532971:Tini Poutini:C8000019:FD32:D0DF8C00:7FC0` - -Zero extended: -`:C8000019:0000FD32:D0DF8C00:` - -Reversed: -`19 00 00 C8 32 FD 00 00 00 8C DF D0` - -The first byte is always the job. -The remaining bytes are a copy of the job gauge memory. - -This job is `0x19` (or black mage). -Interpreting these [values](https://github.com/goaaats/Dalamud/blob/4ad5bee0c62128315b0a247466d28f42264c3069/Dalamud/Game/ClientState/Structs/JobGauge/BLMGauge.cs) means: - -- `short TimeUntilNextPolyglot` = 0x0000 = 0 -- `short ElementTimeRemaining` = 0x32C8 = 13000ms -- `byte ElementStance` = 0xFD = -3 (three stacks of ice) -- `byte NumUmbralHearts` = 0x00 = 0 -- `byte EnoState` = 0x00 = 0 (no enochian) - -There are a number of references for job gauge memory: - - 1) [cactbot FFXIVProcess code](https://github.com/quisquous/cactbot/blob/a4d27eca3628d397cb9f5638fad97191566ed5a1/CactbotOverlay/FFXIVProcessIntl.cs#L267) - 1) [Dalamud code](https://github.com/goaaats/Dalamud/blob/4ad5bee0c62128315b0a247466d28f42264c3069/Dalamud/Game/ClientState/Structs/JobGauge/NINGauge.cs#L15) - -Unfortunately, network data about other player's gauge is not sent. -You are unable to see the abilities of other players, only your own. -(This is probably by design to cut down on the amount of network data sent.) - - - -### Line 32 (0x20): NetworkWorld - -Unused. - - - -### Line 33 (0x21): Network6D (Actor Control) - -See also: [nari director update documentation](https://xivlogs.github.io/nari/types/director.html) - -To control aspects of the user interface, the game sends packets called Actor Controls. -These are broken into 3 types: ActorControl, ActorControlSelf, and ActorControlTarget. -If ActorControl is global, then ActorControlSelf / ActorControlTarget affects individual actor(s). - -Actor control commands are identified by a category, -with parameters passed to it as a handler. -DirectorUpdate is a category of ActorControlSelf and is used to control the events inside content for an individual player: - -- BGM change -- some cutscenes -- barrier up/down -- fade in/out - - - -#### Structure - -```log -Network Log Line Structure: -33|[timestamp]|[instance]|[command]|[data0]|[data1]|[data2]|[data3] - -Parsed Log Line Structure: -[timestamp] Director 21:[instance]:[command]:[data0]:[data1]:[data2]:[data3] -``` - -#### Regexes - -```log -Network Log Line Regex: -^(?33)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\| - -Parsed Log Line Regex: -(?^.{14}) Director (?21):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*)(?:$|:) -``` - -#### Examples - -```log -Network Log Line Examples: -33|2021-04-26T17:23:28.6780000-04:00|80034E6C|4000000F|B5D|00|00|00|f777621829447c53c82c9a24aa25348f -33|2021-04-26T14:17:31.6980000-04:00|80034E5B|8000000C|16|FFFFFFFF|00|00|b543f3c5c715e93d9de2aa65b8fe83ad -33|2021-04-26T14:18:39.0120000-04:00|80034E5B|40000007|00|01|00|00|7a2b827bbc7a58ecc0c5edbdf14a2c14 - -Parsed Log Line Examples: -[17:23:28.678] Director 21:80034E6C:4000000F:B5D:00:00:00 -[14:17:31.698] Director 21:80034E5B:8000000C:16:FFFFFFFF:00:00 -[14:18:39.012] Director 21:80034E5B:40000007:00:01:00:00 -``` - - - -See [Instance Content ID](#instance-content-id) for more details about the `instance` parameter. - -Wipes on most raids and primals these days can be detected via this regex in 6.2: -`21:........:4000000F:`. -Prior to 6.2, you can use this regex: -`21:........:40000010:`. -However, this does not occur on some older fights, -such as coil turns where there is a zone seal. - -Known types: - -- Initial commence: `21:content:40000001:time:` (time is the lockout time in seconds) -- Recommence: `21:content:40000006:time:00:00:00` -- Lockout time adjust: `21:content:80000004:time:00:00:00` -- Charge boss limit break: `21:content:8000000C:value1:value2:00:00` -- Music change: `21:content:80000001:value:00:00:00` -- Fade out: `21:content:40000005:00:00:00:00` (wipe) -- Fade in: `21:content:4000000F:00:00:00:00` (always paired with barrier up) -- Barrier up: `21:content:40000011:00:00:00:00` (always comes after fade in) -- Victory: `21:zone:40000003:00:00:00:00` -- Victory (variant/criterion): `21:zone:40000002:00:00:00:00` - -Note: cactbot uses "fade in" as the wipe trigger, -but probably should switch to "fade out" after testing. - -Still unknown: - -- `21:zone:40000007:00:00:00:00` - - - -### Line 34 (0x22): NetworkNameToggle - -This log message toggles whether the nameplate for a particular entity is visible or not. -This can help you know when a mob is targetable, for example. - -The `toggle` value is either `00` (hide nameplate) or `01` (show nameplate). - - - -#### Structure - -```log -Network Log Line Structure: -34|[timestamp]|[id]|[name]|[targetId]|[targetName]|[toggle] - -Parsed Log Line Structure: -[timestamp] NameToggle 22:[id]:[name]:[targetId]:[targetName]:[toggle] -``` - -#### Regexes - -```log -Network Log Line Regex: -^(?34)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\| - -Parsed Log Line Regex: -^(?34)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\| -``` - -#### Examples - -```log -Network Log Line Examples: -34|2021-04-26T14:19:48.0400000-04:00|4001C51C|Dragon's Head|4001C51C|Dragon's Head|00|a7248aab1da528bf94faf2f4b1728fc3 -34|2021-04-26T14:22:19.1960000-04:00|4000B283|Selene|4000B283|Selene|01|734eef0f5b1b10810af8f7257d738c67 - -Parsed Log Line Examples: -[14:19:48.040] NameToggle 22:4001C51C:Dragon's Head:4001C51C:Dragon's Head:00 -[14:22:19.196] NameToggle 22:4000B283:Selene:4000B283:Selene:01 -``` - - - - - -### Line 35 (0x23): NetworkTether - -This log line is for tethers between enemies or enemies and players. -This does not appear to be used for player to player skill tethers like dragonsight or cover. -(It can be used for enemy-inflicted player to player tethers such as burning chains in Shinryu N/EX.) - -The `id` parameter is an id into the [Channeling table](https://github.com/xivapi/ffxiv-datamining/blob/master/csv/Channeling.csv). - - - -#### Structure - -```log -Network Log Line Structure: -35|[timestamp]|[sourceId]|[source]|[targetId]|[target]|[?]|[?]|[id] - -Parsed Log Line Structure: -[timestamp] Tether 23:[sourceId]:[source]:[targetId]:[target]:[?]:[?]:[id] -``` - -#### Regexes - -```log -Network Log Line Regex: -^(?35)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?:[^|]*\|){2}(?[^|]*)\| - -Parsed Log Line Regex: -(?^.{14}) Tether (?23):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*)(?::[^:]*){2}:(?[^:]*)(?:$|:) -``` - -#### Examples - -```log -Network Log Line Examples: -35|2021-04-26T17:27:07.0310000-04:00|40003202|Articulated Bit|10FF0001|Tini Poutini|0000|0000|0001|10029769|000F|0000|ad71d456437e6792f68b19dbef9507d5 -35|2021-04-27T22:36:58.1060000-04:00|10FF0001|Tini Poutini|4000943B|Bomb Boulder|0000|0000|0007|4000943B|000F|0000|a6adfcdf5dad0ef891deeade4d285eb2 -35|2021-06-13T17:41:34.2230000-04:00|10FF0001|Tini Poutini|10FF0002|Potato Chippy|0000|0000|006E|1068E3EF|000F|0000|c022382c6803d1d6c1f84681b7d8db20 - -Parsed Log Line Examples: -[17:27:07.031] Tether 23:40003202:Articulated Bit:10FF0001:Tini Poutini:0000:0000:0001:10029769:000F:0000 -[22:36:58.106] Tether 23:10FF0001:Tini Poutini:4000943B:Bomb Boulder:0000:0000:0007:4000943B:000F:0000 -[17:41:34.223] Tether 23:10FF0001:Tini Poutini:10FF0002:Potato Chippy:0000:0000:006E:1068E3EF:000F:0000 -``` - - - -The type of tether in the above three lines are `0001`, `0007`, and `006E` respectively. - -Like [NetworkTargetIcon (Head Marker)](#line27), -Type is consistent across fights and represents a particular visual style of tether. - -There are also a number of examples where tethers are generated in some other way: - -- ultima aetheroplasm orbs: NpcSpawn parentActorId set to opposite orb -- t12 redfire orb: NpcSpawn parentActorId set to target -- t13 dark aether orbs: NpcSpawn parentActorId and targetId set to target player -- Suzaku Extreme birbs: who knows -- player to player tethers (dragonsight, cover, fairy tether) - - - -### Line 36 (0x24): LimitBreak - -This log line is recorded every server tick where limit break energy is generated while in combat in a light or full party. -(Generation is not recorded while at cap.) -It starts at `0x0000` at the beginning of the instance (or encounter in the caseof a single-encounter instance,) -and counts up by `0x00DC` (220 decimal,) until the limit break is used, -or the instance's maximum limit value is reached. -This rate of increase is constant, -but other actions taken can cause extra increments to happen independent of the base increase. -(These other increments occur in the same packet as the base rate, but separately.) - -Each limit break bar is `0x2710` (10,000 decimal) units. -Thus, the maximum possible recorded value would be `0x7530`. - - - -#### Structure - -```log -Network Log Line Structure: -36|[timestamp]|[valueHex]|[bars] - -Parsed Log Line Structure: -[timestamp] LimitBreak 24:[valueHex]:[bars] -``` - -#### Regexes - -```log -Network Log Line Regex: -^(?36)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\| - -Parsed Log Line Regex: -(?^.{14}) LimitBreak (?24):(?[^:]*):(?[^:]*)(?:$|:) -``` - -#### Examples - -```log -Network Log Line Examples: -36|2021-04-26T14:20:09.6880000-04:00|6A90|3|88ce578cb8f05d74feb3a7fa155bedc5 -36|2021-04-26T14:20:19.6580000-04:00|4E20|2|a3bf154ba550e147d4fbbd4266db4eb9 -36|2021-04-26T14:20:23.9040000-04:00|0000|0|703872b50849730773f7b21897698d00 -36|2021-04-26T14:22:03.8370000-04:00|0000|1|c85f02ac4780e208357383afb6cbc232 - -Parsed Log Line Examples: -[14:20:09.688] LimitBreak 24:6A90:3 -[14:20:19.658] LimitBreak 24:4E20:2 -[14:20:23.904] LimitBreak 24:0000:0 -[14:22:03.837] LimitBreak 24:0000:1 -``` - - - - - -### Line 37 (0x25): NetworkActionSync - -This log line is a sync packet that tells the client to render an action that has previously resolved. -(This can be an animation or text in one of the game text logs.) -It seems that it is emitted at the moment an action "actually happens" in-game, -while the [NetworkAbility](#line21) or [NetworkAOEAbility](#line22) line is emitted before, -at the moment the action is "locked in". - -[As Ravahn explains it](https://discordapp.com/channels/551474815727304704/551476873717088279/733336512443187231): - -> if I cast a spell, i will get an effectresult packet (line type 21/22) showing the damage amount, -> but the target isnt expected to actually take that damage yet. -> the line 37 has a unique identifier in it which refers back to the 21/22 line and indicates that the damage should now take effect on the target. -> The FFXIV plugin doesn't use these lines currently, they are used by FFLogs. -> It would help though if I did, but ACT doesn't do multi-line parsing very easily, -> so I would need to do a lot of work-arounds." - - - -#### Structure - -```log -Network Log Line Structure: -37|[timestamp]|[id]|[name]|[sequenceId]|[currentHp]|[maxHp]|[currentMp]|[maxMp]|[currentShield]|[?]|[x]|[y]|[z]|[heading] - -Parsed Log Line Structure: -[timestamp] EffectResult 25:[id]:[name]:[sequenceId]:[currentHp]:[maxHp]:[currentMp]:[maxMp]:[currentShield]:[?]:[x]:[y]:[z]:[heading] -``` - -#### Regexes - -```log -Network Log Line Regex: -^(?37)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?:[^|]*\|)(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\| - -Parsed Log Line Regex: -(?^.{14}) EffectResult (?25):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):[^:]*:(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*)(?:$|:) -``` - -#### Examples - -```log -Network Log Line Examples: -37|2023-10-31T10:08:51.4080000-07:00|10FF0001|Tini Poutini|0000003A|117941|117941|10000|10000|0||-660.17|-842.23|29.75|-1.61|1500|0|0|01|5B|0|0|10755CA3|19aff167ea86b371 -37|2023-10-31T22:11:04.8350000-07:00|10FF0002|Potato Chippy|00005AE1|0|88095|0|10000|0||8.61|15.22|0.00|2.69|1E00|0|0|01|0400002C|0|0|E0000000|ef1e0399980c0f47 -37|2023-10-31T22:10:49.5860000-07:00|4000C5B2|Ketuduke|00005AD6|7452804||||||-0.02|-0.02|0.00|1.98|27ee18f38f377d5d - -Parsed Log Line Examples: -[10:08:51.408] EffectResult 25:10FF0001:Tini Poutini:0000003A:117941:117941:10000:10000:0::-660.17:-842.23:29.75:-1.61:1500:0:0:01:5B:0:0:10755CA3 -[22:11:04.835] EffectResult 25:10FF0002:Potato Chippy:00005AE1:0:88095:0:10000:0::8.61:15.22:0.00:2.69:1E00:0:0:01:0400002C:0:0:E0000000 -[22:10:49.586] EffectResult 25:4000C5B2:Ketuduke:00005AD6:7452804::::::-0.02:-0.02:0.00:1.98 -``` - - - - - -### Line 38 (0x26): NetworkStatusEffects - -For NPC opponents (and possibly PvP) this log line is generated alongside [NetworkDoT](#line24) lines. -For non-fairy allies, it is generated alongside [NetworkBuff](#line26), -[NetworkBuffRemove](#line30), -and [NetworkActionSync](#line37). - - - -#### Structure - -```log -Network Log Line Structure: -38|[timestamp]|[targetId]|[target]|[jobLevelData]|[hp]|[maxHp]|[mp]|[maxMp]|[currentShield]|[?]|[x]|[y]|[z]|[heading]|[data0]|[data1]|[data2]|[data3]|[data4]|[data5] - -Parsed Log Line Structure: -[timestamp] StatusList 26:[targetId]:[target]:[jobLevelData]:[hp]:[maxHp]:[mp]:[maxMp]:[currentShield]:[?]:[x]:[y]:[z]:[heading]:[data0]:[data1]:[data2]:[data3]:[data4]:[data5] -``` - -#### Regexes - -```log -Network Log Line Regex: -^(?38)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?:[^|]*\|)(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\| - -Parsed Log Line Regex: -(?^.{14}) StatusList (?26):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):[^:]*:(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*)(?:$|:) -``` - -#### Examples - -```log -Network Log Line Examples: -38|2021-04-26T14:13:16.2760000-04:00|10FF0001|Tini Poutini|46504615|75407|75407|10000|10000|24|0|-645.238|-802.7854|8|1.091302|1500|3C|0|0A016D|41F00000|E0000000|1E016C|41F00000|E0000000|c1b3e1d63f03a265ffa85f1517c1501e -38|2021-04-26T14:13:16.2760000-04:00|10FF0001||46504621|49890|49890|10000|10000|24|0|||||1500|3C|0|f62dbda5c947fa4c11b63c90c6ee4cd9 -38|2021-04-26T14:13:44.5020000-04:00|10FF0002|Potato Chippy|46504621|52418|52418|10000|10000|32|0|99.93127|113.8475|-1.862645E-09|3.141593|200F|20|0|0A016D|41F00000|E0000000|1E016C|41F00000|E0000000|0345|41E8D4FC|10FF0001|0347|80000000|10FF0002|d57fd29c6c4856c091557968667da39d - -Parsed Log Line Examples: -[14:13:16.276] StatusList 26:10FF0001:Tini Poutini:46504615:75407:75407:10000:10000:24:0:-645.238:-802.7854:8:1.091302:1500:3C:0:0A016D:41F00000:E0000000:1E016C:41F00000:E0000000 -[14:13:16.276] StatusList 26:10FF0001::46504621:49890:49890:10000:10000:24:0:::::1500:3C:0 -[14:13:44.502] StatusList 26:10FF0002:Potato Chippy:46504621:52418:52418:10000:10000:32:0:99.93127:113.8475:-1.862645E-09:3.141593:200F:20:0:0A016D:41F00000:E0000000:1E016C:41F00000:E0000000:0345:41E8D4FC:10FF0001:0347:80000000:10FF0002 -``` - - - -It seems likely that this line was added in order to extend functionality -for the [NetworkBuff](#line26), -[NetworkBuffRemove](#line30), -and [NetworkActionSync](#line37) -log lines without breaking previous content or plugins. - - - -### Line 39 (0x27): NetworkUpdateHP - -It's not completely clear what triggers this log line, -but it contains basic information comparable to [NetworkActionSync](#line37) and [NetworkStatusEffects](#line38). -It applies to allies and fairies/pets. - -This log line tends to fire roughly every 3 seconds in some cases. - - - -#### Structure - -```log -Network Log Line Structure: -39|[timestamp]|[id]|[name]|[currentHp]|[maxHp]|[currentMp]|[maxMp]|[?]|[?]|[x]|[y]|[z]|[heading] - -Parsed Log Line Structure: -[timestamp] UpdateHp 27:[id]:[name]:[currentHp]:[maxHp]:[currentMp]:[maxMp]:[?]:[?]:[x]:[y]:[z]:[heading] -``` - -#### Regexes - -```log -Network Log Line Regex: -^(?39)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?:[^|]*\|){2}(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\| - -Parsed Log Line Regex: -(?^.{14}) UpdateHp (?27):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*)(?::[^:]*){2}:(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*)(?:$|:) -``` - -#### Examples - -```log -Network Log Line Examples: -39|2021-04-26T14:12:38.5160000-04:00|10FF0001|Tini Poutini|178669|191948|10000|10000|0|0|-648.3234|-804.5252|8.570148|1.010669|7ebe348673aa2a11e4036274becabc81 -39|2021-04-26T14:13:21.6370000-04:00|10592642|Senor Esteban|54792|54792|10000|10000|0|0|100.268|114.22|-1.837917E-09|3.141593|883da0db11a9c950eefdbcbc50e86eca -39|2021-04-26T14:13:21.6370000-04:00|106F5D49|O'ndanya Voupin|79075|79075|10000|10000|0|0|99.93127|114.2443|-1.862645E-09|-3.141593|8ed73ee57c4ab7159628584e2f4d5243 - -Parsed Log Line Examples: -[14:12:38.516] UpdateHp 27:10FF0001:Tini Poutini:178669:191948:10000:10000:0:0:-648.3234:-804.5252:8.570148:1.010669 -[14:13:21.637] UpdateHp 27:10592642:Senor Esteban:54792:54792:10000:10000:0:0:100.268:114.22:-1.837917E-09:3.141593 -[14:13:21.637] UpdateHp 27:106F5D49:O'ndanya Voupin:79075:79075:10000:10000:0:0:99.93127:114.2443:-1.862645E-09:-3.141593 -``` - - - - - -### Line 40 (0x28): Map - -This line is sent when the map changes. -It will be sent when changing zones, -but is also sent when changing subzones where the map changes -(e.g. crossing a zone line while in a dungeon). - -`regionName` and `placeName` are always present, -but `placeNameSub` is optional. - - - -#### Structure - -```log -Network Log Line Structure: -40|[timestamp]|[id]|[regionName]|[placeName]|[placeNameSub] - -Parsed Log Line Structure: -[timestamp] ChangeMap 28:[id]:[regionName]:[placeName]:[placeNameSub] -``` - -#### Regexes - -```log -Network Log Line Regex: -^(?40)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\| - -Parsed Log Line Regex: -(?^.{14}) ChangeMap (?28):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*)(?:$|:) -``` - -#### Examples - -```log -Network Log Line Examples: -40|2021-07-30T19:43:08.6270000-07:00|578|Norvrandt|The Copied Factory|Upper Stratum|ee5b5fc06ab4610ef6b4f030fc95c90c -40|2021-07-30T19:46:49.3830000-07:00|575|Norvrandt|Excavation Tunnels||41e6dae1ab1a3fe18ce3754d7c45a5d0 -40|2021-07-30T19:49:19.8180000-07:00|192|La Noscea|Mist|Mist Subdivision|f3506f063945500b5e7df2172e2ca4d3 - -Parsed Log Line Examples: -[19:43:08.627] ChangeMap 28:578:Norvrandt:The Copied Factory:Upper Stratum -[19:46:49.383] ChangeMap 28:575:Norvrandt:Excavation Tunnels: -[19:49:19.818] ChangeMap 28:192:La Noscea:Mist:Mist Subdivision -``` - - - - - -### Line 41 (0x29): SystemLogMessage - -This log line is sent when there are system log messages. -As game chat log lines are read from memory in the FFXIV ACT plugin, -[Line 41](#line41) can be sent both before or after the corresponding [Line 00](#line00). -That said, they are usually sequential in the network log, -and so there is no timing advantage to using one over the other, -but the system log message will have a correct timestamp. - -```log -[10:38:40.066] SystemLogMessage 29:00:901:619A9200:00:3C -[10:38:39.000] ChatLog 00:0839::Objective accomplished. If applicable, please make sure to submit items within the time limit. - -[10:50:13.565] SystemLogMessage 29:8004001E:7DD:FF5FDA02:E1B:00 -[10:50:13.000] ChatLog 00:0839::The Theater of One is sealed off! - -[10:55:06.000] ChatLog 00:0839::The teleportation crystal glimmers. -[10:55:06.707] SystemLogMessage 29:8004001E:B3A:00:00:E0000000 -``` - - - -#### Structure - -```log -Network Log Line Structure: -41|[timestamp]|[instance]|[id]|[param0]|[param1]|[param2] - -Parsed Log Line Structure: -[timestamp] SystemLogMessage 29:[instance]:[id]:[param0]:[param1]:[param2] -``` - -#### Regexes - -```log -Network Log Line Regex: -^(?41)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\| - -Parsed Log Line Regex: -(?^.{14}) SystemLogMessage (?29):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*)(?:$|:) -``` - -#### Examples - -```log -Network Log Line Examples: -41|2021-11-21T10:38:40.0660000-08:00|00|901|619A9200|00|3C|c6fcd8a8b198a5da28b9cfe6a3f544f4 -41|2021-11-21T10:50:13.5650000-08:00|8004001E|7DD|FF5FDA02|E1B|00|4eeb89399fce54820eb19e06b4d6d95a -41|2021-11-21T10:55:06.7070000-08:00|8004001E|B3A|00|00|E0000000|1f600f85ec8d36d2b04d233e19f93d39 - -Parsed Log Line Examples: -[10:38:40.066] SystemLogMessage 29:00:901:619A9200:00:3C -[10:50:13.565] SystemLogMessage 29:8004001E:7DD:FF5FDA02:E1B:00 -[10:55:06.707] SystemLogMessage 29:8004001E:B3A:00:00:E0000000 -``` - - - -The `id` parameter is an id into the [LogMessage table](https://github.com/xivapi/ffxiv-datamining/blob/master/csv/LogMessage.csv). - -id (hex) | Link | Shortened Message ---- | --- | --- -0x2EE | [link](https://xivapi.com/LogMessage/750?pretty=true) | You obtain (an item) -0x7DC | [link](https://xivapi.com/LogMessage/2012?pretty=true) | will be sealed off in X seconds -0x7DD | [link](https://xivapi.com/LogMessage/2013?pretty=true) | is sealed off -0x7DE | [link](https://xivapi.com/LogMessage/2014?pretty=true) | is no longer sealed - -The log message itself determines the other parameters. -It seems that `IntegerParameter(1)` in the log message corresponds to `param1` -and `IntegerParameter(2)` corresponds to `param2`. -It is not clear what `param0` does or how other `Parameter` functions work. - -Here are two network log lines: - -```log -41|2022-01-11T16:28:50.6340000-08:00|80030054|7DC|02|1008|0F|1a1b91bd4bf5d5e1^M -00|2022-01-11T16:28:50.0000000-08:00|0839||The shell mound will be sealed off in 15 seconds!|3a0befeef04e203b^M -``` - -See [Instance Content ID](#instance-content-id) for more details about the `instance` parameter. -`00830054` represents instanced content for [The Dead Ends](https://xivapi.com/InstanceContent/84?pretty=true). - -`7DC` is the `id`, which [corresponds](https://xivapi.com/LogMessage/2012?pretty=true) to: -`")\/> will be sealed off in IntegerParameter(2)<\/Value> secondseconds<\/If>!"` - -Use the log message itself to determine what `param1` and `param2` mean, if anything. - -In this case, -`param1` is `1008`, which from the log message you can determine is a PlaceName id. -Looking this up in the [PlaceName](https://xivapi.com/PlaceName/4104?pretty=true) table gets "Shell Mound". - -`param2` is `0x0F`, which from the log message is used for the seconds in the message, i.e. 15 in decimal. - -Here's one other example: - -```log -41|2022-02-18T22:03:00.5130000-08:00|1B01EA|2EE|C2BD6401|758A|45D530|9efb90e26e3b41c3 -00|2022-02-18T22:03:00.0000000-08:00|0BBE||You obtain a little leafman.|51d9427a6354d3af -``` - -`2EE` is the `id`, which [corresponds](https://xivapi.com/LogMessage/750?pretty=true) to: -`"youObjectParameter(2))/> obtainobtains ."` - -Here, `param1` is `758A`, which [corresponds](https://xivapi.com/Item/30090?pretty=true) to "Little Leafman" in the `Item` table. -It is unclear how `ObjectParameter` and `PlayerParameter` work here. - -Future work: - -- What is `param0`? Is it just skipped? -- How do `PlayerParameter` and `ObjectParameter` work in the `LogMessage` table? -- Some log messages don't show as 41 lines, e.g. "You have arrived at a vista" or "Engage!". - - - -### Line 42 (0x2A): StatusList3 - -This line seems to be sent only for the current player and lists some status effects. -More information is needed. - - - -#### Structure - -```log -Network Log Line Structure: -42|[timestamp]|[id]|[name] - -Parsed Log Line Structure: -[timestamp] StatusList3 2A:[id]:[name] -``` - -#### Regexes - -```log -Network Log Line Regex: -^(?42)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\| - -Parsed Log Line Regex: -(?^.{14}) StatusList3 (?2A):(?[^:]*):(?[^:]*)(?:$|:) -``` - -#### Examples - -```log -Network Log Line Examples: -42|2022-06-06T21:57:14.8920000+08:00|10FF0001|Tini Poutini|0A0168|41F00000|E0000000|14016A|41F00000|E0000000|29310030|44835452|10FF0001|4361fffcb50708dd -42|2022-06-06T10:04:52.3370000-07:00|10FF0002|Potato Chippy|037F|0|E0000000|ee5bd3e5dbb46f59 -42|2022-06-06T10:09:06.2140000-07:00|10FF0002|Potato Chippy|0|0|0|f988f962f9c768e3 - -Parsed Log Line Examples: -[21:57:14.892] StatusList3 2A:10FF0001:Tini Poutini:0A0168:41F00000:E0000000:14016A:41F00000:E0000000:29310030:44835452:10FF0001 -[10:04:52.337] StatusList3 2A:10FF0002:Potato Chippy:037F:0:E0000000 -[10:09:06.214] StatusList3 2A:10FF0002:Potato Chippy:0:0:0 -``` - - - - - -### Line 251 (0xFB): Debug - -As network log lines, they often have information like this: -`251|2019-05-21T19:11:02.0268703-07:00|ProcessTCPInfo: New connection detected for Process [2644]: 192.168.1.70:49413=>204.2.229.85:55021|909171c500bed915f8d79fc04d3589fa` - -Parsed log lines are blank for this type. - - - -### Line 252 (0xFC): PacketDump - -If the setting to dump all network data to logfiles is turned on, -then ACT will emit all network data into the network log itself. -This can be used to import a network log file into ffxivmon and inspect packet data. - -Parsed log lines are blank for this type. - -![dump network data screenshot](images/logguide_dumpnetworkdata.png) - - - -### Line 253 (0xFD): Version - -As network log lines, they usually look like this: -`253|2019-05-21T19:11:02.0268703-07:00|FFXIV PLUGIN VERSION: 1.7.2.12, CLIENT MODE: FFXIV_64|845e2929259656c833460402c9263d5c` - -Parsed log lines are blank for this type. - - - -### Line 254 (0xFE): Error - -These are lines emitted directly by the ffxiv plugin when something goes wrong. - -## OverlayPlugin Log Lines - -If you are using OverlayPlugin, -it will emit extra log lines that are not part of the ffxiv plugin. -The ids of these lines start at 256 and go up. -Any id between 0-255 is reserved for the ffxiv plugin. - - - -### Line 256 (0x100): LineRegistration - -This line is emitted into logs when any custom logs are registered with OverlayPlugin. -This is so that it is obvious which log lines and versions to expect for a given log file. - - - -#### Structure - -```log -Network Log Line Structure: -256|[timestamp]|[id]|[source]|[version] - -Parsed Log Line Structure: -[timestamp] 256 100:[id]:[source]:[version] -``` - -#### Regexes - -```log -Network Log Line Regex: -^(?256)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\| - -Parsed Log Line Regex: -(?^.{14}) 256 (?100):(?[^:]*):(?[^:]*):(?[^:]*)(?:$|:) -``` - -#### Examples - -```log -Network Log Line Examples: -256|2022-10-02T10:15:31.5635165-07:00|257|OverlayPlugin|MapEffect|1|594b867ee2199369 -256|2022-10-02T10:15:31.5645159-07:00|258|OverlayPlugin|FateDirector|1|102a238b2495bfd0 -256|2022-10-02T10:15:31.5655143-07:00|259|OverlayPlugin|CEDirector|1|35546b48906c41b2 - -Parsed Log Line Examples: -[10:15:31.563] 256 100:257:OverlayPlugin:MapEffect:1 -[10:15:31.564] 256 100:258:OverlayPlugin:FateDirector:1 -[10:15:31.565] 256 100:259:OverlayPlugin:CEDirector:1 -``` - - - - - -### Line 257 (0x101): MapEffect - -This message is sent to cause a specific visual effect to render -in the game client during instanced content. -MapEffect lines are not tied to any particular actor or action -but may provide visual-based information about how an upcoming mechanic will resolve. - -For example, -after Aetheric Polyominoid or Polyominoid Sigma casts in P6S, -MapEffect messages are sent to cause the game client to render '+' and 'x' effects on specific map tiles, -indicating to the player which tiles will later be rendered unsafe by Polyominous Dark IV. - -This can also include things like: - -- meteor graphics / bridges breaking in Amaurot -- the eye location in DSR -- P8S torch effects - - - -#### Structure - -```log -Network Log Line Structure: -257|[timestamp]|[instance]|[flags]|[location]|[data0]|[data1] - -Parsed Log Line Structure: -[timestamp] 257 101:[instance]:[flags]:[location]:[data0]:[data1] -``` - -#### Regexes - -```log -Network Log Line Regex: -^(?257)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\| - -Parsed Log Line Regex: -(?^.{14}) 257 (?101):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*)(?:$|:) -``` - -#### Examples - -```log -Network Log Line Examples: -257|2022-09-27T18:03:45.2834013-07:00|800375A9|00020001|09|F3|0000|de00c57494e85e79 -257|2022-09-27T18:06:07.7744035-07:00|800375A9|00400020|01|00|0000|72933fe583158786 -257|2022-09-29T20:07:48.7330170-07:00|800375A5|00020001|05|00|0000|28c0449a8d0efa7d - -Parsed Log Line Examples: -[18:03:45.283] 257 101:800375A9:00020001:09:F3:0000 -[18:06:07.774] 257 101:800375A9:00400020:01:00:0000 -[20:07:48.733] 257 101:800375A5:00020001:05:00:0000 -``` - - - -The `instance` parameter is identical to `instance` in an [actor control line](#line33). -See above for more information. - -The `flags` parameter identifies the visual effect that will be rendered in the game. -For example, -in P6S, `00020001` flags equates to a `+`-shaped tile and -`00400020` flags equates to an `x`-shaped tile. -Flags do not appear to be unique across multiple instances: -as the above examples illustrate -the flags `00020001` are used in both P5S and P6S to render completely different visual effects. - -That said, -it does appear from initial analysis that when a map effect is rendered, -a second MapEffect line with `00080004` flags is sent at the conclusion of the effect, -which may correspond to removal of the effect. -This appears to be consistent behavior across several fights so far, -but more information is needed. - -The `location` parameter indicates the location in the current instance where the effect will be rendered. -Locations are not consistent across instances and appear to be unique to each instance. -E.g., a location of '05' in P6S corresponds to one of the 16 tiles on the map floor, -whereas the '05' location in P5S appears to correspond to different map coordinates. - - - -### Line 258 (0x102): FateDirector - -This line indicates changes in fates on the map. -This includes when fates are added, -removed, -or their progress has changed. - - - -#### Structure - -```log -Network Log Line Structure: -258|[timestamp]|[category]|[?]|[fateId]|[progress] - -Parsed Log Line Structure: -[timestamp] 258 102:[category]:[?]:[fateId]:[progress] -``` - -#### Regexes - -```log -Network Log Line Regex: -^(?258)\|(?[^|]*)\|(?[^|]*)\|(?:[^|]*\|)(?[^|]*)\|(?[^|]*)\| - -Parsed Log Line Regex: -(?^.{14}) 258 (?102):(?[^:]*):[^:]*:(?[^:]*):(?[^:]*)(?:$|:) -``` - -#### Examples - -```log -Network Log Line Examples: -258|2022-09-19T17:25:59.5582137-07:00|Add|E601|000000DE|00000000|00000000|00000000|00000000|00000000|00000000|c7fd9f9aa7f56d4d -258|2022-08-13T19:46:54.6179420-04:00|Update|203A|00000287|00000000|00000000|00000000|00000000|00000000|6E756F63|bd60bac0189b571e -258|2022-09-24T12:51:47.5867309-07:00|Remove|0000|000000E2|00000000|00000000|00000000|00000000|00000000|00007FF9|043b821dbfe608c5 - -Parsed Log Line Examples: -[17:25:59.558] 258 102:Add:E601:000000DE:00000000:00000000:00000000:00000000:00000000:00000000 -[19:46:54.617] 258 102:Update:203A:00000287:00000000:00000000:00000000:00000000:00000000:6E756F63 -[12:51:47.586] 258 102:Remove:0000:000000E2:00000000:00000000:00000000:00000000:00000000:00007FF9 -``` - - - - - -### Line 259 (0x103): CEDirector - -This line is like [FateDirector](#line258), -but is for Critical Engagements in Bozja. - - - -#### Structure - -```log -Network Log Line Structure: -259|[timestamp]|[popTime]|[timeRemaining]|[?]|[ceKey]|[numPlayers]|[status]|[?]|[progress] - -Parsed Log Line Structure: -[timestamp] 259 103:[popTime]:[timeRemaining]:[?]:[ceKey]:[numPlayers]:[status]:[?]:[progress] -``` - -#### Regexes - -```log -Network Log Line Regex: -^(?259)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?:[^|]*\|)(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?:[^|]*\|)(?[^|]*)\| - -Parsed Log Line Regex: -(?^.{14}) 259 (?103):(?[^:]*):(?[^:]*):[^:]*:(?[^:]*):(?[^:]*):(?[^:]*):[^:]*:(?[^:]*)(?:$|:) -``` - -#### Examples - -```log -Network Log Line Examples: -259|2022-09-19T18:09:35.7012951-07:00|632912D5|0000|0000|07|01|02|00|00|7F|00|00|4965d513cc7a6dd3 -259|2022-09-19T18:09:39.9541413-07:00|63291786|04B0|0000|07|01|03|00|00|00|00|00|6c18aa16678911ca -259|2022-09-19T18:09:46.7556709-07:00|63291786|04AA|0000|07|01|03|00|02|7F|00|00|5bf224d56535513a - -Parsed Log Line Examples: -[18:09:35.701] 259 103:632912D5:0000:0000:07:01:02:00:00:7F:00:00 -[18:09:39.954] 259 103:63291786:04B0:0000:07:01:03:00:00:00:00:00 -[18:09:46.755] 259 103:63291786:04AA:0000:07:01:03:00:02:7F:00:00 -``` - - - - - -### Line 260 (0x104): InCombat - -This log line tracks in combat state. -`inGameCombat` is whether FFXIV itself considers you in combat. -`inACTCombat` is whether ACT considers you in combat, -which may include other people around you and not yourself -and also takes your ACT encounter settings into consideration. - -`isACTChanged` and `isGameChanged` represent whether the state has changed -since the last log line. -This allows triggers to be written for when a particular one changes, -as lines are emitted if either changes. -These are both true the first time the log is written. - -OverlayPlugin uses `inACTCombat` to re-split your encounters during import -based on how they were split when they were originally recorded. - - - -#### Structure - -```log -Network Log Line Structure: -260|[timestamp]|[inACTCombat]|[inGameCombat]|[isACTChanged]|[isGameChanged] - -Parsed Log Line Structure: -[timestamp] 260 104:[inACTCombat]:[inGameCombat]:[isACTChanged]:[isGameChanged] -``` - -#### Regexes - -```log -Network Log Line Regex: -^(?260)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\| - -Parsed Log Line Regex: -(?^.{14}) 260 (?104):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*)(?:$|:) -``` - -#### Examples - -```log -Network Log Line Examples: -260|2023-01-03T10:17:15.8240000-08:00|0|0|1|1|7da9e0cfed11abfe -260|2023-01-03T17:51:42.9680000-08:00|1|0|0|1|ae12d0898d923251 -260|2023-01-03T17:54:50.0680000-08:00|1|1|1|0|3ba06c97a4cbbf42 - -Parsed Log Line Examples: -[10:17:15.824] 260 104:0:0:1:1 -[17:51:42.968] 260 104:1:0:0:1 -[17:54:50.068] 260 104:1:1:1:0 -``` - - - - - -### Line 261 (0x105): CombatantMemory - -OverlayPlugin has a `getCombatants` function which returns the state of combatants in the game. -However, it is hard to know when it is safe to call this function -and know that combatants have moved into position (or changed model or changed heading). -These lines give more granular information when combatants change their status. -Please note that this is still polling memory (so timing may be racy) -and there are some heuristics to not emit too many lines (so data may be imprecise). - -For more information, -see the [class definition](https://github.com/OverlayPlugin/OverlayPlugin/blob/main/OverlayPlugin.Core/MemoryProcessors/Combatant/LineCombatant.cs#L27) in OverlayPlugin. - -There are three types of this line: - -- Add: emits all initial fields for this combatant that have non-default values -- Change: emits all fields that have changed -- Remove: no fields, combatant is being removed - -Each line may contain an arbitrary number of field name / value pairs. - - - -#### Structure - -```log -Network Log Line Structure: -261|[timestamp]|[change]|[id] - -Parsed Log Line Structure: -[timestamp] 261 105:[change]:[id] -``` - -#### Regexes - -```log -Network Log Line Regex: -^(?261)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?:AggressionStatus\|(?[^|]*)\|)?(?:BNpcID\|(?[^|]*)\|)?(?:BNpcNameID\|(?[^|]*)\|)?(?:CastBuffID\|(?[^|]*)\|)?(?:CastDurationCurrent\|(?[^|]*)\|)?(?:CastDurationMax\|(?[^|]*)\|)?(?:CastTargetID\|(?[^|]*)\|)?(?:CurrentCP\|(?[^|]*)\|)?(?:CurrentGP\|(?[^|]*)\|)?(?:CurrentHP\|(?[^|]*)\|)?(?:CurrentMP\|(?[^|]*)\|)?(?:CurrentWorldID\|(?[^|]*)\|)?(?:Distance\|(?[^|]*)\|)?(?:EffectiveDistance\|(?[^|]*)\|)?(?:Heading\|(?[^|]*)\|)?(?:ID\|(?[^|]*)\|)?(?:IsCasting1\|(?[^|]*)\|)?(?:IsCasting2\|(?[^|]*)\|)?(?:IsTargetable\|(?[^|]*)\|)?(?:Job\|(?[^|]*)\|)?(?:Level\|(?[^|]*)\|)?(?:MaxCP\|(?[^|]*)\|)?(?:MaxGP\|(?[^|]*)\|)?(?:MaxHP\|(?[^|]*)\|)?(?:MaxMP\|(?[^|]*)\|)?(?:ModelStatus\|(?[^|]*)\|)?(?:MonsterType\|(?[^|]*)\|)?(?:Name\|(?[^|]*)\|)?(?:NPCTargetID\|(?[^|]*)\|)?(?:OwnerID\|(?[^|]*)\|)?(?:PartyType\|(?[^|]*)\|)?(?:PCTargetID\|(?[^|]*)\|)?(?:PosX\|(?[^|]*)\|)?(?:PosY\|(?[^|]*)\|)?(?:PosZ\|(?[^|]*)\|)?(?:Radius\|(?[^|]*)\|)?(?:Status\|(?[^|]*)\|)?(?:TargetID\|(?[^|]*)\|)?(?:TransformationId\|(?[^|]*)\|)?(?:Type\|(?[^|]*)\|)?(?:WeaponId\|(?[^|]*)\|)?(?:WorldID\|(?[^|]*)\|)?(?:WorldName\|(?[^|]*)\|)? - -Parsed Log Line Regex: -(?^.{14}) 261 (?105):(?[^:]*):(?[^:]*):(?:AggressionStatus(?:$|:)(?[^:]*)(?:$|:))?(?:BNpcID(?:$|:)(?[^:]*)(?:$|:))?(?:BNpcNameID(?:$|:)(?[^:]*)(?:$|:))?(?:CastBuffID(?:$|:)(?[^:]*)(?:$|:))?(?:CastDurationCurrent(?:$|:)(?[^:]*)(?:$|:))?(?:CastDurationMax(?:$|:)(?[^:]*)(?:$|:))?(?:CastTargetID(?:$|:)(?[^:]*)(?:$|:))?(?:CurrentCP(?:$|:)(?[^:]*)(?:$|:))?(?:CurrentGP(?:$|:)(?[^:]*)(?:$|:))?(?:CurrentHP(?:$|:)(?[^:]*)(?:$|:))?(?:CurrentMP(?:$|:)(?[^:]*)(?:$|:))?(?:CurrentWorldID(?:$|:)(?[^:]*)(?:$|:))?(?:Distance(?:$|:)(?[^:]*)(?:$|:))?(?:EffectiveDistance(?:$|:)(?[^:]*)(?:$|:))?(?:Heading(?:$|:)(?[^:]*)(?:$|:))?(?:ID(?:$|:)(?[^:]*)(?:$|:))?(?:IsCasting1(?:$|:)(?[^:]*)(?:$|:))?(?:IsCasting2(?:$|:)(?[^:]*)(?:$|:))?(?:IsTargetable(?:$|:)(?[^:]*)(?:$|:))?(?:Job(?:$|:)(?[^:]*)(?:$|:))?(?:Level(?:$|:)(?[^:]*)(?:$|:))?(?:MaxCP(?:$|:)(?[^:]*)(?:$|:))?(?:MaxGP(?:$|:)(?[^:]*)(?:$|:))?(?:MaxHP(?:$|:)(?[^:]*)(?:$|:))?(?:MaxMP(?:$|:)(?[^:]*)(?:$|:))?(?:ModelStatus(?:$|:)(?[^:]*)(?:$|:))?(?:MonsterType(?:$|:)(?[^:]*)(?:$|:))?(?:Name(?:$|:)(?[^:]*)(?:$|:))?(?:NPCTargetID(?:$|:)(?[^:]*)(?:$|:))?(?:OwnerID(?:$|:)(?[^:]*)(?:$|:))?(?:PartyType(?:$|:)(?[^:]*)(?:$|:))?(?:PCTargetID(?:$|:)(?[^:]*)(?:$|:))?(?:PosX(?:$|:)(?[^:]*)(?:$|:))?(?:PosY(?:$|:)(?[^:]*)(?:$|:))?(?:PosZ(?:$|:)(?[^:]*)(?:$|:))?(?:Radius(?:$|:)(?[^:]*)(?:$|:))?(?:Status(?:$|:)(?[^:]*)(?:$|:))?(?:TargetID(?:$|:)(?[^:]*)(?:$|:))?(?:TransformationId(?:$|:)(?[^:]*)(?:$|:))?(?:Type(?:$|:)(?[^:]*)(?:$|:))?(?:WeaponId(?:$|:)(?[^:]*)(?:$|:))?(?:WorldID(?:$|:)(?[^:]*)(?:$|:))?(?:WorldName(?:$|:)(?[^:]*)(?:$|:))?(?:$|:) -``` - -#### Examples - -```log -Network Log Line Examples: -261|2023-04-20T19:04:39.3810000-07:00|Add|400139C5|Type|2|TargetID|10FFFFFF|Name|Omega|MaxHP|8557964|PosX|100|PosY|90|PosZ|-5.456968E-12|Heading|-4.792213E-05|Radius|12.006|BNpcID|3D5C|CurrentMP|10000|MaxMP|10000|Level|90|BNpcNameID|1E0F|WorldID|65535|CurrentWorldID|65535|NPCTargetID|1084E23D|CastDurationMax|-3.689349E+19|e173dbd66eb7c1fe -261|2023-04-20T19:04:41.9200000-07:00|Change|400139C5|PosX|100.1179|PosY|95.16841|PosZ|-5.456968E-12|Heading|0.08906358|eac28822c9abcde9 -261|2023-04-20T19:06:46.2900000-07:00|Remove|400139C5|09a3165588ea6b13 - -Parsed Log Line Examples: -[19:04:39.381] 261 105:Add:400139C5:Type:2:TargetID:10FFFFFF:Name:Omega:MaxHP:8557964:PosX:100:PosY:90:PosZ:-5.456968E-12:Heading:-4.792213E-05:Radius:12.006:BNpcID:3D5C:CurrentMP:10000:MaxMP:10000:Level:90:BNpcNameID:1E0F:WorldID:65535:CurrentWorldID:65535:NPCTargetID:1084E23D:CastDurationMax:-3.689349E+19 -[19:04:41.920] 261 105:Change:400139C5:PosX:100.1179:PosY:95.16841:PosZ:-5.456968E-12:Heading:0.08906358 -[19:06:46.290] 261 105:Remove:400139C5 -``` - - - - - -### Line 262 (0x106): RSVData - -Square Enix obfuscates (among other things) ability names, status names, and messages -in current savage and ultimate content in the game data itself. -This is to prevent data mining. -However, as these ability names need to be displayed by the game itself -these ability names are sent as network data upon zoning in. - -These lines display the currently obfuscated abilities -for the current zone for the current game locale. - -Note that `:` characters are not escaped even if the game abilities have a `:` in them. -It is recommended to use the network log line format to parse these lines for that reason. - -These values may also contain special unicode character sequences -that the game client will replace with placeholder values, -such as the current player's name. -CR and LF characters are also escaped and will need to be unescaped. -See the logs below for an example. - - - -#### Structure - -```log -Network Log Line Structure: -262|[timestamp]|[locale]|[?]|[key]|[value] - -Parsed Log Line Structure: -[timestamp] 262 106:[locale]:[?]:[key]:[value] -``` - -#### Regexes - -```log -Network Log Line Regex: -^(?262)\|(?[^|]*)\|(?[^|]*)\|(?:[^|]*\|)(?[^|]*)\|(?[^|]*)\| - -Parsed Log Line Regex: -(?^.{14}) 262 (?106):(?[^:]*):[^:]*:(?[^:]*):(?[^:]*)(?:$|:) -``` - -#### Examples - -```log -Network Log Line Examples: -262|2023-04-21T23:24:05.8320000-04:00|en|0000001C|_rsv_32789_-1_1_0_1_SE2DC5B04_EE2DC5B04|Run: ****mi* (Omega Version)|34159b6f2093e889 -262|2023-04-21T23:24:05.9210000-04:00|en|00000031|_rsv_3448_-1_1_1_0_S74CFC3B0_E74CFC3B0|Burning with dynamis inspired by Omega's passion.|ce9d03bb211d894f -262|2023-04-21T23:24:06.0630000-04:00|en|00000051|_rsv_35827_-1_1_0_0_S13095D61_E13095D61|Further testing is required.�����,\r���)������ ��, assist me with this evaluation.|38151741aad7fe51 - -Parsed Log Line Examples: -[23:24:05.832] 262 106:en:0000001C:_rsv_32789_-1_1_0_1_SE2DC5B04_EE2DC5B04:Run: ****mi* (Omega Version) -[23:24:05.921] 262 106:en:00000031:_rsv_3448_-1_1_1_0_S74CFC3B0_E74CFC3B0:Burning with dynamis inspired by Omega's passion. -[23:24:06.063] 262 106:en:00000051:_rsv_35827_-1_1_0_0_S13095D61_E13095D61:Further testing is required.�����,\r���)������ ��, assist me with this evaluation. -``` - - - - - -### Line 263 (0x107): StartsUsingExtra - -This line contains extra data for ActorCast/StartsUsing network data. - -This line is always output for a given StartsUsing cast. - -If the ability is non-targeted, `x`/`y`/`z`/`heading` will be the source actor's position -and heading data. - -If the ability is actor-targeted, then `x`/`y`/`z` will be the target actor's current -position, and `heading` will be the angle from the source actor to the target actor. - -If the ability purely targets the ground (such as BLU Bomb Toss), then -`x`/`y`/`z`/`heading` be the position data for the target location. - -If the ability purely targets a direction (such as BLU Aqua Breath), then `x`/`y`/`z` -will be the source actor's position, while `heading` is the direction in which the -ability was cast. - -Note that the important part is how the ability is *targeted*, not its actual AoE -type. For example, Pneuma hits everything in a line, as if it were targeting a direction. -However, it is targeted on an actor, and if said actor moves during the cast, then the -cast will "follow" the target. Thus, if the ability has a target (and the target is -neither the caster nor the environment), then the actual location of the target is a -better indication of where it will hit. - - - -#### Structure - -```log -Network Log Line Structure: -263|[timestamp]|[sourceId]|[id]|[x]|[y]|[z]|[heading] - -Parsed Log Line Structure: -[timestamp] 263 107:[sourceId]:[id]:[x]:[y]:[z]:[heading] -``` - -#### Regexes - -```log -Network Log Line Regex: -^(?263)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\| - -Parsed Log Line Regex: -(?^.{14}) 263 (?107):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*)(?:$|:) -``` - -#### Examples - -```log -Network Log Line Examples: -263|2023-11-02T20:53:52.1900000-04:00|10001234|0005|-98.697|-102.359|10.010|1.524|dd76513d3dd59f5a -263|2023-11-02T21:39:18.6200000-04:00|10001234|0085|-6.653|747.154|130.009|2.920|39e0326a5ee47b77 -263|2023-11-02T21:39:12.6940000-04:00|40000D6E|8C45|-14.344|748.558|130.009|-3.142|9c7e421d4e93de7c - -Parsed Log Line Examples: -[20:53:52.190] 263 107:10001234:0005:-98.697:-102.359:10.010:1.524 -[21:39:18.620] 263 107:10001234:0085:-6.653:747.154:130.009:2.920 -[21:39:12.694] 263 107:40000D6E:8C45:-14.344:748.558:130.009:-3.142 -``` - - - - - -### Line 264 (0x108): AbilityExtra - -This line contains extra data for Ability/NetworkAOEAbility network data. - -This line is always output for a given Ability hit, regardless of if that Ability hit had -a corresponding StartsUsing line. - -If the ability has no target, or is single-target, the `dataFlag` value will be `0`, -and the `x`/`y`/`z`/`heading` fields will be blank. - -If the ability targets the ground, for example `Asylum`/`Sacred Soil`/caster LB3, the -`dataFlag` value will be `1` and the `x`/`y`/`z`/`heading` fields will correspond to the -ground target location and heading of the ability target. - -If the ability targets a direction (such as line/cone AoEs), then the `x/y/z` will be the -source actor's position, while `heading` is the direction that the ability is casting -towards. - -If there is some sort of error related to parsing this data from the network packet, -`dataFlag` will be `256`, and the `x`/`y`/`z`/`heading` fields will be blank. - -`globalEffectCounter` is equivalent to `sequence` field in -[NetworkAbility](#line-21-0x15-networkability) and -[NetworkAOEAbility](#line-22-0x16-networkaoeability). - -Note that unlike [StartsUsingExtra](#line-263-0x107-startsusingextra), you do not need -to worry about whether or not there is an actor target, as this represents the final -snapshotted location of the Ability. - - - -#### Structure - -```log -Network Log Line Structure: -264|[timestamp]|[sourceId]|[id]|[globalEffectCounter]|[dataFlag]|[x]|[y]|[z]|[heading] - -Parsed Log Line Structure: -[timestamp] 264 108:[sourceId]:[id]:[globalEffectCounter]:[dataFlag]:[x]:[y]:[z]:[heading] -``` - -#### Regexes - -```log -Network Log Line Regex: -^(?264)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\|(?[^|]*)\| - -Parsed Log Line Regex: -(?^.{14}) 264 (?108):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*):(?[^:]*)(?:$|:) -``` - -#### Examples - -```log -Network Log Line Examples: -264|2023-11-02T20:53:56.6450000-04:00|10001234|0005|000003EF|0|||||9f7371fa0e3a42c8 -264|2023-11-02T21:39:20.0910000-04:00|10001234|0085|0000533E|1|0.000|0.000|0.000|2.920|2e9ae29c1b65f930 -264|2023-11-02T21:39:15.6790000-04:00|40000D6E|8C45|000052DD|1|-14.344|748.558|130.009|2.483|f6b3ffa6c97f0540 - -Parsed Log Line Examples: -[20:53:56.645] 264 108:10001234:0005:000003EF:0:::: -[21:39:20.091] 264 108:10001234:0085:0000533E:1:0.000:0.000:0.000:2.920 -[21:39:15.679] 264 108:40000D6E:8C45:000052DD:1:-14.344:748.558:130.009:2.483 -``` - - + diff --git a/docs/MemorySignatures.md b/docs/MemorySignatures.md index 7b1b7a6a32..09545a80a5 100644 --- a/docs/MemorySignatures.md +++ b/docs/MemorySignatures.md @@ -1,395 +1,5 @@ -# Guide to Memory Signatures and Cheat Engine +# Permanently Moved to OverlayPlugin/cactbot -Memory signatures are unique binary strings -that can be used to find memory locations in an executable. +See: [docs/MemorySignatures.md](https://github.com/OverlayPlugin/cactbot/blob/main/docs/MemorySignatures.md) -Finding these signatures makes it possible to consistently -look up game state -(e.g. am I in combat, what is my job gauge, how much enmity do I have) -even when the game refuses to give you an API for this. - -This guide shows how to use Cheat Engine to find such memory signatures. -It's probably helpful if you know some basic assembly language, -some programming, and have extreme levels of patience. - -## Table of Contents - -* [Installation](#installation) -* [Finding New Memory Signatures](#finding-new-memory-signatures) - * [Connect Cheat Engine to the Game](#connect-cheat-engine-to-the-game) - * [Initial Memory Search](#initial-memory-search) - * [Repeated Scans](#repeated-scans) - * [Browsing Memory](#browsing-memory) - * [Approach 1: Finding Writers](#approach-1-finding-writers) - * [Approach 2: Tracing](#approach-2-tracing) - * [Approach 3: Finding Readers](#approach-3-finding-readers) - * [Assembly Code and Pointers](#assembly-code-and-pointers) - * [Extracting a Signature From Assembly](#extracting-a-signature-from-assembly) -* [Scan For Existing Memory Signatures](#scan-for-existing-memory-signatures) - -## Installation - -Install the [latest version of Cheat Engine](https://github.com/cheat-engine/cheat-engine/releases/latest). -The installer tries to tack some additional garbage on, -so be sure to turn this off and don't blindly click next. -Sorry. It's gross. - -## Finding New Memory Signatures - -![cheat engine screenshot](images/cheatengine_initial.png) - -### Connect Cheat Engine to the Game - -Start Final Fantasy XIV up and log in. - -Then, open up Cheat Engine. -Click on **File**, -select **Open Process**, -and then pick Final Fantasy XIV. - -The top bar should say **ffxiv_dx11.exe** at this point. - -![cheat engine connected screenshot](images/cheatengine_connected.png) - -### Initial Memory Search - -Let's say we're looking for your character's job gauge in memory. -For simplicity, say we're a warrior and we're just looking for beast gauge. -Because so many values in memory are zero, -let's start with a different initial value. - -Switch to warrior in game, -and then hit a striking dummy until your beast gauge is 80. - -Switch back to Cheat Engine. - -![cheat engine initial scan screenshot](images/cheatengine_initialscan.png) - -Put in a value (not hex) of 80. -The scan type should be `Exact Value` with a `Value Type` of byte. -At this point, we don't know how many bytes beast gauge takes in memory. -It would be nice if it took four bytes, -because there'd be a lot fewer options to sift through. -However, we can't make that assumption yet. -We're also looking for memory that is writable and not executable. - -Then, click **First Scan**. - -This will likely give you millions of memory locations with value 80. -A great start! - -![cheat engine found screenshot](images/cheatengine_found.png) - -This is a live view into all of these memory locations. -They turn red when they have changed. -Some of these are flickering values that are changing even without doing something in game. -You can always mash **Next Scan** -a few times to repeat the scan and eliminate them. - -### Repeated Scans - -In game, use a fell cleave to get back to 30 beast gauge. - -Go back to Cheat Engine. -Change the **Value** to 30. -Click **Next Scan**. -This should greatly cut down on the number of memory locations. - -Repeat this process of changing the value in game -and then re-scanning for the new value -until you have a small number of addresses. - -![cheat engine post scan screenshot](images/cheatengine_postscan.png) - -The black addresses are heap addresses. -The green addresses are [static addresses](https://medium.com/@nickteixeira/stack-vs-heap-whats-the-difference-and-why-should-i-care-5abc78da1a88). -In general, you're looking for static addresses -because it's easier to find code that refers to them -and they are permanent. - -Keep scanning until you have a single green (static) address. - -Right click on the address, -and select **Add selected addresses to the address list**. -This will put the address in the list at the bottom. -In this case, our static address is `14116E128`. - -![cheat engine address list screenshot](images/cheatengine_addresslist.png) - -If you are following along with this example, -the static address you have will not match. -Moreover, it will be different each time you restart the game. -This is because Windows has [address space layout randomization](https://en.wikipedia.org/wiki/Address_space_layout_randomization) enabled, -largely to make it harder to do the exact sort of thing we are doing now. - -This is also why we need to find a code signature. -If the executable and DLL addresses weren't randomized, -the static addresses would be the same from run to run. - -### Browsing Memory - -From the address list, -right click on the address that was just added -and select **Browse This Memory Region*. - -That will bring up the Memory Viewer window. -This has a disassembly view at the top and a memory viewer at the bottom. -Confusingly, these are separate views and are *not* synced together even though they are in the same window. -They will sync to whatever the last address you have selected -**Disassemble This Memory Region** -or **Browse This Memory Region** on respectively. - -![cheat engine browse memory screenshot](images/cheatengine_browsememory.png) - -However, you can see that in the top left of this screenshot is the hex value `1E`, -which is [30 in decimal](https://www.google.com/search?q=0x1e+in+decimal). - -Browsing memory can let you see what else is around it. -This is especially useful for things like entity or player data. - -For job data, there's really not much interesting in nearby memory. - -### Approach 1: Finding Writers - -Now, we need to find some code that refers to this. -The easiest way to do this is to find what modifies this value. - -Right click on the address in the address list, -and select **Find out what writes to this address**. -This will ask you to attach the debugger, say yes. -A new window will pop up. - -(If your game has crashed at this point, -ensure "Try to prevent detection of the debugger" -is checked under Edit > Settings > Debugger Options!) - -Go back to FFXIV, and modify the beast gauge. -In this case, we'll hit infuriate to go from 30 to 80. - -Go back to Cheat Engine, -and the new debugger window should have some information. - -![cheat engine debugger screenshot](images/cheatengine_debugger.png) - -This is the assembly that wrote to the beast gauge memory location. - -If you want, -you can click on the **Show disassembler** to see the surrounding code. - -![cheat engine disassembly screenshot](images/cheatengine_disassembly.png) - -Unfortunately, in this case, -this is a two instruction function call. - -The line that is changing the value is `mov [rcx+08], al`. -I don't really know assembly language, -but google tells me that `al` is the last 8 bits of the `eax` register -which was set on the previous `movzx eax, byte ptr [rdx+01]` line. -Given that this is the line that is writing memory, -`[rcx+08]` is the pointer we care about, -but we need to find the calling code that set `rcx`. -This code is likely somewhere very different in the executable. - -We have a couple of different options here. -One option here is to [do a trace to find calling code](#approach-2-tracing). -The second option is to [consider what reads the address](#approach-3-finding-readers) and not just write. -A third option (not explored in this guide) is to find some other code path that modifies the value, -and see if that code path has an easier signature. -(For example, changing jobs likely modifies the value in a different way?) - -### Approach 2: Tracing - -If pure disassembly doesn't yield enough contextual information, -Cheat Engine has "break and trace" functionality. -Go back to the [browsing memory](#browsing-memory) view. -This functionality is not available from the address list directly. - -Right click on that `1E` byte you care about. -Select **Data Breakpoint** and then **Break and trace**. -All the default options are fine. -Since we are still looking for a writer, -we will keep **Break on Write** selected. -Click **Ok**. - -This brings you to a Tracer window. -Go back to Final Fantasy, and do something to modify your beast gauge. -The game will probably hiccup as Cheat Engine tries to record callstacks. -Go back to the Tracer window. - -![cheat engine tracing screenshot](images/cheatengine_tracing.png) - -You can double click on the lines in the Tracer to -have the Memory Viewer disassembly window jump to that location. - -In this case, expanding the arrow and double clicking the `ret` -return assembly instruction goes back to exactly what we were -looking at before in the disassembly window. - -Double clicking on `mov rdx, [rsp+50]` brings us to the code -that called the code we were looking at before. - -![cheat engine tracing 2 screenshot](images/cheatengine_tracing2.png) - -The `call` right before that line is the `call` into the code -we were looking at. -So, we would need to figure out what set `rcx`. -It looks like that's set from `r9`. -`r9` is set indirectly from a pointer in `r14`. -This is getting complicated. -It's possible to keep going back in the assembly to find -some code, -but maybe there's an easier approach. - -### Approach 3: Finding Readers - -Instead of finding code that modifies the value, -we could also find code that reads the value. - -Right click on the address in the address list, -and select **Find out what accesses this address**. - -Unlike writing, -the code is likely constantly accessing this address. -You will need to hit the **Stop** button to stop collecting locations. - -![cheat engine debugger 2 screenshot](images/cheatengine_debugger2.png) - -In this case, there are two places in code that are accessing this memory. -One is hit very frequently (3000 times) and the other infrequently (152). - -Looking at the disassembly in the window, -the second one looks like a much more substantial function, -so let's disassemble that one. - -![cheat engine disassembly 2 screenshot](images/cheatengine_disassembly2.png) - -Perfect! This looks a bit simpler than the code we saw in tracing. - -### Assembly Code and Pointers - -Because we are looking for a static address, -this address will never change once the program has started. -The goal is to find some stable set of assembly code that -surrounds the address we're looking for. -We can then search for this code in memory to get back the address, -no matter where it is that particular run. - -Reading this assembly code, the reading code is `movzx ebx, byte ptr [rcx+08]`. -In English, this looks at the memory location 8 bytes after what is in the -`rcx` register, takes the byte found there, and moves it into the `ebx` register. -(The movzx part means that it [zero extends](https://www.felixcloutier.com/x86/movzx) this value, which is not very relevant to what we're doing.) - -Since it's looking at `rcx`, we need to look backwards in the assembly code -until we find the line that sets `rcx`. -You can see that `rcx` gets set on the `mov rcx,[ffxiv_dx11.exe+1AAE118]` line. -This means that `rcx` is set from whatever is stored in memory at that location. - -```assembly -48 8B 0D 23C14201 - mov rcx,[ffxiv_dx11.exe+1AAE118] { (14116E120) } -48 85 C9 - test rcx,rcx -74 B8 - je ffxiv_dx11.exe+681FB2 -48 8B 05 67C14201 - mov rax,[ffxiv_dx11.exe+1AAE168] { (21) } -``` - -In particular, the `23C14201` value is what we are looking for. -Here's a brief digression on RIP relative addressing modes. -RIP relative addressing means that offsets are relative to the instruction pointer. -The `RIP` register is the instruction pointer register and contains -the address of the instruction immediately following this instruction. -You can find out what this address is by double clicking on the -next line (the `text rcx,rcx` line). -In my case, it says the address is `13FD41FF5`. -Because we are on a [little endian](https://en.wikipedia.org/wiki/Endianness) -system, the `23C14201` hex is the 4 byte integer `01 42 C1 23` (bytes reversed). -If you [add](https://www.google.com/search?q=0x0142C123+%2B+0x13FD41FF5) 0x0142C123 + 0x13FD41FF5, you get 0x14116E118. -Cheat Engine will also calculate this number for you if you just double click -on the instruction itself. -For instance, double clicking on the `mov rcx` line yields the text `mov rcx,[14116E118]`. -So, you don't have to do this math at all, but it's good to know how it works. - -In the comment from Cheat Engine, that `mov rcx` line has the value `14116E120`. -This means that the memory address at `14116E118` has the value `14116E120`. -The memory address we found earlier when scanning was `14116E128`. -So it makes sense that `14116E120 + 08` is the value we want, as the reading -code adds 8 bytes to its address. - -You can manually add `14116E118` to the memory region or just find it -relative to the beast gauge, as it's very close. - -![cheat engine pointer screenshot](images/cheatengine_pointer.png) - -In the above screenshot, -the small circle is the beast gauge values at `14116E128` -and the longer circle is the pointer at `14116E118` that is -being used to load `rcx`. -This memory browsing confirms the comment earlier, -that the memory at `14116E118` contains the pointer `000000014116E120`. -(As always, little endian means reversing the bytes.) - -### Extracting a Signature From Assembly - -So, now we have some assembly code that contains a pointer to a pointer to the beast gauge. -We need to pick out some bytes from the assembly code to serve as the signature. - -There's a little bit of an art to picking good signatures. -You want to always want to ignore relative pointer offsets, -like the `23C14201` value from before. -These offsets will be the same from run to run, -but change from patch to patch with great frequency. -Finding signatures is a huge pain, -so ideally you want to find something that will stand the test of time. - -In this case, let's just start copying bytes out from the bytes column, -starting with the `mov rcx, ...` line. - -```assembly -48 8B 0D 23C14201 - mov rcx,[ffxiv_dx11.exe+1AAE118] { (14116E120) } -48 85 C9 - test rcx,rcx -74 B8 - je ffxiv_dx11.exe+681FB2 -48 8B 05 67C14201 - mov rax,[ffxiv_dx11.exe+1AAE168] { (21) } -``` - -This gives us: `48 8B 0D 23C14201 48 85 C9 74 B8 48 8B 05 67C14201` - -The two four byte patterns are both pointers, -so let's just drop the one on the end and make the internal one a wildcard. -You can make wildcards using question marks, -both in cactbot and in Cheat Engine. - -Thus, our final signature is: `488B0D????????4885C974B8488B05` - -The address that contains the pointer we care about is the four bytes in the question mark. - -You can see this [in cactbot itself](https://github.com/quisquous/cactbot/blob/df176c4feff81bab356a8e5e6e6b453e94626320/CactbotOverlay/FFXIVProcess.cs#L189). - -It's important to do a [scan for existing memory signatures](#scan-for-existing-memory-signatures) to make sure that this signature is unique. - -Then, in your plugin, the process would be the following. - -* search for this signature in memory -* convert the RIP relative addressing to a real pointer (e.g. `14116E118`) -* find the pointer at that memory location (e.g. `14116E120`) -* this pointer is the pointer to beast gauge - -Because `14116E118` points 8 bytes forward to `14116E120`, -we could just also make the assumption that this is always true -and just add 16 bytes to what we find in the signature. -This has been true through all of Stormblood, at least. - -Foof. - -## Scan For Existing Memory Signatures - -If you have an existing memory signature, -you can also use Cheat Engine to find it in memory. - -![cheat engine signature scan screenshot](images/cheatengine_signature_scan.png) - -Start another scan. -This time, set the **Value Type** to **Array of byte**, and select **Search for this array**. -Click the **Hex** checkmark, and paste in the signature that we got previously. -Make sure to click the **Executable** checkmark, as we are searching for code. - -If you click **First scan**, this should find a single result. -If you right click that address, and select **Disassemble this address** -it will bring you right back to the code that we found previously. + diff --git a/docs/OopsyraidsyGuide.md b/docs/OopsyraidsyGuide.md index 1cf77b87e0..88d0543e2f 100644 --- a/docs/OopsyraidsyGuide.md +++ b/docs/OopsyraidsyGuide.md @@ -1,317 +1,5 @@ -# Oopsyraidsy Guide +# Permanently Moved to OverlayPlugin/cactbot -## Overview +See: [docs/OopsyraidsyGuide.md](https://github.com/OverlayPlugin/cactbot/blob/main/docs/OopsyraidsyGuide.md) -The goal of oopsy is to reduce time in understanding why a wipe or a death happened. -Most of the time, a death happens because somebody takes damage that they shouldn't have, -and so most of the oopsy file is about making these mistakes visible quickly. - -A basic oopsy file should cover: - -- abilities nobody should be hit by (e.g. a puddle on the ground) -- effects people only get when they make a mistake (e.g. bleed when walking into the edge of the arena) -- damage that should not be shared (e.g. a spread) -- damage that should not be taken alone (e.g. a stack) - -Any triggers past that are usually a bonus. - -See [e12s](https://github.com/quisquous/cactbot/blob/main/ui/oopsyraidsy/data/05-shb/raid/e12s.ts) -or [TOP](https://github.com/quisquous/cactbot/blob/main/ui/oopsyraidsy/data/06-ew/ultimate/the_omega_protocol.ts) -for examples of more complicated triggers. - -These complicated triggers are things like: - -- whose e12s icicle killed who -- whose e12s statue laser killed who -- which e12s lion cleave killed who -- TOP Looper tower mistakes -- TOP Hello World rot mistakes (extra or missing) - -## Oopsy Mistake Severity - -The TypeScript type `OopsyMistakeType` has all the different types of mistakes that can be made. -See: [oopsy.d.ts](https://github.com/quisquous/cactbot/blob/main/types/oopsy.d.ts#L9). -These each correspond with their own icon. -It is very subjective what you assign to each, so don't worry about it too much. - -- pull: early pull mistakes -- warn: most mistakes, "you took damage you shouldn't have" -- fail: bad mistakes (subjective), "you took obviously avoidable damage like a tank cleave that probably will kill you" or "you died from something that nobody should like UCOB twisters" or "you took damage that will probably wipe the raid" -- potion: unused, intended for "you used the wrong grade potion" -- death: somebody died -- wipe: the entire party wiped -- damage: a dps mistake, e.g. somebody got a damage down or missed somebody with a raid buff -- heal: a healing mistake, e.g. somebody missed a heal or overwrote a rez -- good: unused, intended for "somebody went out of their way to do something that saved the raid" - -Needless to say `warn` and `fail` are largely subjective. Don't worry about it. - -## Making an Oopsy File - -Oopsy files are often very quick to make, but often people just don't bother to do them. - -The `util/logtools/make_timeline.ts` script has a `-la` parameter -that lists all the abilities for an encounter. -If this is filled out, then it becomes very easy to look through all the ability ids -and move the relevant ones into the oopsy file. - -As an example, here is the Zeromus Extreme timeline -[ability table](https://github.com/quisquous/cactbot/blob/f15fab608d1700c7a5db6dca243dcc5b97107fab/ui/raidboss/data/06-ew/trial/zeromus-ex.txt#L142-L227) -and here is the -[oopsy file](https://github.com/quisquous/cactbot/blob/f15fab608d1700c7a5db6dca243dcc5b97107fab/ui/oopsyraidsy/data/06-ew/trial/zeromus-ex.ts) -made from that table. - -## File Structure - -Each file is a module that exports a single oopsy trigger set. - -Most entries in the trigger set are simple maps of string ids to ability ids. -Each entry in a file must start with the same prefix (e.g. `UCU` in the file) -and must be globally unique across all oopsy trigger sets. - -There's nothing magical about any of the `damageWarn` or `shareFail` categories. -They are only helpers to make it easier to make a trigger from an id, -but ultimately are just triggers themselves. -Please promote any commonly used triggers to be helpers as needed. - -```typescript -import ZoneId from '../path/to/resources/zone_id'; -// Other imports here. - -export default { - zoneId: ZoneId.TheUnendingCoilOfBahamutUltimate, - zoneLabel: { - en: 'The Unending Coil of Bahamut (Ultimate)', - }, - damageWarn: { - 'UCU Lunar Dynamo': '26BC', - // ... - }, - damageFail: { - 'UCU Twister': '26AB', - // ... - }, - gainsEffectWarn: { - 'UCU Doom': 'D2', - // ... - }, - gainsEffectFail: { - 'UCU Doom': 'D2', - // ... - }, - shareWarn: { - 'UCU Megaflare': '26DB', - // ... - }, - shareFail: { - 'UCU Megaflare': '26DB', - // ... - }, - soloWarn: { - 'UCU Thermionic Beam': '26BD', - // ... - }, - soloFail: { - 'UCU Thermionic Beam': '26BD', - // ... - }, - triggers: [ - { /* ..trigger 1.. */ }, - { /* ..trigger 2.. */ }, - { /* ..trigger 3.. */ }, - ], -}; -``` - -### Trigger Set Properties - -**zoneId**: -A shortened name for the zone to use these triggers in. -The set of id names can be found in [zone_id.ts](../resources/zone_id.ts). -Prefer using this over zoneRegex. -A trigger set must have one of zoneId or zoneRegex to specify the zone -(but not both). - -**zoneLabel** -An optional name to use for this trigger set in the configuration interface. -Overrides the zone name from [zone_info.ts](../resources/zone_info.ts). - -**zoneRegex** (deprecated): -A regular expression that matches against the zone name (coming from ACT). -If the regular expression matches, then the triggers will apply to that zone. - -**damageWarn** and **damageFail**: -An object contains properties like `'trigger id': 'damage action id'`, -which provides an easy way to apply triggers via damage action id (in hex). -When a player was hit by these action, -a message (default to action name) would be shown. - -**damageWarn** shows the message as `warn`, -and **damageFail** shows it as `fail`. - -**gainsEffectWarn** and **gainsEffectFail**: -Just like **damageWarn** and **damageFail**, but triggered when hit by an effect (id in hex). - -**shareWarn** and **shareFail**: -Just like **damageWarn** and **damageFail**, -triggered when multiple players share damage which should only be on one player (e.g. spread AoE). - -**soloWarn** and **soloFail**: -The opposite of **shareWarn** and **shareFail** -in that they are triggered when something that should be shared hits only one person (e.g. stack markers). - -**triggers**: -An array of triggers in the trigger set. -See below for the format of each of individual triggers. - -## Trigger Structure - -Each trigger is an object with the following fields. All fields are optional. -This parallels the raidboss trigger structure. - -- `id`: a string representing this trigger, for use in disabling triggers. See [oopsyraidsy-example.js](../users/oopsyraidsy-example.js). -- `condition`: function returning bool for whether or not to run this trigger. -- `netRegex`: a regex matching a network line (such as from the `NetRegexes` helper) -- `delaySeconds`: float (or function returning float) for how long to wait before executing this trigger. -- `suppressSeconds`: float (or function returning float) for how long to ignore future matches to this trigger (including additional collection). -- `deathReason`: overrides the reason that a player died if the player dies without taking any more damage. This is for things that kill you without an obvious log line, e.g. forgetting to clear Beyond Death. -- `mistake`: returns a single mistake or an array of mistakes to add to the live list. See below for the `mistake` format. -- `run`: function that just runs, but does not return anything - -### `mistake` format - -- `type` is the icon: pull, warn, fail, potion, death, wipe (:arrow_forward::warning::no_entry_sign::cocktail::skull::toilet:). -- `name` is an optional full player name to list as this mistake happening to. This will prepend their name in the live list. -- `blame` is an optional full player name to blame for this mistake. If `name` is not specified, then the `name` will be the `blame` player. -- `reportId` is an optional player id. If set, it will include this mistake in that player's death report. -- `text` is an optional reason for the mistake. It will be prepended by the blamed player's short name (if it exists). -This will print ":no_entry_sign: Latke: Dynamo" in the live log. - -```typescript -mistake: (data, matches) => { - return { - type: 'fail', - blame: matches.target, - reportId: matches.targetId, - text: 'Dynamo' - }; -}, -``` - -### `deathReason` format - -- `id` is the player id to override the death reason for. -- `name` is the full player name to override the next death reason for. -- `reason` is the string to use. - -If this following trigger is used, then if a player dies without taking any other damage, the log would show ":skull: Chippy: Doom Debuff" instead of assigning it to the last damage the player took before this trigger, which might incorrectly look more like ":skull: Chippy: Auto (3034/38471)". - -```typescript -deathReason: (data, matches) => { - return { - id: matches.targetId, - name: matches.targetName, - text: 'Doom Debuff', - }, -}, -``` - -## Oopsy Trigger Function Parameters - -Every function in an oopsy trigger gets two parameters: `data` and `matches` in that order. - -Current hp/mp/tp values are not 100% precise. ACT polls these values periodically and so it may be out of date by one HoT/DoT tick. The most important consideration is that damage that does more than current hp may not actually be fatal, and vice versa that damage that does less than current hp may turn out to be fatal. There's no way to know until the 'was defeated' message shows up two seconds later. - -```typescript -{ - // 26BB is the ability id for Nael's Iron Chariot. - netRegex: NetRegexes.ability({ id: '26BB' }), - mistake: (_data, matches) => { - // matches here is a single matches object - console.log(matches.target); - }, -}, -``` - -### Data Fields - -`data` is an object that persists for an entire fight and is reset on wipe. It is passed to every function. - -`data` comes prepopulated with the following fields: - -- `data.me`: string, the player's character name. -- `data.job`: string, the player's job, e.g. WAR. -- `data.role`: string, the role of the player's job: tank, healer, dps-melee, dps-ranged, dps-caster, crafting, gathering. -- `data.inCombat`: bool, whether or not the game thinks the player is in combat. This is different than whether ACT thinks the player is in combat. -- `data.IsPlayerId`: helper function to check if a target or attacker id represents a player (vs a pet or a mob). -- `data.party`: the PartyTracker object, you can use this to check names and roles of players in the party, or to call `data.party.member(name).toString()` to get a shorter nickname or job name. - -`data` is something that triggers can and should store state on, if state is needed to be tracked across multiple triggers. - -For example, if you want to store a map of which players have doom or not, that could be stored in `data.hasDoom`. This could then be used across multiple triggers. - -```typescript -{ - netRegex: NetRegexes.gainsEffect({ effect: 'Doom' }), - run: (data, matches) => { - data.hasDoom[matches.target] = true; - }, -}, -``` - -### Match Fields - -`matches` is literally the regex match object returned from whatever regex this trigger matched. `matches[0]` is always the full match, with other array entries being any other groups from the regex (if any). In the case of the single event above, `matches[0] === 'Iron Chariot'`. - -However, if `matches` has any groups -(which all the `Regexes` helper functions do), -then matches will be the groups field directly, -so that you can do things like `matches.target`. - -## Trigger Field Evaluation Order - -The full order of evaluation of functions in a trigger is: - -1. `regex` -1. `disabled` -1. `condition` -1. `delaySeconds` -1. (delay happens here) -1. `suppressSeconds` -1. `mistake` -1. `deathReason` -1. `run` - -## Testing Oopsy - -Oopsy has a playback viewer if you want to test it without running content. - -It is hosted at . -If you are [running locally with the webpack dev server](../CONTRIBUTING.md#validating-changes-via-webpack), -you can also use it via . - -You can drag a network log file to the the viewer and it will process and show all the mistakes. -The [troubleshooting guide](FAQ-Troubleshooting.md#how-to-find-a-network-log) has information on where to find a network log. - -NOTE: any trigger with `delaySeconds` will not work, sorry. PRs welcome! - -## Future Oopsy Work - -Unfortunately, Oopsy is always a little bit less loved than Raidboss and so has fallen behind on features. -There's plenty of work that could be done to make it better if you want to contribute. - -Easier tasks: - -- Make the [Oopsy Viewer](#testing-oopsy) support `delaySeconds` properly -- create an oopsy `Util` library to collect helper functions like [these](https://github.com/quisquous/cactbot/blob/main/ui/oopsyraidsy/data/06-ew/dungeon/aloalo_island.ts) or [these](https://github.com/quisquous/cactbot/blob/main/ui/oopsyraidsy/data/06-ew/raid/p8s.ts) so that it isn't repeated - - bonus: add support for a helper for "you took two of these", for cases where somebody is hit by two stacks - - bonus: add support for "this person missed the stack" -- make it possible to do `netRegex: { id: '1234', source: 'Mob' }` instead of `netRegex: NetRegexes.ability({ etc })` -- make it more clear in the log when somebody has died from multiple of the same damage, e.g. `Burst x2` instead of just `Burst` - -Harder tasks: - -- make early pulls more exact (they are very wrong, now) -- look into network data and try to figure out what happens when there is an instant death from a death wall so that an OverlayPlugin line can be added for it -- more exact hp tracking by using [0x37](LogGuide.md#line-37-0x25-networkactionsync) lines (currently it is often very wrong) -- show which buffs are active and total mitigation for damage (this is a lot of work) + diff --git a/docs/PatchUpdateChecklist.md b/docs/PatchUpdateChecklist.md index 092c9ea599..a87d867a59 100644 --- a/docs/PatchUpdateChecklist.md +++ b/docs/PatchUpdateChecklist.md @@ -1,278 +1,5 @@ -# Patch Update Checklist +# Permanently Moved to OverlayPlugin/cactbot -This is a guide for steps to update cactbot when FFXIV has a patch. +See: [docs/PatchUpdateChecklist.md](https://github.com/OverlayPlugin/cactbot/blob/main/docs/PatchUpdateChecklist.md) -## Game Data Resource Updates - -Once the patch is downloadable, -there are some changes that can be made before the game is up. - -### Update Saint Coinach - -First, run Saint Coinach. -You can download this program from [the githup repo](https://github.com/xivapi/SaintCoinach). -It updates fairly quickly (on the order of a day), -but will usually not be updated if you are running it right when the patch is up. - -If you are running this inside git bash, the command will look like this: -`./SaintCoinach.Cmd.exe "C:\\Program Files (x86)\\SquareEnix\\FINAL FANTASY XIV - A Realm Reborn"` - -After a patch update, this will prompt you with something like this: - -```text -Game version: 2023.09.28.0000.0000 -Definition version: 2023.07.26.0000.0000 -Update is available, perform update (Y/n)? -``` - -"Perform update" is not recommended. -This attempts to update Saint Coinach's definition files based on any changes it finds. -It almost never works, nearly always crashes, and also takes a long time. -Instead, we will just cross our fingers and hope that the old definitions work with the new data. -75% of the time, this is successful and the old version of Saint Coinach is "good enough" -to use for the data cactbot needs. - -Instead of "perform update", -update the `Definitions/game.ver` file manually to have the correct game version. -In this example the content of the file should be `2023.09.28.0000.0000`, -but in practice use whatever the prompt says is the most current version. - -Once done, re-run Saint Coinach and verify that an `exd Status` command works. -It should look something like this: - -```text -Game version: 2023.09.28.0000.0000 -Definition version: 2023.09.28.0000.0000 -SaintCoinach.Cmd (Version 0.1.0.0) -> exd Status -1 files exported, 0 failed -> exit -``` - -If it crashes or the data looks very weird after running scripts (e.g. every zone id changes) -then wait until Saint Coinach updates. -You can copy any release from the github page over top of your Saint Coinach folder with the release, -and then re-run these steps. - -### Run update scripts - -Once Saint Coinach is locally running, you can run cactbot update scripts. -Run all of these scripts in any order and then commit the result. - -```shell -python util/gen_zone_id_and_info.py -python util/gen_weather_rate.py -python util/gen_hunt_data.py - -node --loader=ts-node/esm util/gen_effect_id.ts -node --loader=ts-node/esm util/gen_world_ids.ts -node --loader=ts-node/esm util/gen_pet_names.ts -``` - -Here's an example: - -### Manually handle zone script - -The `gen_zone_id_and_info` script usually needs special handling. -`npm run test` will let you know about this when committing or uploading. - -#### Removed zones - -If any zone has been entirely removed from the game, -but we still want to keep the data around (e.g. an unreal) -then we need to manually add its data to the script. - -For example, in 6.5 Zurvan unreal was removed and Thordan unreal was added. -See - -This needs to be added to the `synthetic_ids` list: - -```python -"ContainmentBayZ1T9Unreal": 1157, -``` - -...and this needs to be added to the `synthetic_zone_info` array. - -```python - 1157: { - "contentType": 4, - "exVersion": 4, - "name": { - "cn": "祖尔宛幻巧战", - "de": "Traumprüfung - Zurvan", - "en": "Containment Bay Z1T9 (Unreal)", - "fr": "Unité de contention Z1P9 (irréel)", - "ja": "幻鬼神ズルワーン討滅戦", - }, - "offsetX": 0, - "offsetY": 0, - "sizeFactor": 400, - "weatherRate": 75, - }, -``` - -This will allow the zone script to continue outputting this non-existent zone -so that zone files can use this id. - -You'll also need to likely reformat the python file via `black **/*.py --line-length 100`. - -#### Zone collisions - -The zone id and info emitting script ignores any zones that have a name collision. -It will print these out to the console when running the script. - -For example, when updating for 6.5, it emits these new lines (among others): - -```text -collision TheBurn: {"territory_id": "789", "cfc_id": "585", "place_id": "2851", "name": "e3d7", "weather_rate": "97", "map_id": "480", "territory_intended_use": "3", "ex_version": "2"} -collision TheBurn: {"territory_id": "1173", "cfc_id": "585", "place_id": "2851", "name": "e3d7_re", "weather_rate": "97", "map_id": "480", "territory_intended_use": "3", "ex_version": "2"} -collision TheGhimlytDark: {"territory_id": "793", "cfc_id": "611", "place_id": "2586", "name": "g3d5", "weather_rate": "0", "map_id": "500", "territory_intended_use": "3", "ex_version": "2"} -collision TheGhimlytDark: {"territory_id": "1174", "cfc_id": "611", "place_id": "2586", "name": "g3d5_re", "weather_rate": "0", "map_id": "500", "territory_inte nded_use": "3", "ex_version": "2"} -``` - -Because these dungeons were updated for trusts in 6.5, there are two dungeons with the same name in the game. - -To handle this we can rename the old dungeon's id to be `TheBurn64` in the `synthetic_ids` array -(i.e. The Burn in 6.4 and earlier) -so that there is no collision. -See: - -Then, copy the raidboss triggers, raidboss timeline, and oopsy triggers into new files, -e.g. `the_burn64.ts` and `the_burn64.txt`. -You'll need to manually rename all the ids as well so they don't conflict. -See: - -The reason for this is so that the new files can be updated without breaking Chinese and Korean -users who are are still using the older dungeon. - -Theoretically, once all regions are past a particular version we can delete the old files. - -#### Formatting, sorry - -Sorry, somebody should fix this to be ignored (or otherwise not happen) -but this always needs to be reformatted and this change should not be committed or lint will fail. - -```diff -- 'fr': -- 'Entraînement: infiltration en base ennemie', -+ 'fr': 'Entraînement: infiltration en base ennemie', -``` - -### Update Content List - -`resources/content_list.ts` is a manually curated list of all content. -This is what appears on the [coverage page](https://quisquous.github.io/cactbot/util/coverage/coverage.html). -It is also the ordering in the cactbot config ui, -although that also sorts by expansion as well. - -Manually add any dungeons, trials, raids, etc to this list. -The idea is to keep them in roughly the same sections and order as the duty finder ui -so that parts of cactbot using this for sorting puts content is a reasonable order. - -See: - -### Create a meta-issue - -Also consider making a meta github issue tracking new content to coordinate who is working on what. -For example, . - -## In Game Memory Verification - -Once the game is back up, memory signatures can be checked. -You do not need a working FFXIV plugin to do this. -This could theoretically be done earlier on game data too with Ghidra etc. - -### Check memory signatures - -cactbot has a number of memory signatures that it uses. -(Maybe some day cactbot plugin will merge with OverlayPlugin?) - -There are four signatures, which all live in [FFXIVProcessIntl.cs](https://github.com/quisquous/cactbot/blob/main/plugin/CactbotEventSource/FFXIVProcessIntl.cs). - -- Charmap (information about your character) -- Job Data (gauge info for your job) -- In Combat (whether the game thinks you are in combat) - -OverlayPlugin also duplicates the charmap and in combat signatures, -so if cactbot is broken please update those as well. - -Look for errors in the OverlayPlugin log (ACT->Plugins->General->the scrolly textbox at the bottom). - -There are two cactbot errors to look out for in the log. - -1) `Charmap signature found 0 matches` -2) `Charmap signature found, but conflicting match` - -The signature should match exactly one location in the executable. -If it finds zero or two in different places, then it will not work. -You will need to find a new signature. -See the [Memory Signatures](memory_signatures.md) documentation for more info. - -OverlayPlugin will also print out lines like: - -```text -[10/6/2023 5:13:18 PM] Info: Found in combat memory via InCombatMemory61. -[10/6/2023 5:13:18 PM] Info: Found combatant memory via CombatantMemory65. -[10/6/2023 5:13:19 PM] Info: Found target memory via TargetMemory63. -[10/6/2023 5:13:19 PM] Info: Found enmity memory via EnmityMemory60. -[10/6/2023 5:13:20 PM] Info: Found aggro memory via AggroMemory60. -[10/6/2023 5:13:20 PM] Info: Found enmity HUD memory via EnmityHudMemory62. -``` - -...and will print errors if it is not found. - -### memtest overlay - -Even if all the signatures are found, -the offsets might be incorrect. - -If you add the [cactbot test overlay](https://github.com/quisquous/cactbot#test-module) -as an Overlay, it will give you a bunch of information from memory. - -The most important values are: zone, name, and job id. - -Here is a screenshot after the 6.5 patch where the job is broken. - -![test overlay](images/newpatch_testoverlay.png) - -If the signatures are correct, but the offsets are wrong they need to be updated in -[FFXIVProcessIntl.cs](https://github.com/quisquous/cactbot/blob/main/plugin/CactbotEventSource/FFXIVProcessIntl.cs). -See as an example. - -#### Verify basic info - -Verify name and id (if you know your own id). -Job breaks more often than not, -but is usually only adjusted slightly. - -Face south. Rotation should be ~0. -Face east. Rotation should be ~pi/2. -Walk east. The x position value should increase. -Walk south. The y position value should increase. -Jump. The z position value should increase. - -The other thing that breaks a lot is the shield percentage. -It would be nice to add this to the test overlay. -You can see it as a yellow overlay on top of hp in the jobs overlay. - -#### Verify In Combat - -Hit a target dummy. -The `game: no` should switch to `game: yes`. -Reset your aggro, it should switch back to `game: no`. -If the FFXIV plugin hasn't been updated, it will likely say `act: no`. - -### Verify Job data - -Job data only infrequently changes (during a job rework or at each expansion). -It is not ~usually verified (as there are a lot of jobs) before doing a release. -Test at least one job with the cactbot jobs overlay and make sure boxes update. - -## et voila, release - -Once the resources are updated and the signatures and memory data look good, -do a cactbot release! - -## Other things - -It'd be nice to have a list of OverlayPlugin steps too, but that could live elsewhere. + diff --git a/docs/RaidbossGuide.md b/docs/RaidbossGuide.md index 12dfa3949a..f453cf16a2 100644 --- a/docs/RaidbossGuide.md +++ b/docs/RaidbossGuide.md @@ -1,1128 +1,5 @@ -# Raidboss Triggers Overview +# Permanently Moved to OverlayPlugin/cactbot -[**English**] [[简体中文](./zh-CN/RaidbossGuide.md)] +See: [docs/RaidbossGuide.md](https://github.com/OverlayPlugin/cactbot/blob/main/docs/RaidbossGuide.md) -## Trigger Guidelines - -As a rule, cactbot defaults to text alarms with a small number of default sounds over custom sounds and tts. -This is because there is a clearer mental separation between visual text for triggers and audio of voice comms. -This separation is easier to process than mixing the audio of voice comms and tts together. -This design choice isn't for everybody, especially those used to tts (which is an option). -However, text triggers will always be the default. -Give it a try. - -As it's easier to disable triggers than to write triggers, -cactbot also tends to be slightly noisier than most people prefer. - -### Trigger Severity - -Here's the general guidelines for how cactbot has triggers. -You can use these when adding new triggers for raids. -As always, try to be consistent with the surrounding code. - -- alarm (red text) - - you will wipe the raid if you mess this up - - ideally used on random mechanics (one person gets X) - - ideally used only once or twice in a raid - -- alert (yellow text) - - you will get killed if you mess this up (or kill others) - - used for important mechanics - - should be about 1/2 of the triggers - -- info (green text) - - you should probably do something about this, but it might not kill you - - good for "move now" sorts of calls - - also used for information like nael dragon dives or grand octet markers - - should be about 1/2 of the triggers - -Another consideration for trigger severity is to make them contextually useful. -For example, if you may get selected for one of two mechanics, -it's preferable to have one mechanic be info and the other alert -(or one alert and the other alarm) -so that it is obvious from the noise which mechanic you have. - -A final consideration is to not overload the player -with too many of the same types of message. -If every trigger is an alert, -it's probably better to change some of them to be info. -Having different sounds helps create a "rhythm" for the fight. -This is especially true for simultaneous alerts. - -Try not to have more than two triggers on screen at once, -and try not to have them be the same type (e.g. two alert texts) -to avoid visual clutter. - -### Trigger Text - -Here's some general guidelines for the text in triggers. -The goal for trigger text is to mimic what a human raidcaller would say. -It should minimize the amount of that the player has to think to do a mechanic. - -- Be concise. Text should be as short as possible, like lalafells -- Tell the player what to do rather than the mechanic name, i.e. prefer `Get Out` vs `Iron Chariot` -- Have the text be positive, i.e. prefer `Left` vs `Don't Go Right` -- Don't prescribe a particular strategy by default if multiple strategies exist, e.g. Hello World -- If multiple strategies exist, tell the player the mechanic (`Jail on YOU`) or add an [option](#strat-specific-options) -- Don't write triggers for obvious ground aoes -- Be careful about telling people what needs to be done vs what to do in the moment (e.g. `Under + Intercards` vs `(Under + Intercards for later)`) -- Keep the text brief and assume that the player has some familiarity with the mechanic; people are only new once, so it's better to optimize for clarity for experienced players -- As always, be consistent with other triggers - -### Strat-specific options - -If a fight does have strat-specific options, -the way to handle this is to add a `config` section to the trigger set. -This will create options in the [cactbot config UI](CactbotCustomization.md#using-the-cactbot-ui) -that will appear at the top of each raidboss file section. - -By default, you should not pick a particular strategy if there are multiple. - -See: [P12S](../ui/raidboss/data/06-ew/raid/p12s.ts) for an example of many different tower and classical concept options. - -TODO: update this guide to explain the structure of the config section better - -### Trigger Implementation Guidelines for Developers - -This is just a grab bag of miscellaneous thoughts about triggers. - -- triggers should be timed so that when the text appears it it safe to do that mechanic, e.g. if there's a stack=>spread don't call spread until the stack has gone off -- use `delaySeconds` sparingly; it's more reliable to trigger off of ability or damage than a delay from a much earlier cast or ability (the stack=>spread example calling spread would use the stack damage) -- knockbacks generally use 5s of delay to make sure that when the trigger text appears it is safe to hit the button -- prefer using a normal trigger over a timeline trigger, as timeline triggers can be inconsistent from unknown hp pushes -- always use `suppressSeconds` for timeline triggers (TODO: make this automatic or suppress until the next jump) -- if you are using custom OverlayPlugin lines (e.g. MapEffect, any log type >= 256), make sure your triggers fail silently or return basic information if they are not present (as they sometimes are not up to date immediately after a patch) -- never return raw strings or concatenated strings; always return `output.something!()`; each parameter should also be `output.somethingElse!()` or an array of outputs (this allows everything to be translated) -- if you have multiple alerts like `Knockback` and `Spread`, or `Right March` and `Stack` that go off at the same time consider adding some logic to combine them into a single trigger (see: `AAI Statice Pop`) -- if you have really wordy triggers, consider disabling tts for that trigger via `tts: null` -- similarly sound can be disabled via `sound: ''` if you don't want to jump scare people who are waiting for a trigger to do something, or if you have a lot of triggers (e.g. counting multhits) -- sometimes triggers give you information egregiously earlier than you could know otherwise; sometimes it's extremely helpful (e.g. caster uptime) and so don't worry overmuch about making things "fair"; if the information comes so early that you might forget it and there's nothing else going on, feel free to delay it until a more reasonable time - -## File Structure - -Each trigger file is a TypeScript module that exports a single trigger set. - -See: [trigger.d.ts](../types/trigger.d.ts) for more details. - -```typescript -import ZoneId from '../path/to/resources/zone_id'; -// Other imports here. - -export default { - id: 'TheWeaponsRefrainUltimate', - zoneId: ZoneId.TheWeaponsRefrainUltimate, - zoneLabel: { - en: 'The Weapon\'s Refrain (Ultimate)', - }, - loadConfigs: ['TheUnendingCoilOfBahamutUltimate'], - config: [ - { - id: 'someOptionId', - comment: { - en: 'This text will show up in the cactbot config ui to explain this to users.', - }, - name: { - en: 'Turn on Fancy Option', - }, - type: 'checkbox', - default: false, - }, - ], - resetWhenOutOfCombat: true, - overrideTimelineFile: false, - timelineFile: 'filename.txt', - timeline: `hideall "Reset"`, - timelineReplace: [ - { - locale: 'en', - replaceText: { - 'regexSearch': 'strReplace', - }, - replaceSync: { - 'regexSearch': 'strReplace', - }, - }, - ], - timelineTriggers: [ - { /* ..trigger 1.. */ }, - { /* ..trigger 2.. */ }, - { /* ..trigger 3.. */ } - ], - triggers: [ - { /* ..trigger 1.. */ }, - { /* ..trigger 2.. */ }, - { /* ..trigger 3.. */ }, - ] -}; -``` - -### Trigger Set Properties - -**id** -A unique string to identify this trigger set. -This value should be unique among all trigger sets. -For cactbot triggers, this should exactly match the `ZoneId` itself for consistency. -If there are multiple zones, then pick a reasonable string, -e.g. `'EurekaOrthosGeneral'` for the set that applies to all Eureka Orthos floors. - -**zoneId** -A shortened name for the zone to use these triggers in. -The set of id names can be found in [zone_id.ts](../resources/zone_id.ts). -Prefer using this over zoneRegex. -A trigger set must have one of zoneId or zoneRegex to specify the zone -(but not both). - -**zoneLabel** -An optional name to use for this trigger set in the configuration interface. -Overrides the zone name from [zone_info.ts](../resources/zone_info.ts). - -**initData** -A function that can be used to initialize the data this trigger set uses. -It should return an object that sets values for any fields in `data` that need to be initialized. -This function is called any time the fight is reset, mainly on zone change or wipe. -See [t1.ts](../ui/raidboss/data/02-arr/raid/t1.ts) for an example implementation. - -**zoneRegex** (deprecated) -A regular expression that matches against the zone name (coming from ACT). -If the regular expression matches, then the triggers will apply to that zone. - -For players in CN/KR, zone names can be Chinese/Korean, though other players always see English. Your Regex should cover them. The current zone name can be found on title or main UI of ACT. - -**config** -An array of `ConfigEntry` objects. -Each object is an option exposed to the user in the [cactbot config UI](CactbotCustomization.md#using-the-cactbot-ui). -See: [user_config.ts](../resources/user_config.ts) for more details. -This is the same format the config ui uses, exposed into a trigger set. -These are exposed via `data.triggerSet.optionName` where `optionName` is the `id` from the `ConfigEntry`. - -**loadConfigs** -An array of strings, which correspond to trigger set ids. - -By default, not all config values from all files are available in `data.triggerSet`. -The current file is always loaded. -If you need to load the config options from another file, -you can specify the `id` from the other trigger set. - -**overrideTimelineFile** -An optional boolean value that specifies that the `timelineFile` and `timeline` -specified in this trigger set override all timelines previously found. -This is a way to replace timelines in user files and is not used inside cactbot itself. - -**timelineFile** -An optional timeline file to load for this zone. -Timeline files in cactbot should be named the same as the `.ts` file they come from, -but with a `.txt` extension instead. -These files live alongside their parent trigger file in the appropriate folder. (As for example `raidboss/data/04-sb/raid/`). - -**timeline** -Optional extra lines to include as part of the timeline. -The value may be a string or an array of strings, -or a `function(data)` that returns string or an array of strings, -or an array contains different kinds of items above. - -There is a complete example that uses the **timeline** property in [test.ts](../ui/raidboss/data/00-misc/test.ts). - -**replaceText** -Key:value pairs to search and replace in timeline ability names. The display name for that ability is changed, but all `hideall`, `infotext`, `alerttext`, `alarmtext`, etc all refer to the original name. This enables translation/localization of the timeline files without having to edit those files directly. - -**replaceSync** -Key:value pairs to search and replace in timeline file sync expressions. Necessary if localized names differ in the sync regexes. - -**resetWhenOutOfCombat** -Boolean, defaults to true. -If true, timelines and triggers will reset automatically when the game is out of combat. -Otherwise it's necessary to manually call `data.StopCombat()`. -This is used for sections where you want the timeline to keep running if you're not in combat, -such as in Bozja or in Phantom Train where sometimes combat stops between train cars. - -**triggers** / **timelineTriggers** - -An array of raidboss triggers. See below for the structure. - -Timeline triggers (whose regex matches timeline text) are in their own section. - -## Trigger Structure - -```typescript -{ - id: 'id string', - type: 'StartsUsing', - disabled: false, - // Note: see `NetFields` from [net_fields.d.ts](https://github.com/quisquous/cactbot/blob/main/types/net_fields.d.ts) - // Note: `netRegex: NetRegexes({ id: 'some-id', source: 'some-name' })` is still supported for backwards compatibility. - netRegex: { id: 'some-id', source: 'some-name' }, - // Note: prefer to use the regex helpers from [regexes.ts](https://github.com/quisquous/cactbot/blob/main/resources/regexes.ts) - regex: Regexes.ability({ id: 'some-id', source: 'some-name' }), - condition: function(data, matches, output) { return true if it should run }, - preRun: function(data, matches, output) { do stuff.. }, - delaySeconds: 0, - durationSeconds: 3, - suppressSeconds: 0, - promise: function(data, matches, output) { return promise to wait for resolution of }, - sound: '', - soundVolume: 1, - response: Responses.doSomething(severity), - alarmText: {en: 'Alarm Popup'}, - alertText: {en: 'Alert Popup'}, - infoText: {en: 'Info Popup'}, - tts: {en: 'TTS text'}, - run: function(data, matches, output) { do stuff.. }, - outputStrings: { - key1: { en: 'output1 ${value}'}, - key2: { en: 'output2 ${value}'}, - }, - comment: { en: 'comment text' }, -}, -``` - -### data, matches, output - -Almost all trigger fields can either return a value or a `function(data, matches, output)`. -For such functions: - -- `data` is a consistent object that is passed to all triggers. - Values can be set on it, - and they will be there for any following functions to use. -- `matches` is the matches from the trigger, - specifically the `matches.groups` field. -- `output` is a special object for turning fields in `outputStrings` into strings to return. - See the `outputStrings` section below for more info. - For triggers that return numbers, e.g. `delaySeconds` or `durationSeconds` and - for triggers that don't output anything, e.g. `preRun` or `run`, - the output field is largely meaningless. - -### Trigger Properties - -**id string** - An id string for the trigger. - Every built-in trigger in cactbot has a unique id, - and it is recommended but not required that user triggers also have them. - -Trigger ids must be unique. -If a trigger is found with the same id as a previous trigger, -then the first trigger will be skipped entirely -and the second trigger will override it and take its place. -This allows easier for copying and pasting of triggers into user overrides for edits. -Triggers without ids cannot be overridden. - -The current structure for `Regexes/NetRegexes` does not require that the ability/effect/whatever name be present as part of the expression. -Because of this, it is extremely important that that information is somewhere close by. -Recommended practice is either to have the effect/ability/NPC name in the trigger ID itself, -or in an explanatory comment alongside. Context solely from the trigger body is not necessarily sufficient! -(As with the id, only triggers intended for the cactbot repository must have this information.) - -**disabled: false** -If this is true, the trigger is completely disabled and ignored. -Defaults to false. - -**netRegex / regex** -The regular expression cactbot will run against each log line -to determine whether the trigger will activate. -The `netRegex` version matches against network log lines, -while the `regex` version matches against parsed ACT log lines. - -More commonly, however, a regex replacement is used instead of a bare regex. -Helper functions defined in [regexes.ts](https://github.com/quisquous/cactbot/blob/main/resources/regexes.ts) -and in [netregexes.ts](https://github.com/quisquous/cactbot/blob/main/resources/netregexes.ts) -take the parameters that would otherwise be extracted via match groups. -From here, the functions automatically construct the regex that should -be matched against. -Unsurprisingly, for `netRegex` use the `NetRegexes` helper -and for `regex` use the `Regexes` helper. - -`regex` and `netRegex` lines are auto-translated using the `timelineReplace` section. - -**condition: function(data, matches, output)** -Activates the trigger if the function returns `true`. -If it does not return `true`, nothing is shown/sounded/run. -If multiple functions are present on the trigger, this has first priority to run. -(Pre-made "canned" conditions are available within [conditions.ts](https://github.com/quisquous/cactbot/blob/main/resources/conditions.ts). -Generally speaking it's best to use one of these if it fits the situation.) - -**preRun: function(data, matches, output)** -If the trigger activates, the function will run as the first action after the activation condition is met. - -**delaySeconds** -An amount of time, in seconds, to wait from the time the regex match is detected until the trigger activates. -May be a number or a `function(data, matches, output)` that returns a number. -This runs after `preRun` and before the `promise`. - -**promise: function(data, matches, output)** -If present and a function which returns a promise, -will wait for promise to resolve before continuing with trigger. -This runs after the delay from `delaySeconds`. - -**durationSeconds** -Time, in seconds, to display the trigger text. -May be a number or a `function(data, matches, output)` that returns a number. If not specified, defaults to 3. - -**suppressSeconds** -Time to wait, in seconds, before showing this trigger again. -May be a number or a `function(data, matches, output)`. -The time to wait begins at the time of the initial regex match -and is unaffected by presence or absence of a delaySeconds value. -Once a trigger with this element activates, -it will not activate again until after its timeout period is over. - -**sound** -Sound file to play, or one of 'Info', 'Alert', 'Alarm', or 'Long'. -Paths to sound files are relative to the ui/raidboss/ directory. - -**soundVolume** -Volume between 0 and 1 to play the sound associated with the trigger. - -**response** -A way to return infoText/alertText/alarmText/tts all from a single entrypoint. -Also used by `resources/responses.ts`. -Response has less priority than an explicitly specified text or tts, -and so can be overridden. -(As with `regex` and `condition`, "canned" responses are available within [responses.ts](https://github.com/quisquous/cactbot/blob/main/resources/responses.ts).) - -**alarmText** -Displays a text popup with Alarm importance when the trigger activates. -This is for high-priority events where failure is guaranteed to kill you, -is likely to wipe the encounter, -or will otherwise make successful completion much more difficult. -(Examples include Allagan Rot in T2, Cursed Shriek in T7, or Ultros' Stoneskin cast in O7s.) -May be a string or a `function(data, matches, output)` that returns a string. - -**alertText** -Displays a text popup with Alert importance when the trigger activates. -This is for medium-priority events that might kill you, -or inflict party-wide damage/debuffs. -(For example, warning the main tank that a buster is incoming, or warning the entire party of an upcoming knockback.) -May be a string or a `function(data, matches, output)` that returns a string. - -**infoText** -Displays a text popup with Info importance when the trigger activates. -This is for low-priority events that will be merely annoying if not attended to immediately. -(For example, warning of an add spawn, or informing healers of incoming raid damage.) -May be a string or a `function(data, matches, output)` that returns a string. - -**tts** -An alternative text string for the chosen TTS option to use for callouts. -This can be a localized object just like the text popups. -If this is set, but there is no key matching your current language, -Raidboss will default to the text from the text popups. - -For example, consider this configuration: - -```typescript -{ - ... - infoText: { - en: 'Tank Buster', - de: 'AoE', - fr: 'Cleave', - }, - tts: { - de: 'Spread', - }, -} -``` - -If your language is `en`, you will receive the `Tank Buster` message. -If your language is `de`, you will receive the `Spread` message. - -**run: function(data, matches, output)** -If the trigger activates, the function will run as the last action before the trigger ends. - -**outputStrings** -`outputStrings` is an optional indirection -so that cactbot can provide customizable UI for overriding trigger strings. -If you are writing your own triggers, you don't need to use this, -and you can just return strings directly from output functions -like `alarmText`, `alertText`, `infoText`, etc. - -The `outputStrings` field is an object mapping `outputStrings` keys to translatable objects. -These translatable objects should have a string entry per language. -In the string, you can use `${param}` constructions to allow for functions to pass variables in. - -Here are two example `outputStrings` entries for a tank buster: - -```typescript -outputStrings: { - noTarget: { - en: 'Tank Buster', - de: 'Tank buster', - fr: 'Tank buster', - ja: 'タンクバスター', - cn: '坦克死刑', - ko: '탱버', - }, - onTarget: { - en: 'Tank Buster on ${name}', - de: 'Tank buster auf ${name}', - fr: 'Tank buster sur ${name}', - ja: '${name}にタンクバスター', - cn: '死刑 点 ${name}', - ko: '"${name}" 탱버', - }, -}, -``` - -`noTarget` and `onTarget` are the two keys for the `outputStrings`. - -Here's an example using these `outputStrings`, passing parameters to the `onTarget` version: - -```typescript -alarmText: (data, matches, output) => { - return output.onTarget!({ name: data.party.member(matches.target) }); -}, -``` - -Calling `output.onTarget()` finds the string in `outputStrings.onTarget` for the current language. -For each `param` passed in, it replaces `${param}` in the string with the value. -Then it returns the replaced string for `alarmText` to use. - -Instead of passing a name into any trigger, always use `data.party.member` to pass a player object instead. -This allows the "use job names instead of player names" option to work. -You can always pass any array (of strings or of player objects) as a parameter -and the result will be a list with commas, e.g. `['a', 'b', 'c']` => `'a, b, c'`. - -Similarly, this is another trigger example, without any parameters. - -```typescript -infoText: (data, matches, output) => { - return output.noTarget!(); -}, -``` - -Triggers that use `response` with `outputStrings` are slightly different. -`outputStrings` should not be set on the trigger itself, -and instead `response` should return a function that calls -`output.responseOutputStrings = {};` -where `{}` is the outputStrings object you would have returned from the trigger `outputStrings` field. -This is a bit awkward, but allows response to both return and use `outputStrings`, -and keeps [resources/responses.ts](../resources/responses.ts) more encapsulated. - -For example: - -```typescript -response: (data, matches, output) => { - output.responseOutputStrings = { text: { en: 'Some Text: ${words}' } }; - return { - alarmText: output.text!({ words: 'words word words' }), - }; -}, -``` - -**comment** -An object where keys represent optional strings in various languages. -This property is an optional auxiliary attribute used to display text around the trigger item in the cactbot configuration panel. -You can use it to explain your trigger, leave some descriptive text, or even include a hyperlink. - -Example: - -```typescript -comment: { - en: `Write your annotation text here. Supports HTML tags`, -}, -``` - -## Miscellaneous Trigger Info - -Any field that can return a function (e.g. `infoText`, `alertText`, `alarmText`, `tts`) -can also return a localized object, -e.g. instead of returning 'Get Out', -they can return {en: 'Get Out', fr: 'something french'} instead. -Fields can also return a function that return a localized object as well. -If the current locale does not exist in the object, the 'en' result will be returned. - -If multiple triggers match the same log line, -they will be executed sequentially based on their order in the relevant zone file. - -Trigger elements are evaluated in this order, and must be listed in this order: - -- id -- disabled -- netRegex -- regex -- beforeSeconds (for timelineTriggers) -- (suppressed triggers early out here) -- condition -- preRun -- delaySeconds -- durationSeconds -- suppressSeconds -- (the delaySeconds occurs here) -- promise -- (awaiting the promise occurs here) -- sound -- soundVolume -- response -- alarmText -- alertText -- infoText -- tts -- run -- outputStrings - -## Regular Expression Extensions - -If you're familiar with regular expressions, -you'll note the the `\y{Name}` and `\y{AbilityCode}` are unfamiliar. -These are extensions provided by cactbot for convenience -to avoid having to match against all possible unicode characters -or to know the details of how the FFXIV ACT plugin writes things. - -The set of extensions are: - -- `\y{Float}`: Matches a floating-point number, accounting for locale-specific encodings. -- `\y{Name}`: Matches any character name (including empty strings which the FFXIV ACT plugin can generate when unknown). -- `\y{ObjectId}`: Matches the 8 hex character object id in network log lines. -- `\y{AbilityCode}`: Matches the FFXIV ACT plugin's format for the number code of a spell or ability. -- `\y{Timestamp}`: Matches the time stamp at the front of each log event such as `[10:23:34.123]`. -- `\y{LogType}`: Matches the FFXIV ACT plugin's format for the number code describing the type of log event, found near the front of each log event. - -## Canned Helper Functions - -In order to unify trigger construction and reduce the manual burden of translation, -cactbot makes widespread use of "canned" trigger elements. -Use of these helpers makes automated testing significantly easier, -and allows humans to catch errors and inconsistencies more easily when reviewing pull requests. - -Currently, three separate elements have pre-made structures defined: -[Condition](https://github.com/quisquous/cactbot/blob/main/resources/conditions.ts), [Regex](https://github.com/quisquous/cactbot/blob/main/resources/regexes.ts), [NetRegex](https://github.com/quisquous/cactbot/blob/main/resources/netregexes.ts), and [Response](https://github.com/quisquous/cactbot/blob/main/resources/responses.ts). -`Condition` functions take no arguments. Almost all `Response` functions take one optional argument, `severity`, -used to determine what level of popup text to display to the user when the trigger activates. -`Regex`(`NetRegex`) functions can take several arguments [(`gainsEffect()` is a good example)](https://github.com/quisquous/cactbot/blob/0bd9095682ec15b35f880d2241be365f4bdf6a87/resources/regexes.ts#L348) depending on which log line is being matched against, -but generally a contributor would include the `source`, (name of the caster/user of the ability to match,) -the `id`, (the hex ability ID, such as `2478`,) and whether or not the regex should capture the matches (`capture: false`.) -`Regex`(`NetRegex`) functions capture by default, but standard practice is to specify non-capturing unless a trigger element requires captures. - -A sample trigger that makes use of all these elements: - -```typescript -{ - id: 'TEA Mega Holy Modified', - type: 'StartsUsing', - netRegex: { source: 'Alexander Prime', id: '4A83', capture: false }, - condition: Conditions.caresAboutMagical(), - response: Responses.bigAoe('alert'), -}, -``` - -This is far less verbose than: - -```typescript -{ - id: 'TEA Mega Holy Modified', - netRegex: /^(?:20)\|(?:[^|]*)\|(?:[^|]*)\|(?:Alexander Prime)\|(?:4A83)\|/i, - condition: function(data) { - return data.role == 'tank' || data.role == 'healer' || data.CanAddle(); - }, - alertText: { - en: 'big aoe!', - de: 'Große AoE!', - fr: 'Grosse AoE !', - ja: '大ダメージAoE', - cn: '大AoE伤害!', - ko: '강한 전체 공격!', - }, -}, -``` - -Use of bare regexes is deprecated. *Always* use the appropriate canned function unless there is a very specific -reason not to. Attempting to use a bare regex will cause a build failure when the pull request is submitted. -If a bare regex must be used for whatever reason (if, say, a new log line is added to ACT,) -pull requests to update `regexes.ts` are strongly encouraged. - -(Note that if you are writing triggers for just your personal use, you are free to do what you want. -This deprecation applies only to work intended for the cactbot repository.) - -Use of canned conditions and responses is recommended where possible, although -given Square's extremely talented fight design team, it's not always going to *be* possible. - -## Outputs - -In order to reduce duplications across trigger sets, -cactbot has a set of locale strings that includes text repeatedly used by triggers. -When writing triggers, prefer using `Outputs` if possible to avoid duplication. - -A simple example using `outputStrings` and `Outputs` as below: - -```typescript -{ - id: 'E9S Zero-Form Devouring Dark', - type: 'StartsUsing', - netRegex: { id: '5623', source: 'Cloud Of Darkness' }, - durationSeconds: 4, - alertText: function(data, matches, output) { - if (data.me === matches.target) - return output.tankBusterOnYou(); - - if (data.role === 'tank') - return output.tankSwap(); - - if (data.role === 'healer') - return output.tankBusters({ player: data.party.member(matches.target) }); - }, - infoText: function(data, _matches, output) { - if (data.role !== 'tank' && data.role !== 'healer') - return output.avoidLaser(); - }, - outputStrings: { - tankBusterOnYou: Outputs.tankBusterOnYou, - tankBusters: Outputs.tankBusters, - tankSwap: Outputs.tankSwap, - avoidLaser: { - en: 'Avoid Laser', - de: 'Laser ausweichen', - fr: 'Évitez le laser', - ja: 'レーザー注意', - cn: '躲避击退激光', - ko: '레이저 피하기', - }, - }, -}, -``` - -## Timeline Info - -The trigger subfolders may contain timeline text files in the format defined by ACT Timeline plugin, which described in here: - - -Each timeline file Cactbot uses has to be loaded by a relative directory reference from the given [TRIGGER-FILE].js. Typically the filename for the timeline file will match the name of the trigger file, and for specific encounters the filenames should at least loosely match the zone name. - -Cactbot implements some extensions to the original format. -These extensions can appear in the file itself or in the `timeline` field in the triggers. -That said, generally these aren't used and timeline triggers are written instead. - -**infotext "event name" before 1** -Show a info-priority text popup on screen before an event will occur. The `event name` matches a timed event in the file and will be shown before each occurrence of events with that name. By default the name of the event will be shown, but you may specify the text to be shown at the end of the line if it should be different. The `before` parameter must be present, but can be 0 if the text should be shown at the same time the event happens. Negative values can be used to show the text after the event. - -**Example infotext which shows the event name 1s before the event happens** -`infotext "event name" before 1` - -**Example infotext which specifies different text to be shown earlier** -`infotext "event name" before 2.3 "alternate text"` - -**Example alert-priority popups using the same parameters** -`alerttext "event name" before 1` -`alerttext "event name" before 2.3 "alternate text"` - -**Example alarm-priority popups using the same parameters** -`alarmtext "event name" before 1` -`alarmtext "event name" before 2.3 "alternate text"` - -## Translation Overview - -This section mostly applies to raidboss (and so is in this document), -but the parts about code translation apply to all parts of cactbot. - -Most cactbot developers play FFXIV in English, -and so any PRs to translate anything missing is much appreciated. -If you need help using github or git, please ask. - -Running `npm run coverage-report` will generate the cactbot coverage report, -which can be found online [here](https://quisquous.github.io/cactbot/util/coverage/coverage.html). - -This report includes links to all of the missing translations: - -- [missing_translations_de.html](https://quisquous.github.io/cactbot/util/coverage/missing_translations_de.html) -- [missing_translations_fr.html](https://quisquous.github.io/cactbot/util/coverage/missing_translations_fr.html) -- [missing_translations_ja.html](https://quisquous.github.io/cactbot/util/coverage/missing_translations_ja.html) -- [missing_translations_cn.html](https://quisquous.github.io/cactbot/util/coverage/missing_translations_cn.html) -- [missing_translations_ko.html](https://quisquous.github.io/cactbot/util/coverage/missing_translations_ko.html) - -You can run `npm run util` and select find translations using the ui. -You can also run `npm run util -- findTranslations -f . -l fr` -(or `-l de` or `-l cn` etc) -if you don't want to select options manually. -This script generates the same information that the online version has. - -These reports have several different categories of errors: - -- other: general miscellaneous errors, usually not related to any line -- code: a block of TypeScript code is missing a translation -- sync: a trigger or a timeline `Ability { source: "something" }` line is missing a translation -- text: timeline text (e.g. `2.0 "text"`) is missing a translation - -### Code Translations - -Most bits of code in cactbot use `LocaleText` for any piece of text -that needs to be translated. - -This ends up looking like an object with keys for each language, -in the order `en`, `de`, `fr`, `ja`, `cn`, `ko`. -Tests will complain if you put them in a different order. -This order is "English first", then "alphabetical for the international version", -and finally "alphabetical for the other regional versions". -English is always required. - -Here's an example, -where the missing translation report for Japanese says this: `ui/oopsyraidsy/data/06-ew/raid/p4n.ts:78 [code] text: {`. -The `text: {` part of the line is the beginning of the code that is missing the translation. -The html report links above have links to the code directly. - -This example corresponds to [code](https://github.com/quisquous/cactbot/blob/e47d34b/ui/oopsyraidsy/data/06-ew/raid/p4n.ts#L78-L84) like this: - -```typescript - text: { - en: 'DPS Tower', - de: 'DD-Turm', - fr: 'Tour DPS', - cn: 'DPS塔', - ko: '딜러 장판', - }, -``` - -As you can see, this object is missing the `ja` key and needs somebody to add it in. - -### Raidboss Translations - -For `sync` and `text` errors, -these must be fixed using the (now poorly named) `timelineReplace` section. -(Once upon a time, this was only for timeline translations. -Now it also handles trigger `netRegex` and `regex` translations as well. -However, for backwards compatibility it's still called `timelineReplace`.) - -It looks something like this: - -```typescript - { - 'locale': 'fr', - 'replaceSync': { - 'Kokytos\'s Echo': 'spectre de Cocyte', - 'Kokytos(?!\')': 'Cocyte', - // etc - }, - 'replaceText': { - 'Aero IV': 'Giga Vent', - 'Archaic Demolish': 'Démolition archaïque', - // etc - }, - }, -``` - -The `replaceSync` section applies to `sync /etc/` parts of lines in the timeline file, -as well as any fields in `netRegex` lines in the trigger file. -The `replaceText` section only applies to the `"Text"` part of lines in the timeline file. -All matches are case insensitive. - -Internally, cactbot takes the `timelineReplace` section and applies it (logically) like this, -so that timelines and triggers will work in French: - -```diff -# p9s.txt timeline file --168.7 "Archaic Demolish" sync / 1[56]:[^:]*:Kokytos:816D:/ -+168.7 "Démolition archaïque" sync / 1[56]:[^:]*:Cocyte:816D:/ -``` - -```diff - // p9s.ts trigger file - { - id: 'P9S Archaic Demolish', - type: 'StartsUsing', -- netRegex: { id: '816D', source: 'Kokytos', capture: false }, -+ netRegex: { id: '816D', source: 'Cocyte', capture: false }, - alertText: (_data, _matches, output) => output.healerGroups!(), - outputStrings: { - healerGroups: Outputs.healerGroups, - }, - }, -``` - -#### Common Replacements - -To avoid having to repeat common translations, -the [common_replacement.ts](https://github.com/quisquous/cactbot/blob/main/ui/raidboss/common_replacement.ts) -file has a `export const commonReplacement` variable with common `replaceSync` and `replaceText` entries -that are implicitly added to all raidboss trigger sets. - -There is no need to repeat these entries (and `npm run test` will give you an error if you try to.) - -#### Collisions - -One important part of translations is that there is NO guaranteed ordering -of how entries in the `replaceSync` and `replaceText` sections are applied. -The reason for this is that it makes it conceptually easier to review a -translation section as for any given substring of text, -at most one entry will apply to it. - -To make this possible, -there's a bunch of "collision" tests in cactbot to make sure that translation entries -don't collide and try to translate the same piece of text differently. -These tests are probably a great source of frustration to people writing translations, -but it prevents a lot of bugs. - -If you have translations but get stuck on errors, -please upload your translation PR with errors and ask for help. - -#### Pre-translation Collision - -One error that `npm run test` might give you is a "pre-translation collision". -This means that two entries in the `replaceSync` or `replaceText` section -both apply to the same text AND cannot be applied in either order. - -Here's a p9s example again, slightly modified. - -```typescript - { - 'locale': 'fr', - 'replaceSync': { - 'Kokytos\'s Echo': 'spectre de Cocyte', - 'Kokytos': 'Cocyte', - // etc - }, - }, -``` - -Let's say we're trying to translate `Kokytos's Echo`. -Both of these entries match, -so there's two orders that these two translations could apply. -We can replace `Kokytos` and then `Kokytos's Echo` or vice versa. - -If we apply `Kokytos's Echo` first, then `Kokytos's Echo` becomes `spectre de Cocyte`, -and then the `Kokytos` translation no longer applies. This is a correct translation. -However, if we apply `Kokytos` first, then `Kokytos's Echo` becomes `Cocyte's Echo`, -and then the `Kokytos's Echo` translation no longer applies, but this is wrong! - -You can see here that applying these translations in different orders produces different results, -which is why there's a pre-collision test error. - -The way to fix this is to use regular expression -"negative lookahead" `(?!text)` or "negative lookbehind" `(? Stack` and then once the spreads go off call `Stack` after that. -It's both a reminder (for people lost in the sauce) but if the second call triggers on the first damage, -it can tell people when it it safe to start moving and the initial mechanic has locked in. - -See: [AAI Ketuduke Hydro Buff Double](https://github.com/quisquous/cactbot/blob/main/ui/raidboss/data/06-ew/dungeon/another_aloalo_island.ts#:~:text=id%3A%20%27AAI%20Ketuduke%20Hydro%20Buff%20Double%27) -and [AAI Ketuduke Hydro Buff Double Followup](https://github.com/quisquous/cactbot/blob/main/ui/raidboss/data/06-ew/dungeon/another_aloalo_island.ts#:~:text=id%3A%20%27AAI%20Ketuduke%20Hydro%20Buff%20Double%20Followup%27) - -### Three-step Mechanics - -There are a few three (or more)-step mechanics in the game that have a sequence of moves to do. -Quintuplecast, P12S door boss wings, and Another Mount Rokkon Triple Kasumi-giri are all examples. - -A common way to call these mechanics that have tells is something like the following, -where "first", "second", "third" could all be things like "spread" or "left" or "swap". - -- [first tell] alert: `First mechanic` (long duration until the first mechanic goes off, so you don't forget while you wait for the tells) -- [second tell] info: `(then second mechanic)` (short duration) -- [third tell] info: `first => second => third` (long duration, so you can read what's coming next while the mechanics go off) -- [first mechanic goes off] alert: `Second` (first tell duration should have ended before this) -- [second mechanic goes off] alert: `Third` - -The benefits of this are: - -- allows somebody to raidcall if they want / everybody is prepared for the steps -- maximum one alert and one info text on screen at once -- people can turn off parts of this they don't want -- if final two `Second` and `Third` calls are based on ability ids of the `First` and `Second` abilities going off, then it's safe to do that movement when it goes off - -See: [P12S First Wing](https://github.com/quisquous/cactbot/blob/main/ui/raidboss/data/06-ew/raid/p12s.ts#:~:text=id%3A%20%27P12S%20First%20Wing%27), -[P12S Wing Collect](https://github.com/quisquous/cactbot/blob/main/ui/raidboss/data/06-ew/raid/p12s.ts#:~:text=id%3A%20%27P12S%20Wing%20Collect%27), -[P12S Wing Followup](https://github.com/quisquous/cactbot/blob/main/ui/raidboss/data/06-ew/raid/p12s.ts#:~:text=id%3A%20%27P12S%20Wing%20Followup%27) - -See also: [AMR Moko Triple Kasumi-giri triggers](https://github.com/quisquous/cactbot/blob/main/ui/raidboss/data/06-ew/dungeon/another_mount_rokkon.ts) - -## Future Work - -- make it easier to find fights and options in the cactbot config (it's kind of tedious now) -- add "shared outputstrings" to a trigger set so multiple triggers can use the same set, there's less translation duplication, and users don't have to add overrides everywhere -- similarly make it possible to share config overrides in criterion vs criterion savage -- raidemulator: show multiple trigger outputs from the same trigger: -- raidemulator: fix resetting issues: + diff --git a/docs/RemoteCactbot.md b/docs/RemoteCactbot.md index 37a5fe93e0..9f0c1c3c28 100644 --- a/docs/RemoteCactbot.md +++ b/docs/RemoteCactbot.md @@ -1,151 +1,5 @@ -# Remote Cactbot +# Permanently Moved to OverlayPlugin/cactbot -This is a guide for allowing others to get their own cactbot callouts and DPS numbers without running ACT themselves. -This is ideal for PS4 users who can't use ACT as well as DX9 users. +See: [docs/RemoteCactbot.md](https://github.com/OverlayPlugin/cactbot/blob/main/docs/RemoteCactbot.md) -The recommended method is to use the port forwarding built into OverlayPlugin. -Port forwarding can give personalized triggers to each person connected. -This method also will allow others to view dps meters -or any other kind of overlay. - -## Alternatives - -Another way is to use the [discord bot plugin](https://github.com/Makar8000/ACT-Discord-Triggers/wiki/First-Time-Setup-Guide). -The discord bot will play all of the tts from cactbot that you would hear -and play it for the rest of the party. - -For cactbot, this is less great -because many cactbot triggers are personalized -and it is not meant to be a generalized "raid caller" -even if it sort of works that way. -Currently, cactbot sounds do not play through the plugin. - -If a single discord tts raidcaller is what you are really looking for, -[Triggernometry](https://github.com/Aho-Senpai/Aho-Triggers/blob/main/Triggernometry/Docs/FAQ.md#discord-callouts-ready) -will be a better fit than cactbot for what you are trying to do. - -## Port Forwarding Overview - -Here are the steps you need to follow as the person running ACT: - -- [Setup OverlayPlugin WSServer](#setup-overlayplugin-wsserver) -- [Test Your Connection](#test-your-connection) -- [Connect to Remote ACT](#connect-to-remote-act) -- [Configure Raidboss](#configure-raidboss) - -If you get lost, see the [HALP](#halp) section. - -## Setup OverlayPlugin WSServer - -OverlayPlugin runs a server which accepts [WebSocket](https://en.wikipedia.org/wiki/WebSocket) connections -that allows other applications to use ACT's data. -(This is different from ACTWebSocket which is no longer maintained -and cactbot does not support.) -It also has built in support for ngrok to enable port forwarding -so that others outside your local network can connect to that WebSocket server. - -To enable this in ACT, -go to **Plugins** -> **OverlayPlugin WSServer** -> **Shared Overlay** - -![image](images/remote_wsserver.png) - -On this page, select your region. -Click `Start`. -Your screen should look like the above image with -`Launching ngrok...Done!` -and it should say -`Status: Active` -at the top of the screen. - -### Test Your Connection - -Now that you've established port forwarding, test your connection. -In all of these examples in the rest of the documentation, -replace the `a31a5403.ngrok.io` with the url shown in the log window. -Going to -(substituting your server url) should give you the following "It Works" page. - -![image](images/remote_itworks.png) - -If this is set up properly, -you can select an overlay preset like `Cactbot Test` -and it will give you a url to open in a browser, e.g. - - -That file is a web page being served by github, -but the query string tells the overlay web page to connect to your ACT instance. -With ACT and FFXIV open, you should see the test data update based on your actions in game. - -![image](images/remote_testui.png) - -If this is not working or you are not seeing any information, see the [HALP](#halp) section. - -## Connect to Remote ACT - -Now, friends can connect to your ACT remotely. -Unfortunately, all of the overlay html pages needs to be hosted remotely. -Rather than loading local urls from your filesystem like -`C:\Users\tinipoutini\cactbot\ui\raidboss\raidboss.html` -you need to use github urls like -`https://quisquous.github.io/cactbot/ui/raidboss/raidboss.html`. - -In general, you can use the URL Generator at the bottom of the OverlayPlugin tab -to generate urls for any presets. - -For example: - -- cactbot raidboss overlay: -- rdmty DPS overlay: -- cactbot oopsy: - -### Configure Raidboss - -If you load raidboss remotely, you will see a player selection dialog. - -![image](images/remote_playerselect.png) - -The list of players will populate once you are in an instance. -Choose yourself in this list, and click `Start Overlay` to begin raidboss. - -If you want TTS and the person hosting does not have TTS turned on, -you can enable this with the `Force Enable Text To Speech` checkbox. - -Unfortunately at the moment, -all of the cactbot customization is local -and any remote player will inherit the customization of the host. -The only option you can override right now is to enable TTS. - -In the future, it may be possible to append some additional parameters to -load your own user directory, but not currently. - -### Other Overlays - -Not all overlays are able to be run remotely. -cactbot, oopsy, pullcounter, and dps overlays are supported. - -radar and jobs will not work -as they require more information about your player. - -## HALP - -If you get stuck or confused on any of these steps, -the best place to get help is the [FFXIV ACT discord](https://discord.gg/ahFKcmx) #troubleshooting channel. -There is no cactbot discord; you should go here. - -Port forwarding is not really part of cactbot, -so please only file a cactbot github issue if your raidboss party override doesn't work. - -### Troubleshooting Hints - -If you are looking at url in the browser like - -and you don't see any data, even though you are in game, -ACT is running, -and the ACT WSServer is running, then you should look at devtools. - -In Chrome, right click on the page and go to `Inspect` (or hit Ctrl+Shift+I). -Then click on `Console` to show the console output: - -![image](images/remote_devtools.png) - -This will give you more information to help diagnose your problem. + diff --git a/docs/TimelineGuide.md b/docs/TimelineGuide.md index 2dac6fc354..cce216a3a2 100644 --- a/docs/TimelineGuide.md +++ b/docs/TimelineGuide.md @@ -1,1259 +1,5 @@ -# Timeline Guide +# Permanently Moved to OverlayPlugin/cactbot -This is a guide for people who want to write timelines, -primarily for cactbot. +See: [docs/TimelineGuide.md](https://github.com/OverlayPlugin/cactbot/blob/main/docs/TimelineGuide.md) -![import screenshot](images/timelineguide_timeline.png) - -cactbot uses the [raidboss module](https://github.com/quisquous/cactbot#raidboss-module) -for triggers and timelines. -These are combined together so that you can make triggers that are based on actions -or triggers that are based on timelines themselves. - -## Table of Contents - -* [Timeline Guide](#timeline-guide) - * [Table of Contents](#table-of-contents) - * [History](#history) - * [How do Timelines Work](#how-do-timelines-work) - * [Timeline File Syntax](#timeline-file-syntax) - * [Comments](#comments) - * [Entries](#entries) - * [Commands](#commands) - * [Basic Testing](#basic-testing) - * [Cactbot Style Guide](#cactbot-style-guide) - * [Guidelines](#guidelines) - * [Trigger Filenames](#trigger-filenames) - * [Pre-timeline combat, starts & resets, and multiple zones](#pre-timeline-combat--starts---resets--and-multiple-zones) - * [Timeline Triggers](#timeline-triggers) - * [Timeline Injection](#timeline-injection) - * [Timeline Translation](#timeline-translation) - * [Making a Timeline](#making-a-timeline) - * [Run the fight a few times](#run-the-fight-a-few-times) - * [Software prerequisites](#software-prerequisites) - * [Timeline Skeleton](#timeline-skeleton) - * [Using make_timeline.ts](#using-make-timelinets) - * [Timeline Ability Tables](#timeline-ability-tables) - * [One Hacky Workflow Suggestion](#one-hacky-workflow-suggestion) - * [Using make_timeline.ts with fflogs](#using-make-timelinets-with-fflogs) - * [Using test_timeline.ts](#using-test-timelinets) - * [Using test_timeline.ts with fflogs](#using-test-timelinets-with-fflogs) - * [Common Timeline Edits](#common-timeline-edits) - * [`-ii` to ignore abilities](#--ii--to-ignore-abilities) - * [`-p` for later phases](#--p--for-later-phases) - * [Targetable Lines](#targetable-lines) - * [Ignoring Combatants](#ignoring-combatants) - * [Adjusting Blocks of Timelines](#adjusting-blocks-of-timelines) - * [Variations vs Simultaneous Abilities](#variations-vs-simultaneous-abilities) - * [Basic Loops](#basic-loops) - * [Branches](#branches) - * [HP% Pushes](#hp--pushes) - * [Doubled Abilities](#doubled-abilities) - * [Doubled Abilities with suffixes](#doubled-abilities-with-suffixes) - * [Multi-hit Abilities](#multi-hit-abilities) - * [Numbering Important Mechanics](#numbering-important-mechanics) - * [Renaming Abilities to `--sync--`](#renaming-abilities-to----sync---) - * [Future Work](#future-work) - * [Smaller Fixes/Changes](#smaller-fixes-changes) - * [Larger Features](#larger-features) - * [Ability Table](#ability-table) - -## History - -Back in 2016, Shasta Kota on the Death and Taxes website made this -[guide](https://web.archive.org/web/20230426121530/https://dtguilds.enjin.com/forum/m/37032836/viewthread/26353492-act-timeline-plugin) to use with anoyetta's [ACT timeline plugin](https://github.com/anoyetta/ACT.Hojoring). -That plugin is now part of Hojoring. - -There's also an older [kaizoban](https://github.com/090/act_timeline/releases) version of the plugin that some people have used that predates anoyetta's work. - -cactbot timeline files were originally intended to be backwards compatible with these. -Eventually when it became clear that nobody else was using this format, -some breaking changes were added, including: - -* `forcejump` keyword -* `label` keyword -* netregex sync syntax, e.g. `Ability { id: "1234", source: "That Mob" }` instead of `sync /etc/` - -## How do Timelines Work - -You can think about timelines as being a very simple state machine. -There are two states: whether it is paused or not, and the current timeline time. - -They start paused at time=0. -As soon as any sync happens, it jumps to that time, then unpauses. -If it ever jumps to time=0, then it pauses again. - -When playing, the timeline time advances in real time. -In other words, if the timeline time is currently `360.2` -and then exactly two seconds of real time pass, -then the timeline time will automatically move to `362.2`. - -To keep the timeline on track, the timeline is full of syncs -which each have a window of time that they are active for. - -Take the line: -`1350.7 "Melt" Ability { id: "5372", source: "Shiva" } window 20,10` - -This line has a `window 20,10` and a time of `1350.7`. -This means that between the timeline times of `1330.7` and `1360.7` this sync is active. - -Once the current timeline time is within that window of 30 seconds, then it can be synced to. -If the regex `Ability { id: "5372", source: "Shiva" }` matches any network log line, -then the current timeline will jump to `1350.7`. -After that the timeline will continue playing and moving forward in real time. -If that line occurs outside the valid window, it is ignored. - -## Timeline File Syntax - -Each line in a timeline file is considered its own timeline entry. -There is no ordering at all. -The fact that timeline files are ordered is as a convenience to the reader. -(Two lines with the same time do keep their relative ordering.) - -### Comments - -On any line, a hash character `#` signifies a comment. -Everything after that on the current line will be ignored. - -### Entries - -Here are some grammar examples of the timeline. -(The parentheses here indicate optionality and are not literal parentheses.) - -There are only a few keywords: `hideall`, `jump`, `forcejump`, `duration`, `window`, `label`. - -Note: `forcejump` and `label` were added during Endwalker and so -many previous timelines do not use these (but could and should). - -```text -# hideall "[string]" -hideall "--sync--" -hideall "Reset" - -# [number] label "[string]" -3631.7 label "oschon-p2-loop" -6508.1 label "statice-10-loop" - -# [number] "[string]" (duration [number]) -677.0 "Heavensfall Trio" -1044 "Enrage" # ??? -35.2 "Flare Breath x3" duration 4 - -# [number] "[string]" [LogType] { [params] } (window [number],[number]) (jump [numberOrLabel]) (duration [number]) -685.5 "Warder's Wrath" Ability { id: "662A", source: "Erichthonios" } jump 1077.3 -267.5 "Resonance" Ability { id: "B6B", source: "Kaliya" } window 10,10 jump 217.5 -28.0 "Damning Edict?" Ability { id: "3150", source: "Chaos" } window 30,10 jump 2028.0 - -# [number] "[string]" [LogType] { [params] } (window [number],[number]) (forcejump [numberOrLabel]) (duration [number]) -5639.7 "Made Magic" Ability { id: "8B94", source: "Quaqua" } window 40,40 forcejump "quaqua-right-untouched-loop" -1258.6 "Immolating Shade (light parties)" Ability { id: "8496", source: "Golbez" } forcejump 1600.0 -``` - -`[number]` can be an integer, e.g. `34`, or a float, e.g. `84.381`. - -`[numberOrLabel]` can be a `[number]` (e.g. `42` or `12.8`) -or a label name with double quotes (e.g. `"loop"` or `"branch2"`). - -`"[string]"` is a character string, e.g. `"Liftoff"` or `"Double Attack"` - -`[LogType]` is a key from [netlog_defs.ts](../resources/netlog_defs.ts), -e.g. `Ability` or `StartsUsing` or `AddedCombatant`. -Any line with `[LogType]` and parameters is a "sync". - -`[params]` are a [JSON5](https://json5.org/) object. -The keys are from the `fields` of the `LogType`. -See: the [Log Guide](LogGuide.md) for more explanation. -The values are either strings or arrays of strings. -Style-wise, prefer using bareword keys with double quotes. -(Even if quoted keys or single quotes are legal.) - -These string param fields are interpreted as regular expressions, -so `"E.D.D."` means `E` followed by any character then `D` followed by any character (etc). -If you want escape a regular expression character, you need two backslashes, e.g. -`"E\\.D\\.D\\."` (i.e. matching the literal string `E.D.D.`). - -Additionally, if you are using [sync_files.ts](RaidbossGuide.md#sync-files) for this timeline -you should spell out ability ids in full, e.g. `id: "(8B43|8B46)"` instead of `id: "8B4[36]"`, -so that the script can find and replace them properly. - -The ability time and ability name always need to come first, -but `duration`, `forcejump`, `jump`, `[LogType]`, and `window` -do not have to be in any order with respect to each other. -Stylistically, usually the`[LogType]` sync is put first. - -**duration** is a time in seconds to display the accompanying action. -Usually, timeline entries disappear immediately, -but sometimes an action is ongoing, like 5x Bahamut's Claw in a row. -You can use `duration` to show the action for that length of time. -It does not need a sync to do this. - -The syntax for **duration** is `duration [number]`, -as `duration 5.5`. - -**forcejump** tells the timeline playback to jump to a particular time -if the sync is encountered *or* if the line containing the **forcejump** -is reached without syncing. -This is intended for loops that will always be taken in an encounter. - -When this is used, no "lookahead" loop unrolling is needed, -and the timeline will use the **forcejump** destination to list events in the future, -because it knows that it will always jump there. -If this line syncs prior to time passing it by, -it will behave exactly like a normal **jump**. -If the time passes this line, -then it will jump as if it had synced exactly on time. -This is not handled specially in `test_timeline`, which expects the sync to be correct. -If there is a **window** parameter on the same line, -and its second value extends past the **forcejump** time, -this "overhang window" will still be respected even after force jumping -until the next sync or jump occurs. - -The syntax for **forcejump** is `forcejump [number]` (e.g. `forcejump 204.2`) -or `forcejump [label]` (e.g. `forcejump "quaqua-middle-poison-loop"`). - -**jump** tells the timeline playback to jump to a particular time -if and only if the sync is encountered. -This is usually used for phase pushes and loops that involve multiple blocks. -The timeline controller does not require a timeline entry at the time you jump to, -but common practice is to ensure there there is one for readability and sanity-check purposes. -If you jump to time 0, the timeline will stop playback. - -The syntax for **jump** is `jump [number]` (e.g. `jump 204.2`) -or `jump [label]` (e.g. `jump "Hieroglyphika"`). - -**window** is the time frame in which to consider the sync. -By default, if **window** is not specified, cactbot considers it the -same as specifying `window 2.5,2.5`. -In other words, -2.5 seconds before the ability time and 2.5 seconds after. -As an example, for the line `3118.9 "Lancing Bolt" Ability { id: "3876", source: "Raiden" }`, -if the log line for this ability is encountered anywhere between `3116.4` and `3121.4` -then it will resync the timeline playback to `3118.9`. -Often timelines will use very large windows for unique abilities, -to make sure that timelines sync to the right place even if started mid-fight -or if hp pushes are discovered when the content is no longer current. - -The syntax for **window** is `window [number],[number]` (e.g `window 10,30`). -There is no space after the comma. - -### Commands - -To hide all instances of an ability, you can use the `hideall` command. -Most timelines start with the line `hideall "--sync--"` -to hide syncs that are just used to keep the timeline on track but should not be shown to the player. -Timeline triggers can still match hidden entries. - -There are a number of other commands for generating alerts based on timeline entries. -These are still supported but are not documented. -Instead, alerts based on timelines in cactbot should use [timeline triggers](#timeline-triggers). - -### Basic Testing - -In cactbot, running `npm run test` will run tests to verify that there are no errors. - -## Cactbot Style Guide - -Timelines are extremely subjective. -The goal is to be clear and useful to people using it. -This means cleaning up things that feel "noisy" but leaving in as much as possible. -It requires a good bit of manual work to create a nice feeling and correct timeline. - -In general, cactbot has decided to use actual ability names instead of things like "raidwide". -It's easier to translate, but it is hard to talk about abilities with others if -"raidwide" could mean "Ultima" or "Gaiochos" or "Caloric Theory". - -### Guidelines - -* add syncs for every ability possible. -* if there is only one boss in a zone, the timeline should start on entering combat. -(The timeline utility will usually add the correct line automatically for you.) -* add a sync for the first cast that will start the timeline so that testing with fflogs will sync properly. -* it is generally unnecessary to sync to the first auto-attack. -(In special cases such as raid bosses with checkpoints, -it may be acceptable to sync an auto for the second part of the encounter.) -* remove all other auto attack syncs, as these are often inconsistent. -* include any special command line flags used to generate the timeline in a comment at the top. -(The ignore-combatant and ignore-ability flags -are automatically added by the timeline utility for your convenience.) -* prefer actions for syncs over game log lines, but sync to game log lines if that's the only option. -* if you do sync a phase with game log lines, -add a large window sync for an action after that line for safety. -* if a boss has multiple phases, -and one or more of those phases begins with a to-that-point unique ability, -add a wide sync from the start of the encounter to each phase-opening line. -If this is not possible, still try to add wide syncs to the beginning of each phase. -* use original names for ability text as much as possible. -(If an ability name is uncomfortably long, -or if it otherwise makes sense to modify how it's displayed, -handle that modification in the timeline replacement section of the trigger file.) -* loops should use `jump` or `forcejump` to return to an earlier point in the timeline, -rather than using a wide window sync at the beginning of the loop for readability. -* liberally use whitespace and comments to make the timeline readable. -* do not put any triggers, tts, or any other form of alerts in the timeline file itself. -* use [timeline triggers](#timeline-triggers) from within a trigger file for any alerts. -* prefer using `label` for all jumps rather than jumping to a number. -* for any loop that is always taken, use `forcejump` to automatically provide lookahead. -* for any loop or branch that is conditionally taken, add a lookahead window of at least 30 seconds *and* 6 abilities. -* for any `jump` or `forcejump`, prefer to jump via `StartsUsing` instead of `Ability` where possible to jump sooner. -* comment out syncs from any displayed abilities that are within 3 seconds of each other, -but do not remove them. (This preserves the ability ID for future maintainers.) -* prefer to use `npcNameId` instead of `name` on `AddedCombatant` lines. -* use `-la` with `make_timeline` to print an [ability table](../ui/raidboss/data/06-ew/dungeon/another_aloalo_island.txt#L92-L142) and fill it out. -* As always, be consistent with other timelines. - -### Trigger Filenames - -The general goal of filenames is to be consistent -with what the community calls these fights. -Trials get called by the name of the boss, -raids get abbreviated and numbered, -dungeons are called by their zone. - -For filenames, use underscores to separate words. -For trials like `nm` (normal mode), `hm` (hard mode), `ex` (extreme mode), `un` (unreal mode) -separate with a hyphen. -Dungeons with hard in the name can spell out "Hard" as a full word. -Articles like `The` can be dropped. -Raids are numbered through the tier, -e.g. `t1` through `t13` and `a1s` through `a12s`. -Savage fights should have an `s` suffix -while normal fights have an `n` suffix. -(However, this does not apply to coil raids.) - -Examples: - -* The Grand Cosmos: `grand_cosmos` -* Titan Extreme: `titan-ex` -* Ruby Weapon Extreme: `ruby_weapon-ex` -* Thordan Unreal: `thordan-un` -* The Great Gubal Library (Hard): `great_gubal_library_hard` -* Sigmascape V2.0 (Savage): `o6s` -* Alexander - The Arm of the Father: `a3n` -* The Final Coil of Bahamut: `t13` - -### Pre-timeline combat, starts & resets, and multiple zones - -There is no one-size-fits-all approach for starting and resetting timelines. - -In single-boss, single-zone content (e.g., most trials), -the timeline should start when combat begins, -and should reset on a wipe or when the player is out of combat. - -However, in dungeons for example, the player is often in combat -with mobs before the timeline should begin for the first boss encounter. -For that matter, there are also several boss encounters in each dungeon. -In those situations, we need discrete timelines for each boss encounter, -and each boss's timeline should start only once that boss encounter begins. - -There are a number of ways we can handle this. - -First, by default, cacbot will reset the timeline to time=0 -whenever a player is out of combat. -This default can be overriden in particular fight's trigger set -with the following property: - -```typescript -resetWhenOutOfCombat: false -``` - -This property is only used in selective circumstances (e.g. zones like Eureka), -so we'll approach timeline creation here assuming default behavior. - -The first step is determining how the timeline should begin running. - -If the timeline should begin when the player first begins combat, -we can use OverlayPlugin's 0x104 InCombat line -to detect when the player enters combat: - -```text -0.0 "--sync--" InCombat { inGameCombat: "1" } window 0,1 -``` - -(`make_timeline` generates this line by default where appropriate.) - -However, if there will be pre-timeline combat (e.g., pre-boss mobs), -this would incorrectly start combat during the pre-boss phase, -so we need a different approach. - -In these situations, boss encounters (and timelines) are often tied -to a specific zone within the instance, -which means we can start the timeline when that zone is sealed off. -For example: - -```text -# Landfast Floe will be sealed off -100.0 "--sync--" SystemLogMessage { id: "7DC", param1: "10CD" } window 100000,0 -``` - -(`make_timeline` generates this by default if the encounter begins with a zone seal.) - -This `SystemLogMessage` is the equivalent of the `GameLog` that looks like -`00|2023-12-14T21:02:36.0000000-08:00|0839||The Landfast Floe will be sealed off in 15 seconds!|419a62a812652b97`. -However, by using `SystemLogMessage` with an id, it will work without chat log lines turned on -and it does not require any translations and thus is more robust. - -For multi-zone instances like dungeons, we can effectively create -separate timelines for each encounter in the same timeline file -by using large gaps between the timelines, coupled with large sync windows. - -For example, in [Alzadaal's Legacy](../ui/raidboss/data/06-ew/dungeon/alzadaals_legacy.txt), -we effectively have three separate timelines, -one for each boss encounter, each spaced 1000 seconds apart. -Because the timeline resets to 0 each time the player is out of combat, -we use large sync windows on each zone-seal message to 'jump' the timeline -to the right place for each encounter: - -```bash -# Undersea Entrance will be sealed off -0.0 "--sync--" SystemLogMessage { id: "7DC", param1: "103E" } window 0,1 -# etc etc -# The Threshold of Bounty will be sealed off -1000.0 "--sync--" SystemLogMessage { id: "7DC", param1: "103F" } window 1000,1 -# etc etc -# Weaver's Warding will be sealed off -2000.0 "--sync--" SystemLogMessage { id: "7DC", param1: "1040" } window 2000,1 -``` - -Finally, because cactbot automatically resets the timeline when the player is out of combat, -there is no need to include specific reset lines in most timeline files by default. - -However, if a trigger set contains the property to NOT reset the timeline -when out of combat, there are several options for manualy triggering a reset. - -On fights where the entire zone resets (e.g. all of omegascape, a4s, a8s, a12s, t9, t13), -you can use the ActorControl line that is sent on a wipe: - -```bash -0.0 "--Reset--" ActorControl { command: "4000000F" } window 100000 jump 0 -``` - -On fights with zones that seal and unseal, (e.g. a1s, t1-8) -you can use the zone unsealing message itself to reset: - -```bash -0.0 "--Reset--" SystemLogMessage { id: "7DE" } window 100000 jump 0 -``` - -This `SystemLogMessage` is the equivalent to the `GameLog` line for something like -`00|2023-12-14T21:06:37.0000000-08:00|0839||The Landfast Floe is no longer sealed!|7505dac81c639ea5`. - -## Timeline Triggers - -Trigger files in cactbot support adding timeline triggers. -Because people may have preferences about what triggers they want enabled by default, -and because cactbot timeline syntax extensions are not compatible with other timeline plugins, -cactbot adds all of its timeline triggers from the timeline file. - -This is done by adding a `timelineTriggers` section to the triggers file. - -Examples: - -* [Orbonne Monastery](https://github.com/quisquous/cactbot/blob/main/ui/raidboss/data/04-sb/alliance/orbonne_monastery.ts) -* [T9](https://github.com/quisquous/cactbot/blob/main/ui/raidboss/data/02-arr/raid/t9.ts) -* [O12 normal](https://github.com/quisquous/cactbot/blob/main/ui/raidboss/data/04-sb/raid/o12n.ts) - -These triggers have the [same syntax](https://github.com/quisquous/cactbot/blob/main/ui/raidboss/data/README.txt) as normal triggers. -They still allow you to use functions if you want to return something. -You can use a [condition](https://github.com/quisquous/cactbot/blob/5a7011c662d65f44c12c2fbff255484f2d31b8ef/ui/raidboss/data/02-arr/raid/t9.js#L10) to have it only trigger for a particular job or role. - -However there are a few differences: - -The `regex` should not be translated and should be based off of whatever is exactly in the timeline file. -It matches against the ability name in quotes on the timeline line. -The `matches` parameter to functions will return this. - -They support a `beforeSeconds` parameter, -that allows you to specify how long before the ability to show the trigger. - -## Timeline Injection - -The timeline files themselves are constructed in a way that should be useful for everybody. -However, sometimes people want to hide parts of timelines or add other things to timelines. - -This is not something that cactbot does by default anywhere, and so it is more a user configuration feature. - -The triggers file supports a `timeline` entry which is an array of things to add to the timeline. -If those things are strings, it will add them directly. -If those things are functions, it will call the function and add the return value. -(The `data` parameter passed only contains a player's role and job and not other things.) - -The test timeline in Summerford Farms that you can start by doing a /countdown or /bow-ing to a Striking Dummy has examples of this. -See: [test.ts](../ui/raidboss/data/00-misc/test.ts). - -You can also add timeline entries to your **cactbot/user/raidboss.js** file for personalized timeline entries and triggers. -See: [user/raidboss.js](https://github.com/quisquous/cactbot-user/blob/641488590e3ea499cc3b54cc9f2f2f856dee4ad8/raidboss.js#L28) - -## Timeline Translation - -To support multiple languages, cactbot trigger files support a `timelineReplace` section. -You can see an example in [o12s.js](https://github.com/quisquous/cactbot/blob/ecbb723f097328c7bd0476352e5135bd5f776248/ui/raidboss/data/triggers/o12s.js#L608). -This section contains a bunch of regular expressions to replace in syncs, texts, and effects. -This has two purposes. - -The first purpose is for tools, to autogenerate regular expression translations for triggers. - -The second purpose is for timelines at runtime. -cactbot will use the `replaceSync` section to auto-replace anything inside curly braces on -a timeline line (e.g. `Ability { id: "1234", source: "Ultima" })`) -and the `replaceText` section to auto-replace anything inside the ability text. - -Each `replaceSync` entry is applied against each parameter individually. -Only some parameters are translated (e.g. never `id`, but always `source` or `name`). -Care is needed to make sure that replacements are not overzealous. - -See also: [Trigger Translation Overview](RaidbossGuide.md#translation-overview) - -## Making a Timeline - -### Run the fight a few times - -The first step in making a timeline is generating a few ACT logs. - -cactbot will also let you make timelines from fflogs clears, but this drops many log lines. -In particular, you can't get rp text lines, the text for the zone sealing/unsealing, and new combatants. - -Once you've run the combat, you'll have generated a couple of [network log files](LogGuide.md#network-log-lines). - -Follow those links, click **Raw**, then right click and **Save As** to save them to disk. - -Good guidelines for getting good logs are: - -1. run long enough to see the enrage -1. have enough people to see all the mechanics (e.g. t11 tethers don't appear without two people) -1. per phase, run long enough to see the mechanics loop -1. run several times so you can test it, especially if there are mechanic variations or hp pushes - -It's also recommended that you record video at the same time. -If you don't want to stream publicly, unlisted youtube videos are a good option. - -### Software prerequisites - -* [Node.js](https://nodejs.org/en/) -* A copy of cactbot's [source code](https://github.com/quisquous/cactbot/archive/main.zip) -* See: [CONTRIBUTING.md](../CONTRIBUTING.md#development-workflow) - -### Timeline Skeleton - -There are three things you need to add a new timeline to cactbot. - -Let's say you are creating a log file for the final extreme of Endwalker. - -(1) Create a blank timeline file. - -Add a new file called **ui/raidboss/data/06-ew/raid/zeromus-ex.txt**. -You can leave it blank. - -(2) Add a new triggers file, if it doesn't exist. - -Create **ui/raidboss/data/06-ew/raid/zeromus-ex.ts**. -Timeline files can only be loaded via trigger files, -so the trigger file is always required. - -(Note that these steps are typically already done by repository contributors around patch release.) - -An initial trigger file should look like the following: - -```typescript -import { RaidbossData } from '../../../../../types/data'; -import { TriggerSet } from '../../../../../types/trigger'; - -export type Data = RaidbossData; - -const triggerSet: TriggerSet = { - id: 'TheAbyssalFractureExtreme', - zoneId: ZoneId.TheAbyssalFractureExtreme, - timelineFile: 'zeromus-ex.txt', - triggers: [], -}; - -export default triggerSet; -``` - -(3) Build cactbot. - -Run `npm run build` in the cactbot source directory. -If you never run `npm install` before, you'll need to do that first. - -(4) Reload raidboss - -Make sure the raidboss URL is pointed to `dist/ui/raidboss/raidboss.html`. -If you've changed any of these files, reload your cactbot raidboss -plugin to pick up the changes. - -If you are using `webpack-dev-server`, it will automatically reload whenever you change the source files. - -### Using make_timeline.ts - -For this example, we'll be using the [TheAbyssalFractureExtreme.log](logs/TheAbyssalFractureExtreme.log) file. -Feel free to play along at home. - -From your cactbot directory, use the command: -`node --loader=ts-node/esm util/logtools/make_timeline.ts -f docs/logs/TheAbyssalFractureExtreme.log -lf` - -```shell -$ node --loader=ts-node/esm util/logtools/make_timeline.ts -f docs/logs/TheAbyssalFractureExtreme.log -lf -(node:19900) ExperimentalWarning: Custom ESM Loaders is an experimental feature. This feature could change at any time -(Use `node --trace-warnings ...` to show where the warning was created) -┌───────┬──────────────┬────────────────┬──────────┬──────────────────────────────────┬──────────────────────────────────┬──────────┐ -│ Index │ Start Date │ Start Time │ Duration │ Zone Name │ Encounter Name │ End Type │ -├───────┼──────────────┼────────────────┼──────────┼──────────────────────────────────┼──────────────────────────────────┼──────────┤ -│ 1 │ 2023-10-06 │ 20:20:08.265 │ 10m │ The Abyssal Fracture (Extreme) │ The Abyssal Fracture (Extreme) │ Win │ -│ 2 │ 2023-10-06 │ 21:09:43.014 │ 12m │ The Abyssal Fracture (Extreme) │ The Abyssal Fracture (Extreme) │ Win │ -│ 3 │ 2023-10-06 │ 21:55:12.239 │ 9m │ The Abyssal Fracture (Extreme) │ The Abyssal Fracture (Extreme) │ Win │ -└───────┴──────────────┴────────────────┴──────────┴──────────────────────────────────┴──────────────────────────────────┴──────────┘ -``` - -`-lf` lists all of the fights and their zones. -This log file has been run through the [log splitter](https://quisquous.github.io/cactbot/util/logtools/splitter.html) -and anonymized, and so there are only three fights. - -You can make a timeline for a particular fight by using `-lf` with the index, e.g. `-lf 1`. -If you run the command, it should look something like this. - -```shell -$ node --loader=ts-node/esm util/logtools/make_timeline.ts -f docs/logs/TheAbyssalFractureExtreme.log -lf 1 -(node:24172) ExperimentalWarning: Custom ESM Loaders is an experimental feature. This - feature could change at any time -(Use `node --trace-warnings ...` to show where the warning was created) -### THE ABYSSAL FRACTURE (EXTREME) -# ZoneId: 491 - -hideall "--Reset--" -hideall "--sync--" - -0.0 "--sync--" InCombat { inGameCombat: "1" } window 0,1 -72.8 "--sync--" Ability { id: "8C49", source: "Zeromus" } -75.8 "--sync--" Ability { id: "8C49", source: "Zeromus" } -83.9 "Abyssal Nox" Ability { id: "8B3F", source: "Zeromus" } -92.9 "--sync--" Ability { id: "8B41", source: "Zeromus" } -92.9 "--sync--" #Ability { id: "8B40", source: "Zeromus" } -92.9 "--sync--" #Ability { id: "8B40", source: "Zeromus" } -92.9 "--sync--" Ability { id: "8D2B", source: "Zeromus" } -97.9 "--sync--" Ability { id: "8B41", source: "Zeromus" } -97.9 "--sync--" Ability { id: "8B40", source: "Zeromus" } -99.9 "Abyssal Echoes" Ability { id: "8B42", source: "Zeromus" } -# etc etc etc -``` - -TODO: for some reason, `make_timeline.ts` is confused here and thinks the first -real ability (Abyssal Nox 8B3F) occurs at time=83.9. -It's clear from the log that it should be t=11.1. -See: - -```text -260|2023-10-06T20:21:19.9510000-07:00|1|0| -20|2023-10-06T20:21:27.1110000-07:00|40022550|Zeromus|8B3F|Abyssal Nox|40022550|Zeromus|4.700|100.00|80.10|0.00|0.00| -22|2023-10-06T20:21:32.1010000-07:00|40022550|Zeromus|8B3F|Abyssal Nox|10FF0007|Kehabiqo Febiqo|4A|10000|E|6E90000|1B|8B3F8000|0|0|0|0|0|0|0|0|0|0|126650|128564|10000|10000|||99.95|97.53|0.00|3.14|39444801|40478540|10000|10000|||100.00|80.10|0.00|0.00|0001A48D|0|8| -``` - -You can fix this by running with `-p 8B3F:11.1` which will set the first use of `8B3F` to be time `11.`1. - -From here, it's a question of massaging this timeline into something that's usable. - -It may be tedious, but the best place to start when making a timeline is by filling out the -[timeline ability table](#timeline-ability-tables). - -After that, you should follow these guidelines and your own subjective opinions to finish the timeline: - -* [cactbot timeline style guide](#cactbot-style-guide) -* [common timeline edits](#common-timeline-edits) - -You can look at [zeromus-ex.txt](../ui/raidboss/data/06-ew/trial/zeromus-ex.txt) to see a final version. - -### Timeline Ability Tables - -If you run `make_timeline.ts` with the `-la` (list abilities) parameter, -it will build a table for you of encounter abilities that it's seen -and print it at the end of the timeline. - -It looks like this: - -```shell -$ node --loader=ts-node/esm util/logtools/make_timeline.ts -f docs/logs/TheAbyssalFractureExtreme.log -lf 1 -la -# (elided normal timeline output here for many lines) - -# ALL ENCOUNTER ABILITIES -# 8AEF --sync-- -# 8B27 --sync-- -# 8B28 --sync-- -# 8B29 --sync-- -# 8B2A --sync-- -# 8B2B --sync-- -# 8B38 Sable Thread -# 8B39 Sable Thread -# 8B3A Sable Thread -# 8B3B Sable Thread -# 8B3D Fractured Eventide -# etc -``` - -The best way to make sense of all of the abilities is to write down some information -about each of the abilities. -Why are there four `Sable Thread` abilities here? What do they all do? - -Things to look at: - -* is there a cast for this ability or is it an immediate ability? -* is this cast on players or a self-targeted cast on the boss? -* does the ability hit players? -* does the ability do damage? (or just give debuffs) -* is this id associated with a version of an attack (e.g. left cleave or right cleave)? - -The reason to build the ability table is: - -* easier to understand what abilities to ignore in timeline -* trivial to build [Oopsy](OopsyraidsyGuide.md) once you know what damage ids are -* easier to find ids when filling out [sync_files.ts](RaidbossGuide.md#sync-files) -* helpful to have ids to write triggers from - -Personally, starting from the ability table and then working out from there -is the best way to attack most new content. - -#### One Hacky Workflow Suggestion - -This is a bit of a hacky workflow, but one way to build the ability table is to filter log lines -down to the useful log lines. - -If you load the log into ACT, [view the logs](LogGuide.md#viewing-logs-after-a-fight), and then search by the following regex, -you will get most of the "interesting" lines for a fight. - -![encounter logs screenshot](images/timelineguide_encounterlogs.png) - -This includes boss abilities and casts, added combatants, tethers, map effects, head markers, and debuffs on players. -It's not perfect, but it's a place to start. - -You can then walk through a log with video, and look at which abilities hit players and do damage -and which are castbars on the boss (not all `StartsUsing` are castbars) and then fill out the ability table -and understand what everything is. - -```text -( 1A:[^:]*:([^:]|: )*:[^:]*:[E4][^:]*:[^:]*:1| 1A:(B9A|808):| 03:| 23:| 101:| 1B:| 1[456]:4.......:(?!\w*-Egi|Liturgic Bell|Thancred|Moonstone Carbuncle|Zero(?!mus)|Alphinaud|Alisaie|Topaz Titan|Emerald Carbuncle|Eos|Rook Autoturret|Seraph|Bishop Autoturret|Ruby Carbuncle|Esteem|Demi-\w*|Earthly Star|Automaton Queen|Bunshin|Selene|Hien|Lyse|Pipin Of The Steel Heart|Yugiri Mistwalker|2B|Topaz Carbuncle|Y'shtola|Hythlodaeus|Emet-Selch|Venat|Krile|Estinien|Urianger|G'raha Tia|Carbuncle|Varshahn|Emerald Garuda|Ruby Ifrit|Bunshin|Earthly Star)[^:]*:(?!IGNOREIDSHERE)[^:]*:(?!Attack|attack)) -``` - -TODO: This could be a lot better. We could add an option in the log splitter to output "interesting" lines. - -### Using make_timeline.ts with fflogs - -Usually you want a network log file for the bulk of timeline work, -but in desperate situations where your fights end too quickly -it can be helpful to look through fflogs and find the longest fight possible. -This may help you find loops or enrages. - -However, fflogs does not include all of the abilities or line types -and so this is a backup method to find extended versions of timelines -once you have already worked on the bulk of it. - -You need your fflogs api v1 key. -Go to and scroll down to `Web API`. - -Add a `V1 Client Name` like "manual" and make a note of your `V1 Client Key`. - -Find any log for the fight you want. -Here's a random one for Zeromus: - - -Then run this command, substituting your `V1 Client Key` for `YourClientKeyHere` below: - -```shell -$ node --loader=ts-node/esm util/logtools/make_timeline.ts -k YourClientKeyHere -r Ktm6hbrTjZAYpQB1 -rf 34` -(node:7972) ExperimentalWarning: Custom ESM Loaders is an experimental feature. -This feature could change at any time -(Use `node --trace-warnings ...` to show where the warning was created) -hideall "--Reset--" -hideall "--sync--" - -0.0 "--sync--" InCombat { inGameCombat: "1" } window 0,1 -11.1 "Abyssal Nox" Ability { id: "8B3F", source: "Zeromus" } -20.1 "--sync--" Ability { id: "8B41", source: "Zeromus" } -20.1 "--sync--" Ability { id: "8D2B", source: "Zeromus" } -# etc -``` - -### Using test_timeline.ts - -Once you have made a timeline, it's now time to verify it works against various logs. - -`test_timeline.ts` uses all the same parameters as `make_timeline.ts`, -but you also need to specify the timeline file (no `.txt` or directory) via `-t`. - -```shell -$ node --loader=ts-node/esm util/logtools/test_timeline.ts -f docs/logs/TheAbyssalFractureExtreme.log -lf 1 -t zeromus-ex -(node:840) ExperimentalWarning: Custom ESM Loaders is an experimental feature. This feature could change at any time -(Use `node --trace-warnings ...` to show where the warning was created) -Timeline: - +0.000 | 14 | 0.0 "--sync--" InCombat { inGameCombat: "1" } window 0,1 - -0.555 | 15 | 6.1 "--sync--" StartsUsing { id: "8B3F", source: "Zeromus" } window 10,10 - +0.010 | 16 | 11.1 "Abyssal Nox" Ability { id: "8B3F", source: "Zeromus" } - -0.078 | 17 | 27.1 "Abyssal Echoes 1" Ability { id: "8B42", source: "Zeromus" } - -0.035 | 18 | 32.1 "Abyssal Echoes 2" Ability { id: "8B42", source: "Zeromus" } - -0.068 | 19 | 42.1 "Sable Thread x6" Ability { id: "8B38", source: "Zeromus" } duration 6.9 - -0.230 | 24 | 49.0 "--sync--" Ability { id: "8B3A", source: "Zeromus" } - +0.275 | 26 | 61.7 "Dark Matter x3" Ability { id: "8B83", source: "Zeromus" } duration 4.1 - -0.062 | 28 | 78.8 "Visceral Whirl" Ability { id: "8B4[36]", source: "Zeromus" } - -0.102 | 29 | 87.8 "Miasmic Blast" Ability { id: "8B49", source: "Zeromus" } - -0.076 | 31 | 101.8 "Flare" Ability { id: "8B5D", source: "Zeromus" } - -0.018 | 32 | 109.9 "Prominence Spine" Ability { id: "8B63", source: "Zeromus" } - -0.030 | 33 | 119.9 "Void Bio" Ability { id: "8B66", source: "Zeromus" } - +0.081 | 34 | 134.1 "Visceral Whirl" Ability { id: "8B4[36]", source: "Zeromus" } - -0.088 | 35 | 143.1 "Miasmic Blast" Ability { id: "8B49", source: "Zeromus" } -``` - -This runs the timeline file against hte network log and the given pull -and then prints out the results of all the syncs to verify that they are -close in time. - -This will catch things like: - -* ability variations (e.g. `Visceral Whirl` being `8B43` vs `8B46` when it can be both) -* hp% phase pushes (e.g. `Rend the Rift` in this fight) -* slight timeline drifts (maybe one ability is +/- 1 second and so needs a large window for safety) -* your own mistakes - -Each line looks like this: - -`-0.555 | 15 | 6.1 "--sync--" StartsUsing { id: "8B3F", source: "Zeromus" } window 10,10` - -This means: - -* `15`: the line in the file -* `6.1`: the original time -* `-0.555`: the time adjustment the timeline made when it did this sync - -`-0.555` means that the original time was `6.1 + 0.555 = 6.655` -and so when this sync occurred it had to do a `-0.555` adjustment to correct for that. -Similarly, it means that if you wanted to fix the timeline -to match the log file, you would add `0.555` to the timeline time to be `6.6`. - -See also: [Adjusting Blocks of Timelines](#adjusting-blocks-of-timelines) - -In general: - -* 1 second or more is a very large discrepancy; 0.5s is ~ok but not ideal, <0.3 is best -* `InCombat` / `SystemLogMessage` to the first ability might have some slop, but ~0.5 is fine in this case -* if you can adjust the timeline to be <0.1 or <0.3 at worst, that's ideal especially if you see this discrepancy in multiple logs -* sometimes a large discrepancy on later abilities in loops means you have the loop wrong -* when there is a jump, it will "miss" abilities until the jump location (this is ok) - -### Using test_timeline.ts with fflogs - -You can do the same thing as [make_timeline.ts with fflogs](#using-make_timelinets-with-fflogs) but with `test_timeline.ts. - -```shell -node --loader=ts-node/esm util/logtools/test_timeline.ts -k YourClientKeyHere -r Ktm6hbrTjZAYpQB1 -rf 34 -t zeromus-ex -(node:18264) ExperimentalWarning: Custom ESM Loaders is an experimental feature. This feature could change at any time -(Use `node --trace-warnings ...` to show where the warning was created) -Timeline: - -0.074 | 15 | 6.1 "--sync--" StartsUsing { id: "8B3F", source: "Zeromus" } window 10,10 - +0.011 | 16 | 11.1 "Abyssal Nox" Ability { id: "8B3F", source: "Zeromus" } - -0.077 | 17 | 27.1 "Abyssal Echoes 1" Ability { id: "8B42", source: "Zeromus" } - -0.031 | 18 | 32.1 "Abyssal Echoes 2" Ability { id: "8B42", source: "Zeromus" } -# etc -``` - -This is a good way to test a variety of logs against your timeline to verify that it works. -Note that fflogs does not always include all of the abilities and line types -and so "missing" abilities may be false positives. - -## Common Timeline Edits - -Here's a playbook of "common operations" when making a timeline. - -Please add to this list as necessary to collect "folk wisdom" and "common practices". - -### `-ii` to ignore abilities - -In order to make it easier to come back later to a timeline that's been heavily edited, -timeline authors try to encode as much as possible into parameters for running `make_timeline.ts`. -(TODO: We could probably add more!) - -`-ii` ignores abilities and drops them from the timeline. - -For example, with the [TheAbyssalFractureExtreme.log](logs/TheAbyssalFractureExtreme.log) example log, -you can see `8C49` is most likely an auto attack. -(It hits one person, it happens roughly every three seconds when the boss is not casting.) -Many auto-attacks are called `Attack` by name and are auto-ignored but this is not always true. -Auto-attacks are also often inconsistent in timing and so should always be ignored in timelines -(unless they are "special" autoattacks like DSR p7 "autos" or Diamond Extreme laser cleave "autos".) - -In this case, you would want to pass `-ii 8C49` to `make_timeline.ts` to ignore this auto. -You can list multiple abilities, e.g. `-ii 8C49 8CB9 8B55 8B5B` etc. - -Note: timeline times are relative to the previous entry to the precision of tenths of seconds. -That means that ignoring abilities may change the times of later non-ignored abilities -and cause some timeline drift. -It's always best to rerun `make_timeline.ts` again to get new times after ignoring abilities. - -### `-p` for later phases - -If there's later phases, use `-p` to specify an ability that marks that phase. -For `zeromus-ex`, you can see the comment `-p 8B3F:11.1 8C0D:1006`. -The first instance of `8B3F` will be set to time=11.1 and the first instead of `8C0D` is set to `1006`. -`1006` is arbitrary here, and is just "very far" from the previous ability. -`1000` is used for `StartsUsing` with a large sync (which `make_timeline.ts` doesn't generate) -and so `1006` is used for the `Ability`. - -### Targetable Lines - -You can pass the parameter `-it` with a mob name, e.g. `-it "Rubicante"` -to automatically generate `--targetable--` and `--untargetable--` lines in a timeline. - -Currently, if there are multiple mobs that you want to track, -you need to run `make_timeline.ts` several times with different `-it` values. - -### Ignoring Combatants - -Sometimes there are NPC combatants who do abilities that you do not want to appear in the timeline.. -For example, `2B` appears in the Shadowbringers alliance raids. -You can remove combatants by name via `-ic 2B`. - -### Adjusting Blocks of Timelines - -It's highly recommended that you use VSCode and the -[Cactbot Highlight](https://marketplace.visualstudio.com/items?itemName=MaikoTan.cactbot-highlight) -extension when adjusting timelines. - -Apart from just syntax formatting, -the most useful part is that you select a block of timeline entries, -right click, -and then select `Set Time of Selection`. -Type in a new time. -This will change the first listed time in the selection to the time you typed in, -and then adjust all following times relative to that. - -It's a great way to handle drift issues that you see in `test_timeline.ts. -If one sync is consistently off by -0.5 and then the rest of the timeline is fine, -you can select that line and all following and then set its time to be 0.5 more -that its original time. - -### Variations vs Simultaneous Abilities - -This is more of a timeline style thing, -but if there are two abilities that are alternatives for each other, -then it is common to put them on the same line with `/` between them. -Abilities that always happen should be on separate timeline lines. -(If there are too many lines for abilities or players don't care about some of them, -ignore as many as make sense to make the timeline more readable.) -Abilities that might or might not happen (e.g. Phantom Edge in [delubrum_reginae.txt](../ui/raidboss/data/05-shb/eureka/delubrum_reginae.txt)) -should be listed with a question mark, e.g. `Phantom Edge?`. - -```text -438.6 "Branding Flare/Sparking Flare" Ability { id: "8B6[45]", source: "Zeromus" } -438.6 "Prominence Spine" Ability { id: "8B63", source: "Zeromus" } -``` - -These two lines means *either* Branding Flare or Sparking Flare (spread or partner stack) will happen, -and then Prominence Spine (fireball explosion lines) also happens at the same time. -Note: some older timelines don't follow this rule, but ~most do and newer ones should for consistency. - -The following are the log entries. -Note: these are ~0.1s apart but sometimes are closer -and so the timeline author chose to put them at the same `438.6` time. - -```text -[21:19:05.359] AOEActionEffect 16:4002248D:Zeromus:8B65:Branding Flare:10FF0003:Gegehi Gehi:150003:A44A0000:180E:8320000:1B:8B658000:0:0:0:0:0:0:0:0:0:0:73814:73814:4950:10000:::117.80:118.35:0.00:-2.98:44:44:0:10000:::105.00:100.00:0.00:0.00:00019F83:0:2 -[21:19:05.447] ActionEffect 15:40022482:Zeromus:8B63:Prominence Spine:E0000000::0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:::::::::::44:44:0:10000:::95.00:100.00:0.00:0.00:00019F88:0:0 -``` - -### Basic Loops - -In content without enrages, often a boss will loop its abilities forever. - -Here's one example: [thaleia.txt](../ui/raidboss/data/06-ew/alliance/thaleia.txt) - -For the first boss Thaliak, there's this loop: - -```text -1272.9 label "thaliak-loop" -1272.9 "Tetraktys (cast)" Ability { id: "88C9", source: "Thaliak" } -1280.9 "Tetraktys 1" Ability { id: "88CA", source: "Thaliak" } - -# many abilities in between - -1442.1 "Tetraktys (cast)" Ability { id: "88C9", source: "Thaliak" } window 40,40 forcejump "thaliak-loop" -``` - -In other words: - -* a `label`, usually named "loop" of some kind -* a `forcejump` to that label with a large window -* both source and destination of the jump are identical - -Note: `label` and `forcejump` were added late in 6.x to cactbot and so many old timelines don't use them. -Feel free to edit old timelines to use them, but please always use them in newer timelines. - -The author of the timeline has also verified that everything after `1442.1` is the same as after `1272.9` -and has run this timeline through `test_timeline.ts` with multiply looping timelines to verify this. -Sometimes things *look* like loops from ability names, but have slightly different timings so it's good to test. - -`forcejump` not only always jumps (as the name implies), but because the timeline knows there will always be a jump, -it can preview abilities ahead. - -In other words, when the timeline is at time `1440.1`, -with 2 seconds until `Tetraktys (cast)`, -it will automatically show 10 seconds until `Tetraktys 1` at `1280.9`. - -### Branches - -[p8s.txt](../ui/raidboss/data/06-ew/raid/p8s.txt) has a classic branch that everybody hates. -Here's what the timeline looks like. - -```text -# => Snake first branch -58.7 "--sync--" #Ability { id: "7108", source: "Hephaistos" } -63.9 "--sync--" Ability { id: "794C", source: "Hephaistos" } window 100,100 jump 163.9 -71.2 "Snaking Kick?" #Ability { id: "7929", source: "Hephaistos" } -77.3 "Gorgomanteia?" #Ability { id: "791A", source: "Hephaistos" } -83.4 "Into the Shadows?" #Ability { id: "792A", source: "Hephaistos" } - -# => Beast first branch -58.7 "--sync--" Ability { id: "7108", source: "Hephaistos" } -63.9 "--sync--" Ability { id: "794B", source: "Hephaistos" } window 100,100 jump 1163.9 -70.3 "Footprint?" #Ability { id: "7109", source: "Hephaistos" } -77.2 "Uplift 1?" #Ability { id: "7935", source: "Hephaistos" } -79.3 "Uplift 2?" #Ability { id: "7935", source: "Hephaistos" } -81.5 "Uplift 3?" #Ability { id: "7935", source: "Hephaistos" } -83.6 "Uplift 4?" #Ability { id: "7935", source: "Hephaistos" } -``` - -Note: this old timeline does not use `forcejump` or `label` but should! - -`794C` and `794B` are the first abilities that differ between the two branches -and so are used as the jumping point with a large `window` (just in case) -Every entry after that gets a `?` in the name, with a commented out `Ability` sync. - -This provides a timeline "preview" of upcoming abilities that might be coming, -and when the jump finally happens to either snake or beast branch, -the question marks will be "removed" when the real entries appear. - -They should match the relative timing exactly. -See [adjusting blocks](#adjusting-blocks-of-timelines) for shortcuts on how to do this easily. - -TODO: This might be a good opportunity for `maybejump` keyword that could show timeline abilities along each branch -with a `?` automatically without having to list them out manually or adjust blocks. - -### HP% Pushes - -Zeromus extreme has an hp percentage push at 25% to phase 2. -In its case, it loops forever in phase one until you get to 25%. -Timeline authors usually start new blocks at a round number far away in this case. - -```text -# Note that this enrage can happen at any time in P2, because it always happens at roughly 11m into the pull regardless of when you hit P2 -658.2 "--sync--" StartsUsing { id: "8C1E", source: "Zeromus" } window 1000,1000 -668.2 "Big Bang (Enrage)" Ability { id: "8C1E", source: "Zeromus" } - -# Phase 2: ~25% push -1000.0 "--sync--" StartsUsing { id: "8C0D", source: "Zeromus" } window 1000,0 -1006.0 "Rend the Rift" Ability { id: "8C0D", source: "Zeromus" } -``` - -Another example is an hp% push where the boss will naturally do all of the abilities in order, -but if pushed low enough will "skip ahead". -In this case, it's best to keep all the ability times for the "non push" case -and then add a large `window` at the hp push. - -This is an example from [delubrum_reginae.txt](../ui/raidboss/data/05-shb/eureka/delubrum_reginae.txt). -The Queen will naturally do all of these abilities in order, -but if its gets low enough it will skip to `9613.9`. - -```text -9132.1 "Queen's Will" Ability { id: "59B9", source: "The Queen" } -9140.2 "Beck And Call To Arms" Ability { id: "5B99", source: "The Queen" } -9143.3 "--untargetable--" -9144.5 "The Means" Ability { id: "59B[BD]", source: ["Queen's Gunner", "Queen's Warrior"] } -9144.5 "The Ends" Ability { id: "59B[AC]", source: ["Queen's Soldier", "Queen's Knight"] } -9151.5 "Judgment Blade" Ability { id: "59C[12]", source: "The Queen" } -9156.2 "--targetable--" - -# HP% push here. -9160.5 "--middle--" Ability { id: "5BCB", source: "The Queen" } -9163.9 "--sync--" Ability { id: "55A8", source: "The Queen" } window 200,10 -9171.0 "Gods Save The Queen" Ability { id: "59C9", source: "The Queen" } -``` - -### Doubled Abilities - -An extremely common pattern in content is to see "doubled" abilities in the timeline. -Here's an example set of loglines from Zeromus. - -```text -[20:22:56.186] StartsCasting 14:40022550:Zeromus:8B5D:Flare:40022550:Zeromus:6.700:100.00:80.10:0.00:0.00 -[20:22:56.186] StartsCasting 14:4002256D:Zeromus:8B60:Flare:4002256D:Zeromus:7.700:110.00:115.00:0.00:0.00 -[20:23:03.177] ActionEffect 15:40022550:Zeromus:8B5D:Flare:40022550:Zeromus:1B:8B5D8000:0:0:0:0:0:0:0:0:0:0:0:0:0:0:32191814:40478540:10000:10000:::100.00:80.10:0.00:0.00:32191814:40478540:10000:10000:::100.00:80.10:0.00:0.00:0001A75D:0:1 -[20:23:04.160] AOEActionEffect 16:4002256D:Zeromus:8B60:Flare:10FF0003:Gegehi Gehi:150003:D55E0000:1B:8B608000:0:0:0:0:0:0:0:0:0:0:0:0:73085:73085:6700:10000:::110.09:114.18:0.00:-2.85:44:44:0:10000:::110.00:115.00:0.00:0.00:0001A763:0:4 -``` - -This turns into this timeline: - -```text -# 8B60 has been ignored -101.8 "Flare" Ability { id: "8B5D", source: "Zeromus" } -``` - -Zeromus has a self-targeted `8B5D` cast and also a player-targeted `8560` cast slightly later. -Often the time disrepancy here is ~0.3s or so but in this case it's 1s which is very large. - -In *general*, cactbot timelines prefer to use the self-targeted castbar time, -as that's when the mitigation usually locks in and timelines are good for mitigation planning. -The damage itself is locked in silently during the `8B60` ability line on players. -(The damage itself doesn't actually happen until the corresponding 0x25 [NetworkActionSync](LogGuide.md#line-37-0x25-networkactionsync) line, -which timelines haven't ever concerned themselves with.) - -However, this is subjective and is really just an author convention and not a hard and fast rule. - -See also the next section about `(cast)`. - -### Doubled Abilities with suffixes - -Often players expect to see castbars show up in the timeline, -and having the timeline say that some ability ends 1s later then the cast bar ends -makes it feel like the timeline is desynced. - -Sometimes when self-targeted and damage are very far away from each other, -they get labelled with a `(cast)` suffix to indicate that this is the boss -casting some prepared move which will resolve a good bit later. - -For instance, in [thaleia.txt](../ui/raidboss/data/06-ew/alliance/thaleia.txt) -there's a Rheognosis castbar and then TWENTY SECONDS LATER there's a knockback. -Because these are so far apart, the original castbar gets a `(cast)`. - -```text -1016.1 "--middle--" Ability { id: "88DB", source: "Thaliak" } -1023.4 "Rheognosis (cast)" Ability { id: "88C4", source: "Thaliak" } -1029.5 "--sync--" Ability { id: "88C6", source: "Thaliak" } -1043.6 "Rheognosis" Ability { id: "88C7", source: "Thaliak" } -``` - -This also differentiates that there are not two Rheognosis abilities, just one extended one. - -### Multi-hit Abilities - -Multihit abilities usually get a `duration` and an `x#` suffix, e.g. `x6` below for "six hits". - -```text -42.1 "Sable Thread x6" Ability { id: "8B38", source: "Zeromus" } duration 6.9 -#43.8 "Sable Thread" #Ability { id: "8B39", source: "Zeromus" } -#45.1 "Sable Thread" #Ability { id: "8B39", source: "Zeromus" } -#46.4 "Sable Thread" #Ability { id: "8B39", source: "Zeromus" } -#47.7 "Sable Thread" #Ability { id: "8B39", source: "Zeromus" } -49.0 "--sync--" Ability { id: "8B3A", source: "Zeromus" } -``` - -There's a couple of reasons for this: - -* it's not that useful to know when the other hits are happening exactly -* having 6 or 7 timeline things that all say "Sable Thread" is noisy -* we can't sync things anyway because the same ability id is too close in time (and so they're commented out) - -Note that `8B39` is just listed out as a convenience to the timeline author to know how many hits there are. -It could be ignored. The actual `8B3B` damage after `8B38`/`8B39`/`8B3A` *is* entirely ignored via `-ii`. - -Another alternative is to list them all and number them, e.g. - -```text -27.1 "Abyssal Echoes 1" Ability { id: "8B42", source: "Zeromus" } -32.1 "Abyssal Echoes 2" Ability { id: "8B42", source: "Zeromus" } -``` - -These are ~5s apart and so don't need a comment. -Numbering makes it clear that there are multiple abilities that need to be dodged. - -Sometimes, if there's nothing else going on and timing is tight -(and especially when there is a set number of abilities that people are counting) -it can be useful to list out everything explicitly. -See this example from [p12s.txt](../ui/raidboss/data/06-ew/raid/p12s.txt): - -```text -# Laser/Cleave order is random here so we can only really timeline the Palladion dashes. -290.6 "Palladion 1" Ability { id: "82F6", source: "Athena" } window 1.5,0.5 -293.7 "Palladion 2" Ability { id: "82F6", source: "Athena" } window 1.5,0.5 -296.8 "Palladion 3" Ability { id: "82F6", source: "Athena" } window 1.5,0.5 -299.9 "Palladion 4" Ability { id: "82F6", source: "Athena" } window 1.5,0.5 -303.0 "Palladion 5" Ability { id: "82F6", source: "Athena" } window 1.5,0.5 -306.1 "Palladion 6" Ability { id: "82F6", source: "Athena" } window 1.5,0.5 -309.2 "Palladion 7" Ability { id: "82F6", source: "Athena" } window 1.5,0.5 -312.4 "Palladion 8" Ability { id: "82F6", source: "Athena" } window 1.5,0.5 -``` - -This is just subjective, so let readability and clarity guide you. - -### Numbering Important Mechanics - -Often players remember things by which instance of a big mechanic it is. -For instance, [rubicante-ex.txt](../ui/raidboss/data/06-ew/trial/rubicante-ex.txt) -has something like `Ordeal of Purgation 4` so that players remember -that this is the one where they have to find the cat ears. - -Similarly, on fights where people do a lot of mit planning (ultimates, fourth floors) -often people will remember "I'm addling the 4th ultima" and so -[p12s.txt](../ui/raidboss/data/06-ew/raid/p12s.txt) has things like `"Ultima 6"` or `"Gaiaochos 2"`. - -This is also subjective, so do what seems useful. - -### Renaming Abilities to `--sync--` - -From [delubrum_reginae_savage.txt](../ui/raidboss/data/05-shb/eureka/delubrum_reginae_savage.txt): - -```text -# Similarly, 57C4 is the cast for Devastating Bolt, but 57C5/57C6 is the delayed damage. -# Both of these casts are renamed as --sync-- to clean up the timeline. - -18247.2 "--sync--" Ability { id: "57C4", source: "Stygimoloch Lord" } -18251.3 "Mana Flame" Ability { id: "57DE", source: "Ball Of Fire" } -18251.7 "Devastating Bolt" Ability { id: "57C5", source: "Stygimoloch Lord" } -``` - -This is an alternative to [doubled abilities with suffixes](#doubled-abilities-with-suffixes). - -See also: - -## Future Work - -There's plenty of feature work and fixes for timelines if you are interested in working on making this code better: - -### Smaller Fixes/Changes - -* update [Cactbot Highlight](https://marketplace.visualstudio.com/items?itemName=MaikoTan.cactbot-highlight) to include `forcejump`, `label`, and new `Ability { params }` style syncs -* make it so you can pass multiple mob names to `-it` -* `test_timeline.ts` could know which file to use without `-t` (it could use `ZoneChange` lines to look up the correct timeline) -* `make_timeline.ts` has issues with empty names: -* `test_timeline.ts` sometimes misses seal lines that are in the log: -* investigate this drift issue: -* make it possible to pass a set of ids that should be named `--sync--`: -* fix the log splitter so that when importing into ACT separate fights stay separate (maybe need to keep one new zone line? or just insert a fake `/echo end`?) -* fix the offset issue with make/test timeline on zeromus log file: - -### Larger Features - -* general timeline improvement thread (many ideas, see especially first comment): -* add a `maybejump` similar to `forcejump` that does "loop lookahead" instead of manually writing out the loop -* named blocks to separate out phases / fights: -* consider moving all "huge syncs" to the beginning (rather than a huge sync for t=2000 for a seal line, maybe all the seal lines only sync `window 0,1` and are listed at the beginning) to reduce active syncs and improve readability -* `testsync` instead of comments (we use `#Ability { params }` to avoid sync issues, but it'd be nice to add a `testsync` command that verifies that the sync was hit in the window but does not resync for testing purposes) -* handle multiple syncs at the same time: -* clean up old timelines to use `label` and `forcejump` -* add some checkbox to the splitter code to only print out "interesting" lines (see: [hacky workflow](#one-hacky-workflow-suggestion)) - -### Ability Table - -The [ability table](#timeline-ability-tables) was added during the 6.x timeframe -and has become an important part of making timelines, triggers, and oopsy. - -In some ways, having it be just text comments in a timeline only goes so far. - -It could become its own json(?) file of ability mappings (auto-generated? partially auto-generated?) that says - -* damage / no damage -* is avoidable / unavoidable damage -* has cast / has castbar in game / just ability -* self-targeted on caster / hits N players / hits all players - -Oopsy could use this to print out extra information (mouseover for what an ability is that somebody died from). -We wouldn't need to duplicate this information in oopsy comments or `sync_files.ts` comments. -You could make a "log viewer" that pulls out interesting lines and annotates them with this information, -or attempts to infer this information to fill out a json file. -[Cactbot Highlight](https://marketplace.visualstudio.com/items?itemName=MaikoTan.cactbot-highlight) could somehow use this information when looking at timeline? - -I think it could be possible to enshrine this in a little bit more formal way -and then it'd be useful in multiple cactbot contexts. -Probably other non-cactbot trigger makers would find this useful too. + diff --git a/docs/ko-KR/CactbotCustomization.md b/docs/ko-KR/CactbotCustomization.md index ed82bdbcbe..af1933f011 100644 --- a/docs/ko-KR/CactbotCustomization.md +++ b/docs/ko-KR/CactbotCustomization.md @@ -1,584 +1,5 @@ -# Cactbot 사용자 설정 +# Permanently Moved to OverlayPlugin/cactbot -🌎 [[English](../CactbotCustomization.md)] [[简体中文](../zh-CN/CactbotCustomization.md)] [[繁體中文](./zh-TW/CactbotCustomization.md)] [**한국어**] +See: [docs/ko-KR/CactbotCustomization.md](https://github.com/OverlayPlugin/cactbot/blob/main/docs/ko-KR/CactbotCustomization.md) -- [Cactbot UI를 사용하는 방법](#Cactbot-UI를-사용하는-방법) -- [User 디렉토리 개요](#User-디렉토리-개요) -- [cactbot UI로 트리거 문자열 수정하기](#cactbot-UI로-트리거-문자열-수정하기) -- [User 디렉토리 설정하기](#User-디렉토리-설정하기) -- [디자인 수정하기](#디자인-수정하기) -- [Raidboss 트리거 덮어쓰기](#Raidboss-트리거-덮어쓰기) - - [예시 1: 출력 문자열 변경하기](#예시-1-출력-문자열-변경하기) - - [예시 2: 도발 알림이 모든 직업에 뜨게 하기](#예시-2-도발-알림이-모든-직업에-뜨게-하기) - - [예시 3: 커스텀 트리거 추가하기](#예시-3-커스텀-트리거-추가하기) -- [Raidboss 타임라인 덮어쓰기](#Raidboss-타임라인-덮어쓰기) -- [초보를 위한 무작정 따라하기](#초보를-위한-무작정-따라하기) - - [Raidboss 트리거 수정하기](#Raidboss-트리거-수정하기) - - [Raidboss 타임라인 수정하기](#Raidboss-타임라인-수정하기) - - [간단한 메모 추가하는 법](#간단한-메모-추가하는-법) -- [기능 사용자 설정하기](#기능-사용자-설정하기) -- [User 파일 디버깅](#User-파일-디버깅) - - [오버레이 플러그인 로그에 에러가 나오는지 확인하세요](#오버레이-플러그인-로그에-에러가-나오는지-확인하세요) - - [User 파일이 로드되는지 확인하세요](#User-파일이-로드되는지-확인하세요) - - [User 파일에 에러가 있는지 확인하세요](#User-파일에-에러가-있는지-확인하세요) - -## cactbot UI를 사용하는 방법 - -cactbot을 사용자 설정하실 때 cactbot의 설정 UI를 이용하는 것을 권장합니다. -ACT -> Plugins -> OverlayPlugin.dll -> Cactbot 에서 찾을 수 있습니다. - -이 방법으로는 다음과 같은 설정이 가능합니다: - -- 트리거에 TTS 설정하기 -- 트리거 비활성화하기 -- cactbot 언어 변경하기 -- 볼륨 설정 -- 음식을 먹으라는 치즈 아이콘 없애기 - -cactbot 설정 UI에서 원하는 모든 것들을 설정할 수는 없을지도 모르지만, -이 방법이 설정하기에 가장 쉬운 방법입니다. -또한, 업데이트를 진행함에 따라 더 많은 옵션이 추가될 것입니다. - -이 옵션들은 -`%APPDATA%\Advanced Combat Tracker\Config\RainbowMage.OverlayPlugin.config.json` -파일에 저장됩니다. -저 파일을 직접 수정하지 않도록 하세요. - -## cactbot UI로 트리거 문자열 수정하기 - -ACT -> Plugins -> OverlayPlugin.dll -> Cactbot -> Raidboss에 있는 -cactbot 설정 UI에는 개별적인 트리거 목록이 나열되어 있습니다. -이 목록 내용을 수정해서 트리거별로 다양한 설정을 수정할 수 있습니다. - -이름 앞에 종 모양(🔔)이 있는 설정값은 트리거에서 출력하는 메시지이며, 이 설정란을 이용해 수정할 수 있습니다. -예를 들어, `"${player}" 탱버`라는 내용이 들어있는 🔔onTarget 입력란이 있다고 칩시다. -이것이 어떤 플레이어에게 탱크 버스터가 오고 있을 때 화면에(또는 TTS로) 출력할 문자열입니다. -그 중 `${player}`는 트리거에 의해 동적으로 설정되는 매개변수입니다. -`${변수명}` 같은 모습의 문자열은 그런 동적 매개변수입니다. - -원래 출력 문자열 대신 `${player}가 죽으려고 해요!`를 출력하도록 바꿀 수 있겠죠. -아니면, 누가 맞을지 신경쓰지 않는다면, `탱버`로 짧게 수정할 수도 있습니다. -만약 덮어쓴 것을 되돌리고 싶다면, 그냥 입력한 텍스트를 비우면 됩니다. - -이 덮어쓰기 기능에는 약간의 제한 사항이 있습니다. -트리거 작동 논리를 변경할 수는 없어요. -또, 대부분의 경우에 `alarmText`와 다른 `TTS`가 출력되도록 할 수 없어요. -매개변수를 새로 추가할 수도 없습니다. -만약 이런 식의 더 복잡한 수정을 하고 싶다면, -[Raidboss 트리거 덮어쓰기](#Raidboss-트리거-덮어쓰기) 문단을 확인하세요. - -## User 디렉토리 개요 - -만약 cactbot UI가 원하는 옵션을 제공하지 않는다면, -User 파일 덮어쓰기(override)를 고려해야 합니다. -이 방법은 JavaScript와 CSS를 작성하는 것이기 때문에, -약간의 프로그래밍 지식이 필요할 수 있습니다. - -cactbot은 "모든 사용자 설정은 User 디렉토리 파일에만 들어가야 한다"는 전제로 설계되어 있습니다. -때문에 cactbot이 업데이트될 때 사용자의 변경 사항이 덮어쓰이지 않게 됩니다. -또한, 추후 cactbot release 파일을 직접 수정하게되면 추가 빌드 작업을 하지 않고는 제대로 작동하지 않을것입니다. - -모든 cactbot UI 모듈은 사용자 설정을 [user/](../../user/) 디렉토리에서 불러옵니다. -`raidboss` 모듈은 `user/raidboss.js`와 `user/raidboss.css`를 불러옵니다. -`oopsyraidsy`모듈은 `user/oopsyraidsy.js`와 `user/oopsyraidsy.css`를 불러옵니다. -다른 모듈들도 마찬가지입니다. -이 파일들은 cactbot의 기본 파일들과 함께 불러오며, 기본 설정들을 덮어씌울 수 있습니다. - -`user/` 디렉토리는 이미 몇몇 예시 설정 파일들을 포함하고 있습니다. -이름을 바꿔서 사용할 수도 있고요. -예를 들어, [user/raidboss-example.js](../../user/raidboss-example.js) 파일은 -`user/raidboss.js`로 이름 바꿀 수 있고 -`raidboss` 모듈의 기능을 바꾸도록 수정할 수 있습니다. - -이 파일들을 수정한 다음 -ACT 오버레이 플러그인 설정 창에 -해당하는 오버레이의 "새로고침" 버튼을 클릭하면 -변경 사항이 적용됩니다. - -## User 디렉토리 설정하기 - -cactbot user 경로는 cactbot 설정 UI에서 설정할 수 있습니다. -ACT -> Plugins -> OverlayPlugin.dll -> Cactbot -> Cactbot 사용자 디렉토리 에서 -`디렉토리 선택` 버튼을 클릭하고 원하는 경로를 선택하세요. - -따로 설정하지 않는다면, -cactbot이 설치된 경로가 기본값으로 설정됩니다. - -가능하면 cactbot이 설치된 `cactbot/user`를 사용하는 것이 좋습니다. -보통은 `%APPDATA%\Advanced Combat Tracker\Plugins\cactbot-version\cactbot\user` -(해루봇을 사용한다면, `ACT 설치 경로\Plugins\cactbot\user`)입니다. - -하지만 해루봇을 사용하신다면, cactbot 업데이트시 cactbot 폴더 내 내용이 -전부 초기화되므로 외부에 다른 user 폴더를 만들고 그곳을 경로로 설정하기를 권장합니다. - -[이 폴더](../../user)에는 예시 설정 파일들이 있습니다. - -## 디자인 수정하기 - -`user/.css` 파일은 각 오버레이 요소들의 위치, 크기, 색깔 등을 수정할 수 있습니다. -`ui//.css`를 보고 수정할 수 있는 셀렉터(selector)를 확인하세요. - -[ui/raidboss/raidboss.css](../../ui/raidboss/raidboss.css)의 예시를 보자면, -`#popup-text-container`와 `#timeline-container`를 확인할 수 있습니다. -이들의 위치를 `user/raidboss.css`에서 원하는 다른 위치로 바꿀 수 있습니다. -`user/raidboss.css`로 다른 추가적인 스타일링도 가능합니다. - -`.info-text` 클래스에 CSS 규칙을 추가하여 Info 텍스트 알람의 크기와 색깔도 바꿀 수 있습니다. - -```css -.info-text { - font-size: 200%; - color: rgb(50, 100, 50); -} -``` - -User 디렉토리의 CSS는 cactbot의 기본 CSS 아래에 추가하는 것입니다. -따라서, [CSS 명시도 규칙](https://developer.mozilla.org/ko/docs/Web/CSS/Specificity)에 따라, -`!important`를 추가해서 설정한 규칙을 강제로 덮어쓰도록 할 수 있습니다. -또한, 기본 설정 값을 해제하려면 해당 값을 `auto`로 설정해야 합니다. - -CSS 문제를 디버그하기 가장 좋은 방법은 [Chrome 개발자 도구](https://developers.google.com/web/tools/chrome-devtools)를 사용하는 것입니다. -ACT -> Plugins -> OverlayPlugin.dll -> 원하는 오버레이 -> 개발자 도구 열기 를 클릭해서 개발자 도구를 열 수 있습니다. - -**참고**: 일부분은 수정하기 어렵거나 불가능합니다. 타임라인 막대 같은 것들이 그 예시입니다. -일부분은 사용자 요소(custom element)를 사용하고, -사용자 요소는 별도로 튜닝하는 방법을 제공하지 않기 때문입니다. -만약 수정이 불가능한 타임라인 막대에 대해 원하는 수정 사항이 있다면, -얼마든지 [Github Issue](https://github.com/quisquous/cactbot/issues/new/choose)에 글을 작성해주세요. - -**경고**: cactbot은 CSS 하위 호환 유지를 보장하지 않습니다. -나중에 cactbot에서 요소들을 재배열 할 수도 있고, -요소 이름과 클래스를 변경할 수도 있고. -스타일링 전체를 바꿀 수도 있습니다. -일반적으로, cactbot의 CSS를 수정하는 것은 스스로 관리해야 합니다. - -## Raidboss 트리거 덮어쓰기 - -`cactbot/user/raidboss.js`를 이용해서 트리거가 작동하는 방식을 덮어씌울 수 있습니다. -출력하는 문자열을 수정하거나, -어떤 직업을 대상으로 발동하는지, -그리고 얼마나 오래 화면에 떠있는지, -이외에 다른 것들을 수정할 수 있습니다. - -`cactbot/user/raidboss.js` 안에 -`Options.Triggers` 리스트를 이용해 새 트리거를 작성하거나 -이미 존재하는 트리거를 수정하는데 사용할 수 있습니다. -만약 User 파일이 이미 있는 트리거(cactbot에 내장된 트리거 포함)와 -같은 id를 사용하는 트리거가 있다면, 기존 트리거를 덮어쓰게 됩니다. - -트리거를 수정하기 전에 -각 트리거에 있는 다양한 설정값이 어떤 것을 의미하는지 이해하기 위해서 -[트리거 가이드](../RaidbossGuide.md)를 읽는 것이 좋습니다. - -일반적으론 다음과 같은 형식의 코드 블록을 -`cactbot/user/raidboss.js`에 추가하면 됩니다. - -```javascript -Options.Triggers.push({ - // 파일 최상단에 있는 ZoneId를 찾으세요 - // 예시) ZoneId.MatchAll (모든 지역) 또는 ZoneId.TheBozjanSouthernFront. - zoneId: ZoneId.PutTheZoneFromTheTopOfTheFileHere, - triggers: [ - { - // 이 곳이 트리거 객체를 넣는 곳입니다. - // 예시) id / netRegex / infoText - }, - ], -}); -``` - -트리거를 수정할 때 가장 쉬운 방법은 -위에 있는 코드 블록을 각 트리거에 붙여넣는 것입니다. -`zoneId`에 이 트리거가 작동할 지역 ID를 입력하세요. -보통 cactbot 트리거 파일 최상단에 적혀있습니다. -그리고 [이 파일](../../resources/zone_id.ts)은 모든 지역 ID 리스트를 저장하고 있습니다. -만약 올바른 지역 ID를 입력하지 않는다면, 오버레이 플러그인 로그 창에 warning이 나오게 됩니다. -그 다음, 트리거 텍스트를 블록 안에 복사하여 필요한 만큼 수정하세요. -이 과정을 수정하고 싶은 모든 트리거에 대해 반복하면 됩니다. -변경 사항을 적용하려면, raidboss 오버레이를 새로고침하세요. - -**참고**: 이 방식은 기존 트리거의 동작을 완전히 제거하게 됩니다. -따라서 수정할 때 동작 로직 부분은 제거하지 마세요. -또한, 이건 JavaScript이기 때문에 유효한 JavaScript 코드여야 합니다. -프로그래머가 아니라면, 무엇을 어떻게 수정하고 있는지 더더욱 조심하세요. - -### 예시 1: 출력 문자열 변경하기 - -당신이 절바하를 하려고 한다고 칩시다. -당신의 공대는 처음 불장판때 cactbot이 기본으로 불러주는 "불 같이맞기" 대신에 -"불 대상자 밖으로"를 먼저 하기로 조율했습니다. - -이를 해결하는 방법으로는 트리거의 출력을 수정하여 조정하는 것이 있습니다. -fireball #1 원본 트리거는 -[ui/raidboss/data/04-sb/ultimate/unending_coil_ultimate.js](https://github.com/quisquous/cactbot/blob/triggers/04-sb/ultimate/unending_coil_ultimate.js#:~:text=UCU%20Nael%20Fireball%201)에서 찾을 수 있습니다. - -이 코드들을 `cactbot/user/raidboss.js` 파일 아래 부분에 붙여넣습니다. - -```javascript -Options.Triggers.push({ - zoneId: ZoneId.TheUnendingCoilOfBahamutUltimate, - triggers: [ - { - id: 'UCU Nael Fireball 1', - netRegex: NetRegexes.ability({ source: 'Ragnarok', id: '26B8', capture: false }), - delaySeconds: 35, - suppressSeconds: 99999, - // infoText는 화면에 초록색으로 표시되는 문구입니다. - infoText: { - ko: '불 대상자 밖으로', - }, - run: function(data) { - data.naelFireballCount = 1; - }, - }, - ], -}); -``` - -이 수정본은 `tts` 부분을 제거하고 한국어 이외에 다른 언어도 제거합니다. - -### 예시 2: 도발 알림이 모든 직업에 뜨게 하기 - -지금은 도발 알림이 같은 파티나 연합 파티에 있는 경우에만 작동하고, 일부 직업에 대해서만 작동하고 있습니다. -이 예시는 어떻게 모든 플레이어에 대해 알림을 보여주도록 만들 수 있는지 보여줍니다. -도발 트리거는 -[ui/raidboss/data/00-misc/general.js](https://github.com/quisquous/cactbot/blob/triggers/00-misc/general.js#:~:text=General%20Provoke)에서 찾을 수 있습니다. - -다음 예시는 `condition` 함수(function)가 수정된 버전입니다. -이 트리거는 cactbot에 내장된 트리거인 `General Provoke`와 id가 동일하기 때문에 -이 트리거가 기본 트리거를 덮어쓸 것 입니다. - -이 코드들을 `cactbot/user/raidboss.js` 파일 아래 부분에 붙여넣습니다. - -```javascript -Options.Triggers.push({ - zoneId: ZoneId.MatchAll, - triggers: [ - { - id: 'General Provoke', - netRegex: NetRegexes.ability({ id: '1D6D' }), - condition: function(data, matches) { - // 같은 파티가 아닌 사람들까지도 도발 알림을 받고 싶거나 - // 내가 탱커가 아닌 경우 - return true; - }, - infoText: (data, matches, output) => { - return output.text!({ player: data.party.member(matches.source) }); - }, - outputStrings: { - text: { - en: 'Provoke: ${player}', - de: 'Herausforderung: ${player}', - fr: 'Provocation: ${player}', - ja: '挑発: ${player}', - cn: '挑衅: ${player}', - ko: '도발: ${player}', - }, - }, - }, - ], -}); -``` - -이 경우에는 그냥 `condition` 함수를 완전히 지워버리는 방법도 있습니다. -condition이 없는 트리거는 정규식이 맞을 때마다 항상 작동하기 때문이죠. - -### 예시 3: 커스텀 트리거 추가하기 - -이와 똑같은 방법으로 커스텀 트리거를 만들 수도 있습니다. - -아래 예시는 "갈래 번개" 디버프를 받은 1초 후에 -"Get out!!!" 문구를 출력해주는 커스텀 트리거입니다. - -```javascript -Options.Triggers.push([ - { - zoneId: ZoneId.MatchAll, - triggers: [ - { - // 이 id는 새로 만든 것이기 때문에 cactbot 트리거를 덮어쓰지 않습니다. - id: 'Personal Forked Lightning', - regex: Regexes.gainsEffect({ effect: '갈래 번개' }), - condition: (data, matches) => { return matches.target === data.me; }, - delaySeconds: 1, - alertText: 'Get out!!!', - }, - - // ... 원한다면 다른 트리거를 추가하세요 - ], - }, - - // ... 원한다면 다른 지역을 추가하세요 -]); -``` - -cactbot 트리거 작성하는 방법을 더 자세히 배우려면 -[트리거 가이드](../RaidbossGuide.md)와 -[ui/raidboss/data](../../ui/raidboss/data)에 이미 존재하는 트리거를 읽어보세요. - -## Raidboss 타임라인 덮어쓰기 - -Raidboss 타임라인을 덮어쓰는 것은 [Raidboss 트리거 덮어쓰기](#Raidboss-트리거-덮어쓰기)와 비슷합니다. - -타임라인을 덮어쓰기 위한 과정: - -1) 타임라인 텍스트 파일을 cactbot에서 user 폴더로 복사합니다. - - 예를 들어, - [ui/raidboss/data/05-shb/ultimate/the_epic_of_alexander.txt](../../ui/raidboss/data/05-shb/ultimate/the_epic_of_alexander.txt)를 - `user/the_epic_of_alexander.txt`로 복사할 수 있겠죠. - -1) user/raidboss.js 파일에 이 타임라인 파일을 덮어쓰기 위한 부분을 추가하세요. - - 트리거를 추가하는 것과 같이, `zoneId`에 구획(section)을 추가합니다. - `overrideTimelineFile: true`를 zoneId 아래에 추가하고, - `timelineFile`에 타임라인 텍스트 파일의 이름을 추가하세요. - - ```javascript - Options.Triggers.push({ - zoneId: ZoneId.TheEpicOfAlexanderUltimate, - overrideTimelineFile: true, - timelineFile: 'the_epic_of_alexander.txt', - }); - ``` - - 당신이 첫번째 과정을 따라서 - `user/the_epic_of_alexander.txt` 파일을 생성했다면, - - `overrideTimelineFile: true`을 설정함으로써 - cactbot이 기본적으로 포함된 타임라인 대신 - 새로 추가한 타임라인을 사용하게 됩니다. - -1) 필요한 만큼 user 폴더에 있는 새 타임라인 파일을 수정하세요. - - 타임라인 구성 방법에 대해 더 알고 싶다면 [타임라인 가이드](../TimelineGuide.md)를 참고하세요. - -**참고**: 타임라인을 수정하는 것은 약간의 위험 요소가 있습니다. -타임라인 텍스트를 참고하여 작동하는 타임라인 트리거가 있을 수도 있기 때문이죠. -예를 들어, 절알렉에는 `Fluid Swing`와 `Propeller Wind` 등을 이용하는 타임라인 트리거가 있습니다. -만약 이 이름들이 바뀌거나 지워진다면, 타임라인 트리거가 작동하지 않게 됩니다. -특히, 한국어 스킬명으로 바꾸는 경우, 타임라인 트리거는 영어 타임라인을 기반으로 작동하기 때문에 -같은 스킬명을 표시한다 하더라도 타임라인 트리거가 작동하지 않습니다. -(타임라인은 각 언어마다 번역 과정을 거쳐 화면에 표시되며, 타임라인 트리거는 영어 타임라인 기반으로 작동합니다.) - -## 기능 사용자 설정하기 - -이 문단은 cactbot 모듈에 사용자 지정할 수 있는 다른 요소들에 대해 다룹니다. -몇몇 변수들은 설정 UI에 있지도 않고 트리거도 아닌 것들이 있습니다. - -각각의 cactbot 모듈은 다양한 옵션을 제어하는 `Options` 변수를 가지고 있습니다. -수정할 수 있는 옵션은 각 `ui//.js` 파일 최상단 `Options` 부분에 나열되어 있습니다. - -예를 들어 [ui/raidboss/raidboss.js](../../ui/raidboss/raidboss.js)에는 -`PlayerNicks` 옵션을 찾아볼 수 있는데, 플레이어 닉네임을 따로 설정하는 옵션이 있습니다. - -```javascript -Options.PlayerNicks = { - // '이름 성': '닉네임', - 'Banana Nana': 'Nana', - 'The Great\'one': 'Joe', // The Great'one와 같이 이름에 작은 따옴표가 포함된 경우 그 앞에 역슬래시를 추가해야 합니다. - 'Viewing Cutscene': 'Cut', - // 기타 더 많은 닉네임을 추가할 수 있습니다. -}; -``` - -**주의**: user 디렉토리에 있는 파일들은 cactbot 설정 UI에서 설정한 값들을 -조용히 덮어씌울 것입니다. -이 부분이 헷갈릴 수 있는데, -따라서 일반적으로 기본으로 제공되는 설정 기능으로 최대한 설정해보고, -그 설정 기능으로는 수정할 수 없는 것들만 user 파일들로 수정하는 것이 좋습니다. - -## 초보를 위한 무작정 따라하기 - -위 설명이 전혀 이해되지 않는 분들을 위해 그냥 무작정 따라하면 되는 설명을 추가했습니다. 지금까지의 내용을 이해하신 분이라면 읽지 않아도 됩니다. - -User 폴더의 위치를 모른다면, [User 디렉토리 설정하기](#User-디렉토리-설정하기)를 확인하세요. 특히, 해루봇 사용자의 경우 User 폴더 경로를 새로 지정하기를 권장합니다. - -### Raidboss 트리거 수정하기 - -1) `user` 폴더의 `raidboss.js` 파일을 편집 프로그램으로 엽니다. (메모장으로도 가능하며, 추가 프로그램을 설치할 의향이 있다면, [notepad++](https://notepad-plus-plus.org/downloads/)를 추천합니다.) 해당 파일이 없다면, 새로 만듭니다. 확장자가 js로 생성됐는지 반드시 확인하세요. - -1) 다음 코드 블록을 `raidboss.js` 파일 가장 아래에 붙여넣습니다. - - ```javascript - Options.Triggers.push({ - zoneId: ZoneId.SomeId, - triggers: [ - { - - }, - ], - }); - ``` - -1) [데이터 목록](https://github.com/quisquous/cactbot/tree/triggers)에서 지금 수정하고 싶은 레이드나 던전의 `.js` 파일을 찾아서 여세요. 해당하는 던전의 영문명은 직접 알아내야 합니다. - -1) `raidboss.js` 파일에 붙여넣은 내용 중, `ZoneId.SomeId`를 지우고 그 위치에 방금 찾아서 연 `.js` 파일에 나와있는 `zoneId`를 붙여넣습니다. -예시) `e8s.js`에는 `ZoneId.EdensVerseRefulgenceSavage`가 `zoneId`로 적혀있으므로, `ZoneId.SomeId`를 지우고 `ZoneId.EdensVerseRefulgenceSavage`를 붙여넣습니다. - -1) trigger 내부 중괄호와 쉼표를 지우고, `.js` 파일 내에서 수정하길 원하는 트리거를 그대로 붙여넣습니다. (id 바로 위에 있는 중괄호부터 복사해야 합니다.) - - 전: - - ```javascript - triggers: [ - { - - }, - ], - ``` - - 후: - - ```javascript - triggers: [ - { // <- id 바로 위의 여는 중괄호가 하나의 트리거의 시작점입니다. - id: 'E8S Reflected Frost 1', - netRegex: NetRegexes.ability({ source: 'Frozen Mirror', id: '4DB[78]', capture: false }), - suppressSeconds: 5, - infoText: { - en: 'Swap Sides', - de: 'Seiten wechseln', - fr: 'Changez de côté', - cn: '换边', - ko: '반대로 이동', - }, - }, // <- 시작한 중괄호의 닫는 쌍이 하나의 트리거의 끝점입니다. 쉼표도 포함한다고 생각하는게 복잡하지 않습니다. - ], - ``` - -1) 수정하고 싶은 부분을 수정합니다. 아래 예시에서는 한국어 출력 문구를 "피하기"로 바꿔보겠습니다. - - ```javascript - triggers: [ - { - id: 'E8S Reflected Frost 1', - netRegex: NetRegexes.ability({ source: 'Frozen Mirror', id: '4DB[78]', capture: false }), - suppressSeconds: 5, - infoText: { - en: 'Swap Sides', - de: 'Seiten wechseln', - fr: 'Changez de côté', - cn: '换边', - ko: '피하기', // <- 여기를 바꿨습니다. - }, - }, - ], - ``` - -1) 더 수정하고 싶은 트리거가 있다면, 이 과정의 맨 처음으로 돌아가 반복합니다. - -**주의**: 과정 도중 쉼표를 지우거나 각 괄호의 쌍이 서로 맞지 않는 등 문법 오류가 발생하지 않도록 주의하세요. - -트리거를 더 자유자재로 수정하고 싶다면, 이 문서의 다른 문단들을 참고하세요. 이 문단은 최소한의 지식으로 수정할 수 있도록 **최대한** 간단히 설명했습니다. - -### Raidboss 타임라인 수정하기 - -1) `user` 폴더의 `raidboss.js` 파일을 편집 프로그램으로 엽니다. (메모장으로도 가능하며, 추가 프로그램을 설치할 의향이 있다면, [notepad++](https://notepad-plus-plus.org/downloads/)를 추천합니다.) 해당 파일이 없다면, 새로 만듭니다. 확장자가 js로 생성됐는지 반드시 확인하세요. - -1) 수정하고 싶은 타임라인을 [데이터 목록](https://github.com/quisquous/cactbot/tree/triggers)에서 다운로드하세요. - 1) 목록에서 해당 파일을 찾습니다. - 1) `Raw` 버튼을 클릭합니다. - 1) 화면 우클릭 후, `다른 이름으로 저장`을 클릭하면 다운로드할 수 있습니다. - 1) 해당 파일을 User 폴더 안에 넣습니다. - - 해당하는 던전의 영문명은 직접 알아내야 합니다. - -1) 다음 코드 블록을 `raidboss.js` 파일 가장 아래에 붙여넣습니다. - - ```javascript - Options.Triggers.push({ - zoneId: ZoneId.SomeId, - overrideTimelineFile: true, - timelineFile: 'some_timeline.txt', - }); - ``` - -1) 수정하고 싶은 타임라인의 던전에 해당하는 `.js` 파일을 [데이터 목록](https://github.com/quisquous/cactbot/tree/triggers)에서 열고, 그 파일 상단에 있는 `zoneId` 값을 `ZoneId.SomeId` 대신 집어 넣습니다. 예를 들어, 절알렉 타임라인을 수정하고 있다면, `the_epic_of_alexander.js`을 열어서 그 곳에 적힌 `ZoneId.TheEpicOfAlexanderUltimate`를 `ZoneId.SomeId`를 대신해 붙여넣습니다. - -1) `timelineFile` 뒤의 `txt` 파일명을 방금 다운로드한 타임라인 `txt` 파일명으로 수정합니다. - -1) 다운로드 한 타임라인을 편집기로 열어 원하는 대로 수정합니다. - -1) 더 수정하고 싶은 타임라인이 있다면, 이 과정의 맨 처음으로 돌아가 반복합니다. - -### 간단한 메모 추가하는 법 - -엔터를 입력해도 딱히 기능은 없으므로 엔터는 마음껏 해도 됩니다.(이어져야 하는 문구 도중에 줄바꿈을 해도 된다는 뜻이 아닙니다.) `js` 파일 내에 "//"를 입력하면, 뒤의 모든 글자는 모두 무시됩니다. (줄바꿈시 무시하지 않음.) 이 방법으로 메모를 추가할 수 있습니다. - -```javascript -Options.Triggers.push({ - zoneId: ZoneId.EdensVerseRefulgenceSavage, - // 뒤에 어떤 내용을 수정한건지 적을 수 있겠죠 - // 이렇게 메모를 줄바꿈하면 새로 //를 입력해야 합니다. - triggers: [ - { - id: 'E8S Reflected Frost 1', // 이렇게 어느 문장 뒤에도 메모를 추가할 수 있습니다. - netRegex: NetRegexes.ability({ source: 'Frozen Mirror', id: '4DB[78]', capture: false }), - suppressSeconds: 5, - infoText: { - en: 'Swap Sides', - de: 'Seiten wechseln', - fr: 'Changez de côté', - cn: '换边', - ko: '피하기', - }, - }, - ], -}); -``` - -## Global 트리거 파일 Import - -유저 파일들은 자바스크립트로 `eval` 됩니다, -따라서 기본으로 제공되는 트리거 파일들과 같은 방식으로 `import` 할 수 없습니다. -유저 자바스크립트 파일들은 다음 global들에 접근할 수 있습니다: - -- [Conditions](../../resources/conditions.ts) -- [ContentType](../../resources/content_type.ts) -- [NetRegexes](../../resources/netregexes.ts) -- [Regexes](../../resources/regexes.ts) -- [Responses](../../resources/responses.ts) -- [Outputs](../../resources/outputs.ts) -- [Util](../../resources/util.ts) -- [ZoneId](../../resources/zone_id.ts) -- [ZoneInfo](../../resources/zone_info.ts) - -## User 파일 디버깅 - -### 오버레이 플러그인 로그에 에러가 나오는지 확인하세요 - -오버레이 플러그인 로그는 스크롤 기능이 있는 텍스트 창인데, -ACT -> Plugins -> OverlayPlugin.dll로 이동해서 -창의 하단을 보면 찾을 수 있습니다. - -만약 에러가 있다면, 여기에 나올 겁니다. - -### User 파일이 로드되는지 확인하세요 - -먼저, raidboss의 디버그 모드를 활성화하세요. -cactbot 설정 UI에서, -`개발자 옵션 표시`를 체크하고 페이지를 새로 고침 하세요. -다음, Raidboss 아래에 있는 `디버그 모드 활성화`를 체크하고 다시 새로 고침 하세요. - -Raidboss 디버그 모드가 활성화되어 있으면, -오버레이 플러그인 로그에 더 많은 정보를 출력합니다. -불러오는 각각의 user 파일 리스트도 출력합니다: -`[10/19/2020 6:18:27 PM] Info: raidbossy: BrowserConsole: local user file: C:\Users\tinipoutini\cactbot\user\raidboss.js` - -User 파일이 로드되었는지 확인하세요. - -### User 파일에 에러가 있는지 확인하세요 - -User 파일은 JavaScript로 작성하기 때문에 JavaScript 문법에 맞지 않게 작성하면, -에러가 발생할 것이고 user 파일은 생략되어 불러오지 않습니다. -로딩할 때 에러가 있는지 OverlayPlugin 로그를 확인하세요. - -예시: - -```log -[10/19/2020 6:18:27 PM] Info: raidbossy: BrowserConsole: local user file: C:\Users\tinipoutini\cactbot\user\raidboss.js (Source: file:///C:/Users/tinipoutini/cactbot/resources/user_config.ts, Line: 83) -[10/19/2020 6:18:27 PM] Info: raidbossy: BrowserConsole: *** ERROR IN USER FILE *** (Source: file:///C:/Users/tinipoutini/cactbot/resources/user_config.ts, Line: 95) -[10/19/2020 6:18:27 PM] Info: raidbossy: BrowserConsole: SyntaxError: Unexpected token : - at loadUser (file:///C:/Users/tinipoutini/cactbot/resources/user_config.ts:92:28) (Source: file:///C:/Users/tinipoutini/cactbot/resources/user_config.ts, Line: 96) -``` + diff --git a/docs/ko-KR/README.md b/docs/ko-KR/README.md index dae7f53e69..f761b44f01 100644 --- a/docs/ko-KR/README.md +++ b/docs/ko-KR/README.md @@ -1,510 +1,5 @@ -# cactbot (ffxiv raiding overlay) +# Permanently Moved to OverlayPlugin/cactbot - +See: [docs/ko-KR/README.md](https://github.com/OverlayPlugin/cactbot/blob/main/docs/ko-KR/README.md) -[![GitHub Workflow Status (branch)](https://img.shields.io/github/actions/workflow/status/quisquous/cactbot/test.yml?branch=main)](https://github.com/quisquous/cactbot/actions?query=workflow%3ATest+branch%3Amain) -[![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/quisquous/cactbot?color=brightgreen&sort=semver)](https://github.com/quisquous/cactbot/releases/latest) - -🌎 [[English](../../README.md)] [[简体中文](../zh-CN/README.md)] [**한국어**] - -1. [정보](#정보) -1. [설치하기](#설치하기) -1. [소스코드 빌드하기](#소스코드-빌드하기) -1. [UI 모듈 개요](#ui-모듈-개요) -1. [문제 해결](#문제-해결) -1. [Cactbot 사용자 설정](#cactbot-사용자-설정) -1. [지원 언어](#지원-언어) - -## 정보 - -cactbot은 [파이널 판타지 14](http://www.ff14.co.kr/)를 위한 레이드 툴을 제공하는 ACT 오버레이 입니다. 이 프로젝트는 -[Advanced Combat Tracker](http://advancedcombattracker.com/)의 플러그인인 -[OverlayPlugin](https://github.com/OverlayPlugin/OverlayPlugin)에서 작동하는 -오버레이 플러그인 입니다. - -cactbot은 다음 모듈을 제공합니다: - -* raidboss: 미리 설정된 타임라인과 트리거: - -![타임라인 스크린샷](../../screenshots/promo_raidboss_timeline.png) -![트리거 스크린샷](../../screenshots/promo_raidboss_triggers.png) - -* oopsyraidsy: 실수와 데스 리포트 - -![oopsy 스크린샷](../../screenshots/promo_oopsy.png) - -* jobs: 간결한 게이지와 버프와 프록 트래킹 - -![rdm jobs 스크린샷](../../screenshots/promo_jobs.png) - -* eureka: 에우레카 NM 트래커 지도 - -![eureka 스크린샷](../../screenshots/promo_eureka.png) - -* radar: 마물 방향, 첫 어글자 알림 - -![radar 스크린샷](../../screenshots/promo_radar.png) - -* dps: DPS 미터기 추가 기능 - -![xephero 스크린샷](../../screenshots/xephero.png) - -## 설치하기 - -**참고**: 해루봇을 사용하는 경우에는 이 문단을 읽지 말고 해루봇에서 다운로드하면 됩니다. - -### 의존성 프로그램 설치 - -[.NET Framework](https://www.microsoft.com/net/download/framework) 4.6.1 버전 이상을 설치하세요. - -반드시 파이널 판타지 14를 [DirectX 11](http://imgur.com/TjcnjmG)로 실행해야 합니다. - -아직 [Advanced Combat Tracker](http://advancedcombattracker.com/)를 설치하지 않았다면, 64비트 버전을 설치하세요. - -### FFXIV ACT Plugin 설치 - -만약 방금 ACT를 설치했다면, -Startup Wizard가 나타날 것 입니다. -Startup Wizard를 다른 방법으로 실행하려면, -`Options`를 클릭하고, `Show Startup Wizard`를 클릭하세요. - -![startup wizard 스크린샷](../../screenshots/ffxiv_plugin_show_startup_wizard.png) - -Startup Wizard에서, -`FFXIV Parsing Plugin`을 선택하고 `Download/Enable Plugin` 버튼을 클릭하세요. -이렇게 해서 `%APPDATA%\Advanced Combat Tracker\Plugins\FFXIV_ACT_Plugin.dll`를 다운로드하고 -플러그인 리스트에서 활성화시킵니다. - -![startup wizard 다운로드 스크린샷](../../screenshots/ffxiv_plugin_parsing_plugin.png) - -다른 FFXIV Plugin 설치 가이드: - -* [fflogs 동영상 가이드](https://www.fflogs.com/help/start/) -* [TomRichter 가이드](https://gist.github.com/TomRichter/e044a3dff5c50024cf514ffb20a201a9#installing-act--ffxiv-plugin) - -### OverlayPlugin 설치 - -이제, `Plugins` 탭을 선택하고 `Plugin Listing`을 클릭해보면, -플러그인 리스트가 다음과 같이 보여야 합니다. - -![blank plugin listing 스크린샷](../../screenshots/get_plugins_blank.png) - -`Get Plugins`을 클릭해서 ACT plugin 설치 도우미를 여세요. - -`Overlay Plugin`을 선택하고 `Download and Enable`을 클릭하세요. - -![overlay plugin 선택 스크린샷](../../screenshots/get_plugins_overlayplugin.png) - -이렇게 해서 OverlayPlugin을 -`%APPDATA%\Advanced Combat Tracker\Plugins\OverlayPlugin`에 다운로드하고 -`OverlayPlugin.dll`을 플러그인 리스트에서 활성화합니다. - -참고로, RainbowMage 버전이나 hibiyasleep 버전, ngld 버전이 아니라 -반드시 [가장 최신 버전](https://github.com/OverlayPlugin/OverlayPlugin) OverlayPlugin을 사용해야 합니다. - -### cactbot 설치 - -다시, `Plugins` 탭을 선택하고 `Plugin Listing`을 클릭한 다음, -`Get Plugins`를 클릭하세요. - -`Cactbot`을 선택하고 `Download and Enable`을 클릭하세요. - -![cactbot selection 스크린샷](../../screenshots/get_plugins_cactbot.png) - -이렇게 해서 cactbot을 -`%APPDATA%\Advanced Combat Tracker\Plugins\cactbot-version\cactbot`에 다운로드하고 -`CactbotOverlay.dll`을 플러그인 리스트에서 활성화합니다. - -**참고**: ACT가 기대하는 압축 파일 구조와 -cactbot이 zip 파일을 생성하는 구조 간의 차이 때문에 -처음 cactbot을 받았던 버전을 포함한 -`cactbot-0.15.2`과 같은 폴더가 생성될 것입니다. -이 폴더명은 상관이 없고 딱히 의미가 없습니다. - -플러그인이 올바른 순서로 배치되었는지 확인하세요. -순서는 반드시 FFXIV Plugin가 가장 먼저, 그 다음 OverlayPlugin, 그 다음으로 cactbot 순서여야 합니다. -만약 위 절차를 그대로 따랐다면, 다음과 같이 보일겁니다. - -![플러그인 순서](../../screenshots/get_plugins_complete.png) - -마지막으로, ACT를 재시작하세요. - -## 오버레이 모듈 추가하기 - -이하 내용은 raidboss 오버레이 모듈을 설치하는 예제입니다. -다른 cactbot 오버레이를 설정하는 방법 또한 모두 동일합니다. - -1. ACT를 여세요. -1. cactbot 플러그인을 추가한 후에 반드시 ACT를 재시작했는지 확인하세요. -1. `Plugins` 탭 안의 `OverlayPlugin.dll` 탭으로 이동하세요. -1. "추가" 버튼을 클릭하고 리스트 안에 있는 `Cactbot Raidboss`를 선택하세요. - - ![overlay plugin 추가 스크린샷](../../screenshots/overlay_plugin_new.png) - -1. 이제, 화면에 어떤 테스트 UI가 보일겁니다. -cactbot은 테스트 UI를 기본으로 제공합니다. -두꺼운 빨간색 경계선과 -파란색 배경화면은 오버레이를 화면에서 크기를 조절하고 위치를 정하는데 도움을 줍니다. -이것들은 오버레이 설정 패널에서 위치 잠금을 설정하면 사라집니다. -크기를 조정하고 위치를 정하는 것이 끝나면 반드시 오버레이 위치를 잠가야 합니다. - - ![raidboss plugin 위치 잠금 해제](../../screenshots/overlay_plugin_new_raidboss_unlocked.png) - -1. 이 오버레이의 이름을 짓고 싶은 대로 입력하세요. 예시) `raidbossy` -1. `OK` 버튼을 클릭해서 오버레이를 추가하세요. -이제 `Plugins` -> `OverlayPlugin.dll` 탭에 있는 오버레이 리스트에 나타날 것입니다. - -1. 드래그하고 크기를 조절해서 오버레이를 원하는대로 위치시키세요. - -1. `Raidboss` 오버레이의 `일반` 탭에서, `위치 잠금`와 `클릭 무시`체크 박스를 선택하세요. -테스트 타임라인 바, 디버그 텍스트, 빨간색 경계선과 옅은 파란색 배경은 오버레이가 잠기면 사라집니다. - - ![raidboss plugin 설정](../../screenshots/overlay_plugin_new_raidboss_locked.png) - -1. raidboss 플러그인을 테스트하고 싶다면, 중부 라노시아 여름여울 농장으로 텔레포한 다음, `/초읽기 5`를 실행하세요. - -1. 다른 cactbot 오버레이를 추가하는 것도 비슷한 과정을 거칩니다. -같은 방법을 따라하고 cactbot 프리셋만 다른 것을 선택하세요. - -## 소스코드 빌드하기 - -먼저 상기 안내에 따라 cactbot을 설치하세요. -의존성 파일들을 설치하기 위해서는 **스크립트 방식** 또는 **수동**, 두 가지 방법이 있습니다. - -### 의존성 설치: 스크립트 방식 - -1. `curl`이 반드시 설치되어 있어야 합니다. (의존성 파일들을 다운로드하기 위해 사용됩니다.) -1. `node --import node --loader=ts-node/esm ./util/fetch_deps.ts` 스크립트를 실행하세요. -1. **빌드하는 단계**로 이동하세요. - -### 의존성 설치: 수동 - -1. 에서 최신 Zip 파일을 다운로드 하세요. -1. `Advanced Combat Tracker.exe`를 `cactbot/plugin/ThirdParty/ACT/`에 압축 해제하세요. -1. 에서 최신 SDK Zip 파일을 받으세요. (파일 이름에 SDK라는 문구가 포함되어 있는지 반드시 확인하세요) -1. `FFXIV_ACT_Plugin.dll`를 포함해서 `SDK folder`를 `cactbot/plugin/ThirdParty/FFXIV_ACT/`에 압축 해제하세요. -1. 에서 최신 Zip 파일을 다운로드 하세요. -1. `OverlayPlugin.dll`를 포함해서 `libs folder`를 `cactbot/plugin/ThirdParty/OverlayPlugin/`에 압축 해제하세요. -1. **빌드하는 단계**로 이동하세요. - -폴더 구조가 다음과 유사해야 합니다. (파일 목록은 추후 업데이트로 변경될 수 있음에 주의): - -```plaintext -ThirdParty -|- ACT -| |- Advanced Combat Tracker.exe -|- FFXIV_ACT -| |- SDK -| | |- FFXIV_ACT_Plugin.Common.dll -| | |- FFXIV_ACT_Plugin.Config.dll -| | |- FFXIV_ACT_Plugin.LogFile.dll -| | |- FFXIV_ACT_Plugin.Memory.dll -| | |- FFXIV_ACT_Plugin.Network.dll -| | |- FFXIV_ACT_Plugin.Overlay.dll -| | |- FFXIV_ACT_Plugin.Parse.dll -| | |- FFXIV_ACT_Plugin.Resource.dll -| |- FFXIV_ACT_Plugin.dll -|- OverlayPlugin - |- libs - | |- HtmlRenderer.dll - | |- Markdig.Signed.dll - | |- Newtonsoft.Json.dll - | |- OverlayPlugin.Common.dll - | |- OverlayPlugin.Core.dll - | |- OverlayPlugin.Updater.dll - | |- SharpCompress.dll - | |- System.ValueTuple.dll - | |- websocket-sharp.dll - |- OverlayPlugin.dll -``` - -### 플러그인을 빌드하는 단계 - -1. 솔루션을 Visual Studio로 여세요. (Visual Studio 2017에서 작동을 테스트하고 있습니다). -1. "Release"와 "x64" 설정으로 빌드하세요. -1. 플러그인은 **bin/x64/Release/CactbotOverlay.dll**에 빌드될 겁니다. -1. 빌드된 플러그인을 ACT에 플러그인으로 직접 추가하세요. -ACT -> Plugins -> Plugin Listing 탭에서, `Browse` 버튼을 클릭하고 이 파일이 빌드된 위치인 **bin/x64/Release/CactbotOverlay.dll**을 선택하세요. 그리고 `Add/Enable Plugin`을 클릭하세요. - -### npm과 webpack - -cactbot 개발자가 아니고 -개인적인 목적으로 수정하는 경우에는 -[Cactbot 사용자 설정](./CactbotCustomization.md) 문서를 참고해야 합니다. -cactbot 파일을 직접 수정하는 것은 권장하지 않습니다. - -npm을 설치하고 Webpack을 실행하려면, 다음 과정을 따르세요: - -1. [nodejs와 npm](https://nodejs.org/ko/download/)을 설치합니다. -1. cactbot 최상위 디렉토리에서 `npm install`을 실행합니다. -1. `npm run build` 또는 `npm start`를 실행합니다. - -Webpack에 대해 더 자세히 알고 싶다면 -[기여하기](../../CONTRIBUTING.md#validating-changes-via-webpack) 문서를 보세요. - -## UI 모듈 개요 - -[ui/](../../ui/) 디렉토리는 cactbot의 ui 모듈을 가지고 있습니다. -만약 cactbot을 상기 설명에 따라 설치했다면, -이 디렉토리는 `%APPDATA%\Advanced Combat Tracker\Plugins\cactbot-version\cactbot\ui\`에 있을 것입니다. - -각각의 cactbot ui 모듈은 분리된 오버레이로 따로 추가해야 합니다. -더욱 자세한 오버레이 설치 방법을 확인하려면 [오버레이 모듈 추가하기](#오버레이-모듈-추가하기) 문단을 확인하세요. - -### [raidboss](../../ui/raidboss) 모듈 - -이 모듈을 사용하려면, -**ui/raidboss/raidboss.html** 파일을 URL 부분에서 선택하거나 `Cactbot Raidboss` 프리셋을 사용하세요. - -이 모듈은 레이드의 타임라인과 레이드에서 놓칠만한 정보들을 알려주는 텍스트/사운드 알림을 제공합니다. -텍스트와 사운드 알람은 ACT의 "커스텀 트리거" 기능과 비슷한 방식으로, 전투 타임라인이나 게임에서 찍히는 로그 메시지를 기반으로 제공됩니다. -이 모듈은 월드 오브 워크래프트의 [BigWigs Bossmods](https://www.curseforge.com/wow/addons/big-wigs) 애드온과 비슷하게 보이고 느껴지도록 디자인 되었습니다. - -[이 페이지](https://quisquous.github.io/cactbot/util/coverage/coverage.html?lang=ko)에는 -현재 cactbot이 지원하는 컨텐츠 목록이 나열되어 있습니다. -지원하는 컨텐츠는 계속해서 늘리고 있습니다. -하지만 많은 수의 오래된 컨텐츠들이 아직 지원되지 않습니다. - -전투 타임라인은 [ACT Timeline](https://github.com/grindingcoil/act_timeline) 플러그인에 맞게 디자인된 파일들을 사용합니다. [이 곳](http://dtguilds.enjin.com/forum/m/37032836/viewthread/26353492-act-timeline-plugin)에 규칙이 정리되어 있으며, -cactbot에서는 [약간의 확장 기능](../TimelineGuide.md)을 추가했습니다. - -텍스트 알람에는 세 단계가 있으며, 중요도에 따라 다음과 같이 분류됩니다: `info`, `alert`, 그리고 `alarm`. -텍스트 메시지는 이 세 개 중 한 가지며, 더 중요한 알림일수록 크고 눈에 잘 띄는 색으로 표현됩니다. -화면에 나오는 텍스트보다는 TTS를 선호하는 경우 TTS로 나오도록 설정할 수 있습니다. - -타임라인 파일과 트리거 파일은 [ui/raidboss/data](../../ui/raidboss/data)에서 찾을 수 있습니다. 타임라인 파일은 `.txt` 확장자를 가지며, 트리거 파일은 `.ts` 확장자를 갖습니다. - -아래 스크린샷은 raidboss 모듈이 하이라이트되어 있습니다. 빨간색 동그라미로 표시된 것이 타임라인이고, 노란색 동그라미로 표시된 것이 `alert` 단계의 텍스트 알람입니다. - -![raidboss 스크린샷](../../screenshots/Raidboss.png) - -### raidboss emulator - -만약 트리거나 타임라인을 작성하는 중이고 그 결과를 테스트하고 싶다면, raidboss emulator를 사용할 수 있습니다: -**ui/raidboss/raidemulator.html**. - -이 기능은 현재 오버레이로 제공되지 않고, 브라우저에서 불러와야 합니다. -이 페이지는 최신 크롬에서 작동하고, -다른 브라우저에서도 작동하겠지만 별로 테스트되지 않았습니다. - -방법: - -1. ACT를 실행하세요. -1. 웹소켓 서버가 실행되고 있는지 확인하세요. Plugins -> OverlayPlugin WSServer -> Stream/Local Overlay에서 확인할 수 있습니다. -1. URL 생성 리스트에서 `Cactbot Raidboss (Combined Alerts and Timelines)`를 선택하세요. -1. URL에서 `raidboss.html` 부분을 `raidemulator.html`로 바꾸세요. -1. 편집된 URL을 크롬에서 여세요. -1. [네트워크 로그](../FAQ-Troubleshooting.md#how-to-find-a-network-log)를 페이지에 드래그 앤 드롭하세요. -1. 지역명과 적을 선택하고, `Load Encounter`를 클릭하세요. - -만약 에뮬레이터가 작동하지 않는다면, 개발자 도구 Console 로그에서 에러를 확인하세요. -웹소켓으로 ACT와 연결되기 전까지는 어떤 버튼도 작동하지 않습니다. - -![raidboss emulator 스크린샷](../../screenshots/raidboss_emulator.png) - -### [oopsyraidsy](../../ui/oopsyraidsy) 모듈 - -이 모듈을 사용하려면, -**ui/oopsyraidsy/oopsyraidsy.html** 파일을 URL 부분에서 선택하거나 `Cactbot OopsyRaidsy` 프리셋을 사용하세요. - -이 모듈은 실수 추적과 사망 리포트를 제공합니다. -Oopsy raidsy는 전투에서 어떤 문제가 있었는지, 왜 죽었는지 이해하는데 낭비되는 시간을 줄이는 목적으로 제작되었습니다. -전투동안은 혼란을 피하기 위해 제한된 수의 실수가 표시되지만, -전투가 종료되면 스크롤이 가능한 전체 실수 리스트를 보여줍니다. - -누군가 죽는다면, 마지막에 받은 데미지가 로그에 남습니다. -예를 들어, 다음과 같은 로그가 나왔다면: ":skull: 아무개: 강철 전차 (82173/23703)" 아무개가 강철 전차에 죽었을 가능성이 아주 높고, 체력이 23703 남았을 때 82173의 데미지를 받았다는 뜻입니다. -체력 값은 완벽하지 않습니다. -서버틱 문제와 동시에 많은 갯수의 데미지가 들어온 경우에 최대 1초 정도의 지연이 있을 수 있습니다. - -피할 수 있는 실수를 했을 경우에는, -oopsy에 경고(:warning:)나 실패(:no_entry_sign:) 로그가 찍혀서 무엇을 실수했는지 알 수 있습니다. - -실수 트리거들은 [ui/oopsyraidsy/data](../../ui/oopsyraidsy/data) 폴더의 각 개별 전투 파일에 자세히 나열되어 있습니다. - -![oopsy 스크린샷](../../screenshots/promo_oopsy.png) - -클릭하면 해당 항목이 클립보드에 복사되는 기능도 있습니다. -(오버레이 `클릭 무시` 설정시 작동하지 않음.) - -### [jobs](../../ui/jobs) 모듈 - -이 모듈을 사용하려면, -**ui/jobs/jobs.html** 파일을 URL 부분에서 선택하거나 `Cactbot Jobs` 프리셋을 사용하세요. - -이 모듈은 체력, 마나와 함께 속임수 공격이나 전투 기도 같은 전투 버프 타이머를 제공합니다. -레벨링이나 레이드를 할 때 음식 시간 부족 경고도 보여주고, -시각적인 초읽기 기능도 제공합니다. - -일부 직업에 대해서는 구체적인 기능을 제공하지만, 대부분의 직업은 기능을 만드는 중입니다. - -
-지원하는 잡 (클릭해서 확장) - -|잡|기능| -|:-:|:-:| -|
나이트|현재 충의 수치와 회한의 검 스택, 도트 남은 시간, 임전무퇴 지속 시간과 쿨, 그리고 속죄 쿨과 콤보 유지 시간을 보여줍니다.| -|
전사|원초 수치, 전장의 폭풍 버프의 남은 시간, 격변과 원초의 해방 쿨, 그리고 콤보 유지 시간을 보여줍니다.| -|
암흑기사|흑혈 수치와 암흑 버프 시간, 피의 칼날/피의 열광/환영 구현 지속 시간과 쿨, 그리고 콤보 유지 시간을 보여줍니다.| -|
건브레이커|무자비 지속 시간과 쿨, 피의 소일/난폭한 송곳니 쿨, 소일 양, 그리고 콤보 유지 시간을 보여줍니다.| -|
백마도사|치유의 백합&피의 백합 수, 다음 백합 충전까지의 시간, 도트 남은 시간, 그리고 심판/자각몽 쿨을 보여줍니다.| -|
학자|에테르 순환 스택, 요정 게이지 양/남은 시간, 도트 남은 시간, 그리고 에테르 순환/자각몽 쿨을 보여줍니다.| -|
점성술사|현재 보유중인 징조의 상태와, 누가 카드 효과를 받고 있는지와, 도트 남은 시간, 그리고 점지/자각몽 쿨을 보여줍니다.| -|
현자|의술석&의술침 스택, 다음 의술석 충전까지의 시간, 도트 남은 시간, 그리고 플레그마/만물의 근원/자각몽 쿨을 보여줍니다.| -|
몽크|투기량을 보여주고, 품새 시간, 몽크 버프와 디버프를 추적해줍니다.| -|
용기사|천룡의 눈과 용의 눈 갯수, 용창 버프 남은 시간, 점프 쿨, 돌격하는 창과 용의 시선 지속 시간/쿨타임을 보여줍니다.| -|
닌자|인법 수치, 풍둔술 지속 시간, 속임수 공격 지속시간/쿨, 분신술/인술 쿨, 그리고 콤보 유지 시간을 보여줍니다.| -|
사무라이|검기 수치, 검압 스택, 풍월/풍화/피안화 지속시간, 제비반전 쿨, 그리고 콤보 유지시간을 보여줍니다.| -|
리퍼|영혼/수의 수치, 죽음 설계 지속시간, 영혼 낫질&탐식 쿨, 신비의 원 지속시간과 쿨, 그리고 콤보 유지시간을 보여줍니다.| -|
음유시인|현재 연주중인 노래와 지속시간, 시상 스택, 영혼의 소리 양, 직선 사격 시전 가능 유지시간, 도트 남은 시간, 그리고 도트 틱을 표시하는 막대를 보여줍니다.| -|
기공사|열기와 충전지 수치, 콤보 유지 시간, 드릴/생화학무기&사슬닻&회전톱 쿨, 소이탄 지속시간&쿨을 보여줍니다. 그리고 소이탄을 사용하였을 때, 몇 글쿨을 때렸는지 보여줍니다.| -|
무도가|콤보 유지 시간, 환상부채 수, 에스프리 수치, 정석무도 쿨, 기교무도&플러리쉬의 쿨과 지속시간을 보여줍니다.| -|
흑마도사|도트 남은 시간, 파이가&선더 프록 지속시간, 다음 언어 통달 획득까지의 시간, MP 틱, 화염/냉기 스택과 얼어붙은 마음 스택을 보여줍니다.| -|
소환사|빙의 지속시간, 에테르 순환 스택, 불/땅/바람 에테르 스택, 생명력 흡수&빙의&자각몽 쿨을 보여줍니다.| -|
적마도사|흑/백마나 양, 버스톤/버파이어 프록 지속시간, 그리고 플레슈&콩트르 식스트 쿨을 보여줍니다.| -|
청마도사|무방비와 자각몽 쿨, 고통의 노래 도트 남은 시간을 보여줍니다.| - -
- -스크린샷을 보시면, job 모듈은 적마도사에 맞춰져 있습니다. -보라색 원이 되어 있는 곳을 보면, 체력과 마나 바와 함께 적마도사의 흰/붉은 마나를 추적 해주고 있습니다. -주황색 화살표를 보면 파티 버프도 추적되고 있습니다. -체력바 위에 있는 노란색 박스가 첫 근접 콤보를 실행했다는 것을 보여줍니다. -프록 트래커는 초록색 원으로 표시된 곳에 나타나고 있습니다. - -![jobs 스크린샷](../../screenshots/Jobs.png) - -### [eureka](../../ui/eureka) 모듈 - -이 모듈을 사용하려면, -**ui/eureka/eureka.html** 파일을 URL 부분에서 선택하거나 `Cactbot Eureka` 프리셋을 사용하세요. - -이 모듈은 자동으로 소환되었거나 죽은 NM을 기록하는 트래커를 제공합니다. -폭풍/밤 타이머를 보여주고, 채팅창에 올라온 에우레카 트래커 링크를 보여줍니다. -채팅창에 올라온 깃발(\)도 지도에 표시해 주고 있습니다. - -현재 트래커 정보를 직접적으로 불러오지는 못하지만, -현재 소환 불가능한 NM 목록을 복사해주는 왼쪽의 빨간색 "토벌한 마물" 버튼을 클릭하면, 게임에 입력할 수 있습니다. 예시) -`/ㄷ 토벌한 마물: 대왕 (89분) → 넘버즈 (97분) → 하즈마트 (104분) → 기수 (107분) → 카임 (119분)` - -이모지가 보이지 않는다면, [이 Windows 업데이트](https://support.microsoft.com/en-us/help/2729094/an-update-for-the-segoe-ui-symbol-font-in-windows-7-and-in-windows-ser)를 설치했는지 확인하세요. - -![eureka 스크린샷](../../screenshots/promo_eureka.png) - -### [radar](../../ui/radar) 모듈 - -이 모듈을 사용하려면, -**ui/radar/radar.html** 파일을 URL 부분에서 선택하거나 `Cactbot Radar` 프리셋을 사용하세요. - -이 모듈은 주위 마물(S급, A급 등)을 알 수 있게 해줍니다. -하나를 발견하면, 대상으로의 화살표(캐릭터의 전방 기준)나 거리를 알려줍니다. - -누가 마물을 처음 공격 또는 애드냈는지 알려주는 옵션도 있습니다. -또, 다른 등급끼리 개별적인 옵션을 설정할 수도 있습니다. -(예를 들어, S급에는 소리 알림을 주게 하고, B급에는 조용히 하게 할 수 있습니다.) -다른 어떤 몬스터 이름이든 커스텀 트리거로 만들 수도 있습니다. - -`cactbot/user/radar-example.js`에서 더 많은 옵션을 확인할 수 있습니다. - -![radar 스크린샷](../../screenshots/promo_radar.png) - -### [dps](../../ui/dps) 미터기 - -cactbot은 오버레이 플러그인의 MiniParse의 애드온에 맞게 제작된 그 어떤 미터기 오버레이와 함께 사용할 수 있습니다. -cactbot의 추가적인 자바스크립트 API를 이용해 더 많은 기능을 사용할 수 있는 옵션을 제공할 수도 있습니다. -cactbot은 전멸시에 자동으로 전투 종료를 실행해줍니다. -따라서 ACT 전투 시간을 `infinity`(무제한)로 설정해도 괜찮습니다. - -[xephero](../../ui/dps/xephero) 미터기는 MiniParse의 미터기와 같은 기반을 사용하였는데, -페이즈별 미터기 기록을 나눠볼 수 있는 기능이 추가되어 있습니다. -아래 스크린샷을 보면, B1, B2, B3 페이즈가 나와있습니다. 이는 던전 보스로부터 자동 생성되지만, 레이드 보스 페이즈를 구분하는데도 사용할 수 있습니다. - -![xephero 스크린샷](../../screenshots/xephero.png) - -[rdmty](../../ui/dps/rdmty) 미터기는 MiniParse의 미터기와 같은 기반을 사용하였는데, -직업의 색깔이 [fflogs](http://ko.fflogs.com)의 색깔에 맞도록 색깔을 조정했습니다. - -![rdmty 스크린샷](../../screenshots/rdmty.png) - -### [pull counter](../../ui/pullcounter) 모듈 - -이 작은 모듈은 레이드 보스를 지금 몇 번째 트라이하고 있는지 보여줍니다. -이 기능은 주로 스트리밍을 많이 하고 영상을 되돌려 보고 싶은 사람들을 위한 것입니다. -화면에 숫자가 표시되면 영상을 정리하거나 원하는 부분을 -찾아가기 쉬워질 것입니다. - -대부분의 경우에는, `/echo pullcounter reset`을 채팅창에 입력해서 현재 보스/지역의 트라이 횟수를 초기화할 수 있습니다. -또한 -`%APPDATA%\Advanced Combat Tracker\Config\RainbowMage.OverlayPlugin.config.json` -파일에서 카운트를 직접 수정할 수 있습니다. - -![pull counter 스크린샷](../../screenshots/pullcounter.png) - -### [test](../../ui/test) 모듈 - -이 모듈을 사용하려면, -**ui/test/test.html** 파일을 URL 부분에서 선택하거나 `Cactbot Test` 프리셋을 사용하세요. - -이 모듈은 cactbot 변수들을 화면에 보여주는 테스트용 모듈입니다. 게임을 플레이 하는 도중에 사용하도록 만들어지지 않았습니다. -모든 것들이 제대로 작동하고 있는지 확인하거나 오버레이 문제를 디버그할 때 유용합니다. - -![테스트 스크린샷](../../screenshots/test.png) - -## 문제 해결 - -자주 나타나는 Cactbot 문제를 포함한 일반적인 FAQ는 [여기](../FAQ-Troubleshooting.md)에서 확인할 수 있습니다. - -## Cactbot 사용자 설정 - -대부분의 cactbot 사용자 설정은 ACT 안에 있는 설정 패널을 통해 할 수 있습니다. - -![설정 패널](../../screenshots/config_panel.png) - -이 화면은 -Plugins -> OverlayPlugin.dll -> Cactbot으로 이동하면 확인할 수 있습니다. - -특히, -만약 raidboss 알림에 TTS를 사용하고 싶다면, -"기본 알람 출력 방식"을 -"TTS만" 또는 "텍스트와 TTS"으로 바꿀 수 있습니다. -이 설정을 각 트리거마다 따로 적용시킬 수도 있습니다. - -또는, 어떤 이유 때문에 (???) 준비 확인 소리 알림을 원치 않을 수도 있습니다. -이 소리는 설정 패널에서 비활성화할 수 있습니다. -Raidboss -> 공용 트리거 -> General -> General Ready Check로 가서, -`기본` 대신 `비활성화`로 설정하세요. - -이 옵션들은 -`%APPDATA%\Advanced Combat Tracker\Config\RainbowMage.OverlayPlugin.config.json` -파일에 저장됩니다. -이 파일을 직접 수정하는 것은 권장하지 않습니다. -이 파일은 [엄격한 json](https://jsonlint.com/) 문법으로 작성해야 하고 -파일이 잘못 작성되면 ACT에서 불러오지 못할 수 있기 때문입니다. - -user 파일을 사용하기 보다는 -대부분의 설정들은 이 설정 패널을 통해서 하기를 권장합니다. -`cactbot/user/`에 있는 파일들은 더 강력하며 -설정 패널에 있는 모든 것들을 덮어쓸 수 있습니다. -하지만, `cactbot/user/` 파일이 조용히 설정을 덮어쓰고 있어서 -설정 패널이 제대로 적용되지 않을 때는 혼란스러울 수 있습니다. - -사용자 Javascript와 css 파일에 대해 더 자세히 알고 싶다면 -[이 문서](CactbotCustomization.md)를 확인하세요. - -## 지원 언어 - -cactbot은 현재 서비스 중인 글로벌 서버 버전(영어, 독일어, 프랑스어, 일본어) -중국 서버 버전(중국어), -그리고 한국 서버 버전(한국어)에서 -테스트되고 작동합니다. -일부 번역은 계속 진행 중입니다. - -## 라이선스, 상표, 저작권 - -cactbot은 [아파치 2.0 라이선스](../../LICENSE)에 따른 오픈 소스 프로젝트입니다. - -FINAL FANTASY / 파이널 판타지는 Square Enix Holdings Co., Ltd의 등록 상표입니다. - -파이널 판타지 아트와 아이콘는 [FINAL FANTASY® XIV Materials Usage License](https://support.na.square-enix.com/rule.php?id=5382)에 따라 비상업적 목적으로 재사용됩니다. - -다른 번들 프로젝트에 대한 자세한 내용은 [LICENSE](../../LICENSE) 파일을 참조하세요. + diff --git a/docs/zh-CN/CactbotCustomization.md b/docs/zh-CN/CactbotCustomization.md index f20723a2d9..2e7df6d7e7 100644 --- a/docs/zh-CN/CactbotCustomization.md +++ b/docs/zh-CN/CactbotCustomization.md @@ -1,360 +1,5 @@ -# Cactbot自定义教程 +# Permanently Moved to OverlayPlugin/cactbot -🌎 [[English](../CactbotCustomization.md)] [**简体中文**] [[繁體中文](./zh-TW/CactbotCustomization.md)] [[한국어](../ko-KR/CactbotCustomization.md)] +See: [docs/zh-CN/CactbotCustomization.md](https://github.com/OverlayPlugin/cactbot/blob/main/docs/zh-CN/CactbotCustomization.md) -- [使用cactbot配置界面](#使用cactbot配置界面) -- [通过cactbot配置界面改变触发器文本](#通过cactbot配置界面改变触发器文本) -- [用户文件夹概览](#用户文件夹概览) -- [设置您自己的用户文件夹](#设置您自己的用户文件夹) -- [样式自定义](#样式自定义) -- [Raidboss触发器自定义](#raidboss触发器自定义) - - [例1:改变输出文本](#例1改变输出文本) - - [例2:使挑衅提示适用于全职业](#例2使挑衅提示适用于全职业) - - [例3:添加自定义触发器](#例3添加自定义触发器) -- [Raidboss时间轴自定义](#raidboss时间轴自定义) -- [行为自定义](#行为自定义) -- [用户文件的调试](#用户文件的调试) - - [检查OverlayPlugin的错误日志](#检查OverlayPlugin的错误日志) - - [检查文件是否加载](#检查文件是否加载) - - [检查文件是否有错误](#检查文件是否有错误) - -## 使用cactbot配置界面 - -自定义cactbot时,推荐使用cactbot的配置界面进行操作。该界面位于 ACT -> Plugins -> OverlayPlugin.dll -> Cactbot。 - -它可以提供如下功能: - -- 设置触发器输出TTS -- 禁用触发器 -- 改变触发器输出 -- 改变cactbot语言 -- 音量设置 -- 隐藏奶酪图标 - -您可能无法通过cactbot配置界面以配置所有您想要的更改。但这是所有自定义方式中最简单的,适合作为您开启定制化的第一步。 以后此界面会添加更多的选项。 - -此处的选项会存储于 `%APPDATA%\Advanced Combat Tracker\Config\RainbowMage.OverlayPlugin.config.json` 文件中。但您并不需要也不应当直接修改该文件。 - -## 通过cactbot配置界面改变触发器文本 - -在位于 ACT -> 插件 -> OverlayPlugin.dll -> Cactbot -> Raidboss 的 cactbot 配置界面中,罗列着所有的触发器。这里的列表让您可以更改每个触发器支持外部更改的配置设置。 - -名称旁边带有铃铛 (🔔) 的设置项的触发器输出文本是可以被覆盖的。举个例子,假设有一个🔔onTarget字段,其文本为 `死刑点${player}`。 当某人接到死刑技能时,这个字符串将出现在屏幕上(或通过tts播报)。 `${player}` 参数由触发器动态设置。任何类似于 `${param}` 的字符串都是动态参数。 - -比如,您可以将这个文本更改为 `${player} 即将死亡!`。或者,也许您不关心谁是目标,那么您可以将其改为 `死刑` 以使文本更加简短。如果您想撤消自己的更改,只需清空文本框即可。 - -但这个方式有一定的限制。例如,您无法更改逻辑;而且在大多数情况下,您无法使 `tts` 的播报内容与 `alarmText` 相区别、无法添加更多的参数。如果您想要对触发器做出更加复杂的覆盖操作,那么您需要查看 [Raidboss触发器自定义](#raidboss触发器自定义) 小节。 - -每一个引用玩家的参数(通常称为 ${player} ,但不总是),都可以进一步修改输出文本: - -- `${player.job}`:职业缩写,例如 白魔 -- `${player.jobFull}`:职业全名,例如 白魔法师 -- `${player.role}`:职能,例如 治疗 -- `${player.name}`:玩家的全名,例如 吉田直树 -- `${player.nick}`:玩家的昵称/名,例如 吉田直树 (注:国服对于昵称和全名不做区分;国际服的昵称指代“first name”) -- `${player.id}`:玩家的ID(用于测试),例如 1000485F - -当发生错误,或者玩家不在你的队伍中,或者使用了无效的后缀时,系统可能会退而使用默认昵称,以确保能够打印出相应的信息。 - -默认设置下,`${player}` 等同于 `${player.nick}`,但是你可以在 cactbot 配置界面的 raidboss 部分下的 “默认玩家代称” 选项来设置此默认值。 - -## 用户文件夹概览 - -若cactbot配置界面不存在您所需的选项,您可能需要考虑以用户文件覆盖的方式进行自定义。您需要编写JavaScript代码和CSS样式表,这意味着您可能需要掌握一点点编程知识。 - -Cactbot的设计哲学要求用户的任何自定义配置都应当存放于用户文件夹中。同时这也能防止您所做的更改在今后cactbot的更新中被覆盖失效。另外,目前您无法通过直接修改cactbot的文件应用您的更改,除非您了解如何构建您自己的项目。 - -所有的cactbot模块都会从 [user/](../../user/) 文件夹加载用户设置。 `raidboss` 模块会加载 `user/raidboss.js` 与 `user/raidboss.css`,以及所有 `user/raidboss/` 目录及子目录下的任意 `.js` 和 `.css` 文件。(时间轴`.txt` 文件必须与引用它们的 `.js`文件放在同一个文件夹中。) 这些用户自定义文件将在cactbot自身加载完毕后加载,并可以覆盖对应的模块的设置。 - -与之类似,`oopsyraidsy` 模块会加载 `user/oopsyraidsy.js` 与 `user/oopsyraidsy.css`,以及 `user/oopsyraidsy/` 目录及子目录下的所有 `.js` 和 `.css` 文件。依此类推,每个模块都支持以此方式加载对应名称的自定义文件。 - -cactbot将按照字母顺序优先加载user文件夹中的子文件夹里的文件,其次加载子文件夹外的文件。这就是为什么 `user/raidboss.js` 文件总是最后被加载并可以覆盖 `user/raidboss/` 文件夹中任何文件中的配置。例如,`user/alphascape/some_file.js` 先加载, `user/mystatic/some_file.js` 再加载,最后是 `user/raidboss.js` 加载。`.css` 文件自然也遵循同样的顺序。 - -在本文档中,“用户自定义js文件”指代以上两者。除了加载顺序以外,`user/raidboss.js` 和 `user/raidboss/some_file.js` 没有任何区别。同样地,“用户自定义css文件”同时指代 `user/radar.css` 和 `user/radar/some_file.css` 二者。用户文件夹中分出子目录是为了让触发器的分享和自定义配置更容易。 - -当开发者模式开启时,你可以从[调试信息](#检查文件是否加载)中得到更多关于加载顺序的信息。 - -`user/` 文件夹中包含了一部分示例配置文件,您可以对其重命名并直接使用。如 [user/raidboss-example.js](../../user/raidboss-example.js) 文件可重命名为 `user/raidboss.js`,对其所做的更改将应用于 `raidboss` 模块。 - -在修改了这些文件之后,单击ACT中OverlayPlugin插件页面对应悬浮窗设置中的“重载悬浮窗”按钮,即可应用更改。 - -## 设置您自己的用户文件夹 - -您可以通过cactbot配置界面设置用户文件夹:ACT -> Plugins -> OverlayPlugin.dll -> Cactbot -> cactbot用户文件夹。单击 `选择文件夹` 按钮,选择磁盘上的任意文件夹。 - -如果没有选择,cactbot将自动选择其安装目录下的默认文件夹。 - -建议您选择cactbot安装目录下的 `cactbot/user` 文件夹。 该文件夹通常为位于 `%APPDATA%\Advanced Combat Tracker\Plugins\cactbot-version\cactbot\user`。 有部分示例配置文件位于 [此文件夹](../../user) 下。 - -## 样式自定义 - -用户自定义css文件可以对UI模块的位置、尺寸、颜色等进行自定义。可用的选择器可以通过阅览 `ui//.css` 文件找到。 - -例如您在 [ui/raidboss/raidboss.css](../../ui/raidboss/raidboss.css) 中,可发现诸如 `#popup-text-container` 与 `#timeline-container` 等选择器, 则您可以在 `user/raidboss.css` 中对其位置进行自定义。您可以在 `user/raidboss.css` 中或其他 `user/raidboss/` 下的 `.css` 中添加更多的样式。 - -同样地,您可以在 `.info-text` 类中添加新的CSS规则,对信息文字的尺寸和颜色进行自定义。例如: - -```css -.info-text { - font-size: 200%; - color: rgb(50, 100, 50); -} -``` - -简单地说,您可以认为cactbot会将用户文件中的CSS规则添加至内置CSS文件的末尾。因此,您需要注意 [CSS优先级规则](https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity),有时需要添加 `!important` 让您的规则可以强制覆盖。另一方面,您可能需要重置某些属性为默认的 `auto` 值。 - -我们推荐使用 [Chrome DevTools](https://developers.google.com/web/tools/chrome-devtools) 以调试CSS。您可以通过 ACT -> Plugins -> OverlayPlugin.dll -> 您的悬浮窗名字 -> 启动Debug工具 按钮以开启DevTools。 - -**注意**:某些组件的自定义较为困难,甚至无法进行自定义,如时间轴的进度条等。原因是,这些组件属于自定义HTML元素,且没有开放外部配置的接口。如果您有特别的需求,但是不知道如何修改,您可以提出一个 [github issue](https://github.com/quisquous/cactbot/issues/new/choose)。 - -**警告**:cactbot不保证CSS的向后兼容性。在以后的更改中,cactbot可能会重新组织网页结构,改变元素名称和类名称,甚至重构所有样式。因此,您需知晓您的自定义CSS有在将来出现问题的风险。 - -## Raidboss触发器自定义 - -您可以通过用户自定义js文件(例如 `user/raidboss.js` 或 `user/raidboss/` 目录下的任意 `.js` 文件)自定义触发器行为。您可以修改输出文本、适用职业、文本显示的时间等等。 - -您可以在[这个分支](https://github.com/quisquous/cactbot/tree/triggers)查看所有触发器的 JavaScript 版本。我们推荐您查看、拷贝并粘贴这个分支中的代码实现您自己的触发器。主分支(main)中的触发器代码基于 TypeScript 写成,无法直接在ACT或浏览器中运行;而发行版本中的代码经过了编译与混淆,对于人类来说难以阅读,因此不作推荐。 - -在您的raidboss模块用户自定义js文件中,`Options.Triggers` 是一个存放了触发器集合的列表。您可以通过此变量添加新触发器,或修改已有的触发器。若用户文件中存在与现有触发器 (cactbot官方提供的) 相同id的触发器,则会将后者完全覆盖。 - -在您修改触发器前,我们推荐您阅读[触发器指南](RaidbossGuide.md)以了解各触发器的诸多属性的含义。 - -一般来说,你需要将形如以下的代码块加入到你的用户自定义js文件(例如 `user/raidboss.js`)中: - -```javascript -Options.Triggers.push({ - // 在文件开头定义ZoneId, - // 例如 ZoneId.MatchAll (指定所有区域) 或 ZoneId.TheBozjanSouthernFront 等 - zoneId: ZoneId.PutTheZoneFromTheTopOfTheFileHere, - triggers: [ - { - // 这里定义的是触发器(trigger)对象。 - // 例如 id, netRegex或infoText等 - }, - ], -}); -``` - -最简单的定制触发器的方式是直接复制上面那一大块代码粘贴到此文件再进行修改。您可以修改 `zoneId` 一行为您想要触发器响应的区域id,通常位于cactbot触发器文件的顶部。[该文件](../../resources/zone_id.ts)中列出了所有可用的区域id。若您定义了错误的id,OverlayPlugin的日志窗口将会输出警告信息。然后[复制触发器文本](https://github.com/quisquous/cactbot/tree/triggers)并粘贴至此,按您的喜好进行修改。当你修改完成后,重载raidboss悬浮窗以应用更改。 - -**注意**:此方式会将原触发器完全移除,因此请在修改时不要删除任何逻辑代码。触发器均采用JavaScript编写,因此必须采用标准JavaScript语法。若您不是字面意义上的程序员,您需要格外注意这点。 - -### 例1:改变输出文本 - -假定您正在攻略巴哈姆特绝境战(UCOB),但您的固定队采用的不是cactbot中默认的火1集合吃的打法,而是先单吃火1。此外,您*同时*还想让触发器通过tts播报与显示文本不同的内容。比如,您总是忘记出人群,因此您想让它重复播报数次。 - -若您只是想修改 `信息文本`,你可以 [通过cactbot配置界面改变触发器文本](#通过cactbot配置界面改变触发器文本) 实现。 - -其中一种调整方式是编辑触发器的输出。您可以在 [ui/raidboss/data/04-sb/ultimate/unending_coil_ultimate.js](https://github.com/quisquous/cactbot/blob/triggers/04-sb/ultimate/unending_coil_ultimate.js#:~:text=UCU%20Nael%20Fireball%201) 中找到原本的 fireball #1 触发器。 - -您需要将以下的代码粘贴至您的用户自定义js文件底部。 - -```javascript -Options.Triggers.push({ - zoneId: ZoneId.TheUnendingCoilOfBahamutUltimate, - triggers: [ - { - id: 'UCU Nael Fireball 1', - netRegex: NetRegexes.ability({ source: 'Ragnarok', id: '26B8', capture: false }), - delaySeconds: 35, - suppressSeconds: 99999, - // infoText 是绿色的文字。 - infoText: { - en: 'Fire OUT', - cn: '火球,出人群', - }, - tts: { - en: 'out out out out out', - cn: '出去出去出去!', - }, - run: function(data) { - data.naelFireballCount = 1; - }, - }, - ], -}); -``` - -这里仅包含了英文和中文。 - -### 例2:使挑衅提示适用于全职业 - -目前,只有团队成员的挑衅会触发提示,并且不是所有职业都能收到提示。该例子展示了如何使其适用于所有职业。挑衅触发器可以在 [ui/raidboss/data/00-misc/general.js](https://github.com/quisquous/cactbot/blob/triggers/00-misc/general.js#:~:text=General%20Provoke) 中找到。 - -我们需要修改 `condition` 函数(function)。此处的id应当与内置的 `General Provoke` 触发器一致,才能正确覆盖同名的内置触发器。 - -您需要将以下的代码粘贴至您的用户自定义js文件底部。 - -```javascript -Options.Triggers.push({ - zoneId: ZoneId.MatchAll, - triggers: [ - { - id: 'General Provoke', - netRegex: NetRegexes.ability({ id: '1D6D' }), - condition: function(data, matches) { - // 我希望看到所有的挑衅提示,不管他们在不在我的队伍中, - // 也不管我是不是坦克。 - return true; - }, - infoText: (data, matches, output) => { - return output.text!({ player: data.party.member(matches.source) }); - }, - outputStrings: { - text: { - en: 'Provoke: ${player}', - de: 'Herausforderung: ${player}', - fr: 'Provocation: ${player}', - ja: '挑発: ${player}', - cn: '挑衅: ${player}', - ko: '도발: ${player}', - }, - }, - }, - ], -}); -``` - -当然,您也可以直接删除整个 `condition` 函数,毕竟没有condition的触发器在匹配到正则时永远会运行。 - -### 例3:添加自定义触发器 - -您也可以用同样的办法添加您的自定义触发器。 - -这是一个示例触发器,当您中了“叉形闪电”效果时,会在1秒后显示“快出去!!!”。 - -```javascript -Options.Triggers.push([ - { - zoneId: ZoneId.MatchAll, - triggers: [ - { - // 这是一个自定义的id,因此不会覆盖任何现有的触发器。 - id: 'Personal Forked Lightning', - regex: Regexes.gainsEffect({ effect: 'Forked Lightning' }), - condition: (data, matches) => { return matches.target === data.me; }, - delaySeconds: 1, - alertText: '快出去!!!', - }, - - // 您的其他触发器…… - ], - }, - - // 其他区域的触发器集合…… -]); -``` - -我们推荐阅读 [触发器指南](RaidbossGuide.md) 以了解如何撰写cactbot的触发器,当然您也可以直接看 [ui/raidboss/data](../../ui/raidboss/data) 中现有的触发器代码。 - -## Raidboss时间轴自定义 - -一些自定义操作可以通过 [cactbot 配置界面](#使用cactbot配置界面) 实现。你可以在这个界面隐藏或重命名现有的时间轴条目,也可以添加自定义时间轴条目等。 - -仅当你需要做的操作超出了配置界面所能提供的范围时才考虑使用本章节的操作,比如完全替换整个时间轴。替换时间轴的操作与 [替换触发器](#替换raidboss触发器) 类似。 - -自定义时间轴的步骤如下: - -1) 复制原有的时间轴文本文件内容至您的用户文件夹 - - 例如,您可以复制 - [ui/raidboss/data/05-shb/ultimate/the_epic_of_alexander.txt](../ui/raidboss/data/05-shb/ultimate/the_epic_of_alexander.txt) - 至 `user/the_epic_of_alexander.txt`。 - -1) 在 user/raidboss.js 中添加代码 - - 如同我们添加触发器一样,您依旧需要定义 `zoneId`、 `overrideTimelineFile: true`, - 以及定义文本文件名称的 `timelineFile` 属性。 - - ```javascript - Options.Triggers.push({ - zoneId: ZoneId.TheEpicOfAlexanderUltimate, - overrideTimelineFile: true, - timelineFile: 'the_epic_of_alexander.txt', - }); - ``` - - (假设您已经做完了第一步,并且该文本文件的名称为 `user/the_epic_of_alexander.txt` ) - - 设置 `overrideTimelineFile: true` 是为了告诉cactbot将内置的时间轴完全替换为您添加的文件。 - -1) 按您的喜好编辑您自己的时间轴文件 - - 阅读 [时间轴指南](TimelineGuide.md) 学习更多关于时间轴的知识。 - -**注意**:编辑时间轴文件有一定的风险,因为部分触发器依赖于时间轴的特定文字。例如在绝亚历山大中,`Fluid Swing` 与 `Propeller Wind` 都有对应的时间轴触发器。如果这些文字被替换或移除,时间轴触发器也同样会失效。 - -## 行为自定义 - -这一文段将讨论自定义cactbot的其他方式。Cactbot中有一些不在配置界面显示,也不是触发器的变量。 - -每个cactbot模块都有一个名为 `Options` 的变量,它包含了若干控制选项。可用的 `Options` 变量会在每个 `ui//.js` 文件的顶部列出。 - -例如在 [ui/raidboss/raidboss.js](../../ui/raidboss/raidboss.js) 文件中,您可以通过 `PlayerNicks` 选项定义玩家的昵称。 - -```javascript -Options.PlayerNicks = { - // 国际服的格式为 '名 姓': '昵称', - // 国服的格式为 '名': '昵称', - 'Banana Nana', 'Nana', - 'The Great\'one', 'Joe', // => 这里需要一个反斜杠转义单引号 - 'Viewing Cutscene': 'Cut', - // 等等 -}; -``` - -你也可以通过 `Options.TransformTts` 函数全局替换 TTS 的文本,如: - -```javascript -Options.TransformTts = (text) => { - return text.replace('a', 'b'); -}; -``` - -**警告**:用户文件夹中的文件会静默覆盖cactbot配置窗口的同名选项。 该行为可能会造成一些困惑,因此您应当直接通过配置窗口设置这些变量,当且仅当配置窗口不提供设置方法时可以采用此方式覆盖默认行为。 - -## 触发器全局可用变量 - -用户文件以 `eval` 的方式加载,因此无法像内置的触发器文件那样使用 import 语句。 -用户文件可以访问如下的变量: - -- [Conditions](../../resources/conditions.ts) -- [ContentType](../../resources/content_type.ts) -- [NetRegexes](../../resources/netregexes.ts) -- [Regexes](../../resources/regexes.ts) -- [Responses](../../resources/responses.ts) -- [Outputs](../../resources/outputs.ts) -- [Util](../../resources/util.ts) -- [ZoneId](../../resources/zone_id.ts) -- [ZoneInfo](../../resources/zone_info.ts) - -## 用户文件的调试 - -### 检查OverlayPlugin的错误日志 - -您可以在 ACT -> Plugins -> OverlayPlugin.dll 找到位于该窗口的底部的OverlayPlugin日志窗口,它是一个自动滚动的文本窗口。 - -当运行错误时,错误信息会显示在此处。 - -### 检查文件是否加载 - -首先,您需要开启raidboss模块的调试模式。 打开cactbot配置窗口,启用 `显示开发者选项` ,然后重新加载悬浮窗。 然后,勾选raidboss模块下的 `启用调试模式`,再次重载悬浮窗。 - -当raidboss模块的调试模式启用时,OverlayPlugin的日志窗口中会打印更多信息。 每次本地的用户文件加载时都会输出类似于这样的信息: `[10/19/2020 6:18:27 PM] Info: raidbossy: BrowserConsole: local user file: C:\Users\tinipoutini\cactbot\user\raidboss.js` - -确认您的用户文件是否正常加载。 - -文件名的打印顺序就是它们的加载顺序。 - -### 检查文件是否有错误 - -用户文件采用JavaScript编写,若代码语法本身有错误,日志窗口会输出错误,您的用户文件也会被跳过而不会被加载。 在文件加载时检查OverlayPlugin的错误日志。 - -此处有一个例子: - -```log -[10/19/2020 6:18:27 PM] Info: raidbossy: BrowserConsole: local user file: C:\Users\tinipoutini\cactbot\user\raidboss.js (Source: file:///C:/Users/tinipoutini/cactbot/resources/user_config.ts, Line: 83) -[10/19/2020 6:18:27 PM] Info: raidbossy: BrowserConsole: *** ERROR IN USER FILE *** (Source: file:///C:/Users/tinipoutini/cactbot/resources/user_config.ts, Line: 95) -[10/19/2020 6:18:27 PM] Info: raidbossy: BrowserConsole: SyntaxError: Unexpected token : - at loadUser (file:///C:/Users/tinipoutini/cactbot/resources/user_config.ts:92:28) (Source: file:///C:/Users/tinipoutini/cactbot/resources/user_config.ts, Line: 96) -``` + diff --git a/docs/zh-CN/MemorySignatures.md b/docs/zh-CN/MemorySignatures.md index b60efbbee4..c3268d3383 100644 --- a/docs/zh-CN/MemorySignatures.md +++ b/docs/zh-CN/MemorySignatures.md @@ -1,231 +1,5 @@ -# 内存签名与CE指南 +# Permanently Moved to OverlayPlugin/cactbot -内存签名是一种独一无二的二进制字符串,用于在程序运行中寻找其特定位置的内存地址。 +See: [docs/zh-CN/MemorySignatures.md](https://github.com/OverlayPlugin/cactbot/blob/main/docs/zh-CN/MemorySignatures.md) -找到这些签名后,即便游戏本身拒绝提供任何API,你也可以通过签名获取游戏状态。(例如:是否处于战斗中,职业量谱的各种数值,仇恨数值的具体量等等) - -这一篇指南旨在介绍如何使用CE(Cheat Engine)查找此类内存签名。如果您了解一些基本的汇编语言或编程经验,并且有水滴石穿的耐心,那么这篇文章可能对您有一定的帮助。 - -## 目录 - -* [安装](#安装) -* [查找新的内存签名](#查找新的内存签名) - * [连接CE到游戏中](#连接CE到游戏中) - * [初始内存搜索](#初始内存搜索) - * [再次扫描](#再次扫描) - * [浏览内存](#浏览内存) - * [方法1:找出写内存的代码](#方法--1-找出写内存的代码) - * [方法2:跟踪](#方法--2-跟踪) - * [方法3:找出读内存的代码](#方法--3-找出读内存的代码) - * [汇编代码和指针](#汇编代码和指针) - * [从汇编代码中提取签名](#从汇编代码中提取签名) -* [扫描现有的内存签名](#扫描现有的内存签名) - -## 安装 - -安装[最新版本的 Cheat Engine](https://github.com/cheat-engine/cheat-engine/releases/latest)。安装程序带了一些流氓软件,因此请确保不要勾选它们,不要盲目地一直“下一步”。抱歉, 真的很流氓。 - -## 查找新的内存签名 - -![Cheat Engine屏幕截图](../images/cheatengine_initial.png) - -### 连接CE到游戏中 - -启动最终幻想14并登录。 - -然后,打开CE。点击**文件**,选择**打开进程**,然后选择最终幻想14。 - -此时,顶部栏应当会显示**ffxiv_dx11.exe**。 - -![Cheat Engine连接截图](../images/cheatengine_connected.png) - -### 初始内存搜索 - -假设我们正在寻找您的角色在内存中的职业量谱。为了简单起见,我们以寻找战士职业量谱中的兽魂值。由于内存中存在许多零值,我们从一个非零值开始会简单一些。 - -在游戏中切换到战士职业,打几下木人,让你的兽魂值达到80。 - -切换到CE。 - -![Cheat Engine首次扫描截图](../images/cheatengine_initialscan.png) - -输入80的值(不要勾选十六进制)。扫描类型应为`精确值`,数值类型应为`4字节`。此时,我们还不知道兽魂值占用了多少字节。如果是4个字节最好了,要进行筛选的选项要少得多。但是,我们目前还不能做这个假设。我们也要寻找可写但不可执行的内存。 - -然后,单击**首次扫描**。 - -这可能会为您提供一大堆值为80的内存地址。但这是一个好的开端! - -![Cheat Engine扫描结果截图](../images/cheatengine_found.png) - -这是所有这些内存地址的实时视图,一旦值被更改就会变为红色。其中有一些值,即使我们完全没有在游戏中做任何事情,它们也在不断闪烁变化。您可以点击多次**再次扫描**按钮以重复扫描并舍弃这些值。 - -### 再次扫描 - -在游戏中使用裂石飞环使兽魂降低到30。 - -切换到CE。将**数值**更改为30。单击**再次扫描**。这将大大减少内存地址的数量。 - -重复此过程,改变游戏中的兽魂值,然后重新扫描新的值,直到地址的数量降低到只有几个为止。 - -![Cheat Engine扫描后截图](../images/cheatengine_postscan.png) - -其中,黑色的地址是堆地址。而绿色的地址是[静态地址](https://medium.com/@nickteixeira/stack-vs-heap-whats-the-difference-and-why-should-i-care-5abc78da1a88)。在一般情况下,你应当使用静态地址。因为引用它的代码更容易寻找,并且这些地址通常是永久性的。 - -继续扫描,直到找到唯一的绿色的静态地址。 - -右键单击地址,然后选择**将选中的地址添加到地址列表**。地址会出现在底部的列表。在我们的例子里,这个静态地址是 `14116E128`。 - -![Cheat Engine地址列表截图](../images/cheatengine_addresslist.png) - -如果您正在按照此示例进行操作,则您所得到的静态地址很可能与此不同。不仅如此,每次在您重新启动游戏时,这个地址都会变化。这是因为Windows启用了[地址空间布局随机化](https://en.wikipedia.org/wiki/Address_space_layout_randomization) ,很大程度上使得难以进行我们现在正在做的事情。 - -这也是我们需要找到内存签名的原因。如果可执行文件和DLL地址布局没有随机化,则静态地址在每次运行时都是一致的。 - -### 浏览内存 - -在地址列表中,右键单击我们刚刚添加的地址,然后选择**浏览相关内存区域**。 - -这将会打开“内存浏览器”窗口。此窗口的上半部分是反汇编视图,下半部分是内存浏览器。虽然这两个视图位于同一窗口中,这些视图的功能是互不干扰的,也*不会*同步,这可能令人有些困惑。在您使用**反汇编此内存区域**或**浏览相关内存区域**时会跳转到您选择的最后一个地址。 - -![Cheat Engine浏览内存截图](../images/cheatengine_browsememory.png) - -但是,您应当可以看到,这个屏幕截图的左上部的数值是十六进制的 `1E`,也就是[十进制的30](https://www.google.com/search?q=0x1e+in+decimal)。 - -浏览内存使您看到周围的其他内容。这对于诸如实体或玩家的数据等尤其有用。 - -对于职业数据,附近的内存确实没有什么有趣的。 - -### 方法1:找出写内存的代码 - -现在,我们需要找到一些与此地址相关的代码。最简单的方式是找到修改此值的代码。 - -右键单击地址列表中的地址,然后选择**找出是什么改写了这个地址**。它会提示您附加调试器,单击“是”。 将会弹出一个新窗口。 - -返回到FF14,并改变兽魂的值。在这种情况下,我们打出咆哮,让兽魂从30涨到80。 - -返回到CE,调试窗口应出现一些新的信息。 - -![Cheat Engine调试器截图](../images/cheatengine_debugger.png) - -这是写入到兽魂内存地址的汇编代码。 - -需要的话,您可以单击**显示反汇编器**以查看周围的代码。 - -![Cheat Engine反汇编截图](../images/cheatengine_disassembly.png) - -不幸的是,在这个例子中,是两个操作数的函数调用。 - -更改值的一行汇编是 `mov [rcx + 08], al`。我不是很了解汇编语言,但是谷歌老师告诉我 `al` 是 `eax` 寄存器的后8位,该寄存器的值是在前面的 `movzx eax, byte ptr [rdx+01]` 一行上设置的。很明显这是写入内存的一行汇编,即 `[rcx + 08]` 是我们关心的指针,但我们需要先找到设置 `rcx` 的值的代码。这个寄存器的值在运行时会被设置多次。 - -我们在这里有两个不同的选择。一个方式是[进行跟踪以找到调用代码](#方法--2-跟踪)。第二种方式是[跟踪读取了该地址的代码](#方法--3-找出读内存的代码)(例如,切换职业会通过其他方式修改其值吗?) - -### 方法2:跟踪 - -若仅仅依靠反汇编无法得知足够的上下文信息,则应当考虑CE的“断点跟踪”功能。返回到[浏览内存](#浏览内存)的视图。此功能无法直接在地址列表中使用。 - -右键单击 `1E` 字节值。选择**数据断点**,然后点击**中断和跟踪**按钮。选项都保持默认就可以了。由于我们在寻找的是写入此区域的代码,因此需要勾选**写入时中断**。点击**确认**。 - -这将会打开跟踪窗口。返回到FF14,并改变兽魂的值。由于CE会尝试记录函数调用栈,此时游戏很可能会很卡。切换回跟踪窗口。 - -![Cheat Engine跟踪截图](../images/cheatengine_tracing.png) - -双击跟踪窗口中的汇编代码会让内存浏览器的反汇编窗口跳转到该位置。 - -此时,我们可以点击三角箭头令其展开,并双击 `ret` (类似return) 汇编指令,让我们得以直通此前我们在反汇编窗口中看到的代码。 - -双击 `mov rdx, [rsp+50]` 则会导航至我们此前看到的代码所调用的代码。 - -![Cheat Engine跟踪截图2](../images/cheatengine_tracing2.png) - -这里的 `call` 正是我们正在寻找的 `call` 调用。因此,接下来就是找到写入 `rcx` 的代码了。可以看出来应该是通过 `r9` 赋值的,而 `r9` 的值来自于存储于 `r14` 的指针。这似乎有点复杂了。 尽管我们可以不停地检视汇编代码以找到对应的代码,但也许有更好的方式。 - -### 方法3:找出读内存的代码 - -寻找写入值的代码也许很不错,但我们还可以寻找读取值的代码。 - -右键选择地址列表中的内存地址,然后点击**找出是什么访问了这个地址**。 - -与写入不同的是,这个地址似乎被某段代码定时访问。因此您需要在收集了足够的位置信息后按下**停止**按钮。 - -![Cheat Engine调试器截图2](../images/cheatengine_debugger2.png) - -在这个例子中,有两处代码访问了这块内存。其中一个访问了3000余次之多,而另一个相对来说没有如此频繁,仅有152次。 - -通过检视反汇编的代码,我们可以发现第二行代码像是个更加翔实的函数,我们从这一个开始吧。 - -![Cheat Engine反汇编截图2](../images/cheatengine_disassembly2.png) - -太好了!这可比上一节的代码简单多了。 - -### 汇编代码和指针 - -由于我们寻找的是静态地址,在程序启动后就不会改变了。我们的目标是找到包含了我们需要的地址的一些稳定的汇编代码。这让我们不管在哪里启动程序,都可以通过搜索这段内存代码找到该地址。 - -汇编代码中读取兽魂值的代码为 `movzx ebx, byte ptr [rcx+08]`。用自然语言描述则是这样:它先读取 `rcx` 寄存器中存储的指针,向后移动8个字节,然后将该指针所指的地址中的值存储到 `ebx` 寄存器中。(movzx 操作会对该值进行 [零扩展(zero extends)](https://www.felixcloutier.com/x86/movzx),尽管这不是我们所关心的。) - -那么,我们现在就需要从 `rcx` 这个寄存器开始,向后查找对 `rcx` 寄存器进行赋值的代码行。在此处的例子中,`mov rcx,[ffxiv_dx11.exe+1AAE118]` 这一行,正是对 `rcx` 进行赋值的代码行。 该行的含义是对 `rcx` 所赋的值来源于此处显示的内存地址中所存储的值。 - -```assembly -48 8B 0D 23C14201 - mov rcx,[ffxiv_dx11.exe+1AAE118] { (14116E120) } -48 85 C9 - test rcx,rcx -74 B8 - je ffxiv_dx11.exe+681FB2 -48 8B 05 67C14201 - mov rax,[ffxiv_dx11.exe+1AAE168] { (21) } -``` - -换句话说,此处的 `23C14201` 就是我们要寻找的内存地址。在此我先简要介绍一下RIP相对寻址方式。RIP相对寻址,即相对于指令指针偏移的寻址方式。其中 `RIP` 指的是指令指针寄存器,其内容为紧跟于当前指令之后的指令的地址。双击下一行(即 `text rcx,rcx` 行)就可以得到该地址。在我们的例子中,这个地址为 `13FD41FF5`。由于我们处在一个 [little endian(小端)](https://en.wikipedia.org/wiki/Endianness)系统中,此处的十六进制数 `23C14201` 应当为 `01 42 C1 23` (即以字节为单位反着读)。若将 0x0142C123 与 0x13FD41FF5 [相加](https://www.google.com/search?q=0x0142C123+%2B+0x13FD41FF5) 则得到0x14116E118。双击指令时,CE也会帮你计算该数字。如当你双击本例中的 `mov rcx` 一行,Cheat Engine会自动计算并显示为 `mov rcx,[14116E118]`。因此,您根本不需要自行运算,但是知道此处的数学原理对我们后续的理解有一定帮助。 - -在Cheat Engine的注释一列中,可以看到`mov rcx` 行的值恰好就是 `14116E120`。这意味着地址 `14116E118` 中存储的值为 `14116E120`。此前我们找到的存储变量的地址为 `14116E128`,则我们可以得出 `14116E120 + 08` 正是我们所需要的值,可以看到读取该变量的代码也确实是在其地址加上了8字节。 - -您可以手动将地址 `14116E118` 添加到内存区域,或在兽魂的地址附近寻找,毕竟离得不远。 - -![Cheat Engine指针截图](../images/cheatengine_pointer.png) - -在以上的屏幕截图中,小圆圈中的 `14116E128` 是兽魂的值所在的地址,而较大的椭圆中的 `14116E118` 则是用于加载 `rcx` 寄存器的地址。内存查找也证明了这一点,可以看到地址 `14116E118` 中存储的值为 `000000014116E120`。(同样,不要忘记对字节进行反向排列,因为此处是小端系统。) - -### 从汇编代码中提取签名 - -现在我们得到了包含了指向兽魂值的指针的指针的汇编代码。我们需要从汇编代码中选出一部分字节作为内存签名。 - -想要挑选好的签名也有一定技巧。譬如,应当忽略相对寻址偏移,在上述例子中则为 `23C14201`。尽管这些偏移量在每次运行游戏时都是一致的,但在游戏客户端跨版本更新后出现变化的概率很大。寻找签名是很麻烦的一件事,因此理想情况下,我们希望能找到经得起时间考验的签名。 - -在这个例子中, 我们先从 `mov rcx, ...` 一行开始,复制左边的字节列中的十六进制代码。 - -```assembly -48 8B 0D 23C14201 - mov rcx,[ffxiv_dx11.exe+1AAE118] { (14116E120) } -48 85 C9 - test rcx,rcx -74 B8 - je ffxiv_dx11.exe+681FB2 -48 8B 05 67C14201 - mov rax,[ffxiv_dx11.exe+1AAE168] { (21) } -``` - -复制出来的是 `48 8B 0D 23C14201 48 85 C9 74 B8 48 8B 05 67C14201` - -其中两个四字节的串都是指针,因此我们可以直接删掉后面那个,并将第一个替换为通配符。通配符可以是问号,在cactbot和CE中都是通用的。 - -因此,最终我们的签名是:`488B0D????????4885C974B8488B05` - -此签名中的问号指代的四字节地址便是我们所关心的指针的地址。 - -Cactbot中也有一个可供参考的[例子](https://github.com/quisquous/cactbot/blob/df176c4feff81bab356a8e5e6e6b453e94626320/CactbotOverlay/FFXIVProcess.cs#L189)。 - -为了确保签名是唯一的,我们需要再做一次[扫描现有的内存签名](#扫描现有的内存签名)。 - -然后,在插件中的逻辑如下: - -* 在内存中搜索此签名 -* 将RIP相对寻址转换为实际指针 (例如 `14116E118`) -* 找到该内存区域中的指针 (例如 `14116E120`) -* 该指针是指向兽魂的指针 - -由于地址 `14116E118` 向后数8个字节就是地址 `14116E120`,我们还可以再作一个假设,我们在签名找到的地址加上16字节即为我们需要的地址。至少在整个4.0版本中都是如此。 - -呵呵。 - -## 扫描现有的内存签名 - -如果您已有内存签名,也可以使用CE在内存中搜索。 - -![Cheat Engine签名扫描截图](../images/cheatengine_signature_scan.png) - -重新开始扫描。这次应当设置**值类型**为**字节数组**,然后勾选**搜索该数组**和**十六进制**的复选框,然后将内存签名粘贴到文本框中。同时确保**可执行**复选框是选中状态,这是因为我们这次要搜索的是代码。 - -点击**首次扫描**应当给出独一无二的结果。右键单击该地址,然后选择**反编译该地址**,应当能跳转到我们之前找到的代码。 + diff --git a/docs/zh-CN/README.md b/docs/zh-CN/README.md index d126b16f00..290877d409 100644 --- a/docs/zh-CN/README.md +++ b/docs/zh-CN/README.md @@ -1,418 +1,5 @@ -# cactbot (ffxiv raid 悬浮窗) +# Permanently Moved to OverlayPlugin/cactbot - +See: [docs/zh-CN/README.md](https://github.com/OverlayPlugin/cactbot/blob/main/docs/zh-CN/README.md) -[![GitHub 工作流程状态(分支)](https://img.shields.io/github/actions/workflow/status/quisquous/cactbot/test.yml?branch=main)](https://github.com/quisquous/cactbot/actions?query=workflow%3ATest+branch%3Amain) [![GitHub 发行版(最新的 SemVer)](https://img.shields.io/github/v/release/quisquous/cactbot?color=brightgreen\&sort=semver)](https://github.com/quisquous/cactbot/releases/latest) - -🌎 [[English](../../README.md)] [**简体中文**] [[한국어](../ko-KR/README.md)] - -1. [关于](#关于) -1. [安装](#安装) -1. [从源码构建](#从源码构建) -1. [UI 模块概述](#ui-模块概述) -1. [疑难解答](#疑难解答) -1. [Cactbot 自定义](#cactbot-自定义教程) -1. [支持语言](#支持语言) - -## 关于 - -cactbot 是一个 ACT 悬浮窗,可为 [Final Fantasy XIV](http://www.finalfantasyxiv.com/)提供战斗辅助。 该项目是 [OverlayPlugin](https://github.com/OverlayPlugin/OverlayPlugin) 的悬浮窗插件,而 OverlayPlugin 是 [Advanced Combat Tracker](http://advancedcombattracker.com/)的插件。 - -cactbot 提供以下模块: - -* raidboss: 内置时间轴和触发器 - -![时间轴屏幕截图](../../screenshots/promo_raidboss_timeline.png) ![触发器屏幕截图](../../screenshots/promo_raidboss_triggers.png) - -* oopsyraidsy: 错误和死亡报告 - -![oopsyraidsy 屏幕截图](../../screenshots/promo_oopsy.png) - -* jobs: 监控职业资源、重要技能冷却、buff 与触发的紧凑型职业量谱 - -![赤魔职业屏幕截图](../../screenshots/promo_jobs.png) - -* eureka: 特殊场景探索(优雷卡、博兹雅)监控地图 - -![优雷卡屏幕截图](../../screenshots/promo_eureka.png) - -* radar: 通知狩猎怪方向及开怪信息 - -![雷达屏幕截图](../../screenshots/promo_radar.png) - -* dps: 提供更多功能的 dps 悬浮窗 - -![xephero 屏幕截图](../../screenshots/xephero.png) - -## 安装 - -### 依赖 - -安装 4.6.1 版本及以上的 [.NET Framework](https://www.microsoft.com/net/download/framework)。 - -您必须为最终幻想 14 启用 [DirectX 11](http://imgur.com/TjcnjmG)。 - -安装 64 位版本的 [Advanced Combat Tracker](http://advancedcombattracker.com/)。 - -### 安装 FFXIV ACT 解析插件 - -如果您刚刚安装了 ACT, 你会看到一个启动向导。否则,您需要通过点击 `Options` ,然后点击 `Show Startup Wizard` 打开启动向导。 - -![启动向导屏幕截图](../../screenshots/ffxiv_plugin_show_startup_wizard.png) - -在启动向导中,选择 `FFXIV Parsing Plugin`,然后单击 `Download/Enable Plugin` 按钮。这将把解析插件下载到 `%APPDATA%\Advanced Combat Tracker\Plugins\FFXIV_ACT_Plugin.dll`,并在插件列表中启用它。 - -![启动向导下载屏幕截图](../../screenshots/ffxiv_plugin_parsing_plugin.png) - -其他 FFXIV 插件指南: - -* [fflogs video guide](https://www.fflogs.com/help/start/) -* [TomRichter guide](https://gist.github.com/TomRichter/e044a3dff5c50024cf514ffb20a201a9#installing-act--ffxiv-plugin) - -### 安装 OverlayPlugin - -此时,如果选择 `插件` 标签,然后切换至 `插件列表`,您的插件列表中应当如下所示: - -![空白插件列表屏幕截图](../../screenshots/get_plugins_blank.png) - -单击 `Get Plugins` 可以打开 ACT 插件安装程序。 - -选择 `Overlay Plugin`,然后单击 `Download and Enable` 按钮。 - -![悬浮窗插件选择屏幕截图](../../screenshots/get_plugins_overlayplugin.png) - -这会将 OverlayPlugin 下载到 `%APPDATA%\Advanced Combat Tracker\Plugins\OverlayPlugin` 文件夹中,并自动在插件列表中启用 `OverlayPlugin.dll`。 - -注意,您必须使用 [当前最新](https://github.com/OverlayPlugin/OverlayPlugin) 版本的 OverlayPlugin,而不是原本 RainbowMage 的版本或 hibiyasleep 的 fork 版本。 - -### 安装 cactbot - -再次点开 `Plugins` 标签,再点开 `Plugin Listing` 标签,然后选择 `Get Plugins`。 - -选择 `Cactbot`,然后单击 `Download and Enable` 按钮。 - -![cactbot 选择屏幕截图](../../screenshots/get_plugins_cactbot.png) - -这会将 cactbot 下载到 `%APPDATA%\Advanced Combat Tracker\Plugins\cactbot-version\cactbot` 文件夹中,并在插件列表中启用 `CactbotOverlay.dll`。 - -**注意**:由于 ACT 和 cactbot 对于 zip 格式处理的差异性,它可能会生成一个形如 `cactbot-0.15.2` 的文件夹,这里的版本号是根据你下载 cactbot 时的最早版本决定的。该文件夹的名称无关紧要,并且很美观。 - -### 插件加载顺序 - -由于 cactbot 的依赖关系,它需要在 FFXIV 解析插件和 OverlayPlugin 之后加载。正确的顺序应该是: - -* FFXIV 解析插件 -* OverlayPlugin, -* cactbot - -![插件顺序](../../screenshots/get_plugins_complete.png) - -最后,重启 ACT。 - -## 添加悬浮窗模块 - -下面的例子展示了如何设置 raidboss 悬浮窗模块。 设置其他的 cactbot 悬浮窗的操作也类似这样。 - -1. 打开 ACT。 -1. 添加了 cactbot 插件后,请确保已重新启动 ACT。 -1. 导航到 ACT 的 `Plugins` 标签页,然后跳转到其中的 `OverlayPlugin.dll` 标签页。 -1. 单击 “新建” 按钮,然后在 “预设” 列表中选择 `Cactbot Raidboss`。 - - ![新建悬浮窗插件屏幕截图](../../screenshots/overlay_plugin_new.png) - -1. 此时,您应该能看到屏幕上会出现测试用 UI。 这是 cactbot 提供的默认测试用 UI, 一个较大的虚线红色边框,以及蓝色的背景, 可以帮助你在屏幕上调整悬浮窗的位置大小等。 当您在悬浮窗的配置面板中锁定该悬浮窗时,这些测试用 UI 都将消失。 在您完成了调整悬浮窗大小和位置之后,应该始终保持锁定悬浮窗。 - - ![raidboss 插件解锁状态](../../screenshots/overlay_plugin_new_raidboss_unlocked.png) - -1. 您可以输入任意的名字作为悬浮窗的名称,例如 `raidbossy`。 - -1. 单击 `确定` 按钮将其添加为悬浮窗。则它将会出现在 `Plugns` -> `OverlayPlugin.dll` 标签页的悬浮窗列表中。 - -1. 将悬浮窗拖放到所需的位置。 - -1. 在 `Raidboss` 悬浮窗的 `通用` 选项卡中,勾选 `锁定悬浮窗` 和 `鼠标穿透` 复选框。一旦悬浮窗被锁定,测试用的进度条,文本,红色虚线边框,以及蓝色阴影背景都将会消失。 - - ![raidboss 插件配置](../../screenshots/overlay_plugin_new_raidboss_locked.png) - -1. 如果要测试 raidboss 插件,请传送到盛夏农庄,然后在聊天框输入 `/countdown 5` 并回车。 - -1. 您可以用同样的方法添加其他 cactbot 悬浮窗。步骤是一样的,只要选择不同的 cactbot 预设即可。 - -## 从源码构建 - -请先遵循上述的步骤安装好 cactbot。有两种方式可以安装依赖:**通过脚本** 或 **手动**。 - -### 安装依赖:脚本方式 - -1. 必须先安装 `curl`(用于下载依赖项) -1. 运行 `node --import node --loader=ts-node/esm ./util/fetch_deps.ts` 脚本 -1. 下转 **构建步骤** - -### 安装依赖:手动方式 - -1. 请从 下载最新的 Zip 文件。 -1. 解压 `Advanced Combat Tracker.exe` 到 `cactbot/plugin/ThirdParty/ACT/` 下 -1. 请从 下载最新的 SDK Zip 文件(确保文件名称中包含 SDK 字样) -1. 解压 `SDK文件夹` 和 `FFXIV_ACT_Plugin.dll` 到 `cactbot/plugin/ThirdParty/FFXIV_ACT/` 下 -1. 请从 下载最新的 Zip 文件。 -1. 解压 `libs文件夹` 和 `OverlayPlugin.dll` 到 `cactbot/plugin/ThirdParty/OverlayPlugin/` 下 -1. 下转 **构建步骤** - -该文件夹应如下所示(请注意,将来文件结构可能会随着更新而更改): - -```plaintext -ThirdParty -|- ACT -| |- Advanced Combat Tracker.exe -|- FFXIV_ACT -| |- SDK -| | |- FFXIV_ACT_Plugin.Common.dll -| | |- FFXIV_ACT_Plugin.Config.dll -| | |- FFXIV_ACT_Plugin.LogFile.dll -| | |- FFXIV_ACT_Plugin.Memory.dll -| | |- FFXIV_ACT_Plugin.Network.dll -| | |- FFXIV_ACT_Plugin.Overlay.dll -| | |- FFXIV_ACT_Plugin.Parse.dll -| | |- FFXIV_ACT_Plugin.Resource.dll -| |- FFXIV_ACT_Plugin.dll -|- OverlayPlugin - |- libs - | |- HtmlRenderer.dll - | |- Markdig.Signed.dll - | |- Newtonsoft.Json.dll - | |- OverlayPlugin.Common.dll - | |- OverlayPlugin.Core.dll - | |- OverlayPlugin.Updater.dll - | |- SharpCompress.dll - | |- System.ValueTuple.dll - | |- websocket-sharp.dll - |- OverlayPlugin.dll -``` - -### 构建插件的步骤 - -1. 在 Visual Studio 中打开解决方案(已在 Visual Studio 2017 测试通过)。 -1. 采用 “Release” 和 “x64” 的配置开始构建。 -1. 该插件将构建到 **bin/x64/Release/CactbotOverlay.dll**。 -1. 将构建好的插件添加到 ACT。在 ACT-> Plugins -> Plugin Listing 标签页中,单击 `Browse` 按钮,然后导航至构建完成的 **bin/x64/Release/CactbotOverlay.dll** 文件。然后单击 `Add/Enable Plugin` 按钮。 - -### npm 和 webpack - -如果您不是 cactbot 开发人员,并且尝试修改 cactbot,添加自己的个人触发器。您不应直接修改本地的 cactbot 文件,而应该参考 [自定义文档](./CactbotCustomization.md) 进行自定义。 - -安装 npm 并启动 Webpack,请按照下列步骤操作: - -1. 安装 [nodejs 和 npm](https://nodejs.org/en/download/) -1. 在 cactbot 的根目录下运行 `npm install`。 -1. 运行 `npm run build` 或 `npm start`。 - -关于使用 Webpack 的更多信息,请参见 [贡献文档](CONTRIBUTING.md#validating-changes-via-webpack) 。 - -## UI 模块概述 - -[ui/](ui/) 文件夹中包含 cactbot 的所有 ui 模块。如果您按照上述说明安装了 cactbot,则很有可能是 `%APPDATA%\Advanced Combat Tracker\Plugins\cactbot-version\cactbot\ui\`。 - -每个 cactbot ui 模块都应当作为单独的悬浮窗添加。有关配置的更多信息,请参见 “ [添加悬浮窗模块](#添加悬浮窗模块) 部分。 - -### [raidboss](../../ui/raidboss) 模块 - -要使用该模块,定位到 cactbot 下面的 **ui/raidboss/raidboss.html** 或使用 `Cactbot Raidboss` 预设。 - -此模块提供预知战斗事件的时间轴,以及文本和音频提示,以帮助提高团队对 raid 的意识。这些文字和声音警报一般基于战斗时间轴,或是来自游戏中发生的日志消息。简单地说,这是一个类似于 ACT 的 “自定义触发器” 的功能。该模块被设计为类似于《魔兽世界》的 [BigWigs Bossmods](https://www.curseforge.com/wow/addons/big-wigs) 插件的外观和感觉。 - -[此页面](https://quisquous.github.io/cactbot/util/coverage/coverage.html) 列出了当前 cactbot 中支持的副本。我们会持续添加更多支持 (随时欢迎贡献代码!) 尽管许多旧副本仍未支持。 - -战斗时间轴原本是设计为用于 [ACT 时间轴](https://github.com/grindingcoil/act_timeline)插件的文件,不过增加了一些[扩展语法](./TimelineGuide.md)。这里是原插件的 [文档](http://dtguilds.enjin.com/forum/m/37032836/viewthread/26353492-act-timeline-plugin)。 - -这里有三个等级的警报提示,重要性从低到高分别为:`info(信息)`、`alert(警告)` 和 `alarm(警报)`。文本信息只会有这三种等级,等级越高,越重要,则文字会越大,颜色也会越醒目。如果你更喜欢文本到语音 (TTS),你也可以配置成语音提示。 - -在[ui/raidboss/data](../../ui/raidboss/data)文件夹下存放了定义了文本显示和声音提示的时间轴和触发器等, 时间轴文件拥有 `.txt` 扩展名,而触发器文件则为 `.ts` 扩展名。 - -在下方的截图中,高亮的是 raidboss 模块,其中时间轴用红色圆圈圈出,文本警报用黄色圆圈圈出,可见的是 `警告`等级的文字提示。 - -![raidboss 屏幕截图](../../screenshots/Raidboss.png) - -### raidboss 模拟器 - -如果在编写触发器或时间轴,并要对其进行测试,您可以使用此处的 raidboss 模拟器:**ui/raidboss/raidemulator.html**。 - -但是,目前只能在浏览器中加载它,不支持加载为悬浮窗。 该模拟器在最新版本的 Chrome 中可以完美运行, 理论上在其他浏览器中也可以运行,但是并没有测试过。 - -操作步骤: - -1. 启动 ACT。 -1. 确保 WS Server 已启动,可以在 Plugins -> OverlayPlugin WSServer-> Stream/Local Overlay 中对此进行配置。 - -如果您正在为cactbot仓库开发触发器, -您可以通过 `npm run start` 启动本地开发服务器, -并在Chrome中访问 `http://127.0.0.1:8080/ui/raidboss/raidemulator.html?OVERLAY_WS=ws://127.0.0.1:10501/ws` - -如果您正在开发用户自定义触发器, -您可以在Chrome中访问 `https://quisquous.github.io/cactbot/ui/raidboss/raidemulator.html?OVERLAY_WS=ws://127.0.0.1:10501/ws` - -如果您正在尝试复现一个问题, -您可以在Chrome中访问 `https://quisquous.github.io/cactbot/ui/raidboss/raidemulator.html` -在这种情况下,您不需要运行WS服务器。 - -成功加载网页后,继续按照以下说明使用模拟器。 - -1. 拖放一个 [网络日志](/docs/FAQ-Troubleshooting.md#how-to-find-a-network-log) 文件到该页面中。 -1. 选择区域和战斗记录,然后单击 `Load Encounter`。 - -如果模拟器无法正常工作,请检查控制台中是否显示了错误日志。 - -![Raidboss模拟器截图](/screenshots/raidboss_emulator.png) - -### [oopsyraidsy](../../ui/oopsyraidsy) 模块 - -要使用该模块,定位到 cactbot 下面的 **ui/oopsyraidsy/oopsyraidsy.html** 或使用 `Cactbot OopsyRaidsy` 预设。 - -此模块提供错误追踪和死亡报告。通过 oopsy raidsy 模块可以检查战斗中出了什么问题,以及队友死亡的原因,以减少攻略副本的时间。在战斗中,仅显示一定数量的错误(以避免混乱),但脱战后可以显示完整的可滚动列表。 - -当某人死亡时,TA 遭受伤害的最后一个伤害事件会记录在日志中。例如,当日志显示:":skull: Poutine: Iron Chariot (82173/23703)",这意味着 Poutine 最有可能死于 Iron Chariot 这个技能,造成了 82173 伤害,且当时他具有 23703 的血量。血量值由于服务器更新周期的存在可能有数秒的延迟。短时间内被多个伤害技能致死的情况下,具体致死的是哪一个可能不准确。 - -当错误本身可以避免时,oopsy 会将其记录为警告(:warning:)和失败(:no\_entry\_sign:)消息,并说明出现了什么问题。 - -[ui/oopsyraidsy/data](../../ui/oopsyraidsy/data) 文件夹中为每个副本指定了错误触发器。 - -![oopsyraidsy 屏幕截图](../../screenshots/promo_oopsy.png) - -点击 oopsy 的每一行即可复制该行的内容到剪贴板中。(你可能需要先解除 OverlayPlugin 选项中的 `允许鼠标穿透` 复选框。) - -### [jobs](../../ui/jobs) 模块 - -要使用该模块,定位到 cactbot 下面的 **ui/raidboss/jobs.html** 或使用 `Cactbot Jobs` 预设。 - -该模块分为三个部分:悬浮窗顶部中间的资源区、顶部右方的团辅区、以及底部的监控区。 - -* **资源区**:显示血条,职业量谱信息,以及各职业可能具有的特色计时器或计数器。 -* **监控区**:显示职业特色的重要 buff 和 debuff 的持续时间、重要技能的冷却时间或持续时间、以及触发 buff 的存在时间。 -* **团辅区**:显示重要团辅的持续时间和即将冷却完毕的团辅冷却时间。 - -通过用户面板设置可以更改部分外观和行为,例如只显示团辅区,以及缩小资源区和监控区之间的空隙等。 - -但 jobs 模块的可自定义程度比较低,如冷却提示阈值、各界面元素顺序等暂不支持调整。 - -在此截图中,以赤魔法师的 jobs 模块为例。上半是游戏内的 UI 显示、下半是 jobs 模块的显示。体力条、魔力条、赤魔法师的黑 / 白魔元信息位于中间的紫色框内,其右侧的黄色框内是团辅监控图标。底部红色框内的 4 个方框分别是赤飞石预备持续时间、赤火炎预备持续时间、飞刺冷却时间和六分反击冷却时间。 - -![jobs 屏幕截图](../../screenshots/Jobs.png) - -#### 各职业的特性 - -
-职业特性表 (点击展开) - -| 职业 | 功能(从左到右,从上到下)| -|:-:|-| -|
骑士 |**资源区**:忠义值、忠义之剑层数、连击计时器、安魂祈祷层数(处于安魂祈祷状态时)。
**监控区**:沥血剑冷却时间、战逃反应持续时间和冷却时间、偿赎剑冷却时间。| -|
战士 |**资源区**:兽魂值、连击计时器。
**监控区**:战场风暴 buff 持续时间、动乱和群山隆起冷却时间、原初的解放冷却时间。| -|
暗黑骑士 |**资源区**:暗血值、连击计时器。
**监控区**:暗黑持续时间、嗜血冷却时间、血乱冷却时间、掠影示现冷却时间。| -|
绝枪战士 |**资源区**:晶壤数量、连击计时器。
**监控区**:烈牙冷却时间、无情持续时间和冷却时间、血壤冷却时间。| -|
白魔法师 |**资源区**:治疗百合获得计时器、治疗百合和血百合的数量。
**监控区**:天辉 DoT 持续时间、法令冷却时间、醒梦冷却时间。| -|
学者 |**资源区**:以太超流档数、异想以太量和炽天使持续时间。
**监控区**:蛊毒法 DoT 持续时间、以太超流冷却时间、醒梦冷却时间。| -|
占星术士 |**资源区**:奥秘卡提示器、持有的小奥秘卡、持有的印记。
**监控区**:焚灼 DoT 持续时间、抽卡冷却时间、小奥秘卡冷却时间、醒梦冷却时间。| -|
贤者 |**资源区**:蛇胆获得计时器、蛇胆和蛇刺的数量。
**监控区**:均衡注药 DoT 持续时间、发炎冷却时间、根素冷却时间、醒梦冷却时间。| -|
武僧 |**资源区**:斗气档数、身形计时器、必杀技量谱。
**监控区**:连击效果提高 buff 持续时间、功力 buff 持续时间、破碎拳 DoT 持续时间。| -|
龙骑士 |**资源区**:巨龙怒目档数或红莲龙血持续时间、天龙眼档数。
**监控区**:跳跃冷却时间、龙枪 buff 持续时间、猛枪持续时间与冷却时间、巨龙视线持续时间与冷却时间。| -|
忍者 |**资源区**:忍气量、连击计时器。
**监控区**:风遁持续时间、攻其不备持续时间和冷却时间、分身之术冷却时间、结印冷却时间。| -|
武士 |**资源区**:剑气量、剑压档数、连击计时器、持有的闪。
**监控区**:风月 buff 持续时间、风花 buff 持续时间、燕回返冷却时间、彼岸花 DoT 持续时间。| -|
钐镰客 |**资源区**:灵魂量值、魂衣量值、连击计时器、夜游魂和虚无魂档数(处于附体状态时)。
**监控区**:死亡烙印持续时间、灵魂切割和灵魂钐割冷却时间、暴食冷却时间、神秘环持续时间和冷却时间。| -|
吟游诗人 |**资源区**:诗心档数、灵魂之声量、诗心获得计时器、持有的尾声。
**监控区**:风蚀箭和毒咬箭 DoT 持续时间、当前战歌持续时间、九天连箭冷却时间、直线射击预备触发持续时间。| -|
机工士 |**资源区**:枪管热度量或过热档数、电能量或后式自走人偶持续时间、连击计时器、野火 GCD 计数器(发动野火时)。
**监控区**:钻头和毒菌冲击冷却时间、空气锚冷却时间、回转飞锯冷却时间、野火持续时间与冷却时间。| -|
舞者 |**资源区**:幻扇数、伶俐量、连击计时器。
**监控区**:标准舞步冷却时间、技巧舞步持续时间与冷却时间、百花争艳持续时间与冷却时间。| -|
黑魔法师 |**资源区**:灵极冰与星极火持续时间、通晓获得计时器、魔力恢复计时器、灵极心档数和通晓档数。
**监控区**:火苗触发持续时间、雷系 DoT 持续时间、雷云触发持续时间。| -|
召唤师 |**资源区**:附体或属性以太持续时间、以太超流档数、持有的宝石奥秘和保存在其中的属性以太。
**监控区**:能量吸收和能量抽取冷却时间、龙神召唤或不死鸟召唤冷却时间、醒梦冷却时间。| -|
赤魔法师 |**资源区**:白魔元与黑魔元量、魔元集档数(如果有)。
**监控区**:赤飞石预备持续时间、赤火炎预备持续时间、飞刺冷却时间、六分反击冷却时间。| -|
青魔法师 |**资源区**:无。
**监控区**:破防和惊奇光冷却时间、苦闷之歌或月下彼岸花或以太火花 DoT 持续时间、醒梦冷却时间。| - -
- -### [eureka](../../ui/eureka) 模块 - -要使用该模块,定位到 cactbot 下面的 **ui/raidboss/eureka.html** 或使用 `Cactbot Eureka` 预设。 - -该模块会自动追踪 NM 或 CE 的出现和死亡,以及博兹雅地图上的冲突战。还会显示特殊天气 / 夜晚计时器以及粘贴到聊天中的优雷卡追踪器链接。聊天中的所有坐标信息也都会临时显示在地图上。 - -当前,该模块不会直接读取优雷卡追踪器的信息。但如果您点击优雷卡追踪器左侧红色的 “复制已杀死的 NM” 按钮来复制当前已死 NM 的列表,则可以将其粘贴到游戏中,例如`/echo 冷却中的NM: 蝎子 (7m) → 魔界花 (24m) → 独眼 (54m)`,以便该模块从此列表自动同步数据。 - -如果您看不到表情符号,请确保已安装[此 Windows 更新](https://support.microsoft.com/en-us/help/2729094/an-update-for-the-segoe-ui-symbol-font-in-windows-7-and-in-windows-ser)。 - -![优雷卡屏幕截图](../../screenshots/promo_eureka.png) - -### [radar](../../ui/radar) 模块 - -要使用该模块,定位到 cactbot 下面的 **ui/radar/radar.html** 或使用 `Cactbot Radar` 预设。 - -该模块可让您发现附近的狩猎怪(S 级,A 级等)。当一个狩猎怪出现时,该模块会显示一个箭头(基于角色面向)指向该狩猎怪并会显示您与该狩猎怪之间的距离。 - -控制面板中的选项可以设置显示哪位玩家开了狩猎怪,还可以配置 radar 模块的显示方式。您还可以为不同等级的狩猎怪设置自定义选项(例如为 S 级怪发出声音,但对 B 级怪保持沉默),或为您想监控的任何目标名称添加自定义触发器。 - -有关更多选项,请参见`cactbot/user/radar-example.js`文件。 - -![雷达屏幕截图](../../screenshots/promo_radar.png) - -### [dps](../../ui/dps) 统计模块 - -cactbot 可以与为 OverlayPlugin 的数据统计功能设计的任何 DPS 统计悬浮窗共同使用,并可以选择通过 cactbot 附加的 Javascript API 扩展更多功能。cactbot 还可以在团灭时自动停止统计,因此您可以将 ACT 的战斗时间配置为无限。 - -[xephero](../../ui/dps/xephero)DPS 统计悬浮窗具有在多列显示对副本的每个阶段的 DPS 数据进行分段的功能。在下面的截图中,各阶段分别命名为 B1、B2、B3。它们会在 4 人本的 BOSS 自动生成,也可以用来区分 Raid 副本的阶段。 -要使用该悬浮窗,定位到 cactbot 下面的 **ui/dps/xephero/xephero-cactbot.html** - -![xephero 屏幕截图](../../screenshots/xephero.png) - -[rdmty](../../ui/dps/rdmty)DPS 统计悬浮窗为 4.X 职业进行了适配更新,并为了匹配[fflogs](http://fflogs.com)进行了重新着色。 -要使用该悬浮窗,定位到 cactbot 下面的 **ui/dps/rdmty/dps.html** - -![rdmty 屏幕截图](../../screenshots/rdmty.png) - -### [pull counter](../../ui/pullcounter) 模块 - -这个小模块可以显示您在高难度副本中当前的重试次数。 此功能是为主播或查看录屏的玩家准备的。 通过这个数字,您可以轻松浏览视频并查找到特定的某场战斗来进行检阅。 - -您可以通过在游戏聊天窗口中键入 `/echo pullcounter reset` 来重置当前副本 / 区域的重试计数。您也可以直接在 `%APPDATA%\Advanced Combat Tracker\Config\RainbowMage.OverlayPlugin.config.json` 文件中修改该计数。 - -![开怪计时截图](../../screenshots/pullcounter.png) - -### [test](../../ui/test) 模块 - -要使用该模块,定位到 cactbot 下面的 **ui/test/test.html** 或使用 `Cactbot Test` 预设。 - -该模块仅用于 cactbot 所用变量的可视化测试,并不适合在正常游戏时使用。该模块可用来测试所有数据的获取是否正常、模块能否按照预期正常工作,或用来调试悬浮窗的错误。 - -![test 屏幕截图](../../screenshots/test.png) - -## 疑难解答 - -您可以在[此链接](./FAQ-Troubleshooting.md)中查看 Cactbot 的常见问题解答。 - -## Cactbot 自定义教程 - -大部分的 cactbot 配置可通过 ACT 内的 cactbot 控制面板来完成。 - -![配置面板](../../screenshots/config_panel.png) - -您可以在插件 ->OverlayPlugin.dll->Cactbot 找到 Cactbot 的控制面板。 - -如果您想使用 TTS 播报 RaidBoss 模块的警报,您可以将 “默认警报提示信息输出方式” 选项修改为 “只使用 TTS” 或 “文字显示与 TTS”。 您还可以对任意触发器进行特殊设置。 - -如果您出于某些原因(???),不想听到 cactbot 的准备确认提示音,您也可以在此控制面板中禁用它。请转到 Raidboss -> 通用触发器 -> General-> General Ready Check,并将其设置为 `禁用`。 - -此处的选项会存储于 `%APPDATA%\Advanced Combat Tracker\Config\RainbowMage.OverlayPlugin.config.json` 文件中。不建议直接编辑该文件,因为它必须是[标准的 JSON](https://jsonlint.com/),如果该文件被错误地修改,ACT 可能无法启动。 - -强烈建议您通过此控制面板而不是用户文件来完成大部分的配置。`cactbot/user/` 中的文件功能更加强大并且可以覆盖控制面板中的任何配置。然而,`cactbot/user/`内的文件可以静默覆盖的控制面板的配置,使行为与控制面板显示不一致而造成困惑。 - -有关自定义 javascript 和 css 文件的更多详细信息,请参见[本文档](CactbotCustomization.md#user-folder-config-overrides)。 - -## 支持语言 - -cactbot 已在当前版本的国际服(英语、德语、法语、日语)、国服以及韩服经过测试并可以正常工作。某些翻译工作仍在进行中。 - -## 许可、商标与授权 - -cactbot 基于 [Apache License, Version 2.0](../../LICENSE) 开放源代码。 - -FINAL FANTASY 是史克威尔艾尼克斯控股公司 (株式会社スクウェア・エニックス・ホールディングス,Square Enix Holdings Co., Ltd.) 的注册商标。 - -《最终幻想》艺术作品和图标基于[FINALFANTASY®XIV 素材使用许可](https://support.na.square-enix.com/rule.php?id=5382),以非商业方式二次使用。 - -有关其他内建项目的详细信息,请参见 [LICENSE](../../LICENSE) 文件。 + diff --git a/docs/zh-CN/RaidbossGuide.md b/docs/zh-CN/RaidbossGuide.md index 8eadaf7c7f..cfb6e3aceb 100644 --- a/docs/zh-CN/RaidbossGuide.md +++ b/docs/zh-CN/RaidbossGuide.md @@ -1,616 +1,5 @@ -# 触发器文件格式 +# Permanently Moved to OverlayPlugin/cactbot -[[English](../RaidbossGuide.md)] [**简体中文**] +See: [docs/zh-CN/RaidbossGuide.md](https://github.com/OverlayPlugin/cactbot/blob/main/docs/zh-CN/RaidbossGuide.md) -## 文件结构 - -每个触发器文件都是一个 JS 模块 (module),并导出一个触发器集合 (Trigger Set)。 - -```javascript -import ZoneId from '../path/to/resources/zone_id'; -// 其他导入语句。 - -export default { - id: 'TheWeaponsRefrainUltimate', - zoneId: ZoneId.TheWeaponsRefrainUltimate, - zoneLabel: { - en: 'The Weapon\'s Refrain (Ultimate)', - }, - overrideTimelineFile: false, - timelineFile: 'filename.txt', - timeline: `hideall`, - timelineReplace: [ - { - locale: 'en', - replaceText: { - 'regexSearch': 'strReplace', - }, - replaceSync: { - 'regexSearch': 'strReplace', - }, - }, - ], - resetWhenOutOfCombat: true, - triggers: [ - { /* ..触发器 1.. */ }, - { /* ..触发器 2.. */ }, - { /* ..触发器 3.. */ }, - ] -}; -``` - -### 元素 - -**id** -该触发器集合的唯一标识字符串。该值必须是独一无二的,且不可重复。一般情况下,我们会写 `ZoneId` 的名称,以保证其一致性。如果存在多个触发器集合作用于同个区域,则自拟一个适合的名称即可。例如用于正统优雷卡 (英语:Eureka Orthos) 所有层的通用触发器可以写作`'EurekaOrthosGeneral'`。 - -**zoneId** -区域名缩写,用于规定触发器的适用范围。这些区域名缩写可以在 [zone_id.ts](../../resources/zone_id.ts) 文件中找到。我们倾向于使用该属性而非 `zoneRegex`。每个触发器集合都必须包含`zoneId` 或 `zoneRegex` (但二者不能并存)。 - -**zoneLabel** -可选的名称,用于指定触发器集合在配置页面中的名称。该属性会覆盖 [zone_info.ts](../../resources/zone_info.ts) 中的默认名称。 - -**initData** -函数,用于初始化该触发器集合所使用的数据 (data)。应当返回一个纯对象,包含所有 `data` 中应当被初始化的属性及其值。在区域转移或战斗结束等需要重置的情况下,该函数可能会被多次调用。示例的初始化方法可以参考 [t1.ts](../../ui/raidboss/data/02-arr/raid/t1.ts) 这个文件。 - -**zoneRegex** -正则表达式,用于匹配该触发器集合所适用的从 ACT 读取的区域名称。当正则表达式匹配当前区域名时,触发器就会被激活。 - -由于中国服和韩国服的区域名称分别是中文和韩文,但国际服却是英文。理论上你需要写一个能够覆盖所有语言的正则表达式,可以在 ACT 的标题或主界面中找到。 - -**overrideTimelineFile** -可选属性,布尔值。该值设定为true时,任何先前被读取的同区域的触发器文件将被该触发器集合中指定的 `timelineFile` 和 `timeline` 属性覆盖。此属性仅用于用户文件,cactbot本身不使用该值。 - -**timelineFile** -可选属性,指定当前区域对应的时间轴文件。这些文件与触发器文件存放在同一文件夹中 (例如放在 `raidboss/data/04-sb/raid/` 中),并与触发器文件 (`.js`) 的命名一致,只是后缀名必须是 `.txt`。 - -**timeline** -可选属性,时间轴的补充行。该值可以是字符串或字符串的数组,也可以是一个返回字符串或字符串数组的形如 `function(data)` 的函数,还可以是一个包含上述所有类型的数组。 - -[test.ts](../../ui/raidboss/data/00-misc/test.ts) 中的 **timeline** 属性是一个很好的示例,展示了它的所有用途。 - -**locale** -可选属性,限定触发器仅在特定语言的客户端上生效。如 'en'、'ko'、'fr'。若该属性未设置,则应用于所有语言。 - -**replaceText** -键值对,用于在时间轴中搜索并替换技能名。在时间轴中显示的条目会被替换,但 `hideall`、`infotext`、`alerttext`、`alarmtext` 等依旧指向其原名称。 这一属性使我们可以对时间轴文件进行翻译/本地化,而不需要直接编辑时间轴文件。 - -**replaceSync** -键值对,用于在时间轴中搜索并替换同步正则表达式。当同步正则表达式包含了本地化名称时,该属性是必要的。 - -**resetWhenOutOfCombat** -布尔值,默认为true。该值为true时,时间轴和触发器均在脱战时自动重置。否则,需要手动调用`data.StopCombat()`使其重置。 - -## 触发器结构 - -```javascript -{ - id: 'id string', - type: 'StartsUsing', - disabled: false, - // 提示:参见 [net_fields.d.ts](https://github.com/quisquous/cactbot/blob/main/types/net_fields.d.ts) 中的 `NetFields` 类型。 - // 提示:写成 `netRegex: NetRegexes({ id: 'some-id', source: 'some-name' })` 也是可以的,这个属性会向后兼容。 - netRegex: { id: 'some-id', source: 'some-name' }, - // 提示:推荐使用 [regexes.ts](https://github.com/quisquous/cactbot/blob/main/resources/regexes.ts) 中的辅助函数。 - regex: Regexes.ability({ id: 'some-id', source: 'some-name' }), - condition: function(data, matches, output) { /* 当需要激活该触发器时返回 true */ }, - preRun: function(data, matches, output) { /* 触发器的预处理 */ }, - delaySeconds: 0, - durationSeconds: 3, - suppressSeconds: 0, - promise: function(data, matches, output) { /* 执行异步操作,应当返回 Promise */ }, - sound: '', - soundVolume: 1, - response: Responses.doSomething(severity), - alarmText: {en: 'Alarm Popup'}, - alertText: {en: 'Alert Popup'}, - infoText: {en: 'Info Popup'}, - tts: {en: 'TTS text'}, - run: function(data, matches, output) { /* 执行某些操作 */ }, - outputStrings: { - key1: { en: 'output1 ${value}'}, - key2: { en: 'output2 ${value}'}, - }, - comment: { en: 'comment text' }, -}, -``` - -### data、matches 和 output - -几乎所有的触发器属性都可以定义字面量值或者一个形如 `function(data, matches, output)` 的函数,这些参数的功能如下: - -- `data` 是一个上下文对象,可以传递数据给所有的触发器。同时触发器也可以修改该对象,以便在后续的触发器中使用。 -- `matches` 是当前触发器的正则表达式匹配结果,一般情况下我们关心 `matches.groups` 这个属性。 -- `output` 是一个特殊对象,能够讲触发器中的 `outputStrings` 的值转换为字符串。更多信息请参见 `outputStrings` 一节。 - 对于 `delaySeconds` 或 `durationSeconds` 等需要返回数字的属性则需要返回数字,对于 `preRun` 和 `run` 等不应当有返回的函数则基本上可以无视该属性。 - -### 触发器元素 - -**id string** -字符串,触发器id。所有cactbot的内置触发器均包含一个独一无二的id。我们同样推荐在用户自定义触发器中包含该属性,但这不是强制要求。 - -触发器id不可重复。若当前触发器的id与某一已定义的触发器完全一致,原触发器的定义则会全部失效,并由其覆盖并替代其位置。这个机制让用户可以方便地复制触发器代码并粘贴到用户文件中,并修改为他们自己喜欢的方式。没有id的触发器无法被覆盖。 - -目前cactbot采用的 `Regexes/NetRegexes` 结构并不要求将技能名/效果名等名称写进正则表达式。因此,将注释写在代码附近尤为重要。因此我们强烈推荐在触发器id中写入效果名/技能名/NPC名称等,或者在旁边撰写包含这些信息的详尽注释。仅仅依赖触发器本体的上下文信息并不足以了解其用处。(与id一样,若您的触发器不是打算提交到cactbot仓库的,这些要求也可以忽略。) - -**disabled: false** -若该值为true,则该触发器将被完全禁用/忽略。默认为false。 - -**netRegex / regex** -正则表达式,cactbot会将该正则表达式与每一条日志行做比对,并在匹配成功时触发当前触发器。`netRegex` 版本用于匹配网络日志行,而 `regex` 版本用于匹配普通的ACT日志行。 - -更多时候,相对于直接使用正则表达式字面量,我们更加推荐使用正则替换函数。正则替换函数是指定义在 [regexes.ts](https://github.com/quisquous/cactbot/blob/main/resources/regexes.ts) 和 [netregexes.ts](https://github.com/quisquous/cactbot/blob/main/resources/netregexes.ts) 中的辅助函数,这些函数可以接受特定参数值用于匹配日志,并通过正则捕获组的方式帮助你提取未定义的参数值。换句话说,这些函数用于自动构建能够匹配指定类型的日志行的正则表达式。顾名思义,`netRegex` 使用 `NetRegexes` 辅助函数,而 `regex` 使用 `Regexes` 辅助函数。 - -`regex` 和 `netRegex` 会使用 `timelineReplace` 中的值自动翻译到对应语言。 - -**condition: function(data, matches)** -当函数返回 `true` 时激活该触发器。若返回的不是 `true`,则当前触发器不会有任何响应。不管触发器对象里定义了多少函数,该函数总是第一个执行。([conditions.ts](https://github.com/quisquous/cactbot/blob/main/resources/conditions.ts) 中定义了一部分高阶条件函数。一般情况下,如果与情境相符,使用这些函数是最佳解决方案。) - -**preRun: function(data, matches)** -当触发器被激活时,该函数会在条件判定成功后立刻执行。 - -**delaySeconds** -时间,单位为秒,定义从正则表达式匹配上到触发器激活之间的等待时间。其值可以是数字或返回数字的 `function(data, matches)`。该函数会在 `preRun` 之后,`promise` 之前执行。 - -**promise: function(data, matches)** -设置该属性为返回Promise的函数,则触发器会在其resolve之前等待。这个函数会在等待了 `delaySeconds` 秒之后执行。 - -**durationSeconds** -时间,单位为秒,显示触发器文本的时长。其值可以是数字或返回数字的 `function(data, matches)`。若该值未设置,默认为3。 - -**suppressSeconds** -等待时间,单位为秒,再次触发该触发器的冷却时间。其值可以是数字或返回数字的 function(data, matches)。该时间从正则表达式匹配之时开始计算,并且不受 delaySeconds 设置与否的影响。当设置了此元素的触发器被激活后,它在这段时间内无法再次被激活。 - -**sound** -用于播放声音的音频文件,也可以是 'Info','Alert','Alarm' 或者 'Long' 之一。文件路径是对于 `ui/raidboss/` 文件夹的相对路径。 - -**soundVolume** -从0到1的音量数值,触发器激活时播放的音量大小。 - -**response** -用于返回 infoText/alertText/alarmText/tts 的快捷方法。 这些函数定义于 `resources/responses.ts`。 Response 的优先级比直接指定的文字或TTS低,因此可以被覆盖。 (如同 `regex` 和 `condition` 一样,[responses.ts](https://github.com/quisquous/cactbot/blob/main/resources/responses.ts) 中定义了一些便于使用的高阶函数。) - -**alarmText** -当触发器激活时显示**警报**级别的文本。该属性一般用于高危事件,如处理失败必死无疑的机制、会导致团灭的机制,或处理失败会导致通关变得更加困难的机制等。(例如T2的亚拉戈病毒,T7的诅咒之嚎,或是O7S里奥尔特罗斯先生的石肤等)。其值可以是字符串或返回字符串的 `function(data, matches)`。 - -**alertText** -当触发器激活时显示**警告**级别的文本。该属性一般用于中危事件,如处理失败可能会致死的机制、会造成全队伤害或全队Debuff的机制等等。(例如,针对坦克的死刑预告,或针对全队的击退预告等)。其值可以是字符串或返回字符串的 `function(data, matches)`。 - -**infoText** -当触发器激活时显示**信息**级别的文本。该属性一般用于低危事件,不需要玩家立即做出反应。(例如,小怪出现时的警告,或针对治疗职业的全体伤害预告等等)。其值可以是字符串或返回字符串的 `function(data, matches)`。 - -**tts** -字符串,替代上述文本,用于输出TTS。该值可以是包含本地化字符串的对象,与触发器文本类似。如果该属性存在,但是没有设置当前语言的本地化字符串,Raidboss 会使用文本属性的值用于 TTS。 - -```typescript -{ - ... - infoText: { - en: 'Tank Buster', - de: 'AoE', - fr: 'Cleave', - }, - tts: { - de: 'Spread', - }, -} -``` - -如果当前语言是英语 (`en`),则会读出 `Tank Buster`。反之,如果你的语言是德语 (`de`),则会读出 `Spread`。 - -**run: function(data, matches)** -当触发器被激活时,该函数会作为触发器最末尾的步骤执行。 - -**outputStrings** -该属性是一个可选的间接属性,用于让 cactbot 能够提供用户自定义输出字符串的功能。在自制触发器的时候该属性可以无视,你可以用 `alarmText`、`alertText` 和 `infoText` 这些属性,直接返回字符串。 - -这个对象可以将某些键映射到对应的可翻译字符串上,因此每一项都应该包含各个语言的字符串。在字符串中,你还可以使用 `${param}` 等占位符,用于在运行时替换为对应的变量。 - -这里有两个针对死刑的 `outputStrings` 的示例: - -```javascript -outputStrings: { - noTarget: { - en: 'Tank Buster', - de: 'Tank buster', - fr: 'Tank buster', - ja: 'タンクバスター', - cn: '坦克死刑', - ko: '탱버', - }, - onTarget: { - en: 'Tank Buster on ${name}', - de: 'Tank buster auf ${name}', - fr: 'Tank buster sur ${name}', - ja: '${name}にタンクバスター', - cn: '死刑 点 ${name}', - ko: '"${name}" 탱버', - }, -}, -``` - -`noTarget` 和 `onTarget` 分别是 `outputStrings` 的两个属性的键。 - -当你想要输出字符串时,可以通过以下方式将参数传递给 `onTarget`。 - -```javascript -alarmText: (data, matches, output) => { - return output.onTarget({ name: matches.target }); -}, -``` - -调用 `output.onTarget()` 这个函数时,cactbot 会自动对应到 `outputStrings.onTarget` 这一项的当前语言的值。然后将传递的参数 (`param`) 替换形如 `${param}` 的值,从而组装出最终可以让 `alarmText` 输出的字符串。 - -同理,调用另一个无参数的字符串也是一样的: - -```javascript -infoText: (data, matches, output) => { - return output.noTarget(); -}, -``` - -但是在 `response` 属性里使用 `outputStrings` 会稍微有些不同。这种情况下不能在触发器上设置 `outputStrings` 这个值,而是应该让 `response` 返回一个函数,并调用 `output.responseOutputStrings {};`。其中 `{}` 的部分就是上面提到的 `outputStrings` 对象。这看起来非常怪异,但是可以让 response 能够使用 outputStrings 的同时可以正常返回,并保证 [resources/responses.ts](../../resources/responses.ts) 更加耦合。 - -例子: - -```javascript -response: (data, matches, output) => { - output.responseOutputStrings = { text: { en: 'Some Text: ${words}' } }; - return { - alarmText: output.text({ words: 'words word words' }), - }; -}, -``` - -**comment** -对象,键是可选的各个语言的字符串。该属性是一个可选的间接属性,用于在 cactbot 配置面板的触发器项附近展示文字。你可以用他解释你的触发器,或是留下一些说明性的文字,亦或是附上一份超链接。 - -示例: - -```javascript -comment: { - cn: `写下你的注解文本。支持HTML标签`, -}, -``` - -## 其他事项 - -任何可以返回一个函数 (如 `infoText`、`alertText`、`alarmText` 和 `tts`)的元素都可以返回一个含有本地化字符串的对象。比如返回形如 `{en: 'Get Out', fr: 'something french'}` 的一维对象,用以支持多语言,而不是仅仅返回单个字符串如 `'Get Out'`。当然,其值也可以返回一个函数,再让该函数返回本地化字符串对象。若当前的区域语言不存在于该对象中,则会返回 `en` 的值。 - -如果有多个触发器同时匹配了同一行日志,触发器会按定义顺序依次执行。 - -触发器元素按以下顺序载入,定义元素时也应该按该顺序排序: - -- id -- disabled -- netRegex -- regex -- beforeSeconds (仅存在于 timelineTriggers 中) -- (休眠的触发器会在此处直接退出) -- condition -- preRun -- delaySeconds -- durationSeconds -- suppressSeconds -- (等待delaySeconds延迟结束) -- promise -- (等待Promise执行完成) -- sound -- soundVolume -- response -- alarmText -- alertText -- infoText -- tts -- run -- outputStrings - -## 正则表达式扩展 - -若您很了解正则表达式, 您会注意到 `\y{Name}` 和 `\y{AbilityCode}` 是个没见过的用法。 这些是cactbot提供的便捷扩展, 以让您不需要针对所有可能出现的Unicode字符撰写正则表达式, 也不需要完整学习 FFXIV ACT Plugin 的输出格式。 - -目前可用的扩展名如下所示: - -- `\y{Float}`:匹配浮点数,包含某些区域特定的编码。 -- `\y{Name}`:匹配任意角色名称 (同样包括 FFXIV ACT Plugin 针对未知名字生成的空字符串)。 -- `\y{ObjectId}`:在网络日志行中匹配8位十六进制的角色对象id。 -- `\y{AbilityCode}`:匹配 FFXIV ACT Plugin 针对技能或能力生成的数字格式。 -- `\y{Timestamp}`:匹配所有日志前端的时间戳,如 `[10:23:34.123]`。 -- `\y{LogType}`:匹配 FFXIV ACT Plugin 生成的用于描述日志类型的数字格式,位于每行日志的稍前方。 - -## 高阶辅助函数 - -为统一触发器构造,以及减轻翻译时的手动负担,cactbot的触发器元素广泛运用了高阶函数。诸如此类的工具函数使自动化测试更为简单,并让人们在审查拉取更改时更容易捕获错误及文本差异。 - -目前我们对于元素的独立预定义结构有4种:[Condition](https://github.com/quisquous/cactbot/blob/main/resources/conditions.ts)、[Regex](https://github.com/quisquous/cactbot/blob/main/resources/regexes.ts)、[NetRegex](https://github.com/quisquous/cactbot/blob/main/resources/netregexes.ts) 以及 [Response](https://github.com/quisquous/cactbot/blob/main/resources/responses.ts)。`Condition` 函数不接受参数,而几乎所有的 `Response` 函数都接受 `severity` 参数,用于定义触发器被激活时输出的警报文本的等级。`Regex` 和 `NetRegex` 函数根据匹配的日志行,接受若干参数 [(例如 `gainsEffect()`)](https://github.com/quisquous/cactbot/blob/0bd9095682ec15b35f880d2241be365f4bdf6a87/resources/regexes.ts#L348),不管哪种日志行一般都接受 `source` 属性 (技能的咏唱者/释放者的名称),`id` 属性 (十六进制的技能ID,例如 `2478`),以及正则表达式匹配时是否启用捕获组 (`capture: false`)。`Regex` 和 `NetRegex` 函数默认开启捕获组,但按惯例应当仅对依赖捕获数据的触发器开启捕获。 - -以下是使用了这三种元素的示例触发器: - -```javascript -{ - id: 'TEA Mega Holy Modified', - netRegex: NetRegexes.startsUsing({ source: 'Alexander Prime', id: '4A83', capture: false }), - condition: Conditions.caresAboutMagical(), - response: Responses.bigAoe('alert'), -}, -``` - -尽管由于我们需要定义所有语言的正则表达式,该方法并未减少代码行数,但仍然远远优于: - -```javascript -{ - id: 'TEA Mega Holy Modified', - netRegex: /^(?:20)\|(?:[^|]*)\|(?:[^|]*)\|(?:Alexander Prime)\|(?:4A83)\|/i, - condition: function(data) { - return data.role == 'tank' || data.role == 'healer' || data.CanAddle(); - }, - alertText: { - en: 'big aoe!', - de: 'Große AoE!', - fr: 'Grosse AoE !', - ja: '大ダメージAoE', - cn: '大AoE伤害!', - ko: '강한 전체 공격!', - }, -}, -``` - -使用正则表达式字面量的方式已被废弃。 *请务必*使用上述的高阶函数生成对应的正则表达式,除非您有特别的原因必须要这样做。在提交拉取请求时使用正则表达式字面量会导致构建失败。当的确存在特定的需求,不得不使用正则表达式字面量时 (例如ACT新增了其他类型的日志行),我们强烈推荐开启一个拉取请求,直接更新 `regexes.ts` 文件。 - -(当然,若您正在撰写仅用于您个人的触发器,您可以随意发挥。此处的警告仅针对想为 cactbot 项目提交贡献的人们。) - -可能的话,建议尽量使用在conditions和responses中定义的高阶函数,尽管SE的“天才”战斗设计组有时真的可以让它*无法生效*。 - -## 输出文本 - -为了减少不同触发器集合中的文本重复,cactbot 包含了一套常用的文本片段。如果你需要使用的输出文本已经存在,推荐直接使用 `Outputs` 而非手写。 - -以下是使用 `outputStrings` 和 `Outputs` 的例子: - -```javascript -{ - id: 'E9S Zero-Form Devouring Dark', - netRegex: NetRegexes.startsUsing({ id: '5623', source: 'Cloud Of Darkness' }), - durationSeconds: 4, - alertText: function(data, matches, output) { - if (data.me === matches.target) - return output.tankBusterOnYou(); - - if (data.role === 'tank') - return output.tankSwap(); - - if (data.role === 'healer') - return output.tankBusters({ player: data.party.member(matches.target) }); - }, - infoText: function(data, _matches, output) { - if (data.role !== 'tank' && data.role !== 'healer') - return output.avoidLaser(); - }, - outputStrings: { - tankBusterOnYou: Outputs.tankBusterOnYou, - tankBusters: Outputs.tankBusters, - tankSwap: Outputs.tankSwap, - avoidLaser: { - en: 'Avoid Laser', - de: 'Laser ausweichen', - fr: 'Évitez le laser', - ja: 'レーザー注意', - cn: '躲避击退激光', - ko: '레이저 피하기', - }, - }, -}, -``` - -## 关于时间轴 - -触发器子目录内可能包含了一部分采用 ACT Timeline 插件格式定义的时间轴文本文件,您可以通过此链接了解: - -Cactbot的每一个时间轴文件均由同文件夹下某个对应的 [TRIGGER-FILE].js 加载。通常情况下,时间轴文件的名称需与触发器文件一致,同时此文件名应当至少与副本区域名称可以一一对应,即不具有歧义。 - -Cactbot在原基础上实现了一部分扩展语法。扩展语法可以在时间轴文本文件内或触发器的 `timeline` 属性中使用。 - -**infotext "event name" before 1** -在某个事件发生之前显示一个“信息”级别的文本。`event name` 与文件中的某个列出的事件名称匹配,并且在所有同名事件发生前均会显示指定文本。默认显示的文本为事件名称,但若您需要显示其他文本,添加指定文本至行末即可。`before` 参数是必填项,但您可以将其设置为0以令文本在事件发生的同时显示。当您需要使文本在事件之后显示时,也可以设置为负值。 - -**例1:在事件发生前1秒时显示“信息”等级文本** -`infotext "event name" before 1` - -**例2:在事件发生前更早显示“信息”等级文本,并修改了默认文本** -`infotext "event name" before 2.3 "alternate text"` - -**例3:与前述同事件名的“警告”等级文本** -`alerttext "event name" before 1` -`alerttext "event name" before 2.3 "alternate text"` - -**例4:与前述同事件名的“警报”等级文本** -`alarmtext "event name" before 1` -`alarmtext "event name" before 2.3 "alternate text"` - -## 关于翻译 - -本文档主要针对 Raidboss 组件编写,但是下方的内容适用于 cactbot 中所有需要翻译的部分。 - -大部分 cactbot 开发者都使用英语游玩 FFXIV,因此我们非常感谢所有能够提交翻译的拉取请求的人们,我们也欢迎针对 github 和 git 的使用的提问。 - -你可以运行 `npm run coverage-report` 以生成 cactbot 覆盖率报告,在[这里](https://quisquous.github.io/cactbot/util/coverage/coverage.html)还有当前主分支的在线版本。 - -覆盖率报告也包含了翻译内容的覆盖率,如: - -- [德文覆盖率报告](https://quisquous.github.io/cactbot/util/coverage/missing_translations_de.html) -- [法文覆盖率报告](https://quisquous.github.io/cactbot/util/coverage/missing_translations_fr.html) -- [日文覆盖率报告](https://quisquous.github.io/cactbot/util/coverage/missing_translations_ja.html) -- [中文覆盖率报告](https://quisquous.github.io/cactbot/util/coverage/missing_translations_cn.html) -- [韩文覆盖率报告](https://quisquous.github.io/cactbot/util/coverage/missing_translations_ko.html) - -待办事项:对于中国服/韩国服,更好地做法是将未公开的版本显示为无需翻译。 - -你可以运行 `npm run util` 然后选择 `find translation` 以查找需要翻译的内容。也可以直接输入 `npm run util -- findTranslations -f . -l cn` 查找缺失的中文翻译(对于法文则是 `-l fr`,对于德文则是 `-l de`,以此类推)。如果选择的是默认选项,该脚本会生成与在线版本完全相同的内容。 - -覆盖率报告会显示为不同的错误类别: - -- other 其他:一般杂项错误,一般不需要关心。 -- code 代码:一段需要翻译的 TypeScript 代码。 -- sync 同步: 包含了需要翻译的 `sync /xxx/` 的触发器或时间轴。 -- text 文本:需要翻译的时间轴文本(如: `2.0 "text"`)。 - -### 代码翻译 - -cactbot 中许多代码使用了 `LocaleText` 类型取代需要翻译的字符串。 - -`LocaleText` 是一个包含了多个语言的字符串的对象,并且按 `en` `de` `fr` `ja` `cn` `ko` 的顺序排列。单元测试会检测顺序是否正确。该顺序的考虑是“英语优先”,然后“国际服的语言按字母顺序排列”,最后是“其他服按字母顺序排列”。英语是唯一必选的语言。 - -下面是一个缺失了日语翻译的例子,测试报告会指出这个问题:`ui/oopsyraidsy/data/06-ew/raid/p4n.ts:78 [code] text: {`。其中 `text: {` 部分是缺失了日语翻译的代码起始部分。HTML 报告页面也有链接到对应代码的链接。 - -这个例子取自[这里](https://github.com/quisquous/cactbot/blob/e47d34b/ui/oopsyraidsy/data/06-ew/raid/p4n.ts#L78-L84): - -```typescript - text: { - en: 'DPS Tower', - de: 'DD-Turm', - fr: 'Tour DPS', - cn: 'DPS塔', - ko: '딜러 장판', - }, -``` - -正如上面的例子所示,这个对象缺失了 `ja` 字段,等待译者填写。 - -### Raidboss 翻译 - -对于 `sync 同步` 和 `text 文本` 错误,应当通过(名字取得很奇怪的)`timelineReplace` 属性进行翻译。(定义这个属性的时候,我们还仅用它翻译时间轴文本。而现在它也用于翻译触发器的 `netRegex` 和 `regex` 字段。但是,为了向后兼容,我们依旧叫它 `timelineReplace`。) - -这个字段的形状如下: - -```typescript - { - 'locale': 'fr', - 'replaceSync': { - 'Kokytos\'s Echo': 'spectre de Cocyte', - 'Kokytos(?!\')': 'Cocyte', - // etc - }, - 'replaceText': { - 'Aero IV': 'Giga Vent', - 'Archaic Demolish': 'Démolition archaïque', - // etc - }, - }, -``` - -其中的 `replaceSync` 部分不仅会应用到时间轴文件的 `sync /xxx/` 行,还在触发器的 `netRegex` 行中起作用。`replaceText` 部分则会应用于时间轴的 `"Text"` 部分。匹配是大小写不敏感的。 - -在内部实现中,cactbot 会以下面所示的替换法则将 `timelineReplace` 部分的字段应用于时间轴和触发器中,但注意这仅作演示,实际要更加复杂: - -```diff -# p9s.txt 时间轴文件 --168.7 "Archaic Demolish" sync / 1[56]:[^:]*:Kokytos:816D:/ -+168.7 "Démolition archaïque" sync / 1[56]:[^:]*:Cocyte:816D:/ -``` - -```diff - // p9s.ts 触发器文件 - { - id: 'P9S Archaic Demolish', - type: 'StartsUsing', -- netRegex: { id: '816D', source: 'Kokytos', capture: false }, -+ netRegex: { id: '816D', source: 'Cocyte', capture: false }, - alertText: (_data, _matches, output) => output.healerGroups!(), - outputStrings: { - healerGroups: Outputs.healerGroups, - }, - }, -``` - -#### 通用翻译替换 - -为了避免重复翻译相似的字段很多次,[common_replacement.ts](https://github.com/quisquous/cactbot/blob/main/ui/raidboss/common_replacement.ts) 文件定义了 `export const commonReplacement` 变量,包含了 `replaceSync` 和 `replaceText`,这个变量会隐式地包含于所有触发器集合中。 - -因此这些字段不需要翻译(当你尝试翻译这些文本时,`npm run test` 也会报告错误)。 - -#### 翻译冲突 (collision) - -这里有一个需要记住的重要概念:`replaceSync` 和 `replaceText` 中的字段在应用替换时**并不会**保证顺序。这让我们理论上可以更好地审查对于翻译替换的更改,因为最多只有一次替换。 - -因此 cactbot 的测试中会有专门的“冲突测试”保证翻译条目在应用时不会相互冲突,对于同一个文本也不会给出不同的翻译。这些测试可能让译者疲于解决冲突,但是避免了很多潜在问题。 - -如果你遇到了冲突错误不知道如何解决时,请上传你的翻译并开启一个包含错误的拉取请求,然后在评论中提出你的问题。我们会帮助你解决冲突。 - -#### 预翻译冲突 - -`npm run test` 可能会报告 **预翻译冲突** (pre-translation collision),这表明你的 `replaceSync` 和 `replaceText` 中存在两条互相冲突的条目,这两个条目都可以应用于同一个文本,但是**不能同时应用**。 - -下面是一个 P9S 的例子,有一部分修改: - -```typescript - { - 'locale': 'fr', - 'replaceSync': { - 'Kokytos\'s Echo': 'spectre de Cocyte', - 'Kokytos': 'Cocyte', - // etc - }, - }, -``` - -当我们要翻译 `Kokytos's Echo` 时,两个条目都可以匹配这个字符串,因此我们可能会先替换`Kokytos`,然后替换 `Kokytos's Echo`,当然反过来也是可能的。 - -这就导致了冲突问题:当我们先替换 `Kokytos's Echo`,它会变成 `spectre de Cocyte`,现在 `Kokytos` 无法匹配,因此我们得到了正确的翻译。 - -但是,当我们先替换 `Kokytos`,它会变成 `Cocyte's Echo`,现在 `Kokytos's Echo` 无法匹配,但这是错误的翻译。 - -你可以看到,这是由于两个条目都可以匹配同一个字符串,但是先后顺序不同导致了不同的结果,这就是预翻译冲突。 - -要解决这个问题,我们可以用正则表达式中的 **先行否定断言** (negative lookahead,即 `(?!text)`) 或者 **后行否定断言** (negative lookbehind,即 `(? diff --git a/docs/zh-CN/TimelineGuide.md b/docs/zh-CN/TimelineGuide.md index 91a64fc94e..66e5895648 100644 --- a/docs/zh-CN/TimelineGuide.md +++ b/docs/zh-CN/TimelineGuide.md @@ -1,1114 +1,5 @@ -# 时间轴指南 +# Permanently Moved to OverlayPlugin/cactbot -本指南旨在帮助用户编写适用于cactbot的时间轴文件。 +See: [docs/zh-CN/TimelineGuide.md](https://github.com/OverlayPlugin/cactbot/blob/main/docs/zh-CN/TimelineGuide.md) -![导入屏幕截图](../images/timelineguide_timeline.png) - -cactbot在[raidboss模块](https://github.com/quisquous/cactbot#raidboss-module)中定义触发器和时间轴。这两者是紧密结合的,因此您既可以基于技能编写触发器,也可以基于时间轴编写触发器。 - -## 目录 - -* [历史](#历史) -* [时间轴文件语法](#时间轴文件语法) - * [注释](#注释) - * [条目](#条目) - * [指令](#指令) - * [示例](#示例) - * [测试](#测试) - * [Shasta Kota的指南](#shasta-kota的指南) -* [Cactbot样式指南](#cactbot样式指南) -* [基于时间轴的触发器](#基于时间轴的触发器) -* [时间轴注入](#时间轴注入) -* [时间轴翻译](#时间轴翻译) -* [创建时间轴示例](#创建时间轴示例) - * [多次攻略副本](#多次攻略副本) - * [软件需求](#软件需求) - * [时间轴基础骨架](#时间轴基础骨架) - * [生成初始时间轴文件](#生成初始时间轴文件) - * [构建循环](#构建循环) - * [添加战斗阶段](#添加战斗阶段) - * [下一个战斗阶段](#下一个战斗阶段) - * [最终阶段](#最终阶段) - * [样板附注](#样板附注) - * [定义循环](#循环循环) - * [珠联璧合](#珠联璧合) - * [测试时间轴](#测试时间轴) - * [测试其他时间轴](#测试其他时间轴) - -## 历史 - -2016年,Shasta Kota 在 the Death and Taxes 网站上发布了一篇[指南](https://dtguilds.enjin.com/forum/m/37032836/viewthread/26353492-act-timeline-plugin),讲解如何使用 anoyetta 的[ACT timeline 插件](https://github.com/anoyetta/ACT.Hojoring)。此插件现在已经成为了Hojoring的一部分。 - -在 anoyetta 之前也有过一个更旧的 [kaizoban](https://github.com/090/act_timeline/releases) 版本,有一部分玩家也曾使用过该插件。 - -cactbot 的时间轴文件原本设计为向后兼容这些文件格式,因此 cactbot 的扩展语法需要通过触发器文件注入。 - -## 时间轴文件语法 - -时间轴文件的每一行称作时间轴的条目。条目与条目之间没有绝对的顺序。时间轴文件中按一定顺序排列仅仅是方便读者阅读而已。 - -### 注释 - -时间轴中的 `#` 符号用于定义注释,其后的所有内容均会被忽略。 - -### 条目 - -以下是一些时间轴条目的语法示例。每一行的条目均以事件时间和事件名称开始。 - -`数字 "字符串" (duration 数字)` - -`数字 "字符串" sync /正则/ (window 数字,数字) (jump 数字) (duration 数字)` - -此处的括号表示这个部分是可选的,括号本身并不是语法的组成部分。 - -数字可以是整数,如 `34`;也可以是浮点数,如 `84.381`。 - -字符串一般为事件名,如 `"坠落"` 或 `"双重攻击"`。 - -正则是一个标准的 [Javascript 正则表达式](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions)。 - -事件时间与事件名称永远在最开始的位置,但 `window`、`jump`、`duration`、以及 `sync` 并没有明确的顺序规定。但是在代码规范里,sync通常放在前面。 - -`duration 数字` 规定了显示该技能的时间长度,单位为秒。通常情况下,时间轴条目在倒数到0后会立刻消失。但有时需要表示一个正在释放或持续释放的技能时,便可使用此参数。例如要使用单行条目表示五次连续的巴哈姆特之爪时,您就可以使用 `duration` 参数规定显示时间长度。这个语法不需要配合sync属性。 - -`window 数字,数字` 规定了同步的时间范围。若 `window` 未设置,cactbot默认将其视同为设置了 `window 2.5,2.5`。也就是,相对于当前事件时间的前2.5秒至后2.5秒之间。例如,对于此时间轴条目:`3118.9 "Lancing Bolt" sync /:Raiden:3876:/`, 当正则表达式`/:Raiden:3876:/` 在 3116.4 到 3121.4 之间的任意时间点被匹配到时,时间轴会同步并回溯至 3118.9。时间轴通常在独特的技能上使用较大的window值,以确保时间轴即使在战斗中才启动也可以正确地同步到正确的位置。 - -`jump 数字` 告诉时间轴在匹配sync成功时跳转至指定的时间点。跳转至0意味着停止时间轴,通常用于阶段跳转或循环。尽管我们一般跳转至时间轴中已有的其他条目的时间点,但跳转到其他时间点也是可行的。 - -### 指令 - -您可以使用 `hideall` 隐藏某个事件。绝大多数时间轴都会定义 `hideall "--sync--"` 来隐藏与游戏战斗阶段同步化的条目,因为玩家没有必要看到这些控制性条目。 - -此外还有一些基于时间轴条目的用于生成警报文本的指令。该指令尽管目前依旧支持,但文档中并没有记载,在cactbot中您应当使用[基于时间轴的触发器](#基于时间轴的触发器) 来实现此需求。 - -### 示例 - -```bash -677.0 "Heavensfall Trio" -1044 "Enrage" # ??? -35.2 "Flare Breath x3" duration 4 -1608.1 "Petrifaction" sync /:Melusine:7B1:/ window 1610,5 -1141.4 "Leg Shot" sync /:Mustadio:3738:/ duration 20 -# 这是一个注释 -hideall "--sync--" - -28.0 "Damning Edict?" sync /:Chaos:3150:/ window 30,10 jump 2028.0 -524.9 "Allagan Field" sync /:The Avatar:7C4:/ duration 31 jump 444.9 -1032.0 "Control Tower" duration 13.5 sync /:Hashmal, Bringer Of Order starts using Control Tower on Hashmal/ window 20,20 # 从开始读条到塔坠落 -``` - -### 测试 - -在cactbot中运行 `npm run test` 会在node中运行 **test/check_timelines.js** 程序以验证时间轴语法。 - -### Shasta Kota的指南 - -您也可以阅读Shasta Kota的原版[指南](https://dtguilds.enjin.com/forum/m/37032836/viewthread/26353492-act-timeline-plugin),至今看来仍然十分优秀。 - -## Cactbot格式指南 - -以下是cactbot中对于时间轴的编写建议: - -* 为所有可能发生的事件添加同步正则 -* 总是添加Engage!(战斗开始!) 的独立条目,同时添加同步正则,以防玩家没有使用倒计时。 -* 若Boss的第一个技能是自动攻击,应把该自动攻击添加为独立条目以启动时间轴。(需要注意的是,有的Boss的自动攻击的技能名并不是“攻击”) -* 在顶部的注释中添加用于生成时间轴的命令行参数 -* 添加同步正则时,除非同步NPC台词是唯一的可行方式,否则应当优先考虑技能。 -* 当用NPC台词同步阶段时,应当为下一个技能添加一个较大的window值 -* 同步正则尽可能短 -* 尽可能使用游戏中的原名 -* 循环应当使用 `jump`,而不是给循环的技能设定较大的window -* 适当换行并添加注释以提升时间轴的可读性 -* 触发器/TTS/提示文本应当与时间轴分离 -* 使用[基于时间轴的触发器](#基于时间轴的触发器)来显示提示文本 -* 出现循环时,应额外在循环后方添加至少30秒的后续时间轴 -* 将7秒内发动的技能的同步正则注释在后方 (使将来的维护人员可以很方便地获知技能ID) - -### 触发器文件名 - -通常触发器的文件名应与玩家社区对此副本的称呼一致。讨伐战一般以Boss名字称呼, raid则通常是带有数字的缩写,而迷宫挑战通常采用副本区域名称。 - -文件名中应使用下划线分割单词。讨伐战中的 `nm` (假神),`hm` (真神) 与 `ex` (极神),则以减号分割。高难度迷宫挑战的名字中应当写出完整的单词“hard”。冠词如 `The` 可以省略。Raid通常会有一定顺序的数字称呼,例如 `t1` 到 `t13` 以及 `a1s` 到 `a12s` 等。零式讨伐通常需要加上 `s` 后缀,而普通难度则采用 `n` 后缀。(然而,这一规则不适用于巴哈姆特大迷宫,它只有6-9层拥有零式副本。) - -示例: - -* 魔法宫殿宇宙宫(The Grand Cosmos): `grand_cosmos` -* 泰坦歼殛战(Titan Extreme): `titan-ex` -* 红宝石神兵狂想作战(Ruby Weapon Extreme): `ruby_weapon-ex` -* 秘本宝库迦巴勒幻想图书馆(The Great Gubal Library (Hard)): `great_gubal_library_hard` -* 欧米茄零式时空狭缝 西格玛幻境2(Sigmascape V2.0 (Savage)): `o6s` -* 亚历山大机神城 启动之章3(Alexander - The Arm of the Father): `a3n` -* 巴哈姆特大迷宫 真源之章4(The Final Coil of Bahamut): `t13` - -## 基于时间轴的触发器 - -Cactbot的触发器文件支持添加基于时间轴的触发器。这是由于人们对于默认启用的触发器有特定的偏好,cactbot时间轴语法扩展又与其他时间轴插件不兼容,所以cactbot在触发器文件中添加了基于时间轴的触发器的功能。 - -您只需要添加 `timelineTriggers` 至触发器文件即可。 - -示例: - -* [乐欲之所瓯博讷修道院](https://github.com/quisquous/cactbot/blob/main/ui/raidboss/data/04-sb/alliance/orbonne_monastery.ts) -* [T9](https://github.com/quisquous/cactbot/blob/main/ui/raidboss/data/02-arr/raid/t9.ts) -* [O12 普通难度](https://github.com/quisquous/cactbot/blob/main/ui/raidboss/data/04-sb/raid/o12n.ts) - -这些触发器和普通的触发器拥有[相同的语法](https://github.com/quisquous/cactbot/blob/main/ui/raidboss/data/README.txt)。这意味着您依旧可以使用函数,返回任何您想要的东西。您也可以使用[condition](https://github.com/quisquous/cactbot/blob/5a7011c662d65f44c12c2fbff255484f2d31b8ef/ui/raidboss/data/02-arr/raid/t9.js#L10),使其仅针对特定职业/职能激活。 - -但是,他们之间仍然有一些区别: - -例如 `regex` 部分不能翻译,而且必须与时间轴文件中的某个条目完全一致。也就是说,它需要匹配时间轴的某一行中用双引号括起来的技能名。函数中的 `matches` 参数也返回此名字。 - -此触发器还支持 `beforeSeconds` 参数,使您可以指定触发器相对于技能提前提示的时间。 - -## 时间轴注入 - -理论上经过精心构造的时间轴适用于任何人。但是,有时候人们想要隐藏时间轴的某些部分,或者添加一些其他项。 - -Cactbot默认并不包含这些配置,这个功能更适合用户自行配置。 - -触发器文件有一个名为 `timeline` 的参数入口,这是一个数组,其中元素会添加到时间轴中。若元素都是字符串,则会直接添加。若元素是函数,那么cactbot会调用该函数,并添加其返回值至时间轴中。(但是传入函数的 `data` 参数仅包含了玩家的职能和职业,不包含其他参数。) - -测试时间轴可以在盛夏农庄通过倒计时或对木人行礼后触发。另行参见:[test.js](https://github.com/quisquous/cactbot/blob/79239abda888dd7a277da0501a7d4ac60d8cf963/ui/raidboss/data/triggers/test.js#L10)。 - -您也可以在 **cactbot/user/raidboss.js** 文件中添加定制化的时间轴和触发器。另行参见:[user/raidboss.js](https://github.com/quisquous/cactbot-user/blob/641488590e3ea499cc3b54cc9f2f2f856dee4ad8/raidboss.js#L28)。 - -## 时间轴翻译 - -为支持多语言,cactbot的触发器文件中提供了 `timelineReplace` 参数。[o12s.js](https://github.com/quisquous/cactbot/blob/ecbb723f097328c7bd0476352e5135bd5f776248/ui/raidboss/data/triggers/o12s.js#L608) 是一个不错的例子。该文件的参数集中包含了许多用于替换同步正则、文字和效果名的正则表达式。这有两个目的: - -其一是为了让工具软件能为触发器自动生成正则表达式翻译。 - -其二是为了能在运行中动态替换时间轴文字和正则。cactbot会使用 `replaceSync` 参数集自动替换 `sync /text/` 中的任意文字,同样地,`replaceText` 参数集则用于自动替换技能名文字。 - -正则表达式仅匹配上述定义的部分文字,不匹配整行。因此您需要格外注意,替换的正则表达式可能匹配到非预期的元素导致翻译错误。 - -## 创建时间轴示例 - -这里有一个例子,展示了如何使用cactbot提供的工具制作里塔提恩强攻战的时间轴。这个副本的逻辑非常简单,而且可以单人进行测试,是一个绝佳的实例副本。 - -### 多次攻略副本 - -制作时间轴的第一步是生成一些ACT日志。 - -您也可以通过fflogs的通关记录生成时间轴,但这个方式会丢失许多日志行,例如NPC台词、区域封锁/解锁的提示文本以及对象实体刷新信息。 - -当您攻略副本之后,您可以获得一些[网络日志文件](LogGuide.md#network-log-lines)。 - -找到对应的副本攻略记录,点击**Raw**,然后右键选择**Save As**,将其保存到磁盘上。 - -要获得一份好的日志,你应当: - -1. 尽量延长攻略时间,直到狂暴 -1. 带足够的队员以看到所有机制 (例如 t11 中连线机制至少要两人才出现) -1. 对于每个阶段,拖够时间直到看到机制循环 -1. 攻略多次以便于之后的测试 - -### 软件需求 - -* [Python 3](https://www.python.org/downloads/release/python-373/) -* [Node.js](https://nodejs.org/en/) -* 一份cactbot的[源代码](https://github.com/quisquous/cactbot/archive/main.zip)拷贝 - -安装 Python 3 时,推荐为该系统上的所有用户安装,这种安装方式会将 Python 写入 Windows 的 PATH 变量中,使您可以从命令提示符方便地运行 Python。 - -### 时间轴基础骨架 - -在cactbot中创建时间轴有三大步骤。 - -(1) 创建一个空时间轴文件。 - -新建一个文本文件名为 **ui/raidboss/data/timelines/cape_westwind.txt**。内容保持空白即可。 - -(2) 若没有触发器文件,则新建一个。 - -新建 **ui/raidboss/data/02-arr/trial/cape_westwind.js**。此文件可任意命名。 时间轴文件仅能被触发器文件加载,因此触发器文件总是必须的。 - -最初的触发器文件应当如下所示: - -```javascript -export default { - zoneId: ZoneId.CapeWestwind, - timelineFile: 'cape_westwind.txt', - triggers: [ - ], -}; -``` - -(3) 更新manifest文件。 - -在 **ui/raidboss/data/raidboss_manifest.txt** 文件中添加您新建的触发器文件与时间轴文件的路径。 - -(4) 构建 cactbot。 - -在 cactbot 源码根目录下运行 `npm run build` 以构建cactbot。 -若在此之前您从未安装依赖,则应当先运行 `npm install`。 - -(4) 重载raidboss悬浮窗 - -确保 raidboss 悬浮窗的路径指向 `dist/ui/raidboss/raidboss.html`。 -若您已经对这些文件进行了修改,则可以通过重载cactbot的raidboss悬浮窗以应用更改。 - -若您使用的是 `webpack-dev-server`,它会在您每次保存更改后自动重载。 - -### 生成初始时间轴文件 - -在你拿到网络日志文件后,您应该找到战斗的开始与结束时间点。 - -[在ACT中查看日志](LogGuide.md#viewing-logs-after-a-fight),找到开始与结束。 - -![战斗记录截图](../images/timelineguide_encounterlogs.png) - -例如,对于此战斗记录,您可以看到与之关联的日志行与时间。 - -```log -[18:42:23.614] 15:105E5703:Potato Chippy:2E:Tomahawk:4000EE16:Rhitahtyn sas Arvina:710003:9450000:1C:2E8000:0:0:0:0:0:0:0:0:0:0:0:0:140279:140279:8010:8010:1000:1000:-707.8608:-822.4221:67.74045:3858:74095:4560:0:1000:1000:-693.7162:-816.4633:65.55687: -[18:49:22.934] 19:Rhitahtyn Sas Arvina was defeated by Potato Chippy. -``` - -(已知缺陷:有时处理来自于他人的网络日志时可能需要转换时差。 欢迎补充相关代码。) - -基于这些时间,您可以通过下面的命令生成时间轴。 - -```bash -python util/make_timeline.py -f CapeWestwind.log -s 18:42:23.614 -e 18:49:22.934 - -0 "Start" -2.0 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -10.6 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -19.0 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -24.4 "Gate Of Tartarus" sync /:Rhitahtyn sas Arvina:473:/ -29.8 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -38.4 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -46.8 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -52.2 "Gate Of Tartarus" sync /:Rhitahtyn sas Arvina:473:/ -57.7 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -66.2 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -74.7 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -80.2 "Gate Of Tartarus" sync /:Rhitahtyn sas Arvina:473:/ -85.6 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -94.0 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -102.5 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -106.1 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -110.4 "Shrapnel Shell" sync /:Rhitahtyn sas Arvina:474:/ -114.9 "Winds Of Tartarus" sync /:Rhitahtyn sas Arvina:472:/ -119.2 "Firebomb" sync /:Rhitahtyn sas Arvina:476:/ -123.5 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -127.8 "Drill Shot" sync /:Rhitahtyn sas Arvina:475:/ -132.1 "Winds Of Tartarus" sync /:Rhitahtyn sas Arvina:472:/ -136.4 "Firebomb" sync /:Rhitahtyn sas Arvina:476:/ -140.7 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -145.2 "Shrapnel Shell" sync /:Rhitahtyn sas Arvina:474:/ -149.8 "Winds Of Tartarus" sync /:Rhitahtyn sas Arvina:472:/ -154.3 "Firebomb" sync /:Rhitahtyn sas Arvina:476:/ -158.8 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -163.3 "Drill Shot" sync /:Rhitahtyn sas Arvina:475:/ -167.8 "Winds Of Tartarus" sync /:Rhitahtyn sas Arvina:472:/ -172.3 "Firebomb" sync /:Rhitahtyn sas Arvina:476:/ -175.8 "Gate Of Tartarus" sync /:Rhitahtyn sas Arvina:473:/ -179.3 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -184.5 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -189.0 "Shrapnel Shell" sync /:Rhitahtyn sas Arvina:474:/ -193.7 "Winds Of Tartarus" sync /:Rhitahtyn sas Arvina:472:/ -195.9 "Fast Blade" sync /:7th Cohort Optio:2CD:/ -196.1 "Fast Blade" sync /:7th Cohort Optio:2CD:/ -198.2 "Firebomb" sync /:Rhitahtyn sas Arvina:476:/ -202.7 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -202.9 "Fast Blade" sync /:7th Cohort Optio:2CD:/ -203.3 "Fast Blade" sync /:7th Cohort Optio:2CD:/ -207.2 "Drill Shot" sync /:Rhitahtyn sas Arvina:475:/ -207.6 "Rampart" sync /:7th Cohort Optio:0A:/ -210.0 "Fast Blade" sync /:7th Cohort Optio:2CD:/ -211.7 "Winds Of Tartarus" sync /:Rhitahtyn sas Arvina:472:/ -212.6 "Shield Bash" sync /:7th Cohort Optio:2CE:/ -214.3 "Fast Blade" sync /:7th Cohort Optio:2CD:/ -214.9 "Fight Or Flight" sync /:7th Cohort Optio:14:/ -216.2 "Firebomb" sync /:Rhitahtyn sas Arvina:476:/ -217.3 "Rampart" sync /:7th Cohort Optio:0A:/ -218.0 "Celeris" sync /:7th Cohort Optio:194:/ -220.8 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -222.0 "Fight Or Flight" sync /:7th Cohort Optio:14:/ -225.2 "Shrapnel Shell" sync /:Rhitahtyn sas Arvina:474:/ -226.3 "Fast Blade" sync /:7th Cohort Optio:2CD:/ -229.9 "Winds Of Tartarus" sync /:Rhitahtyn sas Arvina:472:/ -234.4 "Firebomb" sync /:Rhitahtyn sas Arvina:476:/ -239.0 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -243.4 "Drill Shot" sync /:Rhitahtyn sas Arvina:475:/ -259.1 "Shrapnel Shell" sync /:Rhitahtyn sas Arvina:474:/ -263.6 "Firebomb" sync /:Rhitahtyn sas Arvina:476:/ -267.9 "Winds Of Tartarus" sync /:Rhitahtyn sas Arvina:472:/ -269.1 "Magitek Missiles" sync /:Rhitahtyn sas Arvina:478:/ -274.2 "Drill Shot" sync /:Rhitahtyn sas Arvina:475:/ -278.5 "Firebomb" sync /:Rhitahtyn sas Arvina:476:/ -282.8 "Winds Of Tartarus" sync /:Rhitahtyn sas Arvina:472:/ -299.3 "Shrapnel Shell" sync /:Rhitahtyn sas Arvina:474:/ -303.8 "Firebomb" sync /:Rhitahtyn sas Arvina:476:/ -308.1 "Winds Of Tartarus" sync /:Rhitahtyn sas Arvina:472:/ -309.3 "Magitek Missiles" sync /:Rhitahtyn sas Arvina:478:/ -314.4 "Drill Shot" sync /:Rhitahtyn sas Arvina:475:/ -318.7 "Firebomb" sync /:Rhitahtyn sas Arvina:476:/ -323.0 "Winds Of Tartarus" sync /:Rhitahtyn sas Arvina:472:/ -339.5 "Shrapnel Shell" sync /:Rhitahtyn sas Arvina:474:/ -344.0 "Firebomb" sync /:Rhitahtyn sas Arvina:476:/ -348.3 "Winds Of Tartarus" sync /:Rhitahtyn sas Arvina:472:/ -349.5 "Magitek Missiles" sync /:Rhitahtyn sas Arvina:478:/ -354.6 "Drill Shot" sync /:Rhitahtyn sas Arvina:475:/ -358.9 "Firebomb" sync /:Rhitahtyn sas Arvina:476:/ -363.2 "Winds Of Tartarus" sync /:Rhitahtyn sas Arvina:472:/ -378.7 "Shrapnel Shell" sync /:Rhitahtyn sas Arvina:474:/ -383.2 "Firebomb" sync /:Rhitahtyn sas Arvina:476:/ -387.5 "Winds Of Tartarus" sync /:Rhitahtyn sas Arvina:472:/ -388.7 "Magitek Missiles" sync /:Rhitahtyn sas Arvina:478:/ -393.8 "Drill Shot" sync /:Rhitahtyn sas Arvina:475:/ -398.1 "Firebomb" sync /:Rhitahtyn sas Arvina:476:/ -402.4 "Winds Of Tartarus" sync /:Rhitahtyn sas Arvina:472:/ -``` - -(注意:您也可以通过 `-lf` 列出网络日志中所有的独立战斗记录。 - -```bash -python make_timeline.py -f CapeWestwind.log -lf -1. 02:03:44.018 02:16:53.632 Cape Westwind -2. 18:32:52.981 18:36:14.086 Cape Westwind -3. 18:42:23.614 18:49:22.934 Cape Westwind -4. 18:57:09.114 19:10:13.200 Cape Westwind -5. 19:29:42.265 19:36:22.437 Cape Westwind -6. 19:40:20.606 19:46:44.342 Cape Westwind -``` - -基于此,您可以加上战斗记录id然后重新运行命令,比如 `-lf 3`。) - -当然,此时间轴仍无法使用,但是千里之行,始于足下。将输出的内容粘贴至 **ui/raidboss/data/timelines/cape_westwind.txt**。 - -若您正在使用的终端是Windows的命令提示符或者MINGW32,则您可以点击窗口左上方的图标,然后分别点击**编辑** -> **标记**。然后您可以用鼠标选择文本,被选中的文本会高亮显示。按下回车键后选中的文本会复制到剪贴板,然后您就可以随意粘贴了。 - -![标记截图](../images/timelineguide_copy.png) - -应当注意的是,日志中来自于小怪的无用日志行不胜枚举。大多数情况下,我们不可依赖小怪的时间去推测Boss的机制,因此通常我们会移除小怪的相关日志。 - -make_timeline.py 脚本支持两个选项以提供此功能。其一为“忽略实体”,其一为“忽略id”。如 `-ic "7Th Cohort Optio"` 或 `-ii 0A 2CD 2CE 194 14` 就可以隐藏这些技能。我们在这里使用忽略id的功能。 - -重新运行命令,这次带上忽略id的选项:`python util/make_timeline.py -f CapeWestwind.log -s 18:42:23.614 -e 18:49:22.934 -ii 0A 2CD 2CE 194 14` - -您最好再次阅览生成的时间轴,以确认是否还有小怪的日志行。我们通常要手动添加 [added combatant (添加实体)](LogGuide.md#line-03-0x03-addcombatant) 或用于匹配的NPC话语的 [game log lines (游戏日志行)](LogGuide.md#line-00-0x00-logline)。您可以自己研究如何将其添加进时间轴中。(欢迎向 **make_timeline.py** 贡献代码以自动实现这一步骤。) - -相关日志如下所示: - -```log -[18:45:27.041] 03:Added new combatant 7Th Cohort Optio. Job: 0 Level: 49 Max HP: 24057 Max MP: 8010 Pos: (-665.5159,-804.6631,62.33055). -[18:45:27.041] 03:Added new combatant 7Th Cohort Optio. Job: 0 Level: 49 Max HP: 24057 Max MP: 8010 Pos: (-665.5013,-807.1013,62.45256). -[18:42:24.000] 00:0044:Rhitahtyn sas Arvina:I will suffer none to oppose Lord van Baelsar! -[18:44:08.000] 00:0044:Rhitahtyn sas Arvina:My shields are impregnable! Join the countless challengers who have dashed themselves against them! -[18:46:27.000] 00:0044:Rhitahtyn sas Arvina:Your defeat will bring Lord van Baelsar's noble conquest one step closer to fruition! -[18:48:27.000] 00:0044:Rhitahtyn sas Arvina:Ungh... Though it cost me my life...I will strike you down! -``` - -您可以将其出现的时间与战斗开始时间相减,即可得到正确的相对时间。对于此实例,小怪出现在183.5秒 (通过 18:45:27.041 - 18:42:23.614 得到)。 - -### 构建循环 - -下一步是在每个阶段内构建循环。观察可得,此处有若干个不同阶段。 - -这是我们的初始阶段划分,此处加了一些空行以便清晰的分辨。 - -```bash -2.0 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -10.6 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -19.0 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -24.4 "Gate Of Tartarus" sync /:Rhitahtyn sas Arvina:473:/ - -29.8 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -38.4 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -46.8 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -52.2 "Gate Of Tartarus" sync /:Rhitahtyn sas Arvina:473:/ - -57.7 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -66.2 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -74.7 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -80.2 "Gate Of Tartarus" sync /:Rhitahtyn sas Arvina:473:/ -``` - -可以看出来,这里的循环长度大约在27.8到27.9之间。我们假定它是27.8吧。 - -对于构建循环,我们有个优秀的工具 **util/timeline_adjust.py**。 这个脚本可以遍历整个时间轴文件,并以可正可负的一定偏移值调整时间轴, 最后将调整后的时间轴输出。 - -若您在使用VSCode,您还可以使用 [Cactbot Highlight](https://marketplace.visualstudio.com/items?itemName=MaikoTan.cactbot-highlight) 插件中的 [调整时间功能](https://github.com/MaikoTan/cactbot-highlight#adjust-time),非常方便简单,一键即可调整时间。 - -(注意:不管哪种方式都不会调整jump。) - -下面是通过该脚本调整后的时间轴的一部分: - -```bash -python util/timeline_adjust.py --file=ui/raidboss/data/timelines/cape_westwind.txt --adjust=27.8 - -29.8 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -38.4 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -46.8 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -52.2 "Gate Of Tartarus" sync /:Rhitahtyn sas Arvina:473:/ - -57.6 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -66.2 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -74.6 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -80.0 "Gate Of Tartarus" sync /:Rhitahtyn sas Arvina:473:/ - -85.5 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -94.0 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -102.5 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -108.0 "Gate Of Tartarus" sync /:Rhitahtyn sas Arvina:473:/ -``` - -相比于原本的时间轴,该循环已经接近完美。第一个循环非常完美,但第二个则略有一点偏移,这里调整后的时间为57.6、74.6、80.0,但原本是57.7、74.7、80.2。 虽然不够完美,但已经很接近了。 - -在cactbot中,有一个配置项可以设置显示多久之后的时间轴。默认是30秒,因此您应额外在循环后方添加至少30秒的后续时间轴。 - -那么第一阶段的最终版本就完成了。 - -注意,我们倾向于使用 **timeline_adjust.py** 生成的时间,而不是原本的时间。(您也可以使用 `Cactbot Highlight` 插件。) 这样我们从52.2跳转到24.4的时候,时间差依旧是正确的。每次 `Gate Of Tartarus` 释放后5.4秒总会出现 `Shield Skewer`。 - -之后我们会添加jump。现在它如下所示: - -```bash -2.0 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -10.6 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -19.0 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -24.4 "Gate Of Tartarus" sync /:Rhitahtyn sas Arvina:473:/ - -29.8 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -38.4 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -46.8 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -52.2 "Gate Of Tartarus" sync /:Rhitahtyn sas Arvina:473:/ - -57.6 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -66.2 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -74.6 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -80.0 "Gate Of Tartarus" sync /:Rhitahtyn sas Arvina:471:/ -``` - -### 添加新的战斗阶段 - -现在开始编写P2的时间轴。通过观察接下来的日志文件可以发现,boss会在血量降至80%之后喊话并开始使用新的技能。 - -不幸的是,boss开启P2的技能是 `Shield Skewer`,这一技能在P1被多次使用,这使得通过同步该技能来确认是否进入P2这一方式并不太合理。 - -**make_timeline.py** 中有一项功能可以把某技能的第一次使用定义至特定的一个时间点。由于cactbot的时间轴只会显示该时间点向后30秒内的内容,因此您可以在30秒后的任意时间点定义你的分P起点。 - -让我们假定P2第一个技能开始的时间点为200。`Shrapnel Shell` 发动时间位于第一个技能发动4.3秒后;我们把 `Shrapnel Shell` 的初次使用时间点定义为204.3。 - -下面给出了一个新的命令行: `python util/make_timeline.py -f CapeWestwind.log -s 18:42:23.614 -e 18:49:22.934 -ii 0A 2CD 2CE 194 14 -p 474:204.3` - -执行上述命令行可获得P2时间轴如下所示,我们在文件中手动添加了一些空白行以区分不同的循环阶段: - -```bash -# 手动添加同步 -199.0 "--sync--" sync /00:0044:[^:]*:My shields are impregnable/ -200.0 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ - -# make_timeline 的输出 -204.3 "Shrapnel Shell" sync /:Rhitahtyn sas Arvina:474:/ -208.8 "Winds Of Tartarus" sync /:Rhitahtyn sas Arvina:472:/ -213.1 "Firebomb" sync /:Rhitahtyn sas Arvina:476:/ - -217.4 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -221.7 "Drill Shot" sync /:Rhitahtyn sas Arvina:475:/ -226.0 "Winds Of Tartarus" sync /:Rhitahtyn sas Arvina:472:/ -230.3 "Firebomb" sync /:Rhitahtyn sas Arvina:476:/ - -234.6 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -239.1 "Shrapnel Shell" sync /:Rhitahtyn sas Arvina:474:/ -243.7 "Winds Of Tartarus" sync /:Rhitahtyn sas Arvina:472:/ -248.2 "Firebomb" sync /:Rhitahtyn sas Arvina:476:/ - -252.7 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -257.2 "Drill Shot" sync /:Rhitahtyn sas Arvina:475:/ -261.7 "Winds Of Tartarus" sync /:Rhitahtyn sas Arvina:472:/ -266.2 "Firebomb" sync /:Rhitahtyn sas Arvina:476:/ -``` - -这一段时间轴内存在一个清晰的循环,每次循环内必定有2次 `Firebomb` 和2次 `Shield Skewer`。 -该循环时长为34.6秒。是时候再次执行 **timeline_adjust.py** 了。 - -执行 `python util/timeline_adjust.py --file=ui/raidboss/data/timelines/cape_westwind.txt --adjust=27.8`,输出结果如下: - -```bash -234.6 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -238.9 "Shrapnel Shell" sync /:Rhitahtyn sas Arvina:474:/ -243.4 "Winds Of Tartarus" sync /:Rhitahtyn sas Arvina:472:/ -247.7 "Firebomb" sync /:Rhitahtyn sas Arvina:476:/ - -252.0 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -256.3 "Drill Shot" sync /:Rhitahtyn sas Arvina:475:/ -260.6 "Winds Of Tartarus" sync /:Rhitahtyn sas Arvina:472:/ -264.9 "Firebomb" sync /:Rhitahtyn sas Arvina:476:/ -``` - -这次获得的时间轴与原始时间轴存在较大的误差,最后一次 Firebomb 出现时间为264.9,而原始时间轴中为266.2。如果你想要获得更加精确的时间轴,那么你需要将脚本执行结果所得时间轴与其他脚本运行结果做对比校核。 - -在 Cape Westwind 这个副本中,这点误差将不会造成太大的影响,因此我们将直接使用 **timeline_adjust.py** 运行所得时间轴替换掉原始时间轴中P2部分。 - -由此所得的时间轴文件如下所示: - -```bash -0 "Start" -2.0 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -10.6 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -19.0 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -24.4 "Gate Of Tartarus" sync /:Rhitahtyn sas Arvina:473:/ - -29.8 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -38.4 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -46.8 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -52.2 "Gate Of Tartarus" sync /:Rhitahtyn sas Arvina:473:/ - -57.6 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -66.2 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -74.6 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -80.0 "Gate Of Tartarus" sync /:Rhitahtyn sas Arvina:473:/ - -# 80% -199.0 "--sync--" sync /00:0044:[^:]*:My shields are impregnable/ -200.0 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -204.3 "Shrapnel Shell" sync /:Rhitahtyn sas Arvina:474:/ -208.8 "Winds Of Tartarus" sync /:Rhitahtyn sas Arvina:472:/ -213.1 "Firebomb" sync /:Rhitahtyn sas Arvina:476:/ - -217.4 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -221.7 "Drill Shot" sync /:Rhitahtyn sas Arvina:475:/ -226.0 "Winds Of Tartarus" sync /:Rhitahtyn sas Arvina:472:/ -230.3 "Firebomb" sync /:Rhitahtyn sas Arvina:476:/ - -234.6 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -238.9 "Shrapnel Shell" sync /:Rhitahtyn sas Arvina:474:/ -243.4 "Winds Of Tartarus" sync /:Rhitahtyn sas Arvina:472:/ -247.7 "Firebomb" sync /:Rhitahtyn sas Arvina:476:/ - -252.0 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -256.3 "Drill Shot" sync /:Rhitahtyn sas Arvina:475:/ -260.6 "Winds Of Tartarus" sync /:Rhitahtyn sas Arvina:472:/ -264.9 "Firebomb" sync /:Rhitahtyn sas Arvina:476:/ - -# 60% -``` - -### 下一个战斗阶段 - -通过观察,我们发现下一阶段 (P3) 将于boss血量降至60%开始,同时boss会召唤2只小怪。 - -观察时间轴可以发现,小怪出现的时间点附近有一个叫做 "Gate of Tartarus" 的技能。 - -下面是未经调整的原始时间轴文件: - -```bash -175.8 "Gate Of Tartarus" sync /:Rhitahtyn sas Arvina:473:/ -183.5 "Adds" -``` - -遗憾的是,boss在P1阶段就会使用 `Gate of Tartarus`,因此我们不能像在P2阶段那样用 `-p` 这个参数来处理。(欢迎各位发布可以增加相关优化选项的补丁?) - -为了解决这个问题,我们可以使用 **timeline_adjust.py** 将时间轴向前移动一段时间。 如果我们将原始时间轴整体附加 400-175.8=224.2 的时间偏移, 则可以从 t = 400 开始定义P3阶段。 - -下面是包含手动添加的召唤小怪时间点的调整后的时间轴: - -```bash -400.0 "Gate Of Tartarus" sync /:Rhitahtyn sas Arvina:473:/ -403.5 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ - -407.7 "Adds" -408.7 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -413.2 "Shrapnel Shell" sync /:Rhitahtyn sas Arvina:474:/ -417.9 "Winds Of Tartarus" sync /:Rhitahtyn sas Arvina:472:/ -422.4 "Firebomb" sync /:Rhitahtyn sas Arvina:476:/ - -426.9 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -431.4 "Drill Shot" sync /:Rhitahtyn sas Arvina:475:/ -435.9 "Winds Of Tartarus" sync /:Rhitahtyn sas Arvina:472:/ -440.4 "Firebomb" sync /:Rhitahtyn sas Arvina:476:/ - -445.0 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -449.4 "Shrapnel Shell" sync /:Rhitahtyn sas Arvina:474:/ -454.1 "Winds Of Tartarus" sync /:Rhitahtyn sas Arvina:472:/ -458.6 "Firebomb" sync /:Rhitahtyn sas Arvina:476:/ - -463.2 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -467.6 "Drill Shot" sync /:Rhitahtyn sas Arvina:475:/ - -# 40% 转阶段?? -483.3 "Shrapnel Shell" sync /:Rhitahtyn sas Arvina:474:/ -487.8 "Firebomb" sync /:Rhitahtyn sas Arvina:476:/ -492.1 "Winds Of Tartarus" sync /:Rhitahtyn sas Arvina:472:/ - -493.3 "Magitek Missiles" sync /:Rhitahtyn sas Arvina:478:/ -407.7 "Adds" -``` - -由于没有战斗过程的录像,我们并不能非常肯定 `Shrapnel Shell` 这一技能是属于P3还是P4阶段。不过通过观察日志文件,我们可以确定 `Magitek Missiles` 这一技能属于最后一个战斗阶段,也即P4。由于这个 `Sharpnel Shell` 打断了之前的技能循环模式,让我们假设是这个技能开启了P4阶段。我们会在稍后的内容里对它进行测试。 - -新阶段的时间轴的循环与P2有些类似。 - -首先来判断一下这一循环是否与P2的循环完全一致。您可以使用 **timeline_adjust.py** 把P3之前的P2最后一段 `Shield Skewer` 小循环整体偏移 +208.7。遗憾的是,您可以发现偏移后的时间轴与新阶段的小循环并不完全相同。因此,我们需要构建P3阶段的时间轴。 - -很明显,P3阶段的小循环也是一个36.2秒的循环。 使用 **timeline_adjust.py** 对时间轴中第一个小循环附加一个36.2秒偏移调整,可以得到如下结果: - -```bash -445.0 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -449.5 "Shrapnel Shell" sync /:Rhitahtyn sas Arvina:474:/ -454.2 "Winds Of Tartarus" sync /:Rhitahtyn sas Arvina:472:/ -458.7 "Firebomb" sync /:Rhitahtyn sas Arvina:476:/ - -463.2 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -467.7 "Drill Shot" sync /:Rhitahtyn sas Arvina:475:/ -472.2 "Winds Of Tartarus" sync /:Rhitahtyn sas Arvina:472:/ -476.7 "Firebomb" sync /:Rhitahtyn sas Arvina:476:/ -``` - -添加偏移后的小循环时间点与原始时间轴中的第二小循环时间点非常接近,因此我们可以认为它就是P3的技能循环。 - -### 最终阶段 - -战斗的最后一个阶段将在boss血量降至40%的时候开启。我们在这里假设由 `Shrapnel Shell` 这一技能开启这一阶段的战斗。显而易见的是,这个技能并不是这一阶段独有的技能。`Magitek Missiles` 才是P4独有技能,它的初次使用位于 Shrapnel Shell 这一技能的10秒后。让我们把P4的开始时间定义在600秒处,把 `Magitek Missile` (技能ID 478) 的初次使用时间定为610。 - -下面执行最后一个命令行,包括第二阶段:`python util/make_timeline.py -f CapeWestwind.log -s 18:42:23.614 -e 18:49:22.934 -ii 0A 2CD 2CE 194 14 -p 474:204.3 478:610` - -```bash -# 手动添加 -595.0 "--sync--" sync /00:0044:[^:]*:Your defeat will bring/ window 600,0 -600.0 "Shrapnel Shell" sync /:Rhitahtyn sas Arvina:474:/ -604.5 "Firebomb" sync /:Rhitahtyn sas Arvina:476:/ -608.8 "Winds Of Tartarus" sync /:Rhitahtyn sas Arvina:472:/ - -# make_timeline的部分输出 -610.0 "Magitek Missiles" sync /:Rhitahtyn sas Arvina:478:/ -615.1 "Drill Shot" sync /:Rhitahtyn sas Arvina:475:/ -619.4 "Firebomb" sync /:Rhitahtyn sas Arvina:476:/ -623.7 "Winds Of Tartarus" sync /:Rhitahtyn sas Arvina:472:/ -640.2 "Shrapnel Shell" sync /:Rhitahtyn sas Arvina:474:/ -644.7 "Firebomb" sync /:Rhitahtyn sas Arvina:476:/ -649.0 "Winds Of Tartarus" sync /:Rhitahtyn sas Arvina:472:/ - -650.2 "Magitek Missiles" sync /:Rhitahtyn sas Arvina:478:/ -655.3 "Drill Shot" sync /:Rhitahtyn sas Arvina:475:/ -659.6 "Firebomb" sync /:Rhitahtyn sas Arvina:476:/ -663.9 "Winds Of Tartarus" sync /:Rhitahtyn sas Arvina:472:/ -680.4 "Shrapnel Shell" sync /:Rhitahtyn sas Arvina:474:/ -684.9 "Firebomb" sync /:Rhitahtyn sas Arvina:476:/ -689.2 "Winds Of Tartarus" sync /:Rhitahtyn sas Arvina:472:/ - -690.4 "Magitek Missiles" sync /:Rhitahtyn sas Arvina:478:/ -695.5 "Drill Shot" sync /:Rhitahtyn sas Arvina:475:/ -699.8 "Firebomb" sync /:Rhitahtyn sas Arvina:476:/ -704.1 "Winds Of Tartarus" sync /:Rhitahtyn sas Arvina:472:/ -719.6 "Shrapnel Shell" sync /:Rhitahtyn sas Arvina:474:/ -724.1 "Firebomb" sync /:Rhitahtyn sas Arvina:476:/ -728.4 "Winds Of Tartarus" sync /:Rhitahtyn sas Arvina:472:/ - -729.6 "Magitek Missiles" sync /:Rhitahtyn sas Arvina:478:/ -734.7 "Drill Shot" sync /:Rhitahtyn sas Arvina:475:/ -739.0 "Firebomb" sync /:Rhitahtyn sas Arvina:476:/ -743.3 "Winds Of Tartarus" sync /:Rhitahtyn sas Arvina:472:/ -``` - -很明显P4拥有一个40.2秒的小循环。使用 **timeline_adjust.py** 对时间轴中的第一个小循环附加一个40.2秒的偏移调整,我们可以得到如下结果: - -```bash -650.2 "Magitek Missiles" sync /:Rhitahtyn sas Arvina:478:/ -655.3 "Drill Shot" sync /:Rhitahtyn sas Arvina:475:/ -659.6 "Firebomb" sync /:Rhitahtyn sas Arvina:476:/ -663.9 "Winds Of Tartarus" sync /:Rhitahtyn sas Arvina:472:/ -680.4 "Shrapnel Shell" sync /:Rhitahtyn sas Arvina:474:/ -684.9 "Firebomb" sync /:Rhitahtyn sas Arvina:476:/ -689.2 "Winds Of Tartarus" sync /:Rhitahtyn sas Arvina:472:/ -``` - -所得结果与原始时间轴的第二小循环完美契合,因此我们可以认定该循环即为P4的技能循环。 - -### 样板附注 - -通常来说,大多数时间轴文件都应该在文件抬头给出一些模板,类似下面这样: - -```bash -# Cape Westwind -# -ii 0A 2CD 2CE 194 14 -p 474:204 478:610 - -hideall "--Reset--" -hideall "--sync--" - -0.0 "--Reset--" sync / 21:........:4000000F:/ window 10000 jump 0 - -0 "Start" -0.0 "--sync--" sync /:Engage!/ window 0,1 -2.0 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -``` - -您最好能给出您用于生成代码所用的命令,以便他人可以追溯您制作代码时所作的前期工作。 - -`hideall` 命令可以隐藏某一命令的执行信息,因此玩家不会看到 `--sync--` 的执行信息,但这不意味着时间轴本身没有执行这些同步命令。`hideall` 后面的文本内容可以是任意命令名,这里只是以 `--sync--` 作为例子进行说明。 - -当战斗过程存在转阶段时,最好使用重置时间轴来停止之前的时间轴。在战斗过程中存在场地转换的副本中 (例如欧米茄系列, a4s, a8s, a12s, t9, t13),`sync / 21:........:4000000F:/` 是一个相当好用的同步命令。在进入战斗后会封锁战斗区域的副本 (例如a1s, t1-8),则应使用区域封锁这条消息来进行时间轴重置。 -” -您的时间轴文件抬头应该设置一句与“开始战斗!”同步的命令以开启战斗同步,同时保持与战斗倒计时同步。如果boss的第一个技能在战斗开始后很长一段时候后才会使用,那么您应该把它也加入时间轴文件以保证时间轴正常启动。 - -举个栗子,在o11s的时间轴文件中,抬头两行如下所示: - -```bash -0.0 "--sync--" sync /:Engage!/ window 0,1 -2.5 "--sync--" sync /:Omega:368:/ window 3,0 -``` - -### 定义循环 - -下文再次给出了P1阶段的小循环。我们将对它进行改写,以便时间轴每次执行至52.2秒时,它会直接跳回24.4秒处。 - -```bash -2.0 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -10.6 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -19.0 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -24.4 "Gate Of Tartarus" sync /:Rhitahtyn sas Arvina:473:/ - -29.8 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -38.4 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -46.8 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -52.2 "Gate Of Tartarus" sync /:Rhitahtyn sas Arvina:473:/ - -57.6 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -66.2 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -74.6 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -80.0 "Gate Of Tartarus" sync /:Rhitahtyn sas Arvina:473:/ -``` - -我们在52.2这一行上添加一句 `window 1,100 jump 24.4` 。这一句命令的意思是,如果您在这一行执行前10秒至执行后100秒内看到boss使用该技能,那么时间轴将跳回24.4秒处。默认情况下,除了一些需要特殊设置同步时间范围的语句,所有的同步语句都应该设置 `window 5,5` ,它表示在该行执行的前后5秒内与该技能进行同步。 - -添加该语句之后的时间轴文件中,57.6至80.0秒范围内的技能将不会被同步,因为当时间轴运行到52.2秒的技能后,它会跳回我们设置的24.4秒处。因此我们可以把这一部分内容删除。 - -最后值得一提的是,由于有时候日志文件中会丢失一些信息,而ACT也可能是在战斗中途才被开启,您最好在那些boss不常用或者非常重要的技能上设置一个较长的同步窗口期,以便即使出现上述情况也能使得时间轴能及时与战斗同步。 - -当您做完上述编辑后,我们可以得到下面这样的时间轴文件,它就是初始小循环的最终版本。 - -```bash -2.0 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -10.6 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -19.0 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -24.4 "Gate Of Tartarus" sync /:Rhitahtyn sas Arvina:473:/ window 30,10 - -29.8 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -38.4 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -46.8 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -52.2 "Gate Of Tartarus" sync /:Rhitahtyn sas Arvina:473:/ window 10,100 jump 24.4 - -# 假的时间轴 仅用于显示 -57.6 "Shield Skewer" -66.2 "Shield Skewer" -74.6 "Shield Skewer" -80.0 "Gate Of Tartarus" -``` - -您可以对其余几个阶段的小循环执行相同的改写。 - -### 珠联璧合 - -让我们把已经改写完毕的全部小循环整合到一起,可以得到如下时间轴: - -由于本文举例的时间轴文件中 `Shield Skewer` 这一技能频繁出现,各个小循环重复率较低,在编写时间轴的时候需要更加小心。 - -```bash -# Cape Westwind -# -ii 0A 2CD 2CE 194 14 -p 474:204 478:610 - -hideall "--Reset--" -hideall "--sync--" - -0.0 "--Reset--" sync / 21:........:400000)F:/ window 10000 jump 0 - -### Phase 1: skewers and stuns -0 "Start" -0.0 "--sync--" sync /:Engage!/ window 0,1 -2.0 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -10.6 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -19.0 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -24.4 "Gate Of Tartarus" sync /:Rhitahtyn sas Arvina:473:/ window 30,10 - -29.8 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -38.4 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -46.8 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -52.2 "Gate Of Tartarus" sync /:Rhitahtyn sas Arvina:473:/ window 10,100 jump 24.4 - -57.6 "Shield Skewer" -66.2 "Shield Skewer" -74.6 "Shield Skewer" -80.0 "Gate Of Tartarus" - - -### Phase 2 (80%): firebombs -199.0 "--sync--" sync /00:0044:[^:]*:My shields are impregnable/ window 200,0 -200.0 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -204.3 "Shrapnel Shell" sync /:Rhitahtyn sas Arvina:474:/ window 205,10 -208.8 "Winds Of Tartarus" sync /:Rhitahtyn sas Arvina:472:/ -213.1 "Firebomb" sync /:Rhitahtyn sas Arvina:476:/ - -217.4 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -221.7 "Drill Shot" sync /:Rhitahtyn sas Arvina:475:/ -226.0 "Winds Of Tartarus" sync /:Rhitahtyn sas Arvina:472:/ -230.3 "Firebomb" sync /:Rhitahtyn sas Arvina:476:/ - -234.6 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -238.9 "Shrapnel Shell" sync /:Rhitahtyn sas Arvina:474:/ window 20,100 jump 204.3 -243.4 "Winds Of Tartarus" -247.7 "Firebomb" - -252.0 "Shield Skewer" -256.3 "Drill Shot" -260.6 "Winds Of Tartarus" -264.9 "Firebomb" - - -### Phase 3 (60%): Adds -400.0 "Gate Of Tartarus" sync /:Rhitahtyn sas Arvina:473:/ window 200,20 -403.5 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ - -407.7 "Adds" -408.7 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -413.2 "Shrapnel Shell" sync /:Rhitahtyn sas Arvina:474:/ window 20,20 -417.9 "Winds Of Tartarus" sync /:Rhitahtyn sas Arvina:472:/ -422.4 "Firebomb" sync /:Rhitahtyn sas Arvina:476:/ - -426.9 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -431.4 "Drill Shot" sync /:Rhitahtyn sas Arvina:475:/ -435.9 "Winds Of Tartarus" sync /:Rhitahtyn sas Arvina:472:/ -440.4 "Firebomb" sync /:Rhitahtyn sas Arvina:476:/ - -445.0 "Shield Skewer" sync /:Rhitahtyn sas Arvina:471:/ -449.5 "Shrapnel Shell" sync /:Rhitahtyn sas Arvina:474:/ window 20,100 jump 413.2 -454.2 "Winds Of Tartarus" -458.7 "Firebomb" - -463.2 "Shield Skewer" -467.7 "Drill Shot" -472.2 "Winds Of Tartarus" -476.7 "Firebomb" - - -### Phase 4 (40%): magitek missiles -595.0 "--sync--" sync /00:0044:[^:]*:Your defeat will bring/ window 600,0 - -600.0 "Shrapnel Shell" sync /:Rhitahtyn sas Arvina:474:/ -604.5 "Firebomb" sync /:Rhitahtyn sas Arvina:476:/ -608.8 "Winds Of Tartarus" sync /:Rhitahtyn sas Arvina:472:/ - -610.0 "Magitek Missiles" sync /:Rhitahtyn sas Arvina:478:/ window 610,30 -615.1 "Drill Shot" sync /:Rhitahtyn sas Arvina:475:/ -619.4 "Firebomb" sync /:Rhitahtyn sas Arvina:476:/ -623.7 "Winds Of Tartarus" sync /:Rhitahtyn sas Arvina:472:/ -640.2 "Shrapnel Shell" sync /:Rhitahtyn sas Arvina:474:/ -644.7 "Firebomb" sync /:Rhitahtyn sas Arvina:476:/ -649.0 "Winds Of Tartarus" sync /:Rhitahtyn sas Arvina:472:/ - -650.2 "Magitek Missiles" sync /:Rhitahtyn sas Arvina:478:/ window 20,100 jump 610.0 -655.3 "Drill Shot" -659.6 "Firebomb" -663.9 "Winds Of Tartarus" -680.4 "Shrapnel Shell" -684.9 "Firebomb" -689.2 "Winds Of Tartarus" -``` - -### 测试时间轴 - -cactbot有一个名为 **util / test_timeline.py** 的测试工具,可以使用网络日志文件或fflog来测试现有的时间轴文件。 - -这个测试工具可以告知您时间轴是否存在与战斗无法同步的情况,还能告知您在同步过程中那些未能匹配而被跳过的时间轴内容。但它不会告诉您时间轴文件中没有包含的却出现在战斗中的技能。 - -它会告知您实际战斗与时间轴的同步偏移有多大,这可以帮助您调整时间轴文件以使其更加准确。 - -这里有一个例子。 这里的 `-t` 参数是用来指代您在 **ui/raidboss/data/timelines** 文件夹下面的想要测试的时间轴文件名称,-t 后的文件名应舍弃文件后缀 .txt。 (与 `make_timeline` 类似,您可以使用 `-lf` 参数来列出全部的时间轴文件。) - -```bash -$ python util/test_timeline.py -f CapeWestwind.log -s 18:42:23.614 -e 18:49:22. -934 -t cape_westwind -0.000: Matched entry: 2.0 Shield Skewer (+2.000s) -10.556: Matched entry: 10.6 Shield Skewer (+0.044s) -18.985: Matched entry: 19.0 Shield Skewer (+0.015s) -24.411: Matched entry: 24.4 Gate Of Tartarus (-0.011s) -29.810: Matched entry: 29.8 Shield Skewer (-0.010s) -38.372: Matched entry: 38.4 Shield Skewer (+0.028s) -46.835: Matched entry: 46.8 Shield Skewer (-0.035s) -52.239: Matched entry: 52.2 Gate Of Tartarus (-0.039s) - Jumping to 24.400 -29.897: Matched entry: 29.8 Shield Skewer (-0.097s) -38.286: Matched entry: 38.4 Shield Skewer (+0.114s) -46.851: Matched entry: 46.8 Shield Skewer (-0.051s) -52.291: Matched entry: 52.2 Gate Of Tartarus (-0.091s) - Jumping to 24.400 -29.831: Matched entry: 29.8 Shield Skewer (-0.031s) -38.204: Matched entry: 38.4 Shield Skewer (+0.196s) -46.888: Matched entry: 46.8 Shield Skewer (-0.088s) -48.695: Matched entry: 199.0 --sync-- (+150.305s) - Missed sync: Gate Of Tartarus at 52.2 (last seen at 24.411) -200.693: Matched entry: 200.0 Shield Skewer (-0.693s) -204.273: Matched entry: 204.3 Shrapnel Shell (+0.027s) -208.809: Matched entry: 208.8 Winds Of Tartarus (-0.009s) -213.111: Matched entry: 213.1 Firebomb (-0.011s) -217.417: Matched entry: 217.4 Shield Skewer (-0.017s) -221.711: Matched entry: 221.7 Drill Shot (-0.011s) -226.017: Matched entry: 226.0 Winds Of Tartarus (-0.017s) -230.313: Matched entry: 230.3 Firebomb (-0.013s) -234.642: Matched entry: 234.6 Shield Skewer (-0.042s) -239.144: Matched entry: 238.9 Shrapnel Shell (-0.244s) - Jumping to 204.300 -208.885: Matched entry: 208.8 Winds Of Tartarus (-0.085s) -213.304: Matched entry: 213.1 Firebomb (-0.204s) -217.623: Matched entry: 217.4 Shield Skewer (-0.223s) -221.857: Matched entry: 221.7 Drill Shot (-0.157s) -226.191: Matched entry: 226.0 Winds Of Tartarus (-0.191s) -230.480: Matched entry: 230.3 Firebomb (-0.180s) -233.846: Matched entry: 400.0 Gate Of Tartarus (+166.154s) - Missed sync: Shield Skewer at 234.6 (last seen at 217.623) - Missed sync: Shrapnel Shell at 238.9 (last seen at 204.273) -403.546: Matched entry: 403.5 Shield Skewer (-0.046s) -408.741: Matched entry: 408.7 Shield Skewer (-0.041s) -413.195: Matched entry: 413.2 Shrapnel Shell (+0.005s) -417.897: Matched entry: 417.9 Winds Of Tartarus (+0.003s) -422.385: Matched entry: 422.4 Firebomb (+0.015s) -426.890: Matched entry: 426.9 Shield Skewer (+0.010s) -431.389: Matched entry: 431.4 Drill Shot (+0.011s) -435.882: Matched entry: 435.9 Winds Of Tartarus (+0.018s) -440.393: Matched entry: 440.4 Firebomb (+0.007s) -444.969: Matched entry: 445.0 Shield Skewer (+0.031s) -449.380: Matched entry: 449.5 Shrapnel Shell (+0.120s) - Jumping to 413.200 -417.884: Matched entry: 417.9 Winds Of Tartarus (+0.016s) -422.366: Matched entry: 422.4 Firebomb (+0.034s) -426.975: Matched entry: 426.9 Shield Skewer (-0.075s) -431.292: Matched entry: 431.4 Drill Shot (+0.108s) -431.400: Matched entry: 595.0 --sync-- (+163.600s) - Missed sync: Winds Of Tartarus at 435.9 (last seen at 417.884) - Missed sync: Firebomb at 440.4 (last seen at 422.366) - Missed sync: Shield Skewer at 445.0 (last seen at 426.97499999999997) - Missed sync: Shrapnel Shell at 449.5 (last seen at 413.195) -620.783: Matched entry: 610.0 Magitek Missiles (-10.783s) - Missed sync: Shrapnel Shell at 600.0 (last seen at 610.739) - Missed sync: Firebomb at 604.5 (last seen at 615.247) - Missed sync: Winds Of Tartarus at 608.8 (last seen at 619.564) -615.098: Matched entry: 615.1 Drill Shot (+0.002s) -619.409: Matched entry: 619.4 Firebomb (-0.009s) -623.711: Matched entry: 623.7 Winds Of Tartarus (-0.011s) -640.166: Matched entry: 640.2 Shrapnel Shell (+0.034s) -644.715: Matched entry: 644.7 Firebomb (-0.015s) -649.012: Matched entry: 649.0 Winds Of Tartarus (-0.012s) -650.179: Matched entry: 650.2 Magitek Missiles (+0.021s) - Jumping to 610.000 -615.137: Matched entry: 615.1 Drill Shot (-0.037s) -619.413: Matched entry: 619.4 Firebomb (-0.013s) -623.709: Matched entry: 623.7 Winds Of Tartarus (-0.009s) -640.170: Matched entry: 640.2 Shrapnel Shell (+0.030s) -644.712: Matched entry: 644.7 Firebomb (-0.012s) -649.013: Matched entry: 649.0 Winds Of Tartarus (-0.013s) -650.177: Matched entry: 650.2 Magitek Missiles (+0.023s) - Jumping to 610.000 -615.135: Matched entry: 615.1 Drill Shot (-0.035s) -619.418: Matched entry: 619.4 Firebomb (-0.018s) -623.709: Matched entry: 623.7 Winds Of Tartarus (-0.009s) -639.166: Matched entry: 640.2 Shrapnel Shell (+1.034s) -644.713: Matched entry: 644.7 Firebomb (-0.013s) -649.014: Matched entry: 649.0 Winds Of Tartarus (-0.014s) -650.175: Matched entry: 650.2 Magitek Missiles (+0.025s) - Jumping to 610.000 -615.131: Matched entry: 615.1 Drill Shot (-0.031s) -619.413: Matched entry: 619.4 Firebomb (-0.013s) -623.709: Matched entry: 623.7 Winds Of Tartarus (-0.009s) -``` - -由于此时间轴是针对网络日志生成的,因此大多数时间都非常准确。 - -您应该格外关注 `Missed sync` 所在的行。 - -下面给出的三个示例实际上并没有什么影响,因为未匹配的内容出现在某一战斗阶段的结尾处,时间轴已经在循环结束之后跳到了下一个阶段。(欢迎提供相关补丁以弄清楚如何对这种跳转不发出警告。) - -```text -48.695: Matched entry: 199.0 --sync-- (+150.305s) - Missed sync: Gate Of Tartarus at 52.2 (last seen at 24.411) - -233.846: Matched entry: 400.0 Gate Of Tartarus (+166.154s) - Missed sync: Shield Skewer at 234.6 (last seen at 217.623) - Missed sync: Shrapnel Shell at 238.9 (last seen at 204.273) - -431.400: Matched entry: 595.0 --sync-- (+163.600s) - Missed sync: Winds Of Tartarus at 435.9 (last seen at 417.884) - Missed sync: Firebomb at 440.4 (last seen at 422.366) - Missed sync: Shield Skewer at 445.0 (last seen at 426.97499999999997) - Missed sync: Shrapnel Shell at 449.5 (last seen at 413.195) -``` - -下面这个示例则确实属于时间轴出现了问题: - -```text -620.783: Matched entry: 610.0 Magitek Missiles (-10.783s) - Missed sync: Shrapnel Shell at 600.0 (last seen at 610.739) - Missed sync: Firebomb at 604.5 (last seen at 615.247) - Missed sync: Winds Of Tartarus at 608.8 (last seen at 619.564) -``` - -看起来我们似乎把boss喊话时间放在了错误的位置。所幸我们在 `Magitek Missile` 这个技能上设置了较长的同步窗口期,使得时间轴文件在这个技能处重新同步 (运气真好),但是在这个技能之前的那些内容存在错误。 - -原来的时间轴是这样的: - -```bash -595.0 "--sync--" sync /00:0044:[^:]*:Your defeat will bring/ window 600,0 -600.0 "Shrapnel Shell" sync /:Rhitahtyn sas Arvina:474:/ -``` - -然而,实际上 Shrapnel Shell 技能使用的时间比时间轴设定的时间点迟了10.7秒,测试结果显示它出现在了610.739处。解决办法是将boss喊话的文本同步时间往前偏移这一差值。我们获得的新的喊话同步点为 595-10.7=584.3。 - -```bash -584.3 "--sync--" sync /00:0044:[^:]*:Your defeat will bring/ window 600,0 -600.0 "Shrapnel Shell" sync /:Rhitahtyn sas Arvina:474:/ -``` - -让我们重新运行一次测试脚本 (下面的结果已省略大部分无关的输出) - -```bash -$ python util/test_timeline.py -f CapeWestwind.log -s 18:42:23.614 -e 18:49:22. -934 -t cape_westwind - -431.400: Matched entry: 584.3 --sync-- (+152.900s) - Missed sync: Winds Of Tartarus at 435.9 (last seen at 417.884) - Missed sync: Firebomb at 440.4 (last seen at 422.366) - Missed sync: Shield Skewer at 445.0 (last seen at 426.97499999999997) - Missed sync: Shrapnel Shell at 449.5 (last seen at 413.195) -600.039: Matched entry: 600.0 Shrapnel Shell (-0.039s) -604.508: Matched entry: 604.5 Firebomb (-0.008s) -608.817: Matched entry: 608.8 Winds Of Tartarus (-0.017s) -610.019: Matched entry: 610.0 Magitek Missiles (-0.019s) -``` - -这样的结果才是我们想要的。前面这些关于丢失同步的报错内容都是由于阶段跳转,而 `Shrapnel Shell` 技能现在出现在了正确的位置。 - -### 测试其他时间轴 - -为了保证时间轴的正确运行,您应该尽可能多的进行实战测试。 下面给出了针对 **CapeWestwind2.log** 的测试结果: - -执行 `python util/test_timeline.py -f CapeWestwind2.log -s 13:21:00.688 -e 13:29:36.976 -t cape_westwind`,可以发现至少2个问题。 - -第一个问题属于小毛病,测试结果显示时间轴文件给出的boss技能时间点与实际有偏差: - -```text -447.329: Matched entry: 445.0 Shield Skewer (-2.329s) -443.789: Matched entry: 445.0 Shield Skewer (+1.211s) -444.792: Matched entry: 445.0 Shield Skewer (+0.208s) -447.361: Matched entry: 445.0 Shield Skewer (-2.361s) -``` - -`Shield Skewer` 这一技能在P3阶段的不同时间点均会出现。然而,这技能前后的技能时间轴看上去都没什么问题。一般来说,如果出现了这种个别频繁使用的技能时间不匹配的情况,最好的办法就是确保该技能附近的其他技能拥有一个较长的同步窗口期,以整个时间轴文件不会因为这些偏差导致延迟。 - -第二个问题显然更为严重,测试结果显示时间轴文件同步过程中丢失了一个 `Shield Skewer`: - -```text -403.454: Matched entry: 403.5 Shield Skewer (+0.046s) -407.876: Matched entry: 413.2 Shrapnel Shell (+5.324s) - Missed sync: Shield Skewer at 408.7 (last seen at 403.454) -417.748: Matched entry: 417.9 Winds Of Tartarus (+0.152s) -``` - -在我们的多次测试结果中有一次时间轴成功同步了2次 `Shield Skewer`,而另外一次只同步了1次。`Shrapnel Shell` 在这里丢失1次同步是一个很糟糕的情况。 - -该如何处理这一问题就因人而异了。下面给出了一些参考建议: - -* 获取更多的战斗数据,然后针对更为通用的情况调整时间轴文件。 -* 在时间轴这一部分留下注释。 -* 如果丢失同步的技能是一个非常重要的技能 (例如死刑),请在丢失技能的时间轴行上打上问号,以便玩家能够明白这部分时间轴并不能保证准确。 + diff --git a/docs/zh-TW/CactbotCustomization.md b/docs/zh-TW/CactbotCustomization.md index a88d71d373..c775a5d9b8 100644 --- a/docs/zh-TW/CactbotCustomization.md +++ b/docs/zh-TW/CactbotCustomization.md @@ -1,317 +1,5 @@ -# Cactbot自定義教學 +# Permanently Moved to OverlayPlugin/cactbot -🌎 [[English](../CactbotCustomization.md)] [[简体中文](../zh-CN/CactbotCustomization.md)] [**繁體中文**] [[한국어](../ko-KR/CactbotCustomization.md)] +See: [docs/zh-TW/CactbotCustomization.md](https://github.com/OverlayPlugin/cactbot/blob/main/docs/zh-TW/CactbotCustomization.md) -- [使用cactbot使用者介面](#使用cactbot使用者介面) -- [透過cactbot使用者介面改變觸發器文本](#透過cactbot使用者介面改變觸發器文本) -- [使用者資料夾概況](#使用者資料夾概況) -- [設置您自己的使用者資料夾](#設置您自己的使用者資料夾) -- [樣式自定義](#樣式自定義) -- [Raidboss觸發器自定義](#raidboss觸發器自定義) - - [例1:改變輸出文本](#例1改變輸出文本) - - [例2:使挑釁提示適用於全職業](#例2使挑釁提示適用於全職業) - - [例3:添加自定義觸發器](#例3添加自定義觸發器) -- [Raidboss時間軸自定義](#raidboss時間軸自定義) -- [行為自定義](#行為自定義) -- [使用者檔案的除錯](#使用者檔案的除錯) - - [檢查OverlayPlugin的錯誤日誌](#檢查OverlayPlugin的錯誤日誌) - - [檢查檔案是否載入](#檢查檔案是否載入) - - [檢查檔案是否有錯誤](#檢查檔案是否有錯誤) - -## 使用cactbot使用者介面 - -自定義cactbot時,推薦透過cactbot的使用者介面進行操作。 該介面處於 ACT -> Plugins -> OverlayPlugin.dll -> Cactbot。 - -它可以提供如下功能: - -- 設置觸發器輸出TTS -- 禁用觸發器 -- 改變觸發器輸出 -- 改變cactbot語言 -- 音量設置 -- 隱藏起司圖標 - -您可能無法透過cactbot使用者介面以配置所有您想要的更改。 但是它是最容易的方法,適合作為您定制化的第一步。 以後此介面會添加更多的選項。 - -此處的選項會存儲於 `%APPDATA%\Advanced Combat Tracker\Config\RainbowMage.OverlayPlugin.config.json` 檔案中。 但您並不需要也不應當直接修改該檔案。 - -## 透過cactbot使用者介面改變觸發器文本 - -在位於ACT-> 插件> OverlayPlugin.dll-> Cactbot-> Raidboss的cactbot使用者介面中, 羅列著所有的觸發器。 這裡的列表讓您可以更改每個觸發器支持外部更改的配置設置。 - -名稱旁邊帶有鈴鐺(🔔) 的設置項的觸發器輸出文本是可以被覆蓋的。 舉個例子,假設有一個🔔onTarget欄位,其文字為 `死刑點${player}`。 當某人接到死刑技能時,這個字符串將出現在熒幕上(或通過tts播報)。 `${player}` 是一個將由觸發器動態設置的引數。 任何類似於 `${param}` 的字符串都是動態引數。 - -比如,您可以將這個文字更改為 `${player} 即將死亡!`。 或者,也許您不關心誰是目標,那麼您可以將其改為 `死刑` 以使文字更加簡短。 如果您想撤消自己的更改,只需清空文字框即可。 - -但這個方式有一定的限制。 例如,您無法更改邏輯。 而且在大多數情況下,您無法使 `tts` 的播報與 `alarmText` 不同。 您無法新增更多的引數。 如果您想要對觸發器做出更加複雜的覆蓋操作, 那麼您需要檢視 [Raidboss觸發器自定義](#overriding-raidboss-triggers) 小節。 - -## 使用者資料夾概覽 - -若cactbot使用者介面不存在您所需的選項,您可能需要考慮以使用者檔案覆蓋的方式進行自定義。 您需要編寫JavaScript程式碼和CSS樣式,這意味著您可能需要掌握一點點程式設計知識。 - -Cactbot的設計哲學要求任何使用者的自定義配置應當存放於使用者資料夾的檔案中。 同時這也能防止您所做的更改在今後cactbot的更新中被覆蓋失效。 另外,目前您無法透過直接修改cactbot的檔案應用您的更改,除非您瞭解如何構建您自己的專案。 - -所有的cactbot模組都會從 [user/](../../user/) 資料夾載入使用者設定。 `raidboss` 模組會載入 `user/raidboss.js` 與 `user/raidboss.css`,以及所有`user/raidboss/` 目錄下及其子目錄下的 `.js` 和 `.css` 檔案。 (時間軸`.txt`檔案必須與引用它們的`.js`檔案放在同一個資料夾中。) 這些使用者自定義檔案將在cactbot自身載入完畢後被載入,並可以覆蓋對應的模組的設定。 - -`oopsyraidsy` 模組會載入 `user/oopsyraidsy.js` 與 `user/oopsyraidsy.css`。 依此類推,每個模組都支援以此方式(以檔名)載入對應自定義檔案。 - -cactbot將按照字母順序優先載入user資料夾中的子資料夾裡的檔案,其次載入子資料夾外的檔案。 這就是為什麼`user/raidboss.js`檔案總是最後被載入並可以覆蓋`user/raidboss/`資料夾中任何檔案中的配置。 例如,`user/alphascape/some_file.js` 先載入, `user/mystatic/some_file.js` 再載入,最後是`user/raidboss.js` 載入。 `.css` 檔案遵循同樣的順序。 - -在本文件中,“使用者自定義js檔案”指代以上兩者。 除了載入順序以外,`user/raidboss.js` 和 `user/raidboss/some_file.js` 沒有區別。 同樣地,“使用者自定義css檔案”同時指代 `user/radar.css` 和 `user/radar/some_file.css` 二者。 使用者資料夾中分出子目錄是為了讓分享觸發器和自定義配置更容易。 - -當開發者模式開啟時,你可以從[除錯資訊](#檢查檔案是否載入)中得到更多關於載入順序的資訊。 - -`user/` 資料夾中包含了一部分示例配置檔案,您可以對其重新命名並直接使用。 如 [user/raidboss-example.js](../../user/raidboss-example.js) 檔案 可被重新命名為 `user/raidboss.js`,對其所做的更改可應用於 `raidboss` 模組。 - -在修改了這些檔案之後,單擊ACT中OverlayPlugin外掛對應懸浮窗設定中的“重載懸浮窗”按鈕,即可應用更改。 - -## 設定您自己的使用者資料夾 - -您可以透過cactbot配置介面設定使用者資料夾: ACT -> Plugins -> OverlayPlugin.dll -> Cactbot -> cactbot使用者資料夾。 單擊 `選擇資料夾` 按鈕,選擇磁碟上的一個資料夾。 - -如果沒有選擇,cactbot將自動選擇其安裝目錄下的預設資料夾。 - -建議您選擇cactbot安裝目錄下的 `cactbot/user` 資料夾。 該資料夾通常為位於 `%APPDATA%\Advanced Combat Tracker\Plugins\cactbot-version\cactbot\user`。 有部分示例配置檔案位於 [此資料夾](../../user) 下。 - -## 樣式自定義 - -使用者自定義css檔案可以對UI模組的位置、尺寸、顏色等進行自定義。 可用的選擇器可以透過閱覽 `ui//.css` 檔案找到。 - -例如您在 [ui/raidboss/raidboss.css](../../ui/raidboss/raidboss.css) 中,可發現諸如 `#popup-text-container` 與 `#timeline-container` 等選擇器, 則您可以在 `user/raidboss.css` 中對其位置進行自定義。 您可以在 `user/raidboss.css` 中或其他 `user/raidboss/` 下的 `.css` 中新增更多的樣式。 - -同樣地,您可以在 `.info-text` 類中新增新的CSS規則,對資訊文字的尺寸和顏色進行自定義。 例如: - -```css -.info-text { - font-size: 200%; - color: rgb(50, 100, 50); -} -``` - -簡單地說,您可以認為cactbot會將使用者檔案中的CSS規則新增至內建CSS檔案的末尾。 也就是說,您需要注意 [CSS優先順序規則](https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity), 例如新增 `!important` 讓您的規則可以強制覆蓋。 另一方面,您可能需要重置某些屬性為預設的 `auto` 值。 - -我們推薦使用 [Chrome DevTools](https://developers.google.com/web/tools/chrome-devtools) 以除錯CSS問題。 您可以透過 ACT -> Plugins -> OverlayPlugin.dll -> 您的懸浮窗名字 -> 啟動Debug工具 以開啟DevTools。 - -**注意**:某些元件的自定義較為困難,甚至無法進行自定義,如時間軸的進度條等。 原因是,這些元件屬於自定義HTML元素,且沒有開放外部配置的介面。 如果您有特別的需求,但是不知道如何修改,您可以提出一個 [github issue](https://github.com/quisquous/cactbot/issues/new/choose)。 - -**警告**:cactbot不保證CSS的向後相容性。 在以後的更改中,cactbot可能會重新組織網頁結構,改變元素名稱和類名稱,甚至完全重構所有樣式。 因此,您需知曉您的自定義CSS有在將來出現問題的風險。 - -## Raidboss觸發器自定義 - -您可以透過使用者自定義js檔案(例如 `user/raidboss.js` 或 `user/raidboss/` 目錄下的任意`.js` 檔案)自定義觸發器行為。 您可以修改輸出文字、適用職業、文字顯示的時間等等。 - -在您的raidboss模組使用者自定義js檔案中, `Options.Triggers` 是一個存放了觸發器集合的列表。 您可以透過此變數新增新觸發器,或修改已有的觸發器。 若使用者檔案中存在與現有觸發器 (cactbot官方提供的) 相同id的觸發器,則會將後者其覆蓋。 - -在您修改觸發器前,我們推薦閱讀 [觸發器指南](RaidbossGuide.md) 以瞭解各觸發器的諸多屬性的含義。 - -一般來說,你需要將形如以下的程式碼塊加入到你的使用者自定義js檔案(例如 `user/raidboss.js`)中: - -```javascript -Options.Triggers.push({ - // 在檔案開頭定義ZoneId, - // 例如 ZoneId.MatchAll (指定所有區域) 或 ZoneId.TheBozjanSouthernFront 等 - zoneId: ZoneId.PutTheZoneFromTheTopOfTheFileHere, - triggers: [ - { - // 這裡定義的是觸發器(trigger)物件。 - // 例如 id, netRegex或infoText等 - }, - ], -}); -``` - -最簡單的定制觸發器方式是直接複製上面那一大塊程式碼粘貼到此檔案再進行修改。 您可以修改 `zoneId` 一行為您想要觸發器響應的區域id,這一行通常位於cactbot觸發器檔案的頂部。 [該檔案](../resources/zone_id.ts) 列出了所有可用的區域id。 若您定義了錯誤的id,OverlayPlugin的日誌視窗將會輸出警告資訊。 然後複製觸發器文本並粘貼至此, 按您的喜好進行修改。 當你改完所有你想改的觸發器後, 重載raidboss懸浮窗以應用更改。 - -**注意**:此方式會將原觸發器完全移除,因此請在修改時不要刪除任何邏輯。 此外,觸發器均採用JavaScript編寫,因此必須採用標準JavaScript語法。 若您不是程式設計師,您需要格外注意編輯方法。 - -### 例1:改變輸出文字 - -假定您正在攻略巴哈姆特絕境戰(UCOB), 您的固定隊採用的不是cactbot默認的火1集合吃的打法, 而是先單吃火1。 另外,您 *同時* 還想讓觸發器通過tts播報與文本不同的內容。 比如,您總是忘記出人群,因此您想讓它重復播報數次。 - -若您只是想修改 `資訊文字`,你可以 [透過cactbot配置介面改變觸發器文字](#changing-trigger-text-with-the-cactbot-ui) 實現。 - -其中一種調整方式是編輯觸發器的輸出。 您可以在 [ui/raidboss/data/04-sb/ultimate/unending_coil_ultimate.js](https://github.com/quisquous/cactbot/blob/triggers/04-sb/ultimate/unending_coil_ultimate.js#:~:text=UCU%20Nael%20Fireball%201) 中找到原本的 fireball #1 觸發器。 - -您需要將以下的程式碼貼上至您的使用者自定義js檔案底部。 - -```javascript -Options.Triggers.push({ - zoneId: ZoneId.TheUnendingCoilOfBahamutUltimate, - triggers: [ - { - id: 'UCU Nael Fireball 1', - netRegex: NetRegexes.ability({ source: 'Ragnarok', id: '26B8', capture: false }), - delaySeconds: 35, - suppressSeconds: 99999, - // infoText 是綠色的文字。 - infoText: { - en: 'Fire OUT', - }, - tts: { - en: 'out out out out out', - }, - run: function(data) { - data.naelFireballCount = 1; - }, - }, - ], -}); -``` - -此處還刪除了英語以外的語言。 - -### 例2:使挑釁提示適用於全職業 - -目前,只有團隊成員的挑釁會觸發提示,並且不是所有職業都能收到提示。 該例子展示了如何使其適用於所有職業。 該挑釁觸發器可以在 [ui/raidboss/data/00-misc/general.js](https://github.com/quisquous/cactbot/blob/triggers/00-misc/general.js#:~:text=General%20Provoke) 中找到。 - -我們需要修改 `condition` 函式(function)。 由於此處的id與內建的 `General Provoke` 觸發器一致,因此會覆蓋同名的內建觸發器。 - -您需要將以下的程式碼貼上至您的使用者自定義js檔案底部。 - -```javascript -Options.Triggers.push({ - zoneId: ZoneId.MatchAll, - triggers: [ - { - id: 'General Provoke', - netRegex: NetRegexes.ability({ id: '1D6D' }), - condition: function(data, matches) { - // 我希望看到所有的挑釁提示,即便他們不在我的隊伍中, - // 即便我不是坦克。 - return true; - }, - infoText: (data, matches, output) => { - return output.text!({ player: data.party.member(matches.source) }); - }, - outputStrings: { - text: { - en: 'Provoke: ${player}', - de: 'Herausforderung: ${player}', - fr: 'Provocation: ${player}', - ja: '挑発: ${player}', - cn: '挑衅: ${player}', - ko: '도발: ${player}', - }, - }, - }, - ], -}); -``` - -當然,您也可以直接刪除整個 `condition` 函式, 這是因為沒有condition的觸發器在匹配到正則時永遠會執行。 - -### 例3:添加自定義觸發器 - -您也可以用同樣的辦法新增您的自定義觸發器。 - -這是一個示例觸發器,當您中了“Forked Lightning”效果時,會在1秒後顯示“Get out!!!”。 - -```javascript -Options.Triggers.push([ - { - zoneId: ZoneId.MatchAll, - triggers: [ - { - // 這是一個自定義的id,因此不會覆蓋任何現有的觸發器。 - id: 'Personal Forked Lightning', - regex: Regexes.gainsEffect({ effect: 'Forked Lightning' }), - condition: (data, matches) => { return matches.target === data.me; }, - delaySeconds: 1, - alertText: 'Get out!!!', - }, - - // 您的其他觸發器…… - ], - }, - - // 其他區域的觸發器集合…… -]); -``` - -我們推薦閱讀 [觸發器指南](RaidbossGuide.md) 以瞭解如何撰寫cactbot的觸發器, 當然您也可以直接看 [ui/raidboss/data](../ui/raidboss/data) 中現有的觸發器程式碼。 - -## Raidboss時間軸自定義 - -自定義時間軸與 [自定義觸發器](#overriding-raidboss-triggers) 差不多。 - -自定義時間軸的步驟如下: - -1) 複製原有的時間軸文字檔案內容至您的使用者資料夾 - - 例如,您可以複製 - [ui/raidboss/data/05-shb/ultimate/the_epic_of_alexander.txt](../ui/raidboss/data/05-shb/ultimate/the_epic_of_alexander.txt) - 至 `user/the_epic_of_alexander.txt`。 - -1) 在 user/raidboss.js 中新增程式碼 - - 如同我們新增觸發器一樣,您依舊需要定義 `zoneId`、 `overrideTimelineFile: true`, - 以及定義文字檔名稱的`timelineFile` 屬性。 - - ```javascript - Options.Triggers.push({ - zoneId: ZoneId.TheEpicOfAlexanderUltimate, - overrideTimelineFile: true, - timelineFile: 'the_epic_of_alexander.txt', - }); - ``` - - (假設您已經做完了第一步,並且該文本檔案的名稱為 `user/the_epic_of_alexander.txt` ) - - 設置 `overrideTimelineFile: true` 是為了告訴cactbot將內置的時間軸完全替換為您添加的檔案。 - -1) 按您的喜好編輯您自己的時間軸檔案 - - 閱讀 [時間軸指南](TimelineGuide.md) 學習更多關於時間軸的知識。 - -**注意**:編輯時間軸檔案有一定的風險, 這是因為部分觸發器依賴於時間軸的特定文字。 例如在絕亞歷山大中,`Fluid Swing` 與 `Propeller Wind` 都有對應的時間軸觸發器。 如果這些文字被替換或移除,時間軸觸發器也同樣會失效。 - -## 行為自定義 - -這一文段將討論cactbot的其他方式。 Cactbot中有一些不在使用者介面顯示,也不是觸發器的變量。 - -每個cactbot模組都有一個名為 `Options` 的變數,它包含了若干控制選項。 可用的 `Options` 變數會在每個 `ui//.js` 檔案的頂部列出。 - -例如在 [ui/raidboss/raidboss.js](../ui/raidboss/raidboss.js) 檔案中, 您可以透過 `PlayerNicks` 選項定義玩家的暱稱。 - -```javascript -Options.PlayerNicks = { - // 'Firstname Lastname': 'Nickname', - 'Banana Nana', 'Nana', - 'The Great\'one', 'Joe', // => 這裡需要一個反斜槓轉義單引號 - 'Viewing Cutscene': 'Cut', - // 等等 -}; -``` - -**警告**:使用者資料夾中的檔案會靜默覆蓋cactbot使用者介面的同名選項。 該行為可能會造成一些困惑,因此您應當直接通過使用者介面設置這些變量, 僅當使用者介面不提供設置方法時採用此方式覆蓋預設行為。 - -## 使用者檔案的除錯 - -### 檢查OverlayPlugin的錯誤日誌 - -您可以在 ACT -> Plugins -> OverlayPlugin.dll 找到位於該視窗的底部的OverlayPlugin日誌視窗,它是一個自動滾動的文字視窗。 - -當執行錯誤時,錯誤資訊會顯示在此處。 - -### 檢查檔案是否載入 - -首先,您需要開啟raidboss模組的除錯模式。 開啟cactbot配置視窗,啟用 `顯示開發者選項` ,然後重新載入懸浮窗。 然後,勾選raidboss模組下的 `啟用除錯模式`,再次重載懸浮窗。 - -當raidboss模組的除錯模式啟用時,OverlayPlugin的日誌視窗中會列印更多資訊。 每次本地的使用者檔案載入時都會輸出類似於這樣的資訊: `[10/19/2020 6:18:27 PM] Info: raidbossy: BrowserConsole: local user file: C:\Users\tinipoutini\cactbot\user\raidboss.js` - -確認您的使用者檔案是否正常載入。 - -檔名的列印順序就是它們的載入順序。 - -### 檢查檔案是否有錯誤 - -使用者檔案採用JavaScript編寫,若程式碼語法本身有錯誤,日誌視窗會輸出錯誤,您的使用者檔案也會被跳過而不會被載入。 在檔案載入時檢查OverlayPlugin的錯誤日誌。 - -此處有一個例子: - -```log -[10/19/2020 6:18:27 PM] Info: raidbossy: BrowserConsole: local user file: C:\Users\tinipoutini\cactbot\user\raidboss.js (Source: file:///C:/Users/tinipoutini/cactbot/resources/user_config.ts, Line: 83) -[10/19/2020 6:18:27 PM] Info: raidbossy: BrowserConsole: *** ERROR IN USER FILE *** (Source: file:///C:/Users/tinipoutini/cactbot/resources/user_config.ts, Line: 95) -[10/19/2020 6:18:27 PM] Info: raidbossy: BrowserConsole: SyntaxError: Unexpected token : - at loadUser (file:///C:/Users/tinipoutini/cactbot/resources/user_config.ts:92:28) (Source: file:///C:/Users/tinipoutini/cactbot/resources/user_config.ts, Line: 96) -``` + diff --git a/ui/raidboss/emulator/Readme.md b/ui/raidboss/emulator/Readme.md index 5dbb364269..230c9dd3b7 100644 --- a/ui/raidboss/emulator/Readme.md +++ b/ui/raidboss/emulator/Readme.md @@ -1,47 +1,5 @@ -# Raid Emulator +# Permanently Moved to OverlayPlugin/cactbot -The raid emulator consists of five core components: +See: [ui/raidboss/emulator/Readme.md](https://github.com/OverlayPlugin/cactbot/blob/main/ui/raidboss/emulator/Readme.md) -* `NetworkLogConverter` - `emulator/data/NetworkLogConverter.js` - * Parses and converts `Network__.log` files -* `Encounter` - `emulator/data/Encounter.js` - * Accepts an array of lines and info, pulls some basic information about an encounter -* `Persistor` - `emulator/data/Persistor.js` - * Persists encounters in `IndexedDB` across sessions -* `AnalyzedEncounter` - `emulator/data/AnalyzedEncounter.js` - * Analyzes an encounter - * Tracks combatant stats over the duration of the encounter - * Provides perspectives of each player in the encounter, including what triggers were fired -* `RaidEmulator` - `emulator/data/RaidEmulator.js` - * Handles playback of an encounter - * Dispatches events to UI elements - -There are two general data flows: - -* Drop network file on window or otherwise upload it - * File is read into a string buffer in `raidemulator.js` - * File contents are passed to `NetworkLogConverter` - * `NetworkLogConverter` splits the string into lines - * Those lines are then parsed out for information via `emulator/data/network_log_converter/ParseLine.js` - * Lines are sorted based on timestamp and then index, to account for chat lines (`00`) being out of order from network lines - * Converted log lines are passed into `emulator/data/LogEventHandler.js` - * `LogEventHandler` splits the log lines into individual encounters, pulling zone info along the way - * Encounters are passed to `Persistor` -* Encounter is loaded from IndexedDB - * `Encounter` ID is passed to `RaidEmulator` to be loaded - * `AnalyzedEncounter` is built from encounter if this is the first time the encounter has been loaded since refresh - * For each player in the encounter - * `AnalyzedEncounter` bootstraps `PopupText`, `TimelineController`, and `Timeline` classes - * Encounter lines are passed to bootstrapped classes to build list of triggers for each player's perspective - * `AnalyzedEncounter` is loaded and UI elements are updated - * First player in party object is set as current perspective - * `RaidEmulator` seeks to start of combat in encounter - -## Other Information - -* `raidemulator.js` acts as glue to hold the various pieces together and coordinate them -* `emulator/EventBus.js` is an event bus implementation that objects can extend to inherit `on` and `dispatch` event handlers, so that other objects can listen for events -* `emulator/EmulatorCommon.js` is a static object that has helper methods and global regexes -* `emulator/ui` contains UI elements created specifically for the emulator -* `emulator/overrides` contains overriding classes from the base raidboss implementation -* `emulator/data/network_log_converter` contains the parsers for each individual line event + diff --git a/ui/raidboss/skins/dorgrin/Readme.md b/ui/raidboss/skins/dorgrin/Readme.md index 6e98f1faee..6baefb3498 100644 --- a/ui/raidboss/skins/dorgrin/Readme.md +++ b/ui/raidboss/skins/dorgrin/Readme.md @@ -1,29 +1,5 @@ -# Dorgrin's Raidboss Customisations +# Permanently Moved to OverlayPlugin/cactbot -Some customisations I mate to make raidboss a bit nicer on my eyes. Changes include colours, font face, bar and alert sizes. +See: [ui/raidboss/skins/dorgrin/Readme.md](https://github.com/OverlayPlugin/cactbot/blob/main/ui/raidboss/skins/dorgrin/Readme.md) -Warnings are larger and have a translucent background to make them more easily noticable. Timelines are taller, wider, and brighter. - -A custom font is used, which is optional. See below for installation instructions and source. - -## Screenshots - -### Alerts/Warnings - -These come in purple, orange and red. - -#### with default Font - -![Critical Warning](https://imgur.com/BOqm6cY) -![Medium Warning](https://imgur.com/ZE8VI04) -![Standard Warning](https://imgur.com/m3YAip4) - -#### with Peace Sans Font - -![Critical Warning](https://imgur.com/EbuHsMV) -![Medium Warning](https://imgur.com/70qDlv3) -![Standard Warning](https://imgur.com/khnZb3Z) - -### Timelines - -![Timelines featuring an active ability](https://imgur.com/XScpnXi) + diff --git a/util/README.md b/util/README.md index 766ae66f2d..1e8a5cd1a3 100644 --- a/util/README.md +++ b/util/README.md @@ -1,17 +1,5 @@ -# How to run python utils +# Permanently Moved to OverlayPlugin/cactbot -Download and install [python](https://www.python.org/). +See: [util/README.md](https://github.com/OverlayPlugin/cactbot/blob/main/util/README.md) -Download [SaintCoinach.Cmd-master-\*-\*.zip](https://github.com/ufx/SaintCoinach/releases) and extract on C Drive root or D Drive root. - -Saint coinach is only accepted in 2 directories by default, 'C:\\SaintCoinach\\' and 'D:\\SaintCoinach\\' -These MUST include the one of those must include the SaintCoinach.Cmd.exe (with all other needed files for it to run). -If you use a different path, you can add it to the coinach.py _DEFAULT_COINACH_PATHS variable - -## Troubleshooting with SaintCoinach - -### SaintCoinach FFXIV client version mismatch - -When you run SaintCoinach manually, does it shows you need to update? This means that definitions are not updated to the latest patch. For minor patches, SaintCoinach does not need to update definitions, so you need to do is just change the version data to latest version. - -In the SainCoinach dir open the \Definitions\game.ver file and change the version number to latest version which showed when you launch SaintCoinach manually. +