Skip to content

Commit

Permalink
Move oracle rolling into campaign context
Browse files Browse the repository at this point in the history
  • Loading branch information
cwegrzyn committed Jul 28, 2024
1 parent fee26fe commit 68e534c
Show file tree
Hide file tree
Showing 23 changed files with 290 additions and 213 deletions.
5 changes: 4 additions & 1 deletion src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { IDataContext } from "datastore/data-context";
import { rootLogger, setLogLevel } from "logger";
import loglevel from "loglevel";
import { App, getLinkpath, parseLinktext } from "obsidian";
import { OracleRoller } from "oracles/roller";
import { ProgressIndex } from "tracks/indexer";
import { CharacterTracker } from "./character-tracker";
import { Datastore } from "./datastore";
Expand Down Expand Up @@ -44,7 +45,9 @@ export class IronVaultAPI {
}

public async roll(oracle: string): Promise<RollWrapper> {
return this.globalDataContext.roller.roll(oracle);
return new OracleRoller(this.plugin, this.activeDataContext.oracles).roll(
oracle,
);
}

public stripLinks(input: string): string {
Expand Down
28 changes: 23 additions & 5 deletions src/campaigns/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,18 @@ import {
DataswornTypes,
MoveWithSelector,
} from "datastore/datasworn-indexer";
import IronVaultPlugin from "index";
import { ReadonlyIndex } from "indexer/index-interface";
import { OracleRoller } from "oracles/roller";
import { Ruleset } from "rules/ruleset";
import { TrackedEntities } from "te/index-interface";
import { ProgressTrackFileAdapter } from "tracks/progress";
import {
AsyncDiceRoller,
DiceRoller,
GraphicalDiceRoller,
PlainDiceRoller,
} from "utils/dice-roller";
import { projectedVersionedMap } from "utils/versioned-map";
import { ZodError } from "zod";
import { CampaignFile } from "./entity";
Expand All @@ -26,9 +33,13 @@ import { Determination, IPlaysetConfig } from "./playsets/config";
export class CampaignDataContext
implements TrackedEntities, ICompleteDataContext
{
dataContext: PlaysetAwareDataContext;
readonly dataContext: PlaysetAwareDataContext;
readonly oracleRoller: OracleRoller;

constructor(
// TODO(@cwegrzyn): Once we have a campaign settings object (which must overlay the
// overall plugin settings somehow), we can replace this plugin call.
private readonly plugin: IronVaultPlugin,
base: TrackedEntities,
indexer: DataIndexer<DataswornTypes>,
public readonly campaign: CampaignFile,
Expand All @@ -45,6 +56,7 @@ export class CampaignDataContext
);

this.dataContext = new PlaysetAwareDataContext(indexer, campaign.playset);
this.oracleRoller = new OracleRoller(plugin, this.oracles);
}

campaigns: ReadonlyIndex<CampaignFile, ZodError>;
Expand Down Expand Up @@ -76,10 +88,6 @@ export class CampaignDataContext
return this.dataContext.truths;
}

get roller(): OracleRoller {
return this.dataContext.roller;
}

get rulesPackages(): StandardIndex<RulesPackage> {
return this.dataContext.rulesPackages;
}
Expand All @@ -91,6 +99,16 @@ export class CampaignDataContext
get prioritized() {
return this.dataContext.prioritized;
}
diceRollerFor(kind: "move"): AsyncDiceRoller & DiceRoller {
switch (kind) {
case "move":
return this.plugin.settings.graphicalActionDice
? new GraphicalDiceRoller(this.plugin)
: PlainDiceRoller.INSTANCE;
default:
throw new Error(`unexpected kind ${kind}`);
}
}
}

