Skip to content

Commit

Permalink
feat: Add action+trigger descriptions & deprecation messages
Browse files Browse the repository at this point in the history
  • Loading branch information
Alorel committed Jan 25, 2023
1 parent 87f205e commit bb58617
Show file tree
Hide file tree
Showing 18 changed files with 93 additions and 63 deletions.
1 change: 1 addition & 0 deletions src/actions/combat/activate-prayer-action.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ defineLocalAction<Props>({
return <Fragment>Deactivate prayers</Fragment>;
}
},
description: 'Activates/deactivates prayers',
execute({prayers}) {
const player = game.combat.player;
player.disableActivePrayers();
Expand Down
3 changes: 3 additions & 0 deletions src/actions/combat/activate-spell-actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ const mkAction = ({media, registry, ...init}: Init): void => {
};

mkAction({
description: 'Cast a spell from the standard spellbook',
id: ActionId.CombatCastSpellStd,
label: 'Cast Standard spell',
media: 'combat/spellbook.svg',
Expand All @@ -73,6 +74,7 @@ mkAction({
});

mkAction({
description: 'Cast a spell from the ancient spellbook',
id: ActionId.CombatCastSpellAncient,
label: 'Cast Ancient spell',
media: 'combat/ancient.svg',
Expand All @@ -82,6 +84,7 @@ mkAction({
});

mkAction({
description: 'Cast a spell from the archaic spellbook',
id: ActionId.CombatCastSpellArchaic,
label: 'Cast Archaic spell',
media: 'magic/archaic_book.svg',
Expand Down
1 change: 1 addition & 0 deletions src/actions/core/buy-item-action.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ defineLocalAction<Props>({
<RenderNodeMedia label={item.name} media={item.media}/>
</Fragment>
),
description: 'Buy an item from the shop',
execute({item, qty}) {
if (!passesLimitCount(item)) {
return;
Expand Down
2 changes: 2 additions & 0 deletions src/actions/core/delay-action.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ defineLocalAction<Props>({
<span class={'text-primary'}>{`${duration.toLocaleString()}ms`}</span>
</Fragment>
),
deprecated: 'Deprecated and will be removed soon™. It was a hacky workaround to begin with.',
description: 'Delay the execution of subsequent actions & triggers by n milliseconds',
execute: ({duration}) => timer(duration),
id: ActionId.CoreDelay,
label: 'Wait',
Expand Down
1 change: 1 addition & 0 deletions src/actions/core/equip-food-action.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ defineLocalAction<Data>({
<RenderNodeMedia label={item.name} media={item.media}/>
</Fragment>
),
description: 'Equip food to the first free slot',
execute({item, qty}) {
const bankQty = game.bank.getQty(item);
if (!bankQty) {
Expand Down
1 change: 1 addition & 0 deletions src/actions/core/equip-item-action.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ defineLocalAction<Props>({
)}
</Fragment>
),
description: 'Equip an item. You can set a slot & the equip amount when applicable.',
execute({item, qty, slot}) {
const bankQty = game.bank.getQty(item);
if (!bankQty) {
Expand Down
1 change: 1 addition & 0 deletions src/actions/core/load-agi-blueprint.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ interface Props {

defineLocalAction<Props>({
category: InternalCategory.CORE,
description: 'Loads an agility blueprint. Make sure you have enough resources & currency!',
execute({idx}) {
const agi = game.agility;
const blueprint = agi.blueprints.get(idx);
Expand Down
1 change: 1 addition & 0 deletions src/actions/core/sell-item-action.mts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ interface Props {

defineLocalAction<Props>({
category: InternalCategory.CORE,
description: 'This will sell the whole stack',
execute({items}) {
for (const item of items) {
const qty = game.bank.getQty(item);
Expand Down
2 changes: 2 additions & 0 deletions src/actions/workflow/exec-workflow-action.mts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ const base = {

defineLocalAction<Props>({
...base,
description: 'Run another workflow as an action!',
execute({name, stop}, ctx) {
const workflow = WorkflowRegistry.inst.workflows
.find(wf => wf.name === name);
Expand All @@ -76,6 +77,7 @@ defineLocalAction<Props>({

defineLocalAction<Props>({
...base,
description: 'Similar to "Run workflow", but runs one of the workflows defined at the bottom of the page.',
execute({name, stop}, ctx) {
const workflow: Workflow | undefined = (ctx!.workflow as WorkflowRefImpl)
.getEmbeddedWorkflow(name)
Expand Down
1 change: 1 addition & 0 deletions src/actions/workflow/set-step-idx-action.mts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ interface Props {

defineLocalAction<Props>({
category: InternalCategory.WORKFLOW,
description: 'Forcibly make the workflow jump to another step to repeat the whole (or part of the) workflow. Note that the step cannot jump to itself.',
execContext: true,
execute({idx}, ctx) {
ctx!.setActiveStepIdx(idx);
Expand Down
10 changes: 9 additions & 1 deletion src/lib/public-api-validation.mts
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,17 @@ function isNodeDefinition(v: any): v is (NodeDefinition & Obj<any>) {
return false;
}

const {compactRender, media, options} = v as Partial<NodeDefinition>;
const {
compactRender,
deprecated,
description,
media,
options,
} = v as Partial<NodeDefinition>;

return typeof media === 'string'
&& typeIs(deprecated, 'undefined', 'boolean', 'string')
&& isUndefinedOr(description, 'string')
&& isUndefinedOr(compactRender, 'function')
&& (options === undefined || (Array.isArray(options) && options.every(isNodeOption)));
}
Expand Down
6 changes: 6 additions & 0 deletions src/public_api/core.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ export interface NodeDefinition<T extends object = {}> extends Referenceable {
*/
compactRender?: (props: T) => VNode | null | undefined;

/** Flag this node for deprecation. Optionally include a deprecation message */
deprecated?: true | string;

/** Node description */
description?: string;

/** The icon */
media: string;

Expand Down
1 change: 1 addition & 0 deletions src/triggers/combat/enemy-attack-type-trigger.mts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ interface Data {
const triggerCtx = defineLocalTrigger<Data>({
category: InternalCategory.COMBAT,
check: d => game.combat.isActive && game.combat.enemy.attackType === d.type,
description: 'Fires when the monster has the given attack type',
id: TriggerId.CombatEnemyAtkType,
init() {
ctx.patch(CombatManager, 'spawnEnemy').after(() => {
Expand Down
1 change: 1 addition & 0 deletions src/triggers/combat/enemy-special-attack-trigger.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const triggerCtx = defineLocalTrigger<Data>({
<span>{atk?.name}</span>
</Fragment>
),
description: 'Fires when the monster is casting this special attack',
id: TriggerId.CombatSpecialAttack,
init() {
ctx.patch(Enemy, 'queueNextAction').after(() => {
Expand Down
81 changes: 34 additions & 47 deletions src/ui/components/trigger-config.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import type {VNode} from 'preact';
import {Fragment} from 'preact';
import {memo} from 'preact/compat';
import {useCallback} from 'preact/hooks';
import type {TriggerDefinitionContext} from '../../lib/data/trigger-definition-context.mjs';
import type {Obj} from '../../public_api';
import Td from './td';
import {TriggerSelect} from './workflow-editor/categorised-node-select/categorised-node-select-impl';
import RenderNodeOption from './workflow-editor/render-node-option/render-node-option';

Expand Down Expand Up @@ -33,56 +32,44 @@ const TriggerConfig = memo<Props>(
const {opts, trigger} = value;
const triggerId = trigger?.id;

return (
<div class={'table-responsive'}>
<table class={'table table-sm font-size-sm'}>
<tbody>
<Trigger value={trigger} onChange={onTriggerInput}/>
const options = trigger?.def.options;

{trigger?.def.options?.map(opt => {
const show = opt.showIf?.(opts);
return (
<Fragment>
<TriggerSelect value={trigger} onChange={onTriggerInput}/>
{options && (
<div className={'table-responsive'}>
<table className={'table table-sm font-size-sm'}>
<tbody>
{options.map(opt => {
const show = opt.showIf?.(opts);

return show !== false && (
<RenderNodeOption
key={`${opt.id}@${triggerId}`}
onChange={newVal => {
const newOpts = {
...opts,
[opt.id]: newVal,
};
opt.resets?.forEach(prop => {
delete newOpts[prop];
});
return show !== false && (
<RenderNodeOption
key={`${opt.id}@${triggerId}`}
onChange={newVal => {
const newOpts = {
...opts,
[opt.id]: newVal,
};
opt.resets?.forEach(prop => {
delete newOpts[prop];
});

onChange({...value, opts: newOpts});
}}
option={opt}
otherValues={opts}
value={opts[opt.id]}/>
);
})}
</tbody>
</table>
</div>
onChange({...value, opts: newOpts});
}}
option={opt}
otherValues={opts}
value={opts[opt.id]}/>
);
})}
</tbody>
</table>
</div>
)}
</Fragment>
);
}
);

export default TriggerConfig;

interface TriggerProps {
value: TriggerConfigValue['trigger'] | undefined;

onChange(val: TriggerConfigValue['trigger']): void;
}

const Trigger = memo<TriggerProps>(function Trigger({value, onChange}): VNode {
return (
<tr>
<Td class={'font-w600'}>{'Trigger'}</Td>
<Td>
<TriggerSelect value={value} onChange={onChange}/>
</Td>
</tr>
);
});
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import type {ComponentChildren, VNode} from 'preact';
import {useCallback} from 'preact/compat';
import {memo, useCallback} from 'preact/compat';
import type {NamespacedDefinition} from '../../../../lib/namespaced-definition.mjs';
import type {StdRegistryKey} from '../../../../lib/registries/registries.mjs';
import type {CategorisedObject} from '../../../../lib/util/categorise-registry-objects.mjs';
import {errorLog} from '../../../../lib/util/log.mjs';
import type {Referenceable} from '../../../../public_api';
import useTippy from '../../../hooks/tippy.mjs';
import {mkClass} from '../../../util/mk-class.mjs';
import Btn from '../../btn';
import {BinSvg} from '../../svg';

type Def = NamespacedDefinition<Referenceable>;
type Def = NamespacedDefinition<CategorisedNode>;

interface Props<T> {
children?: ComponentChildren;
Expand All @@ -26,6 +28,12 @@ interface Props<T> {

export {Props as CategorisedNodeSelectProps};

export interface CategorisedNode extends Referenceable {
deprecated?: true | string;

description?: string;
}

/** Common select for actions & triggers */
export default function CategorisedNodeSelect<T extends Def>({
children,
Expand All @@ -35,22 +43,36 @@ export default function CategorisedNodeSelect<T extends Def>({
value,
values,
}: Props<T>): VNode {

const selectedId = value?.id;
const clear = useClear(value, onChange);
const onSelectChange = useOnSelectChange(selectedId, onChange, registry, values);

const {description, deprecated} = value?.def ?? {} as Partial<CategorisedNode>;

return (
<div class={'input-group'}>
<div className={'input-group'}>
{children}
{clearable && <Btn size={'sm'} kind={'default'} onClick={clear}><BinSvg/></Btn>}
<select class={'form-control form-control-sm'} value={selectedId} onChange={onSelectChange}>
<select className={'form-control form-control-sm'} value={selectedId} onChange={onSelectChange}>
{values.map(optGroupMapper)}
</select>
{description && <InfoBubble text={description}/>}
{deprecated && (
<InfoBubble clazz={'text-warning'}
text={deprecated === true ? 'This feature is deprecated and will be removed soon' : deprecated}/>
)}
</div>
);
}

const InfoBubble = memo<{clazz?: string; text: string;}>(
function InfoBubble({clazz, text}) {
const ref = useTippy(text);

return <i class={mkClass('fa fa-info-circle align-self-center ml-1', clazz)} ref={ref}/>;
}
);

function useClear<T>(value: T, onChange: () => void): () => void {
const isClearable = value != null;

Expand Down
3 changes: 1 addition & 2 deletions src/ui/pages/help-page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type {VNode} from 'preact';
import PageContainer from '../components/page-container';
import autoId from '../util/id-gen.mjs';
import {EmbeddedWorkflowsHelp, LoopsHelp, OfflineHelp, SettingUpHelp} from './help-page/help-pages';
import {LoopsHelp, OfflineHelp, SettingUpHelp} from './help-page/help-pages';

export const HELP_PAGE_ID = autoId();

Expand All @@ -12,7 +12,6 @@ export default function HelpPage(): VNode {
<SettingUpHelp/>
<OfflineHelp/>
<LoopsHelp/>
<EmbeddedWorkflowsHelp/>
</div>
</PageContainer>
);
Expand Down
8 changes: 0 additions & 8 deletions src/ui/pages/help-page/help-pages.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,3 @@ export function OfflineHelp(): VNode {
</HelpPageSection>
);
}

export function EmbeddedWorkflowsHelp(): VNode {
return (
<HelpPageSection heading={'How do embedded workflows work?'}>
{'They\'re for use with the "Run embedded workflow" action - they\'re essentially the same as normal workflows, but are only usable within the workflow they\'re defined in'}
</HelpPageSection>
);
}

0 comments on commit bb58617

Please sign in to comment.