Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,6 @@ env/
venv/
npm-package/
.eslintcache

ui/raidboss/data/99-custom/*
!ui/raidboss/data/99-custom/readme.txt
22 changes: 22 additions & 0 deletions docs/CactbotCustomization.md
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,28 @@ 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).

### Trigger Set Override

When you define a trigger set in your user files with the same `id` as a built-in trigger set, the entire built-in trigger set will be completely overridden.

For example:

```javascript
Options.Triggers.push({
id: 'TheUnendingCoilOfBahamutUltimate', // Same ID as the built-in trigger set
zoneId: ZoneId.TheUnendingCoilOfBahamutUltimate,
triggers: [
// Your custom triggers
{
id: 'My Custom Trigger',
// ... trigger content
},
],
});
```

In this example, because the `id` matches the built-in Unending Coil of Bahamut (Ultimate) trigger set, none of the built-in triggers will execute, only your custom triggers will run. You need to re-implement all necessary trigger logic in your custom file.

## Overriding Raidboss Timelines

Some customization of timelines can be done via the [cactbot config UI](#using-the-cactbot-ui).
Expand Down
22 changes: 22 additions & 0 deletions docs/ko-KR/CactbotCustomization.md
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,28 @@ cactbot 트리거 작성하는 방법을 더 자세히 배우려면
[트리거 가이드](../RaidbossGuide.md)와
[ui/raidboss/data](../../ui/raidboss/data)에 이미 존재하는 트리거를 읽어보세요.

### 트리거 세트 덮어쓰기

사용자 파일에서 내장 트리거 세트와 동일한 `id`를 가진 트리거 세트를 정의하면, 전체 내장 트리거 세트가 완전히 덮어쓰여집니다.

예시:

```javascript
Options.Triggers.push({
id: 'TheUnendingCoilOfBahamutUltimate', // 내장 트리거 세트 ID와 동일
zoneId: ZoneId.TheUnendingCoilOfBahamutUltimate,
triggers: [
// 사용자 정의 트리거
{
id: 'My Custom Trigger',
// ... 트리거 내용
},
],
});
```

이 예시에서 `id`가 내장된 바하무트 절경전 트리거 세트와 동일하기 때문에, 내장 트리거는 실행되지 않고 사용자 정의 트리거만 실행됩니다. 사용자 정의 파일에서 필요한 모든 트리거 로직을 다시 구현해야 합니다.

## Raidboss 타임라인 덮어쓰기

Raidboss 타임라인을 덮어쓰는 것은 [Raidboss 트리거 덮어쓰기](#raidboss-트리거-덮어쓰기)와 비슷합니다.
Expand Down
22 changes: 22 additions & 0 deletions docs/zh-CN/CactbotCustomization.md
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,28 @@ Options.Triggers.push([

我们推荐阅读 [触发器指南](RaidbossGuide.md) 以了解如何撰写 Cactbot 的触发器,当然你也可以直接看 [ui/raidboss/data](../../ui/raidboss/data) 中现有的触发器代码。

### 触发器集合覆盖

当你在用户文件中定义了与内置触发器集合相同 `id` 的触发器集合时,整个内置触发器集合将被完全覆盖。

例如:

```javascript
Options.Triggers.push({
id: 'TheUnendingCoilOfBahamutUltimate', // 与内置的触发器集合ID相同
zoneId: ZoneId.TheUnendingCoilOfBahamutUltimate,
triggers: [
// 你的自定义触发器
{
id: 'My Custom Trigger',
// ... 触发器内容
},
],
});
```

在这个例子中,由于 `id` 与内置的巴哈姆特绝境战触发器集合相同,内置的所有触发器都不会执行,只会执行你自定义的触发器。你需要在自定义文件中重新实现所有需要的触发器逻辑。

## Raidboss 时间轴自定义

一些自定义操作可以通过 [Cactbot 配置界面](#使用-cactbot-配置界面) 实现。你可以在这个界面隐藏或重命名现有的时间轴条目,也可以添加自定义时间轴条目等。
Expand Down
22 changes: 22 additions & 0 deletions docs/zh-TW/CactbotCustomization.md
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,28 @@ Options.Triggers.push([

我們推薦閱讀 [觸發器指南](RaidbossGuide.md) 以瞭解如何撰寫cactbot的觸發器, 當然您也可以直接看 [ui/raidboss/data](../ui/raidboss/data) 中現有的觸發器程式碼。

### 觸發器集合覆蓋

當您在使用者檔案中定義了與內建觸發器集合相同 `id` 的觸發器集合時,整個內建觸發器集合將被完全覆蓋。

例如:

```javascript
Options.Triggers.push({
id: 'TheUnendingCoilOfBahamutUltimate', // 與內建的觸發器集合ID相同
zoneId: ZoneId.TheUnendingCoilOfBahamutUltimate,
triggers: [
// 您的自定義觸發器
{
id: 'My Custom Trigger',
// ... 觸發器內容
},
],
});
```

在這個例子中,由於 `id` 與內建的巴哈姆特絕境戰觸發器集合相同,內建的所有觸發器都不會執行,只會執行您自定義的觸發器。您需要在自定義檔案中重新實現所有需要的觸發器邏輯。

## Raidboss時間軸自定義

自定義時間軸與 [自定義觸發器](#raidboss觸發器自定義) 差不多。
Expand Down
2 changes: 1 addition & 1 deletion test/test_data_files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const oopsyFiles: string[] = [];
const processInputs = (inputPath: string[]) => {
inputPath.forEach((path: string) => {
walkDirSync(path, (filepath) => {
if (/\/(?:raidboss|oopsy)_manifest.txt/.test(filepath)) {
if (/\/(?:raidboss|oopsy)_manifest.txt/.test(filepath) || /\/99-custom\//.test(filepath)) {
return;
}
if (/\/raidboss\/data\/.*\.txt/.test(filepath)) {
Expand Down
1 change: 1 addition & 0 deletions types/trigger.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@ export type LooseTriggerSet =
timelineTriggers?: LooseTimelineTrigger[];
filename?: string;
isUserTriggerSet?: boolean;
overriddenByFile?: string;
};

export interface RaidbossFileData {
Expand Down
10 changes: 10 additions & 0 deletions ui/config/config.css
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,16 @@ html {
margin-right: 20px;
}

.trigger-set-override-warning {
padding: 20px;
text-align: center;
color: #856404;
background-color: #fff3cd;
border: 1px solid #ffc107;
border-radius: 4px;
margin: 10px;
}

.timeline-edit-container {
margin: 12px 0 12px 12px;
grid-column-end: span 2;
Expand Down
1 change: 1 addition & 0 deletions ui/raidboss/data/99-custom/readme.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This directory is for local, developer-specific TypeScript trigger sets.
27 changes: 15 additions & 12 deletions ui/raidboss/popup-text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -703,21 +703,24 @@ export class PopupText {
// User triggers must come last so that they override built-in files.
this.triggerSets.push(...this.options.Triggers);

// Eliminate any trigger sets with duplicate ids and record a lookup by id.
this.triggerSets = this.triggerSets.filter((triggerSet) => {
if (triggerSet.id === undefined)
return true;
if (this.triggerSetsById[triggerSet.id] !== undefined) {
// Eliminate any trigger sets with duplicate ids, allowing later ones to override earlier ones.
const uniqueById = new Map<string, typeof this.triggerSets[number]>();
const noIdList: typeof this.triggerSets = [];
for (const triggerSet of this.triggerSets) {
if (triggerSet.id === undefined) {
noIdList.push(triggerSet);
continue;
}
const existing = uniqueById.get(triggerSet.id);
if (existing !== undefined) {
console.log(
`${
triggerSet.filename ?? '???'
} has duplicate triggerSet id ${triggerSet.id}, ignoring triggers`,
`Overriding trigger set id '${triggerSet.id}' from '${existing.filename}' with '${triggerSet.filename}'`,
);
return false;
}
this.triggerSetsById[triggerSet.id] = triggerSet;
return true;
});
uniqueById.set(triggerSet.id, triggerSet);
}
this.triggerSets = [...noIdList, ...uniqueById.values()];
this.triggerSetsById = Object.fromEntries(uniqueById);
}

OnChangeZone(e: EventResponses['ChangeZone']): void {
Expand Down
29 changes: 29 additions & 0 deletions ui/raidboss/raidboss_config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -745,6 +745,18 @@ class RaidbossConfigurator {
triggerOptions.classList.add('trigger-file-options');
triggerContainer.appendChild(triggerOptions);

// If this trigger set is overridden, show warning instead of triggers
if (info.triggerSet.overriddenByFile !== undefined) {
const warningDiv = document.createElement('div');
warningDiv.classList.add('trigger-set-override-warning');
const baseText = this.base.translate(kMiscTranslations.overriddenByFile);
const detailText = baseText.replace('${file}', info.triggerSet.overriddenByFile);
const warningText = this.base.translate(kMiscTranslations.warning);
warningDiv.innerHTML = `<strong>${warningText}</strong>: ${detailText}`;
triggerOptions.appendChild(warningDiv);
continue;
}

for (const [trigId, trig] of Object.entries(info.triggers ?? {})) {
// Don't construct triggers that won't show anything.
let hasOutputFunc = false;
Expand Down Expand Up @@ -1528,6 +1540,12 @@ class RaidbossConfigurator {
// id so that the ui can disable overriding information.
const previousTriggerWithId: { [id: string]: ConfigLooseTrigger } = {};

// While walking through trigger sets, record any previous trigger sets with the same
// id so that the ui can show overriding information.
const previousTriggerSetWithId: {
[id: string]: { triggerSet: ConfigLooseTriggerSet; filename?: string };
} = {};

for (const item of Object.values(map)) {
// TODO: maybe each trigger set needs a zone name, and we should
// use that instead of the filename???
Expand All @@ -1544,6 +1562,17 @@ class RaidbossConfigurator {
if (!triggerSet.isUserTriggerSet && triggerSet.filename !== undefined)
flattenTimeline(triggerSet, triggerSet.filename, timelineFiles);

// Track if this trigger set overrides any previous trigger set with the same id.
if (triggerSet.id !== undefined) {
const previous = previousTriggerSetWithId[triggerSet.id];
if (previous)
previous.triggerSet.overriddenByFile = triggerSet.filename;
previousTriggerSetWithId[triggerSet.id] = {
triggerSet: triggerSet,
filename: triggerSet.filename,
};
}

item.triggers = {};
for (const [key, triggerArr] of Object.entries(rawTriggers)) {
for (const baseTrig of triggerArr) {
Expand Down