export class PlaysetAwareDataContext extends BaseDataContext {
Expand Down
3 changes: 2 additions & 1 deletion src/campaigns/manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,8 @@ export class CampaignManager extends Component {
this.#campaignDataContexts.set(
campaign,
(context = new CampaignDataContext(
this.plugin,
this.plugin, // this is for the settings/for dice roller
this.plugin, // this is the tracked entities
this.plugin.datastore.indexer,
campaign,
// TODO(cwegrzyn): need to confirm that file equality comparison is safe
Expand Down
10 changes: 6 additions & 4 deletions src/characters/action-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ export interface IActionContext extends IDataContext {
| MeterWithoutLens<ConditionMeterDefinition>
)[];

readonly oracleRoller: OracleRoller;

readonly momentum?: number;

getWithLens<T>(op: (lenses: CharacterLens) => CharReader<T>): T | undefined;
Expand All @@ -51,8 +53,8 @@ export class NoCharacterActionConext implements IActionContext {
public readonly campaignContext: CampaignDataContext,
) {}

get roller(): OracleRoller {
return this.campaignContext.roller;
get oracleRoller(): OracleRoller {
return this.campaignContext.oracleRoller;
}

get rulesPackages(): StandardIndex<RulesPackage> {
Expand Down Expand Up @@ -126,8 +128,8 @@ export class CharacterActionContext implements IActionContext {
public readonly characterContext: CharacterContext,
) {}

get roller(): OracleRoller {
return this.campaignContext.roller;
get oracleRoller(): OracleRoller {
return this.campaignContext.oracleRoller;
}

get rulesPackages(): StandardIndex<RulesPackage> {
Expand Down
7 changes: 2 additions & 5 deletions src/datastore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,10 +141,7 @@ export class Datastore extends Component {
priority,
sourceTags: { [SourceTag.RulesetId]: pkg._id },
});
this.indexer.index(
source,
walkDataswornRulesPackage(source, pkg, this.plugin),
);
this.indexer.index(source, walkDataswornRulesPackage(source, pkg));

this.app.metadataCache.trigger("iron-vault:index-changed");
}
Expand Down Expand Up @@ -193,7 +190,7 @@ export class Datastore extends Component {
});
this.indexer.index(
source,
walkDataswornRulesPackage(source, dataswornPackage, this.plugin),
walkDataswornRulesPackage(source, dataswornPackage),
);
} catch (e) {
new Notice(`Unable to import homebrew file: ${file.basename}`, 0);
Expand Down
8 changes: 2 additions & 6 deletions src/datastore/data-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ export interface IDataContext {
readonly oracles: StandardIndex<DataswornTypes["oracle"]>;
readonly truths: StandardIndex<DataswornTypes["truth"]>;

readonly roller: OracleRoller;
readonly rulesPackages: StandardIndex<DataswornTypes["rules_package"]>;
readonly ruleset: Ruleset;
}
Expand Down Expand Up @@ -65,7 +64,8 @@ export class MockDataContext implements IDataContext {
}

get roller() {
return new OracleRoller(this.oracles);
// TODO(@cwegrzyn): maybe we should have a way of passing a normal roller through here?
return new OracleRoller(undefined as never, this.oracles);
}
}

Expand Down Expand Up @@ -107,10 +107,6 @@ export class BaseDataContext implements ICompleteDataContext {
return this.prioritized.ofKind("truth").projected((entry) => entry.value);
}

get roller(): OracleRoller {
return new OracleRoller(this.oracles);
}

get rulesPackages(): StandardIndex<DataswornTypes["rules_package"]> {
return this.prioritized
.ofKind("rules_package")
Expand Down
13 changes: 4 additions & 9 deletions src/datastore/datasworn-indexer.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Datasworn } from "@datasworn/core";
import IronVaultPlugin from "index";
import {
Oracle,
OracleCollectionGrouping,
Expand Down Expand Up @@ -61,7 +60,6 @@ export function createSource(fields: {
export function* walkDataswornRulesPackage(
source: Source,
input: Datasworn.RulesPackage,
plugin?: IronVaultPlugin,
): Iterable<DataswornSourced> {
function make<T extends { _id: string; type: string }>(
obj: T,
Expand Down Expand Up @@ -98,7 +96,7 @@ export function* walkDataswornRulesPackage(
yield {
id: oracle._id,
kind: "oracle",
value: new DataswornOracle(oracle, moveOracleGroup, plugin),
value: new DataswornOracle(oracle, moveOracleGroup),
source,
};
}
Expand All @@ -123,7 +121,7 @@ export function* walkDataswornRulesPackage(
}
}

for (const oracle of walkOracles(input, plugin)) {
for (const oracle of walkOracles(input)) {
yield { id: oracle.id, kind: "oracle", value: oracle, source };
}

Expand All @@ -134,10 +132,7 @@ export function* walkDataswornRulesPackage(
yield { id: input._id, kind: "rules_package", value: input, source };
}

function* walkOracles(
data: Datasworn.RulesPackage,
plugin?: IronVaultPlugin,
): Generator<Oracle> {
function* walkOracles(data: Datasworn.RulesPackage): Generator<Oracle> {
function* expand(
collection: Datasworn.OracleCollection,
parent: OracleGrouping,
Expand All @@ -162,7 +157,7 @@ function* walkOracles(
for (const oracle of Object.values<Datasworn.OracleRollable>(
collection.contents,
)) {
yield new DataswornOracle(oracle, newParent, plugin);
yield new DataswornOracle(oracle, newParent);
}
}

Expand Down
51 changes: 19 additions & 32 deletions src/datastore/parsers/datasworn/oracles.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { type Datasworn } from "@datasworn/core";
import IronVaultPlugin from "index";
import { rootLogger } from "logger";
import { DiceGroup } from "utils/dice-group";
import { NoSuchOracleError } from "../../../model/errors";
Expand Down Expand Up @@ -40,7 +39,6 @@ export class DataswornOracle implements Oracle {
| Datasworn.OracleRollable
| Datasworn.EmbeddedOracleRollable,
public readonly parent: OracleGrouping,
private readonly plugin?: IronVaultPlugin,
) {}

get raw(): Datasworn.OracleRollable | Datasworn.EmbeddedOracleRollable {
Expand Down Expand Up @@ -78,15 +76,13 @@ export class DataswornOracle implements Oracle {
}

get dice(): Dice {
return Dice.fromDiceString(this.table.dice, this.plugin, DieKind.Oracle);
return Dice.fromDiceString(this.table.dice, DieKind.Oracle);
}

get cursedBy(): Oracle | undefined {
cursedBy(rollContext: RollContext): Oracle | undefined {
for (const val of Object.values(this.table.tags ?? {})) {
if (typeof val.cursed_by === "string") {
// TODO(@cwegrzyn): implement this
throw new Error("reimplement me");
// return this.plugin?.datastore.oracles.get(val.cursed_by);
return rollContext.lookup(val.cursed_by);
}
}
return;
Expand All @@ -102,35 +98,28 @@ export class DataswornOracle implements Oracle {
}

async roll(context: RollContext): Promise<Roll> {
const cursed = this.cursedBy;
if (cursed && this.plugin && this.plugin.settings.enableCursedDie) {
const group = new DiceGroup(
[
this.dice,
new Dice(
1,
this.plugin.settings.cursedDieSides,
this.plugin,
DieKind.Cursed,
),
],
this.plugin,
);
const res = await group.roll(this.plugin.settings.graphicalOracleDice);
return this.evaluate(context, res[0].value, res[1].value, cursed.id);
const diceRoller = context.diceRoller();

const cursed = this.cursedBy(context);
const cursedDice = context.cursedDice(); // non-null if cursed die is enabled

if (cursed && cursedDice) {
const group = DiceGroup.of(this.dice, cursedDice);
const roll = await diceRoller.rollAsync(group);

return {
...(await this.evaluate(context, roll[0].value)),
cursedRoll: roll[1].value,
cursedTableId: cursed.id,
};
}
return this.evaluate(
context,
await this.dice.roll(this.plugin?.settings.graphicalOracleDice ?? true),
(await diceRoller.rollAsync(new DiceGroup([this.dice])))[0].value,
);
}

async evaluate(
context: RollContext,
roll: number,
cursedRoll?: number,
cursedTableId?: string,
): Promise<Roll> {
async evaluate(context: RollContext, roll: number): Promise<Roll> {
const row = this.rowFor(roll);

const subrolls: Record<string, Subroll<Roll>> = {};
Expand Down Expand Up @@ -246,8 +235,6 @@ export class DataswornOracle implements Oracle {
roll,
tableId: this.id,
subrolls,
cursedRoll,
cursedTableId,
};
}

Expand Down
6 changes: 3 additions & 3 deletions src/entity/command.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { CampaignDataContext } from "campaigns/context";
import { determineCampaignContext } from "campaigns/manager";
import { IDataContext } from "datastore/data-context";
import { extractDataswornLinkParts } from "datastore/parsers/datasworn/id";
import Handlebars from "handlebars";
import { createOrAppendMechanics } from "mechanics/editor";
Expand Down Expand Up @@ -82,10 +82,10 @@ export async function promptOracleRow(

export async function generateEntity(
app: App,
dataContext: IDataContext,
dataContext: CampaignDataContext,
entityDesc: EntityDescriptor<EntitySpec>,
): Promise<EntityModalResults<EntitySpec>> {
const rollContext = dataContext.roller;
const rollContext = dataContext.oracleRoller;
const attributes = Object.entries(entityDesc.spec)
.filter(
(keyAndSpec): keyAndSpec is [string, EntityAttributeFieldSpec] =>
Expand Down
10 changes: 9 additions & 1 deletion src/model/oracle.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import { type Datasworn } from "@datasworn/core";
import { Dice } from "utils/dice";
import { AsyncDiceRoller, DiceRoller } from "utils/dice-roller";
import { NumberRange, Roll } from "./rolls";

export interface RollContext {
/** Fetch the oracle with this ID if it exists. */
lookup(id: string): Oracle | undefined;

/** Dice roller to use for oracle rolls */
diceRoller(): AsyncDiceRoller & DiceRoller;

/** If cursed die is enabled, return the Dice object for a cursed dice. */
cursedDice(): Dice | undefined;
}

export enum OracleGroupingType {
Expand Down Expand Up @@ -41,7 +49,7 @@ export interface Oracle {
// TODO(@cwegrzyn): exposed raw rollable for use in the oracle reference modal. not sure
// to what extent it is useful to abstract some of this stuff away...
readonly raw: Datasworn.OracleRollable | Datasworn.EmbeddedOracleRollable;
readonly cursedBy?: Oracle;
cursedBy(rollContext: RollContext): Oracle | undefined;
readonly curseBehavior?: CurseBehavior;
readonly recommended_rolls?: NumberRange;

Expand Down
12 changes: 12 additions & 0 deletions src/model/rolls.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { DiceGroup } from "utils/dice-group";
import { BaseRollSchema, RollSchema } from "../oracles/schema";
import { Oracle, OracleRow, RollContext } from "./oracle";

Expand Down Expand Up @@ -174,6 +175,17 @@ export class RollWrapper {
: undefined;
}

async rerollCursed(): Promise<RollWrapper> {
const cursedDice = this.context.cursedDice();
if (cursedDice == null) {
throw new Error("Cursed dice not in use. Cannot reroll curse!");
}
const cursedRoll = (
await this.context.diceRoller().rollAsync(DiceGroup.of(cursedDice))
)[0];
return this.withCursedRoll(cursedRoll.value);
}

withCursedRoll(cursedRoll: number | undefined, cursedTableId?: string) {
return new RollWrapper(this.oracle, this.context, {
...this.roll,
Expand Down
Loading

0 comments on commit 68e534c

Please sign in to comment.