-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add character and game commands
- Loading branch information
1 parent
345e0ec
commit b2fe88c
Showing
50 changed files
with
1,040 additions
and
190 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,85 +1,57 @@ | ||
import { type Result, err, ok } from 'neverthrow' | ||
import hapi, { ObjectSchema } from 'joi' | ||
|
||
const { object, string } = hapi | ||
export interface IAttribute { | ||
name: string | ||
value: 0 | 1 | 2 | 3 | 4 | 5 | ||
} | ||
|
||
export interface ISkill { | ||
name: string | ||
value: 0 | 1 | 2 | 3 | 4 | 5 | ||
} | ||
|
||
export interface IStats { | ||
health: number | ||
} | ||
|
||
export interface IVampireStats extends IStats { | ||
willpower: number | ||
hunger: number | ||
humanity: number | ||
import Ajv, { type Schema } from 'ajv' | ||
import { type Validate, validate } from './validator.js' | ||
import { type Result } from 'neverthrow' | ||
import { v4 } from 'uuid' | ||
|
||
const ajv = new Ajv() | ||
|
||
const characterSchema: Schema = { | ||
type: 'object', | ||
properties: { | ||
name: { | ||
type: 'string', | ||
}, | ||
referenceId: { | ||
type: 'string', | ||
}, | ||
referenceType: { | ||
type: 'string', | ||
enum: ['discord'], | ||
}, | ||
}, | ||
required: ['name'], | ||
} | ||
|
||
export type IHumanStats = IStats | ||
const validateCharacter = ajv.compile(characterSchema) | ||
const validateCharacterEntity = validate(validateCharacter) | ||
|
||
export interface IVampireCharacteristics { | ||
sire: string | ||
predator: string | ||
clan: string | ||
generation: string | ||
} | ||
|
||
export type Splat = 'vampire' | 'human' | ||
|
||
export interface ICharacter<Stats extends IStats> { | ||
export interface Character { | ||
id: string | ||
chronicleId: string | ||
name: string | ||
concept: string | ||
ambition: string | ||
desire: string | ||
splat: Splat | ||
// TODO: Do we want these explicit? | ||
attributes: { | ||
[name: string]: IAttribute | ||
} | ||
// TODO: Do we want these explicit? | ||
skills: { | ||
[name: string]: ISkill | ||
} | ||
stats: Stats | ||
created: string | ||
modified: string | ||
referenceId: string | ||
referenceType: string | ||
} | ||
|
||
export type Vampire = ICharacter<IVampireStats> | ||
|
||
export type Human = ICharacter<IHumanStats> | ||
|
||
export type Character = Vampire | Human | ||
|
||
// This is only what is required to create, we will probably want another validation | ||
// for locking a character in? | ||
export const Validation = object({ | ||
name: string().required(), | ||
concept: string(), | ||
ambition: string(), | ||
desire: string(), | ||
splat: string().valid('vampire', 'human').required(), | ||
export type CreateCharacterEntity = Pick< | ||
Character, | ||
'id' | 'name' | 'referenceId' | 'referenceType' | ||
> | ||
|
||
export type CreateCharacterRequest = Pick< | ||
CreateCharacterEntity, | ||
'name' | 'referenceId' | 'referenceType' | ||
> | ||
|
||
const addId = (character: CreateCharacterRequest): CreateCharacterEntity => ({ | ||
...character, | ||
id: character.referenceId | ||
? `${character.referenceType}:${character.referenceId}` | ||
: // TODO: WE need to remove this from here and pass it in | ||
`uuid:${v4()}`, | ||
}) | ||
|
||
export type CreateCharacterEntity = Pick<Vampire | Human, 'name' | 'splat'> | ||
|
||
export const makeCreateCharacterEntity = | ||
(schema: ObjectSchema) => | ||
(c: CreateCharacterEntity): Result<CreateCharacterEntity, string> => { | ||
const { error, value } = schema.validate(c) | ||
return error ? err(error.message) : ok(value) | ||
} | ||
const makeCharacterEntity = | ||
(validate: Validate) => | ||
(character: CreateCharacterRequest): Result<CreateCharacterEntity, string> => | ||
validate(character).map(addId) | ||
|
||
/** | ||
* Creates a Character entity. A character is any player or non-player character in the game. | ||
*/ | ||
export const createCharacterEntity = makeCreateCharacterEntity(Validation) | ||
export const characterEntity = makeCharacterEntity(validateCharacterEntity) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,14 @@ | ||
import { Schema } from 'joi' | ||
import { type Result, err, ok } from 'neverthrow' | ||
import { type ValidateFunction } from 'ajv' | ||
|
||
export const validator = (schema: Schema) => (payload: object) => { | ||
const { error } = schema.validate(payload) | ||
if (error) { | ||
const message = error.details.map((el) => el.message).join('\n') | ||
return { | ||
error: message, | ||
} | ||
export type Validate = <T>(payload: T) => Result<T, string> | ||
|
||
export const validate = | ||
(validatior: ValidateFunction): Validate => | ||
(payload) => { | ||
const valid = validatior(payload) | ||
|
||
return !valid | ||
? err(validatior.errors.map((e) => e.message).join()) | ||
: ok(payload) | ||
} | ||
return true | ||
} |
45 changes: 45 additions & 0 deletions
45
packages/bot/src/framework/discord/commands/character/create.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
// Need to add ability to make the character public or private | ||
// We will probably want separata commands for setting skill, attribute, discipline-power | ||
// Basically setting one of these to 0-5, 0 would be remove. | ||
// Need live tracking for setting, health, willpower, etc. | ||
// Can we use subcommands to handle setting a skill, attriute,or power? | ||
// If a character is "locked", only an admin should be able to change the character or exp would need to be spent | ||
import { type Gateways } from '../../../../gateway/index.js' | ||
import { type ICommand } from '../../types.js' | ||
import { SlashCommandBuilder } from 'discord.js' | ||
import { characterMessage } from '../../messages/character.js' | ||
import { createCharacter } from '../../../../use-case/create-character.js' | ||
|
||
const command: ICommand = { | ||
command: new SlashCommandBuilder() | ||
.setName('add-character') | ||
.setDescription('Add a Player Character or Non-Player Character') | ||
.addStringOption((option) => | ||
option | ||
.setName('name') | ||
.setDescription('Name of character') | ||
.setRequired(true) | ||
) | ||
.addUserOption((option) => | ||
option | ||
.setName('target') | ||
.setDescription('Add user target if a Player Character.') | ||
.setRequired(false) | ||
), | ||
execute(interaction, gateway: Gateways) { | ||
// Using a TypeGuard here but not sure it should ever get that far?> | ||
// Might be better to make ICommand a generic | ||
if (!interaction.isChatInputCommand()) { | ||
return | ||
} | ||
|
||
const target = interaction.options.getUser('target') | ||
|
||
return createCharacter(gateway)({ | ||
name: interaction.options.getString('name'), | ||
referenceId: target ? target.id : null, | ||
}).map(characterMessage) | ||
}, | ||
} | ||
|
||
export default command |
Empty file.
Empty file.
Empty file.
Empty file.
22 changes: 22 additions & 0 deletions
22
packages/bot/src/framework/discord/commands/character/list.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { type Gateways } from '../../../../gateway/index.js' | ||
import { type ICommand } from '../../types' | ||
import { SlashCommandBuilder } from 'discord.js' | ||
import { charactersMessage } from '../../messages/character.js' | ||
import { listCharacters } from '../../../../use-case/list-characters.js' | ||
|
||
const command: ICommand = { | ||
command: new SlashCommandBuilder() | ||
.setName('list-characters') | ||
.setDescription('Retrieves a list of characters.'), | ||
execute(interaction, gateways: Gateways) { | ||
// Using a TypeGuard here but not sure it should ever get that far?> | ||
// Might be better to make ICommand a generic | ||
if (!interaction.isChatInputCommand()) { | ||
return | ||
} | ||
|
||
return listCharacters(gateways)().map(charactersMessage) | ||
}, | ||
} | ||
|
||
export default command |
69 changes: 69 additions & 0 deletions
69
packages/bot/src/framework/discord/commands/character/set-property.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
// Can we use subcommands to handle setting a skill, attriute,or power? | ||
// If a character is "locked", only an admin should be able to change the character or exp would need to be spent | ||
import { type ICommand } from '../../types' | ||
import { SlashCommandBuilder } from 'discord.js' | ||
import { okAsync } from 'neverthrow' | ||
import { v4 } from 'uuid' | ||
|
||
const command: ICommand = { | ||
command: new SlashCommandBuilder() | ||
.setName('set-character-property') | ||
.setDescription('Set an attribute, skill, or power on a character') | ||
.addSubcommand((subcommand) => | ||
subcommand | ||
.setName('skill') | ||
.setDescription('Set the skill of a character') | ||
.addUserOption((option) => | ||
option.setName('target').setDescription('The user') | ||
) | ||
) | ||
.addSubcommand((subcommand) => | ||
subcommand | ||
.setName('attribute') | ||
.setDescription('Set the attribute of a character') | ||
.addStringOption((option) => | ||
option | ||
.setName('character') | ||
.setDescription('Character to apply the attribute change to') | ||
.setAutocomplete(true) | ||
) | ||
) | ||
.addSubcommand((subcommand) => | ||
subcommand.setName('power').setDescription('Set the power of a character') | ||
), | ||
// eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
execute(interaction, _) { | ||
// Using a TypeGuard here but not sure it should ever get that far?> | ||
// Might be better to make ICommand a generic | ||
if (!interaction.isChatInputCommand()) { | ||
return | ||
} | ||
|
||
const subcommand = interaction.options.getSubcommand() | ||
|
||
// if (subcommand === 'skill') { | ||
// } else if (subcommand === 'attribute') { | ||
// } else if (subcommand === 'power') { | ||
// } | ||
|
||
return okAsync( | ||
`Valid command ${subcommand} was passed. ${interaction.options.getString( | ||
'character' | ||
)}` | ||
) | ||
}, | ||
// eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
autocomplete(interaction, _) { | ||
if (!interaction.isAutocomplete()) { | ||
return | ||
} | ||
const focusedValue = interaction.options.getFocused() | ||
console.log('Search value', focusedValue) | ||
return [ | ||
{ name: 'Foo', value: v4() }, | ||
{ name: 'Bar', value: v4() }, | ||
] | ||
}, | ||
} | ||
|
||
export default command |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.