From 412ba8066b45a7456e8bf13f7e3510891a6a5446 Mon Sep 17 00:00:00 2001 From: Nikolaj Andresen Date: Wed, 31 Jul 2024 22:04:56 +0200 Subject: [PATCH 01/11] Set up conditionally adding Starfinder-specific actions only when the SF2e playtest module is active --- src/scripts/hooks/init.ts | 8 ++++++++ static/lang/action-en.json | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/src/scripts/hooks/init.ts b/src/scripts/hooks/init.ts index e15c9751525..db4e4b1ebfc 100644 --- a/src/scripts/hooks/init.ts +++ b/src/scripts/hooks/init.ts @@ -18,6 +18,7 @@ import { SetGamePF2e } from "@scripts/set-game-pf2e.ts"; import { registerSettings } from "@system/settings/index.ts"; import { htmlQueryAll } from "@util"; import * as R from "remeda"; +import { Action } from "@actor/actions/index.ts"; export const Init = { listen: (): void => { @@ -167,6 +168,13 @@ export const Init = { } game.pf2e.StatusEffects.initialize(); + + if (game.modules.get("starfinder-field-test-for-pf2e")?.active) { + const actions: Action[] = [ + // SF2E-specific actions + ]; + actions.forEach((action) => game.pf2e.actions.set(action.slug, action)); + } }); }, }; diff --git a/static/lang/action-en.json b/static/lang/action-en.json index 9ee189676cc..098b40f7fdf 100644 --- a/static/lang/action-en.json +++ b/static/lang/action-en.json @@ -1031,5 +1031,9 @@ "Nonexistent": "Specified variant '{variant}' does not exist for action '{action}'" } } + }, + "SF2E": { + "Actions": { + } } } From 703276f220d8d4e18b6a5b8b628a23d6d7400960 Mon Sep 17 00:00:00 2001 From: Nikolaj Andresen Date: Wed, 31 Jul 2024 22:08:25 +0200 Subject: [PATCH 02/11] Add access infosphere action implementation --- .../computers/access-infosphere.ts | 20 +++++++++++++++++++ src/scripts/hooks/init.ts | 7 ++----- static/lang/action-en.json | 9 +++++++++ 3 files changed, 31 insertions(+), 5 deletions(-) create mode 100644 src/module/system/action-macros/computers/access-infosphere.ts diff --git a/src/module/system/action-macros/computers/access-infosphere.ts b/src/module/system/action-macros/computers/access-infosphere.ts new file mode 100644 index 00000000000..2a182d6ec27 --- /dev/null +++ b/src/module/system/action-macros/computers/access-infosphere.ts @@ -0,0 +1,20 @@ +import { SingleCheckAction } from "@actor/actions/index.ts"; + +const PREFIX = "SF2E.Actions.AccessInfosphere"; + +const accessInfosphere = new SingleCheckAction({ + description: `${PREFIX}.Description`, + name: `${PREFIX}.Title`, + notes: [ + { outcome: ["criticalSuccess"], text: `${PREFIX}.Notes.criticalSuccess` }, + { outcome: ["success"], text: `${PREFIX}.Notes.success` }, + { outcome: ["criticalFailure"], text: `${PREFIX}.Notes.criticalFailure` }, + ], + rollOptions: ["action:access-infosphere"], + section: "skill", + slug: "access-infosphere", + statistic: "computers", + traits: ["concentrate", "exploration", "secret"], +}); + +export { accessInfosphere }; diff --git a/src/scripts/hooks/init.ts b/src/scripts/hooks/init.ts index db4e4b1ebfc..676b57f159a 100644 --- a/src/scripts/hooks/init.ts +++ b/src/scripts/hooks/init.ts @@ -18,7 +18,7 @@ import { SetGamePF2e } from "@scripts/set-game-pf2e.ts"; import { registerSettings } from "@system/settings/index.ts"; import { htmlQueryAll } from "@util"; import * as R from "remeda"; -import { Action } from "@actor/actions/index.ts"; +import { accessInfosphere } from "@system/action-macros/computers/access-infosphere.ts"; export const Init = { listen: (): void => { @@ -170,10 +170,7 @@ export const Init = { game.pf2e.StatusEffects.initialize(); if (game.modules.get("starfinder-field-test-for-pf2e")?.active) { - const actions: Action[] = [ - // SF2E-specific actions - ]; - actions.forEach((action) => game.pf2e.actions.set(action.slug, action)); + [accessInfosphere].forEach((action) => game.pf2e.actions.set(action.slug, action)); } }); }, diff --git a/static/lang/action-en.json b/static/lang/action-en.json index 098b40f7fdf..e9f38e3015c 100644 --- a/static/lang/action-en.json +++ b/static/lang/action-en.json @@ -1034,6 +1034,15 @@ }, "SF2E": { "Actions": { + "AccessInfosphere": { + "Description": "

You attempt to access a local network, known as an infosphere, to come up with information on a topic. This typically takes 10 minutes of research to find information, with results like Recalling Knowledge on a topic. Larger investigations or topics could require several hours, while single-word answers of information common to that settlement's infosphere may only take a minute, based on the GM's discretion. Significant and longer investigations should use the research subsystem. The GM determines the DC based on the topic you're researching. This can be further influenced by the quality of the local infosphere, what information it might possess on the topic, and how well organized it is.

Critical Success You find the information you were searching for. If this was used on a topic you could have used Recall Knowledge on, you instead find this information in 1 minute.
Success You find the information you were looking for.
Critical Failure You find misleading information thanks to incorrect data or online pranksters. The GM provides you false information on your topic of investigation (or decides to give you no information, as on a failure).

", + "Notes": { + "criticalFailure": "Critical Failure You find misleading information thanks to incorrect data or online pranksters. The GM provides you false information on your topic of investigation (or decides to give you no information, as on a failure).", + "criticalSuccess": "Critical Success You find the information you were searching for. If this was used on a topic you could have used Recall Knowledge on, you instead find this information in 1 minute.", + "success": "Success You find the information you were looking for." + }, + "Title": "Access Infosphere" + } } } } From c01ae74074fa41cd64acb435113c36d310c07cca Mon Sep 17 00:00:00 2001 From: Nikolaj Andresen Date: Wed, 31 Jul 2024 22:09:45 +0200 Subject: [PATCH 03/11] Add drive action implementation --- .../system/action-macros/piloting/drive.ts | 54 +++++++++++++++++++ src/scripts/hooks/init.ts | 3 +- static/lang/action-en.json | 29 ++++++++++ 3 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 src/module/system/action-macros/piloting/drive.ts diff --git a/src/module/system/action-macros/piloting/drive.ts b/src/module/system/action-macros/piloting/drive.ts new file mode 100644 index 00000000000..676dea78a65 --- /dev/null +++ b/src/module/system/action-macros/piloting/drive.ts @@ -0,0 +1,54 @@ +import { SingleCheckAction } from "@actor/actions/index.ts"; + +const PREFIX = "SF2E.Actions.Drive"; + +const drive = new SingleCheckAction({ + description: `${PREFIX}.Description`, + name: `${PREFIX}.Title`, + rollOptions: ["action:drive"], + section: "skill", + slug: "drive", + statistic: "piloting", + traits: ["move"], + variants: [ + { + cost: 1, + description: `${PREFIX}.Drive1.Description`, + name: `${PREFIX}.Drive1.Title`, + notes: [ + { outcome: ["criticalSuccess", "success"], text: `${PREFIX}.Drive1.Notes.success` }, + { outcome: ["failure"], text: `${PREFIX}.Drive1.Notes.failure` }, + { outcome: ["criticalFailure"], text: `${PREFIX}.Drive1.Notes.criticalFailure` }, + ], + rollOptions: ["action:drive", "action:drive:drive1"], + slug: "drive1", + }, + { + cost: 2, + description: `${PREFIX}.Drive2.Description`, + name: `${PREFIX}.Drive2.Title`, + notes: [ + { outcome: ["criticalSuccess", "success"], text: `${PREFIX}.Drive2.Notes.success` }, + { outcome: ["criticalFailure", "failure"], text: `${PREFIX}.Drive2.Notes.failure` }, + ], + rollOptions: ["action:drive", "action:drive:drive2"], + slug: "drive2", + traits: ["move", "reckless"], + }, + { + cost: 3, + description: `${PREFIX}.Drive3.Description`, + modifiers: [{ label: `${PREFIX}.Drive3.Title`, modifier: -5 }], + name: `${PREFIX}.Drive3.Title`, + notes: [ + { outcome: ["criticalSuccess", "success"], text: `${PREFIX}.Drive3.Notes.success` }, + { outcome: ["criticalFailure", "failure"], text: `${PREFIX}.Drive3.Notes.failure` }, + ], + rollOptions: ["action:drive", "action:drive:drive3"], + slug: "drive3", + traits: ["move", "reckless"], + }, + ], +}); + +export { drive }; diff --git a/src/scripts/hooks/init.ts b/src/scripts/hooks/init.ts index 676b57f159a..1c1cdb771c4 100644 --- a/src/scripts/hooks/init.ts +++ b/src/scripts/hooks/init.ts @@ -19,6 +19,7 @@ import { registerSettings } from "@system/settings/index.ts"; import { htmlQueryAll } from "@util"; import * as R from "remeda"; import { accessInfosphere } from "@system/action-macros/computers/access-infosphere.ts"; +import { drive } from "@system/action-macros/piloting/drive.ts"; export const Init = { listen: (): void => { @@ -170,7 +171,7 @@ export const Init = { game.pf2e.StatusEffects.initialize(); if (game.modules.get("starfinder-field-test-for-pf2e")?.active) { - [accessInfosphere].forEach((action) => game.pf2e.actions.set(action.slug, action)); + [accessInfosphere, drive].forEach((action) => game.pf2e.actions.set(action.slug, action)); } }); }, diff --git a/static/lang/action-en.json b/static/lang/action-en.json index e9f38e3015c..dd41ada5789 100644 --- a/static/lang/action-en.json +++ b/static/lang/action-en.json @@ -1042,6 +1042,35 @@ "success": "Success You find the information you were looking for." }, "Title": "Access Infosphere" + }, + "Drive": { + "Description": "

You pilot your vehicle to move. Decide how many actions you intend to spend before you begin Driving. The effects depend on the number of actions you spend. You can't Drive through spaces occupied by creatures, even if they are allies.

1 Attempt a Piloting check. On a success, the vehicle moves up to its Speed and can turn normally. On a failure, the vehicle moves its Speed in a straight line. On a critical failure, the vehicle moves its Speed in a straight line and becomes uncontrolled.
2 (reckless) The vehicle moves up to twice its Speed in a straight line at the vehicle's current heading.
3 (reckless) You take a -5 penalty on your piloting check to maintain control of the vehicle. The vehicle moves up to three times its Speed in a straight line at the vehicle's current heading.

", + "Drive1": { + "Description": "

Attempt a Piloting check. On a success, the vehicle moves up to its Speed and can turn normally. On a failure, the vehicle moves its Speed in a straight line. On a critical failure, the vehicle moves its Speed in a straight line and becomes uncontrolled.

", + "Notes": { + "criticalFailure": "Critical Failure The vehicle moves its Speed in a straight line and becomes uncontrolled.", + "failure": "Failure The vehicle moves its Speed in a straight line.", + "success": "Success The vehicle moves up to its Speed and can turn normally." + }, + "Title": "Drive1" + }, + "Drive2": { + "Description": "

(reckless) The vehicle moves up to twice its Speed in a straight line at the vehicle's current heading.

", + "Notes": { + "failure": "Failure The vehicle moves its Speed in a straight line along its most recent heading, drifting up to 45 degrees at the GM's discretion, and becomes uncontrolled.", + "success": "Success The vehicle moves up to twice its Speed in a straight line at the vehicle's current heading." + }, + "Title": "Drive2" + }, + "Drive3": { + "Description": "

(reckless) You take a -5 penalty on your piloting check to maintain control of the vehicle. The vehicle moves up to three times its Speed in a straight line at the vehicle's current heading.

", + "Notes": { + "failure": "Failure The vehicle moves its Speed in a straight line along its most recent heading, drifting up to 45 degrees at the GM's discretion, and becomes uncontrolled.", + "success": "Success The vehicle moves up to three times its Speed in a straight line at the vehicle's current heading." + }, + "Title": "Drive3" + }, + "Title": "Drive" } } } From 2e08cc150c411862de7915106763355f142b25d8 Mon Sep 17 00:00:00 2001 From: Nikolaj Andresen Date: Wed, 31 Jul 2024 22:15:22 +0200 Subject: [PATCH 04/11] Add hack action implementation --- .../system/action-macros/computers/hack.ts | 21 +++++++++++++++++++ src/scripts/hooks/init.ts | 3 ++- static/lang/action-en.json | 10 +++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 src/module/system/action-macros/computers/hack.ts diff --git a/src/module/system/action-macros/computers/hack.ts b/src/module/system/action-macros/computers/hack.ts new file mode 100644 index 00000000000..495ac4d988c --- /dev/null +++ b/src/module/system/action-macros/computers/hack.ts @@ -0,0 +1,21 @@ +import { SingleCheckAction } from "@actor/actions/index.ts"; + +const PREFIX = "SF2E.Actions.Hack"; + +const hack = new SingleCheckAction({ + description: `${PREFIX}.Description`, + name: `${PREFIX}.Title`, + notes: [ + { outcome: ["criticalSuccess"], text: `${PREFIX}.Notes.criticalSuccess` }, + { outcome: ["success"], text: `${PREFIX}.Notes.success` }, + { outcome: ["failure"], text: `${PREFIX}.Notes.failure` }, + { outcome: ["criticalFailure"], text: `${PREFIX}.Notes.criticalFailure` }, + ], + rollOptions: ["action:hack"], + section: "skill", + slug: "hack", + statistic: "computers", + traits: ["exploration"], +}); + +export { hack }; diff --git a/src/scripts/hooks/init.ts b/src/scripts/hooks/init.ts index 1c1cdb771c4..bd80de7044a 100644 --- a/src/scripts/hooks/init.ts +++ b/src/scripts/hooks/init.ts @@ -20,6 +20,7 @@ import { htmlQueryAll } from "@util"; import * as R from "remeda"; import { accessInfosphere } from "@system/action-macros/computers/access-infosphere.ts"; import { drive } from "@system/action-macros/piloting/drive.ts"; +import { hack } from "@system/action-macros/computers/hack.ts"; export const Init = { listen: (): void => { @@ -171,7 +172,7 @@ export const Init = { game.pf2e.StatusEffects.initialize(); if (game.modules.get("starfinder-field-test-for-pf2e")?.active) { - [accessInfosphere, drive].forEach((action) => game.pf2e.actions.set(action.slug, action)); + [accessInfosphere, drive, hack].forEach((action) => game.pf2e.actions.set(action.slug, action)); } }); }, diff --git a/static/lang/action-en.json b/static/lang/action-en.json index dd41ada5789..fb21b570046 100644 --- a/static/lang/action-en.json +++ b/static/lang/action-en.json @@ -1071,6 +1071,16 @@ "Title": "Drive3" }, "Title": "Drive" + }, + "Hack": { + "Description": "

You try to access, control, or make changes to an active, secured system. In most cases, this is going to be an attempt made against a local computer system that you access directly or indirectly through the use of a hacking toolkit. Attempt a Computers check to determine if you can access the targeted system. Computers with improved systems might require multiple successes to access.

Critical Success You access the system, or you achieve two successes toward accessing a system requiring more than one success. You leave no trace of your tampering.
Success You access the system, or you achieve one success toward accessing a system that requires more than one success.
Failure You fail to access the system or achieve any success toward accessing a system that requires more than one success. Some computerized systems might have security measures that activate on a failure.
Critical Failure You activate specially ingrained security protocols or completely lock yourself out of the system. Being locked out of a system prevents you from attempting another check to access it for the next hour.

", + "Notes": { + "criticalFailure": "Critical Failure You activate specially ingrained security protocols or completely lock yourself out of the system. Being locked out of a system prevents you from attempting another check to access it for the next hour.", + "criticalSuccess": "Critical Success You access the system, or you achieve two successes toward accessing a system requiring more than one success. You leave no trace of your tampering.", + "failure": "Failure You fail to access the system or achieve any success toward accessing a system that requires more than one success. Some computerized systems might have security measures that activate on a failure.", + "success": "Success You access the system, or you achieve one success toward accessing a system that requires more than one success." + }, + "Title": "Hack" } } } From cfde6dd1b3a61149b6d1c568f34da602abfb1d49 Mon Sep 17 00:00:00 2001 From: Nikolaj Andresen Date: Wed, 31 Jul 2024 22:17:54 +0200 Subject: [PATCH 05/11] Add navigate action implementation --- .../system/action-macros/piloting/navigate.ts | 19 +++++++++++++++++++ src/scripts/hooks/init.ts | 5 ++++- static/lang/action-en.json | 8 ++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 src/module/system/action-macros/piloting/navigate.ts diff --git a/src/module/system/action-macros/piloting/navigate.ts b/src/module/system/action-macros/piloting/navigate.ts new file mode 100644 index 00000000000..220e653ef0d --- /dev/null +++ b/src/module/system/action-macros/piloting/navigate.ts @@ -0,0 +1,19 @@ +import { SingleCheckAction } from "@actor/actions/index.ts"; + +const PREFIX = "SF2E.Actions.Navigate"; + +const navigate = new SingleCheckAction({ + description: `${PREFIX}.Description`, + name: `${PREFIX}.Title`, + notes: [ + { outcome: ["criticalSuccess"], text: `${PREFIX}.Notes.criticalSuccess` }, + { outcome: ["success"], text: `${PREFIX}.Notes.success` }, + ], + rollOptions: ["action:navigate"], + section: "skill", + slug: "navigate", + statistic: "piloting", + traits: ["exploration", "secret"], +}); + +export { navigate }; diff --git a/src/scripts/hooks/init.ts b/src/scripts/hooks/init.ts index bd80de7044a..83435c3f9f7 100644 --- a/src/scripts/hooks/init.ts +++ b/src/scripts/hooks/init.ts @@ -21,6 +21,7 @@ import * as R from "remeda"; import { accessInfosphere } from "@system/action-macros/computers/access-infosphere.ts"; import { drive } from "@system/action-macros/piloting/drive.ts"; import { hack } from "@system/action-macros/computers/hack.ts"; +import { navigate } from "@system/action-macros/piloting/navigate.ts"; export const Init = { listen: (): void => { @@ -172,7 +173,9 @@ export const Init = { game.pf2e.StatusEffects.initialize(); if (game.modules.get("starfinder-field-test-for-pf2e")?.active) { - [accessInfosphere, drive, hack].forEach((action) => game.pf2e.actions.set(action.slug, action)); + [accessInfosphere, drive, hack, navigate].forEach((action) => + game.pf2e.actions.set(action.slug, action), + ); } }); }, diff --git a/static/lang/action-en.json b/static/lang/action-en.json index fb21b570046..3b7d00942cb 100644 --- a/static/lang/action-en.json +++ b/static/lang/action-en.json @@ -1081,6 +1081,14 @@ "success": "Success You access the system, or you achieve one success toward accessing a system that requires more than one success." }, "Title": "Hack" + }, + "Navigate": { + "Description": "

You plan a short journey. While this is most often used when traveling with a mech, vehicle, or starship, the same skill can be used to determine the optimal route to take while traveling on foot. This lets you plan a general heading on a planetoid or guide your starship on an in-system voyage. If you are navigating a short journey, you typically attempt a Piloting check only once per day, but some environments or changes might necessitate rolling more often. The GM determines the DC and how long this activity takes (usually just a minute or so). More unusual locales or those you're unfamiliar with might require you to have a minimum proficiency rank to Navigate.

Critical Success You get an excellent sense of where you are. If you are in an environment with cardinal directions, you know them exactly.
Success You gain enough orientation to avoid becoming hopelessly lost. If you are in an environment with cardinal directions, you have a sense of those directions.

", + "Notes": { + "criticalSuccess": "Critical Success You get an excellent sense of where you are. If you are in an environment with cardinal directions, you know them exactly.", + "success": "Success You gain enough orientation to avoid becoming hopelessly lost. If you are in an environment with cardinal directions, you have a sense of those directions." + }, + "Title": "Navigate" } } } From decc2c86df5c0e2eef7a522db0ebfc59b0e93173 Mon Sep 17 00:00:00 2001 From: Nikolaj Andresen Date: Wed, 31 Jul 2024 22:18:58 +0200 Subject: [PATCH 06/11] Add plot course action implementation --- .../action-macros/piloting/plot-course.ts | 21 +++++++++++++++++++ src/scripts/hooks/init.ts | 3 ++- static/lang/action-en.json | 10 +++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 src/module/system/action-macros/piloting/plot-course.ts diff --git a/src/module/system/action-macros/piloting/plot-course.ts b/src/module/system/action-macros/piloting/plot-course.ts new file mode 100644 index 00000000000..2e2476de1a4 --- /dev/null +++ b/src/module/system/action-macros/piloting/plot-course.ts @@ -0,0 +1,21 @@ +import { SingleCheckAction } from "@actor/actions/index.ts"; + +const PREFIX = "SF2E.Actions.PlotCourse"; + +const plotCourse = new SingleCheckAction({ + description: `${PREFIX}.Description`, + name: `${PREFIX}.Title`, + notes: [ + { outcome: ["criticalSuccess"], text: `${PREFIX}.Notes.criticalSuccess` }, + { outcome: ["success"], text: `${PREFIX}.Notes.success` }, + { outcome: ["failure"], text: `${PREFIX}.Notes.failure` }, + { outcome: ["criticalFailure"], text: `${PREFIX}.Notes.criticalFailure` }, + ], + rollOptions: ["action:plot-course"], + section: "skill", + slug: "plot-course", + statistic: "piloting", + traits: ["exploration", "secret"], +}); + +export { plotCourse }; diff --git a/src/scripts/hooks/init.ts b/src/scripts/hooks/init.ts index 83435c3f9f7..4c636e8f68c 100644 --- a/src/scripts/hooks/init.ts +++ b/src/scripts/hooks/init.ts @@ -22,6 +22,7 @@ import { accessInfosphere } from "@system/action-macros/computers/access-infosph import { drive } from "@system/action-macros/piloting/drive.ts"; import { hack } from "@system/action-macros/computers/hack.ts"; import { navigate } from "@system/action-macros/piloting/navigate.ts"; +import { plotCourse } from "@system/action-macros/piloting/plot-course.ts"; export const Init = { listen: (): void => { @@ -173,7 +174,7 @@ export const Init = { game.pf2e.StatusEffects.initialize(); if (game.modules.get("starfinder-field-test-for-pf2e")?.active) { - [accessInfosphere, drive, hack, navigate].forEach((action) => + [accessInfosphere, drive, hack, navigate, plotCourse].forEach((action) => game.pf2e.actions.set(action.slug, action), ); } diff --git a/static/lang/action-en.json b/static/lang/action-en.json index 3b7d00942cb..7c7ffcdaf71 100644 --- a/static/lang/action-en.json +++ b/static/lang/action-en.json @@ -1089,6 +1089,16 @@ "success": "Success You gain enough orientation to avoid becoming hopelessly lost. If you are in an environment with cardinal directions, you have a sense of those directions." }, "Title": "Navigate" + }, + "PlotCourse": { + "Description": "

You prepare for a longer journey into the stars, likely making use of your starship's Drift Engine (or similar FTL device). Attempt a Piloting check to determine if you can chart a suitable path to your ultimate destination. The GM determines how long this activity takes and the DC.

Critical Success You manage to reach your destination in half the expected time (minimum 1 day).
Success You create a usable navigational plan that gets you to your intended destination on time.
Failure Your plan has several problems, and you end up reaching your destination late.
Critical Failure You arrive in an unfamiliar star system, requiring you to plot a new course to your destination (likely at an increased DC due to the unfamiliar starting point).

", + "Notes": { + "criticalFailure": "Critical Failure You arrive in an unfamiliar star system, requiring you to plot a new course to your destination (likely at an increased DC due to the unfamiliar starting point).", + "criticalSuccess": "Critical Success You manage to reach your destination in half the expected time (minimum 1 day).", + "failure": "Failure Your plan has several problems, and you end up reaching your destination late.", + "success": "Success You create a usable navigational plan that gets you to your intended destination on time." + }, + "Title": "Plot Course" } } } From 417bfa178ef7ddff26e14b887b5d17ad14d5e9aa Mon Sep 17 00:00:00 2001 From: Nikolaj Andresen Date: Wed, 31 Jul 2024 22:19:47 +0200 Subject: [PATCH 07/11] Add program action implementation --- .../system/action-macros/computers/program.ts | 21 +++++++++++++++++++ src/scripts/hooks/init.ts | 3 ++- static/lang/action-en.json | 10 +++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 src/module/system/action-macros/computers/program.ts diff --git a/src/module/system/action-macros/computers/program.ts b/src/module/system/action-macros/computers/program.ts new file mode 100644 index 00000000000..3d8b5a3a7e0 --- /dev/null +++ b/src/module/system/action-macros/computers/program.ts @@ -0,0 +1,21 @@ +import { SingleCheckAction } from "@actor/actions/index.ts"; + +const PREFIX = "SF2E.Actions.Program"; + +const program = new SingleCheckAction({ + description: `${PREFIX}.Description`, + name: `${PREFIX}.Title`, + notes: [ + { outcome: ["criticalSuccess"], text: `${PREFIX}.Notes.criticalSuccess` }, + { outcome: ["success"], text: `${PREFIX}.Notes.success` }, + { outcome: ["failure"], text: `${PREFIX}.Notes.failure` }, + { outcome: ["criticalFailure"], text: `${PREFIX}.Notes.criticalFailure` }, + ], + rollOptions: ["action:program"], + section: "skill", + slug: "program", + statistic: "computers", + traits: ["downtime", "manipulate"], +}); + +export { program }; diff --git a/src/scripts/hooks/init.ts b/src/scripts/hooks/init.ts index 4c636e8f68c..d34f6451458 100644 --- a/src/scripts/hooks/init.ts +++ b/src/scripts/hooks/init.ts @@ -23,6 +23,7 @@ import { drive } from "@system/action-macros/piloting/drive.ts"; import { hack } from "@system/action-macros/computers/hack.ts"; import { navigate } from "@system/action-macros/piloting/navigate.ts"; import { plotCourse } from "@system/action-macros/piloting/plot-course.ts"; +import { program } from "@system/action-macros/computers/program.ts"; export const Init = { listen: (): void => { @@ -174,7 +175,7 @@ export const Init = { game.pf2e.StatusEffects.initialize(); if (game.modules.get("starfinder-field-test-for-pf2e")?.active) { - [accessInfosphere, drive, hack, navigate, plotCourse].forEach((action) => + [accessInfosphere, drive, hack, navigate, plotCourse, program].forEach((action) => game.pf2e.actions.set(action.slug, action), ); } diff --git a/static/lang/action-en.json b/static/lang/action-en.json index 7c7ffcdaf71..2d56bd83f08 100644 --- a/static/lang/action-en.json +++ b/static/lang/action-en.json @@ -1099,6 +1099,16 @@ "success": "Success You create a usable navigational plan that gets you to your intended destination on time." }, "Title": "Plot Course" + }, + "Program": { + "Description": "

You spend your time creating lines of computer code that can be integrated into various computerized systems. You need the Programmer skill feat to write computer code, Programming items that have the virtual trait. You must have the formulas to craft these items, but they can often be found sold by other programmers on an infosphere. You can buy lines of code from other programmers to help complete your virtual item. You can use the Program action to create formulas for tech items if you have the Inventor skill feat. You can use the Program action to create tech items without the virtual trait by using a creator capsule if you have the Fabricator skill feat.

Critical Success Your attempt is successful. Each additional day spent Programming reduces the code needed to complete the item by an amount based on your level + 1 and your proficiency rank in Computers.
Success Your attempt is successful. Each additional day spent Programming reduces the materials needed to complete the item by an amount based on your level and your proficiency rank.
Failure You fail to complete the item. You can salvage some of the code you've written for its full value. If you want to try again, you must start over.
Critical Failure You fail to complete the item. 10% of the code you've written or purchased is unusable, but you can salvage the rest. If you want to try again, you must start over.

", + "Notes": { + "criticalFailure": "Critical Failure You fail to complete the item. 10% of the code you've written or purchased is unusable, but you can salvage the rest. If you want to try again, you must start over.", + "criticalSuccess": "Critical Success Your attempt is successful. Each additional day spent Programming reduces the code needed to complete the item by an amount based on your level + 1 and your proficiency rank in Computers.", + "failure": "Failure You fail to complete the item. You can salvage some of the code you've written for its full value. If you want to try again, you must start over.", + "success": "Success Your attempt is successful. Each additional day spent Programming reduces the materials needed to complete the item by an amount based on your level and your proficiency rank." + }, + "Title": "Program" } } } From 04a621a091d5a86f359bf48c1a08d26096a4c91d Mon Sep 17 00:00:00 2001 From: Nikolaj Andresen Date: Wed, 31 Jul 2024 22:20:31 +0200 Subject: [PATCH 08/11] Add run over action implementation --- .../system/action-macros/piloting/run-over.ts | 17 +++++++++++++++++ src/scripts/hooks/init.ts | 3 ++- static/lang/action-en.json | 7 +++++++ 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 src/module/system/action-macros/piloting/run-over.ts diff --git a/src/module/system/action-macros/piloting/run-over.ts b/src/module/system/action-macros/piloting/run-over.ts new file mode 100644 index 00000000000..4dd0d4e5977 --- /dev/null +++ b/src/module/system/action-macros/piloting/run-over.ts @@ -0,0 +1,17 @@ +import { SingleCheckAction } from "@actor/actions/index.ts"; + +const PREFIX = "SF2E.Actions.RunOver"; + +const runOver = new SingleCheckAction({ + cost: 3, + description: `${PREFIX}.Description`, + name: `${PREFIX}.Title`, + notes: [{ outcome: ["criticalFailure", "failure"], text: `${PREFIX}.Notes.failure` }], + rollOptions: ["action:run-over"], + section: "skill", + slug: "run-over", + statistic: "piloting", + traits: ["move", "reckless"], +}); + +export { runOver }; diff --git a/src/scripts/hooks/init.ts b/src/scripts/hooks/init.ts index d34f6451458..11725c482bb 100644 --- a/src/scripts/hooks/init.ts +++ b/src/scripts/hooks/init.ts @@ -24,6 +24,7 @@ import { hack } from "@system/action-macros/computers/hack.ts"; import { navigate } from "@system/action-macros/piloting/navigate.ts"; import { plotCourse } from "@system/action-macros/piloting/plot-course.ts"; import { program } from "@system/action-macros/computers/program.ts"; +import { runOver } from "@system/action-macros/piloting/run-over.ts"; export const Init = { listen: (): void => { @@ -175,7 +176,7 @@ export const Init = { game.pf2e.StatusEffects.initialize(); if (game.modules.get("starfinder-field-test-for-pf2e")?.active) { - [accessInfosphere, drive, hack, navigate, plotCourse, program].forEach((action) => + [accessInfosphere, drive, hack, navigate, plotCourse, program, runOver].forEach((action) => game.pf2e.actions.set(action.slug, action), ); } diff --git a/static/lang/action-en.json b/static/lang/action-en.json index 2d56bd83f08..b57344ec26c 100644 --- a/static/lang/action-en.json +++ b/static/lang/action-en.json @@ -1109,6 +1109,13 @@ "success": "Success Your attempt is successful. Each additional day spent Programming reduces the materials needed to complete the item by an amount based on your level and your proficiency rank." }, "Title": "Program" + }, + "RunOver": { + "Description": "

You try to run over creatures or ram another vehicle with your vehicle. If you maintain control of your vehicle, it moves up to twice its Speed in a straight line at the vehicle's current heading. You attempt to run over any creatures in your path two sizes smaller than the vehicle or smaller, and you can attempt to ram one target creature or object in your path one size smaller than the vehicle or larger.

Each creature in your path, including a rammed target, takes the vehicle's collision damage (basic Reflex save at vehicle's collision DC). If the rammed target is a vehicle, its pilot can attempt a piloting check in place of this Reflex save, with the same results. If the target of your ram takes damage, you and your vehicle each take collision damage (no save) and your movement ends.

", + "Notes": { + "failure": "Failure The vehicle moves its Speed in a straight line along its most recent heading, drifting up to 45 degrees at the GM's discretion, and becomes uncontrolled." + }, + "Title": "Run Over" } } } From 4ca1e46f5b38b163b75c745053f3cc4a874bdefe Mon Sep 17 00:00:00 2001 From: Nikolaj Andresen Date: Wed, 31 Jul 2024 22:21:35 +0200 Subject: [PATCH 09/11] Add stop action implementation --- src/module/system/action-macros/piloting/stop.ts | 14 ++++++++++++++ src/scripts/hooks/init.ts | 3 ++- static/lang/action-en.json | 4 ++++ 3 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 src/module/system/action-macros/piloting/stop.ts diff --git a/src/module/system/action-macros/piloting/stop.ts b/src/module/system/action-macros/piloting/stop.ts new file mode 100644 index 00000000000..dc033f144e3 --- /dev/null +++ b/src/module/system/action-macros/piloting/stop.ts @@ -0,0 +1,14 @@ +import { SimpleAction } from "@actor/actions/index.ts"; + +const PREFIX = "SF2E.Actions.Stop"; + +const stop = new SimpleAction({ + cost: 1, + description: `${PREFIX}.Description`, + name: `${PREFIX}.Title`, + section: "skill", + slug: "stop", + traits: ["manipulate"], +}); + +export { stop }; diff --git a/src/scripts/hooks/init.ts b/src/scripts/hooks/init.ts index 11725c482bb..179c89005ea 100644 --- a/src/scripts/hooks/init.ts +++ b/src/scripts/hooks/init.ts @@ -25,6 +25,7 @@ import { navigate } from "@system/action-macros/piloting/navigate.ts"; import { plotCourse } from "@system/action-macros/piloting/plot-course.ts"; import { program } from "@system/action-macros/computers/program.ts"; import { runOver } from "@system/action-macros/piloting/run-over.ts"; +import { stop } from "@system/action-macros/piloting/stop.ts"; export const Init = { listen: (): void => { @@ -176,7 +177,7 @@ export const Init = { game.pf2e.StatusEffects.initialize(); if (game.modules.get("starfinder-field-test-for-pf2e")?.active) { - [accessInfosphere, drive, hack, navigate, plotCourse, program, runOver].forEach((action) => + [accessInfosphere, drive, hack, navigate, plotCourse, program, runOver, stop].forEach((action) => game.pf2e.actions.set(action.slug, action), ); } diff --git a/static/lang/action-en.json b/static/lang/action-en.json index b57344ec26c..b5ccd255d24 100644 --- a/static/lang/action-en.json +++ b/static/lang/action-en.json @@ -1116,6 +1116,10 @@ "failure": "Failure The vehicle moves its Speed in a straight line along its most recent heading, drifting up to 45 degrees at the GM's discretion, and becomes uncontrolled." }, "Title": "Run Over" + }, + "Stop": { + "Description": "

You bring the vehicle to a stop.

", + "Title": "Stop" } } } From b3a3998df451482f03583c95ff3c5ea39e6d2858 Mon Sep 17 00:00:00 2001 From: Nikolaj Andresen Date: Wed, 31 Jul 2024 23:34:05 +0200 Subject: [PATCH 10/11] Add stunt action implementation --- .../system/action-macros/piloting/stunt.ts | 110 ++++++++++++++++++ src/scripts/hooks/init.ts | 3 +- static/lang/action-en.json | 52 +++++++++ 3 files changed, 164 insertions(+), 1 deletion(-) create mode 100644 src/module/system/action-macros/piloting/stunt.ts diff --git a/src/module/system/action-macros/piloting/stunt.ts b/src/module/system/action-macros/piloting/stunt.ts new file mode 100644 index 00000000000..61e7572e02a --- /dev/null +++ b/src/module/system/action-macros/piloting/stunt.ts @@ -0,0 +1,110 @@ +import { SingleCheckAction } from "@actor/actions/index.ts"; + +const PREFIX = "SF2E.Actions.Stunt"; + +const stunt = new SingleCheckAction({ + cost: 1, + description: `${PREFIX}.Description`, + name: `${PREFIX}.Title`, + notes: [{ outcome: ["criticalFailure", "failure"], text: `${PREFIX}.Notes.failure` }], + rollOptions: ["action:stunt"], + section: "skill", + slug: "stunt", + statistic: "piloting", + traits: ["manipulate", "move", "reckless"], + variants: [ + { + modifiers: [ + { label: `${PREFIX}.BackOff.Title`, modifier: -1 }, + { label: `${PREFIX}.Modifiers.RecklessPiloting`, modifier: -5, predicate: ["reckless-piloting"] }, + ], + name: `${PREFIX}.BackOff.Title`, + notes: [ + { outcome: ["criticalSuccess", "success"], text: `${PREFIX}.BackOff.Notes.success` }, + { outcome: ["criticalFailure", "failure"], text: `${PREFIX}.Notes.failure` }, + ], + rollOptions: ["action:stunt", "action:stunt:back-off"], + slug: "back-off", + }, + { + modifiers: [ + { label: `${PREFIX}.Evade.Title`, modifier: -1 }, + { label: `${PREFIX}.Modifiers.RecklessPiloting`, modifier: -5, predicate: ["reckless-piloting"] }, + ], + name: `${PREFIX}.Evade.Title`, + notes: [ + { outcome: ["criticalSuccess", "success"], text: `${PREFIX}.Evade.Notes.success` }, + { outcome: ["criticalFailure", "failure"], text: `${PREFIX}.Notes.failure` }, + ], + rollOptions: ["action:stunt", "action:stunt:evade"], + slug: "evade", + }, + { + modifiers: [ + { label: `${PREFIX}.FlipAndBurn.Title`, modifier: -1 }, + { label: `${PREFIX}.Modifiers.RecklessPiloting`, modifier: -5, predicate: ["reckless-piloting"] }, + ], + name: `${PREFIX}.FlipAndBurn.Title`, + notes: [ + { outcome: ["criticalSuccess", "success"], text: `${PREFIX}.FlipAndBurn.Notes.success` }, + { outcome: ["criticalFailure", "failure"], text: `${PREFIX}.Notes.failure` }, + ], + rollOptions: ["action:stunt", "action:stunt:flip-and-burn"], + slug: "flip-and-burn", + }, + { + modifiers: [ + { label: `${PREFIX}.BarrelRoll.Title`, modifier: -2 }, + { label: `${PREFIX}.Modifiers.RecklessPiloting`, modifier: -5, predicate: ["reckless-piloting"] }, + ], + name: `${PREFIX}.BarrelRoll.Title`, + notes: [ + { outcome: ["criticalSuccess", "success"], text: `${PREFIX}.BarrelRoll.Notes.success` }, + { outcome: ["criticalFailure", "failure"], text: `${PREFIX}.Notes.failure` }, + ], + rollOptions: ["action:stunt", "action:stunt:barrel-roll"], + slug: "barrel-roll", + }, + { + modifiers: [ + { label: `${PREFIX}.Flyby.Title`, modifier: -2 }, + { label: `${PREFIX}.Modifiers.RecklessPiloting`, modifier: -5, predicate: ["reckless-piloting"] }, + ], + name: `${PREFIX}.Flyby.Title`, + notes: [ + { outcome: ["criticalSuccess", "success"], text: `${PREFIX}.Flyby.Notes.success` }, + { outcome: ["criticalFailure", "failure"], text: `${PREFIX}.Notes.failure` }, + ], + rollOptions: ["action:stunt", "action:stunt:flyby"], + slug: "flyby", + }, + { + modifiers: [ + { label: `${PREFIX}.Drift.Title`, modifier: -2 }, + { label: `${PREFIX}.Modifiers.RecklessPiloting`, modifier: -5, predicate: ["reckless-piloting"] }, + ], + name: `${PREFIX}.Drift.Title`, + notes: [ + { outcome: ["criticalSuccess", "success"], text: `${PREFIX}.Drift.Notes.success` }, + { outcome: ["criticalFailure", "failure"], text: `${PREFIX}.Notes.failure` }, + ], + rollOptions: ["action:stunt", "action:stunt:drift"], + slug: "drift", + }, + { + modifiers: [ + { label: `${PREFIX}.TurnInPlace.Title`, modifier: -2 }, + { label: `${PREFIX}.Modifiers.RecklessPiloting`, modifier: -5, predicate: ["reckless-piloting"] }, + ], + name: `${PREFIX}.TurnInPlace.Title`, + notes: [ + { outcome: ["criticalSuccess", "success"], text: `${PREFIX}.TurnInPlace.Notes.success` }, + { outcome: ["criticalFailure", "failure"], text: `${PREFIX}.Notes.failure` }, + ], + rollOptions: ["action:stunt", "action:stunt:turn-in-place"], + slug: "turn-in-place", + }, + ], +}); + +export { stunt }; diff --git a/src/scripts/hooks/init.ts b/src/scripts/hooks/init.ts index 179c89005ea..574b8d075e4 100644 --- a/src/scripts/hooks/init.ts +++ b/src/scripts/hooks/init.ts @@ -26,6 +26,7 @@ import { plotCourse } from "@system/action-macros/piloting/plot-course.ts"; import { program } from "@system/action-macros/computers/program.ts"; import { runOver } from "@system/action-macros/piloting/run-over.ts"; import { stop } from "@system/action-macros/piloting/stop.ts"; +import { stunt } from "@system/action-macros/piloting/stunt.ts"; export const Init = { listen: (): void => { @@ -177,7 +178,7 @@ export const Init = { game.pf2e.StatusEffects.initialize(); if (game.modules.get("starfinder-field-test-for-pf2e")?.active) { - [accessInfosphere, drive, hack, navigate, plotCourse, program, runOver, stop].forEach((action) => + [accessInfosphere, drive, hack, navigate, plotCourse, program, runOver, stop, stunt].forEach((action) => game.pf2e.actions.set(action.slug, action), ); } diff --git a/static/lang/action-en.json b/static/lang/action-en.json index b5ccd255d24..a4071fca88a 100644 --- a/static/lang/action-en.json +++ b/static/lang/action-en.json @@ -1120,6 +1120,58 @@ "Stop": { "Description": "

You bring the vehicle to a stop.

", "Title": "Stop" + }, + "Stunt": { + "Description": "

You perform a stunt while Driving your vehicle, temporarily improving its effective capabilities at the risk of losing control. Drive your vehicle and choose a stunt. All piloting checks attempted as part of your Stunt receive the listed penalty, including piloting checks made to take a reckless action. If the Drive action and Stunt are both reckless, you must attempt the piloting check to keep control of the vehicle twice.

Stunts
StuntPenaltyBenefit
Back Off-1Move half Speed backwards.
Evade-1Move half Speed to gain a +1 status bonus to AC.
Flip and Burn-1Move half Speed, then turn and direction.
Barrel Roll-2Move half Speed to gain a +2 status bonus to AC.
Flyby-2Move half Speed through an enemy square.
Drift-2Move half Speed sideways without turning.
Turn in Place-2Turn, then move full Speed.
", + "BackOff": { + "Notes": { + "success": "Success Move half Speed backwards." + }, + "Title": "Back Off" + }, + "BarrelRoll": { + "Notes": { + "success": "Success Move half Speed to gain a +2 status bonus to AC." + }, + "Title": "Barrel Roll" + }, + "Drift": { + "Notes": { + "success": "Success Move half Speed sideways without turning." + }, + "Title": "Drift" + }, + "Evade": { + "Notes": { + "success": "Success Move half Speed to gain a +1 status bonus to AC." + }, + "Title": "Evade" + }, + "FlipAndBurn": { + "Notes": { + "success": "Success Move half Speed, then turn and direction." + }, + "Title": "Flip and Burn" + }, + "Flyby": { + "Notes": { + "success": "Success Move half Speed through an enemy square." + }, + "Title": "Flyby" + }, + "Modifiers": { + "RecklessPiloting": "Reckless Piloting" + }, + "Notes": { + "failure": "Failure The vehicle moves its Speed in a straight line along its most recent heading, drifting up to 45 degrees at the GM's discretion, and becomes uncontrolled." + }, + "Title": "Stunt", + "TurnInPlace": { + "Notes": { + "success": "Success Turn, then move full Speed." + }, + "Title": "Turn in Place" + } } } } From 22312159316048c1b7a71d891e19978d6da9c2ac Mon Sep 17 00:00:00 2001 From: Nikolaj Andresen Date: Wed, 31 Jul 2024 23:43:30 +0200 Subject: [PATCH 11/11] Add take control action implementation --- .../action-macros/piloting/take-control.ts | 17 +++++++++++++++++ src/scripts/hooks/init.ts | 16 +++++++++++++--- static/lang/action-en.json | 7 +++++++ 3 files changed, 37 insertions(+), 3 deletions(-) create mode 100644 src/module/system/action-macros/piloting/take-control.ts diff --git a/src/module/system/action-macros/piloting/take-control.ts b/src/module/system/action-macros/piloting/take-control.ts new file mode 100644 index 00000000000..ea33aab8fff --- /dev/null +++ b/src/module/system/action-macros/piloting/take-control.ts @@ -0,0 +1,17 @@ +import { SingleCheckAction } from "@actor/actions/index.ts"; + +const PREFIX = "SF2E.Actions.TakeControl"; + +const takeControl = new SingleCheckAction({ + cost: 1, + description: `${PREFIX}.Description`, + name: `${PREFIX}.Title`, + notes: [{ outcome: ["criticalSuccess", "success"], text: `${PREFIX}.Notes.success` }], + rollOptions: ["action:take-control"], + section: "skill", + slug: "take-control", + statistic: "piloting", + traits: ["manipulate"], +}); + +export { takeControl }; diff --git a/src/scripts/hooks/init.ts b/src/scripts/hooks/init.ts index 574b8d075e4..e88b34e8e7c 100644 --- a/src/scripts/hooks/init.ts +++ b/src/scripts/hooks/init.ts @@ -27,6 +27,7 @@ import { program } from "@system/action-macros/computers/program.ts"; import { runOver } from "@system/action-macros/piloting/run-over.ts"; import { stop } from "@system/action-macros/piloting/stop.ts"; import { stunt } from "@system/action-macros/piloting/stunt.ts"; +import { takeControl } from "@system/action-macros/piloting/take-control.ts"; export const Init = { listen: (): void => { @@ -178,9 +179,18 @@ export const Init = { game.pf2e.StatusEffects.initialize(); if (game.modules.get("starfinder-field-test-for-pf2e")?.active) { - [accessInfosphere, drive, hack, navigate, plotCourse, program, runOver, stop, stunt].forEach((action) => - game.pf2e.actions.set(action.slug, action), - ); + [ + accessInfosphere, + drive, + hack, + navigate, + plotCourse, + program, + runOver, + stop, + stunt, + takeControl, + ].forEach((action) => game.pf2e.actions.set(action.slug, action)); } }); }, diff --git a/static/lang/action-en.json b/static/lang/action-en.json index a4071fca88a..d72e7f0dc2c 100644 --- a/static/lang/action-en.json +++ b/static/lang/action-en.json @@ -1172,6 +1172,13 @@ }, "Title": "Turn in Place" } + }, + "TakeControl": { + "Description": "

You take control of a vehicle. Attempt a Piloting check; on a success, you become the vehicle's pilot, or regain control of the vehicle if it was uncontrolled. Some vehicles have complicated controls that cause this action to become a multi-action activity, and most vehicles take at least 3 actions if they aren't activated.

", + "Notes": { + "success": "Success You become the vehicle's pilot, or regain control of the vehicle if it was uncontrolled." + }, + "Title": "Take Control" } } }