Homebrew manager completed !
This commit is contained in:
parent
80a94bee86
commit
da93fcd82d
BIN
db.sqlite-shm
BIN
db.sqlite-shm
Binary file not shown.
BIN
db.sqlite-wal
BIN
db.sqlite-wal
Binary file not shown.
|
|
@ -52,7 +52,7 @@ export const characterTable = table("character", {
|
||||||
id: int().primaryKey({ autoIncrement: true }),
|
id: int().primaryKey({ autoIncrement: true }),
|
||||||
name: text().notNull(),
|
name: text().notNull(),
|
||||||
owner: int().notNull().references(() => usersTable.id, { onDelete: 'cascade', onUpdate: 'cascade' }),
|
owner: int().notNull().references(() => usersTable.id, { onDelete: 'cascade', onUpdate: 'cascade' }),
|
||||||
people: int().notNull(),
|
people: text().notNull(),
|
||||||
level: int().notNull().default(1),
|
level: int().notNull().default(1),
|
||||||
aspect: int(),
|
aspect: int(),
|
||||||
notes: text(),
|
notes: text(),
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
PRAGMA foreign_keys=OFF;--> statement-breakpoint
|
||||||
|
CREATE TABLE `__new_character` (
|
||||||
|
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||||
|
`name` text NOT NULL,
|
||||||
|
`owner` integer NOT NULL,
|
||||||
|
`people` text NOT NULL,
|
||||||
|
`level` integer DEFAULT 1 NOT NULL,
|
||||||
|
`aspect` integer,
|
||||||
|
`notes` text,
|
||||||
|
`health` integer DEFAULT 0 NOT NULL,
|
||||||
|
`mana` integer DEFAULT 0 NOT NULL,
|
||||||
|
`visibility` text DEFAULT 'private' NOT NULL,
|
||||||
|
`thumbnail` blob,
|
||||||
|
FOREIGN KEY (`owner`) REFERENCES `users`(`id`) ON UPDATE cascade ON DELETE cascade
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
INSERT INTO `__new_character`("id", "name", "owner", "people", "level", "aspect", "notes", "health", "mana", "visibility", "thumbnail") SELECT "id", "name", "owner", "people", "level", "aspect", "notes", "health", "mana", "visibility", "thumbnail" FROM `character`;--> statement-breakpoint
|
||||||
|
DROP TABLE `character`;--> statement-breakpoint
|
||||||
|
ALTER TABLE `__new_character` RENAME TO `character`;--> statement-breakpoint
|
||||||
|
PRAGMA foreign_keys=ON;
|
||||||
|
|
@ -0,0 +1,810 @@
|
||||||
|
{
|
||||||
|
"version": "6",
|
||||||
|
"dialect": "sqlite",
|
||||||
|
"id": "6651137c-a198-4538-86be-7cb8b88ca998",
|
||||||
|
"prevId": "8f89d284-71da-46ae-a282-538f8a901294",
|
||||||
|
"tables": {
|
||||||
|
"character_abilities": {
|
||||||
|
"name": "character_abilities",
|
||||||
|
"columns": {
|
||||||
|
"character": {
|
||||||
|
"name": "character",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"ability": {
|
||||||
|
"name": "ability",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"name": "value",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": 0
|
||||||
|
},
|
||||||
|
"max": {
|
||||||
|
"name": "max",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"character_abilities_character_character_id_fk": {
|
||||||
|
"name": "character_abilities_character_character_id_fk",
|
||||||
|
"tableFrom": "character_abilities",
|
||||||
|
"tableTo": "character",
|
||||||
|
"columnsFrom": [
|
||||||
|
"character"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "cascade"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {
|
||||||
|
"character_abilities_character_ability_pk": {
|
||||||
|
"columns": [
|
||||||
|
"character",
|
||||||
|
"ability"
|
||||||
|
],
|
||||||
|
"name": "character_abilities_character_ability_pk"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"character_choices": {
|
||||||
|
"name": "character_choices",
|
||||||
|
"columns": {
|
||||||
|
"character": {
|
||||||
|
"name": "character",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"choice": {
|
||||||
|
"name": "choice",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"character_choices_character_character_id_fk": {
|
||||||
|
"name": "character_choices_character_character_id_fk",
|
||||||
|
"tableFrom": "character_choices",
|
||||||
|
"tableTo": "character",
|
||||||
|
"columnsFrom": [
|
||||||
|
"character"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "cascade"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {
|
||||||
|
"character_choices_character_id_choice_pk": {
|
||||||
|
"columns": [
|
||||||
|
"character",
|
||||||
|
"id",
|
||||||
|
"choice"
|
||||||
|
],
|
||||||
|
"name": "character_choices_character_id_choice_pk"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"character_leveling": {
|
||||||
|
"name": "character_leveling",
|
||||||
|
"columns": {
|
||||||
|
"character": {
|
||||||
|
"name": "character",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"level": {
|
||||||
|
"name": "level",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"choice": {
|
||||||
|
"name": "choice",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"character_leveling_character_character_id_fk": {
|
||||||
|
"name": "character_leveling_character_character_id_fk",
|
||||||
|
"tableFrom": "character_leveling",
|
||||||
|
"tableTo": "character",
|
||||||
|
"columnsFrom": [
|
||||||
|
"character"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "cascade"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {
|
||||||
|
"character_leveling_character_level_pk": {
|
||||||
|
"columns": [
|
||||||
|
"character",
|
||||||
|
"level"
|
||||||
|
],
|
||||||
|
"name": "character_leveling_character_level_pk"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"character_modifiers": {
|
||||||
|
"name": "character_modifiers",
|
||||||
|
"columns": {
|
||||||
|
"character": {
|
||||||
|
"name": "character",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"modifier": {
|
||||||
|
"name": "modifier",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"name": "value",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"character_modifiers_character_character_id_fk": {
|
||||||
|
"name": "character_modifiers_character_character_id_fk",
|
||||||
|
"tableFrom": "character_modifiers",
|
||||||
|
"tableTo": "character",
|
||||||
|
"columnsFrom": [
|
||||||
|
"character"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "cascade"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {
|
||||||
|
"character_modifiers_character_modifier_pk": {
|
||||||
|
"columns": [
|
||||||
|
"character",
|
||||||
|
"modifier"
|
||||||
|
],
|
||||||
|
"name": "character_modifiers_character_modifier_pk"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"character_spell": {
|
||||||
|
"name": "character_spell",
|
||||||
|
"columns": {
|
||||||
|
"character": {
|
||||||
|
"name": "character",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"name": "value",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"character_spell_character_character_id_fk": {
|
||||||
|
"name": "character_spell_character_character_id_fk",
|
||||||
|
"tableFrom": "character_spell",
|
||||||
|
"tableTo": "character",
|
||||||
|
"columnsFrom": [
|
||||||
|
"character"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "cascade"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {
|
||||||
|
"character_spell_character_value_pk": {
|
||||||
|
"columns": [
|
||||||
|
"character",
|
||||||
|
"value"
|
||||||
|
],
|
||||||
|
"name": "character_spell_character_value_pk"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"character": {
|
||||||
|
"name": "character",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": true
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"owner": {
|
||||||
|
"name": "owner",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"people": {
|
||||||
|
"name": "people",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"level": {
|
||||||
|
"name": "level",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": 1
|
||||||
|
},
|
||||||
|
"aspect": {
|
||||||
|
"name": "aspect",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"notes": {
|
||||||
|
"name": "notes",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"health": {
|
||||||
|
"name": "health",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": 0
|
||||||
|
},
|
||||||
|
"mana": {
|
||||||
|
"name": "mana",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": 0
|
||||||
|
},
|
||||||
|
"visibility": {
|
||||||
|
"name": "visibility",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'private'"
|
||||||
|
},
|
||||||
|
"thumbnail": {
|
||||||
|
"name": "thumbnail",
|
||||||
|
"type": "blob",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"character_owner_users_id_fk": {
|
||||||
|
"name": "character_owner_users_id_fk",
|
||||||
|
"tableFrom": "character",
|
||||||
|
"tableTo": "users",
|
||||||
|
"columnsFrom": [
|
||||||
|
"owner"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "cascade"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"character_training": {
|
||||||
|
"name": "character_training",
|
||||||
|
"columns": {
|
||||||
|
"character": {
|
||||||
|
"name": "character",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"stat": {
|
||||||
|
"name": "stat",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"level": {
|
||||||
|
"name": "level",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"choice": {
|
||||||
|
"name": "choice",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"character_training_character_character_id_fk": {
|
||||||
|
"name": "character_training_character_character_id_fk",
|
||||||
|
"tableFrom": "character_training",
|
||||||
|
"tableTo": "character",
|
||||||
|
"columnsFrom": [
|
||||||
|
"character"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "cascade"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {
|
||||||
|
"character_training_character_stat_level_pk": {
|
||||||
|
"columns": [
|
||||||
|
"character",
|
||||||
|
"stat",
|
||||||
|
"level"
|
||||||
|
],
|
||||||
|
"name": "character_training_character_stat_level_pk"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"email_validation": {
|
||||||
|
"name": "email_validation",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"timestamp": {
|
||||||
|
"name": "timestamp",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"project_content": {
|
||||||
|
"name": "project_content",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"content": {
|
||||||
|
"name": "content",
|
||||||
|
"type": "blob",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"project_files": {
|
||||||
|
"name": "project_files",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"path": {
|
||||||
|
"name": "path",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"owner": {
|
||||||
|
"name": "owner",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"title": {
|
||||||
|
"name": "title",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"name": "type",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"navigable": {
|
||||||
|
"name": "navigable",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"private": {
|
||||||
|
"name": "private",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"order": {
|
||||||
|
"name": "order",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"timestamp": {
|
||||||
|
"name": "timestamp",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {
|
||||||
|
"project_files_path_unique": {
|
||||||
|
"name": "project_files_path_unique",
|
||||||
|
"columns": [
|
||||||
|
"path"
|
||||||
|
],
|
||||||
|
"isUnique": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"foreignKeys": {
|
||||||
|
"project_files_owner_users_id_fk": {
|
||||||
|
"name": "project_files_owner_users_id_fk",
|
||||||
|
"tableFrom": "project_files",
|
||||||
|
"tableTo": "users",
|
||||||
|
"columnsFrom": [
|
||||||
|
"owner"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "cascade"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"user_permissions": {
|
||||||
|
"name": "user_permissions",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"permission": {
|
||||||
|
"name": "permission",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"user_permissions_id_users_id_fk": {
|
||||||
|
"name": "user_permissions_id_users_id_fk",
|
||||||
|
"tableFrom": "user_permissions",
|
||||||
|
"tableTo": "users",
|
||||||
|
"columnsFrom": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "cascade"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {
|
||||||
|
"user_permissions_id_permission_pk": {
|
||||||
|
"columns": [
|
||||||
|
"id",
|
||||||
|
"permission"
|
||||||
|
],
|
||||||
|
"name": "user_permissions_id_permission_pk"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"user_sessions": {
|
||||||
|
"name": "user_sessions",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"user_id": {
|
||||||
|
"name": "user_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"timestamp": {
|
||||||
|
"name": "timestamp",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"user_sessions_user_id_users_id_fk": {
|
||||||
|
"name": "user_sessions_user_id_users_id_fk",
|
||||||
|
"tableFrom": "user_sessions",
|
||||||
|
"tableTo": "users",
|
||||||
|
"columnsFrom": [
|
||||||
|
"user_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "cascade"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {
|
||||||
|
"user_sessions_id_user_id_pk": {
|
||||||
|
"columns": [
|
||||||
|
"id",
|
||||||
|
"user_id"
|
||||||
|
],
|
||||||
|
"name": "user_sessions_id_user_id_pk"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"users_data": {
|
||||||
|
"name": "users_data",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"signin": {
|
||||||
|
"name": "signin",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"lastTimestamp": {
|
||||||
|
"name": "lastTimestamp",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"users_data_id_users_id_fk": {
|
||||||
|
"name": "users_data_id_users_id_fk",
|
||||||
|
"tableFrom": "users_data",
|
||||||
|
"tableTo": "users",
|
||||||
|
"columnsFrom": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "cascade"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"users": {
|
||||||
|
"name": "users",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": true
|
||||||
|
},
|
||||||
|
"username": {
|
||||||
|
"name": "username",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"email": {
|
||||||
|
"name": "email",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"hash": {
|
||||||
|
"name": "hash",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"state": {
|
||||||
|
"name": "state",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {
|
||||||
|
"users_username_unique": {
|
||||||
|
"name": "users_username_unique",
|
||||||
|
"columns": [
|
||||||
|
"username"
|
||||||
|
],
|
||||||
|
"isUnique": true
|
||||||
|
},
|
||||||
|
"users_email_unique": {
|
||||||
|
"name": "users_email_unique",
|
||||||
|
"columns": [
|
||||||
|
"email"
|
||||||
|
],
|
||||||
|
"isUnique": true
|
||||||
|
},
|
||||||
|
"users_hash_unique": {
|
||||||
|
"name": "users_hash_unique",
|
||||||
|
"columns": [
|
||||||
|
"hash"
|
||||||
|
],
|
||||||
|
"isUnique": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"views": {},
|
||||||
|
"enums": {},
|
||||||
|
"_meta": {
|
||||||
|
"schemas": {},
|
||||||
|
"tables": {},
|
||||||
|
"columns": {}
|
||||||
|
},
|
||||||
|
"internal": {
|
||||||
|
"indexes": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -106,6 +106,13 @@
|
||||||
"when": 1753175811770,
|
"when": 1753175811770,
|
||||||
"tag": "0014_careless_nick_fury",
|
"tag": "0014_careless_nick_fury",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 15,
|
||||||
|
"version": "6",
|
||||||
|
"when": 1756214160038,
|
||||||
|
"tag": "0015_typical_blade",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -58,6 +58,7 @@ export default defineEventHandler(async (e) => {
|
||||||
modifiers: true,
|
modifiers: true,
|
||||||
spells: true,
|
spells: true,
|
||||||
training: true,
|
training: true,
|
||||||
|
choices: true,
|
||||||
user: {
|
user: {
|
||||||
columns: { username: true }
|
columns: { username: true }
|
||||||
}
|
}
|
||||||
|
|
@ -80,9 +81,10 @@ export default defineEventHandler(async (e) => {
|
||||||
|
|
||||||
training: character.training.reduce((p, v) => { if(!(v.stat in p)) p[v.stat] = []; p[v.stat].push([v.level as TrainingLevel, v.choice]); return p; }, {} as Record<MainStat, DoubleIndex<TrainingLevel>[]>),
|
training: character.training.reduce((p, v) => { if(!(v.stat in p)) p[v.stat] = []; p[v.stat].push([v.level as TrainingLevel, v.choice]); return p; }, {} as Record<MainStat, DoubleIndex<TrainingLevel>[]>),
|
||||||
leveling: character.levels.map(e => [e.level as Level, e.choice] as DoubleIndex<Level>),
|
leveling: character.levels.map(e => [e.level as Level, e.choice] as DoubleIndex<Level>),
|
||||||
abilities: group(character.abilities.map(e => ({ ...e, value: [e.value, e.max] as [number, number] })), "ability", "value"),
|
abilities: group(character.abilities, "ability", "value"),
|
||||||
spells: character.spells.map(e => e.value),
|
spells: character.spells.map(e => e.value),
|
||||||
modifiers: group(character.modifiers, "modifier", "value"),
|
modifiers: group(character.modifiers, "modifier", "value"),
|
||||||
|
choices: character.choices.reduce((p, v) => { p[v.id] ??= []; p[v.id]?.push(v.choice); return p; }, {} as Record<string, number[]>),
|
||||||
|
|
||||||
owner: character.owner,
|
owner: character.owner,
|
||||||
username: character.user.username,
|
username: character.user.username,
|
||||||
|
|
|
||||||
|
|
@ -1,149 +0,0 @@
|
||||||
import useDatabase from '~/composables/useDatabase';
|
|
||||||
import { type Character, type CharacterConfig, type CompiledCharacter, type DoubleIndex, type Level, type MainStat, type TrainingLevel, type TrainingOption } from '~/types/character';
|
|
||||||
import characterData from '#shared/character-config.json';
|
|
||||||
import { group } from '#shared/general.util';
|
|
||||||
import { defaultCharacter, MAIN_STATS } from '#shared/character.util';
|
|
||||||
|
|
||||||
export default defineCachedEventHandler(async (e) => {
|
|
||||||
const id = getRouterParam(e, "id");
|
|
||||||
if(!id)
|
|
||||||
{
|
|
||||||
setResponseStatus(e, 400);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const db = useDatabase();
|
|
||||||
const character = db.query.characterTable.findFirst({
|
|
||||||
with: {
|
|
||||||
abilities: true,
|
|
||||||
levels: true,
|
|
||||||
modifiers: true,
|
|
||||||
spells: true,
|
|
||||||
training: true,
|
|
||||||
user: {
|
|
||||||
columns: { username: true }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
where: (character, { eq }) => eq(character.id, parseInt(id, 10)),
|
|
||||||
}).sync();
|
|
||||||
|
|
||||||
if(character !== undefined)
|
|
||||||
{
|
|
||||||
return compileCharacter(Object.assign(defaultCharacter, {
|
|
||||||
id: character.id,
|
|
||||||
|
|
||||||
name: character.name,
|
|
||||||
people: character.people,
|
|
||||||
level: character.level,
|
|
||||||
aspect: character.aspect,
|
|
||||||
notes: character.notes,
|
|
||||||
health: character.health,
|
|
||||||
mana: character.mana,
|
|
||||||
|
|
||||||
training: character.training.reduce((p, v) => { if(!(v.stat in p)) p[v.stat] = []; p[v.stat].push([v.level as TrainingLevel, v.choice]); return p; }, {} as Record<MainStat, DoubleIndex<TrainingLevel>[]>),
|
|
||||||
leveling: character.levels.map(e => [e.level as Level, e.choice] as DoubleIndex<Level>),
|
|
||||||
abilities: group(character.abilities.map(e => ({ ...e, value: [e.value, e.max] as [number, number] })), "ability", "value"),
|
|
||||||
spells: character.spells.map(e => e.value),
|
|
||||||
modifiers: group(character.modifiers, "modifier", "value"),
|
|
||||||
|
|
||||||
owner: character.owner,
|
|
||||||
username: character.user.username,
|
|
||||||
visibility: character.visibility,
|
|
||||||
} as Character) as Character);
|
|
||||||
}
|
|
||||||
|
|
||||||
setResponseStatus(e, 404);
|
|
||||||
return;
|
|
||||||
}, { name: "character", getKey: (e) => getRouterParam(e, "id") || 'error' });
|
|
||||||
|
|
||||||
function compileCharacter(character: Character & { username?: string }): CompiledCharacter
|
|
||||||
{
|
|
||||||
const config = characterData as CharacterConfig;
|
|
||||||
const race = character.people !== undefined ? config.peoples[character.people] : undefined;
|
|
||||||
const raceOptions = race ? character.leveling!.map(e => race.options[e[0]][e[1]]) : [];
|
|
||||||
const features = Object.entries(config.training).map(e => [e[0], getFeaturesOf(e[0] as MainStat, character.training[e[0] as MainStat])]) as [MainStat, TrainingOption[]][];
|
|
||||||
|
|
||||||
const compiled: CompiledCharacter = {
|
|
||||||
id: character.id,
|
|
||||||
owner: character.owner,
|
|
||||||
username: character.username,
|
|
||||||
name: character.name,
|
|
||||||
health: raceOptions.reduce((p, v) => p + (v.health ?? 0), 0),
|
|
||||||
mana: raceOptions.reduce((p, v) => p + (v.mana ?? 0), 0),
|
|
||||||
race: character.people!,
|
|
||||||
modifier: features.map(e => [e[0], Math.floor((e[1].length - 1) / 3) + (character.modifiers[e[0]] ?? 0)] as [MainStat, number]).reduce((p, v) => { p[v[0]] = v[1]; return p }, {} as Record<MainStat, number>),
|
|
||||||
level: character.level,
|
|
||||||
values: {
|
|
||||||
health: character.health,
|
|
||||||
mana: character.mana
|
|
||||||
},
|
|
||||||
features: {
|
|
||||||
action: [],
|
|
||||||
reaction: [],
|
|
||||||
freeaction: [],
|
|
||||||
passive: [],
|
|
||||||
},
|
|
||||||
abilities: {
|
|
||||||
athletics: 0,
|
|
||||||
acrobatics: 0,
|
|
||||||
intimidation: 0,
|
|
||||||
sleightofhand: 0,
|
|
||||||
stealth: 0,
|
|
||||||
survival: 0,
|
|
||||||
investigation: 0,
|
|
||||||
history: 0,
|
|
||||||
religion: 0,
|
|
||||||
arcana: 0,
|
|
||||||
understanding: 0,
|
|
||||||
perception: 0,
|
|
||||||
performance: 0,
|
|
||||||
medecine: 0,
|
|
||||||
persuasion: 0,
|
|
||||||
animalhandling: 0,
|
|
||||||
deception: 0
|
|
||||||
},
|
|
||||||
spellslots: 0,
|
|
||||||
artslots: 0,
|
|
||||||
spellranks: {
|
|
||||||
instinct: 0,
|
|
||||||
knowledge: 0,
|
|
||||||
precision: 0,
|
|
||||||
arts: 0,
|
|
||||||
},
|
|
||||||
spells: character.spells ?? [],
|
|
||||||
speed: false,
|
|
||||||
defense: {
|
|
||||||
hardcap: Infinity,
|
|
||||||
static: 6,
|
|
||||||
activeparry: 0,
|
|
||||||
activedodge: 0,
|
|
||||||
passiveparry: 0,
|
|
||||||
passivedodge: 0,
|
|
||||||
},
|
|
||||||
mastery: {
|
|
||||||
strength: 0,
|
|
||||||
dexterity: 0,
|
|
||||||
shield: 0,
|
|
||||||
armor: 0,
|
|
||||||
multiattack: 1,
|
|
||||||
magicpower: 0,
|
|
||||||
magicspeed: 0,
|
|
||||||
magicelement: 0,
|
|
||||||
magicinstinct: 0,
|
|
||||||
},
|
|
||||||
resistance: {},//Object.fromEntries(MAIN_STATS.map(e => [e as MainStat, [0, 0]])) as Record<MainStat, [number, number]>,
|
|
||||||
initiative: 0,
|
|
||||||
aspect: "",
|
|
||||||
notes: character.notes ?? "",
|
|
||||||
};
|
|
||||||
|
|
||||||
//features.forEach(e => e[1].forEach(_e => _e.features?.forEach(f => applyFeature(compiled, f))));
|
|
||||||
|
|
||||||
return compiled;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getFeaturesOf(stat: MainStat, progression: DoubleIndex<TrainingLevel>[]): TrainingOption[]
|
|
||||||
{
|
|
||||||
const config = characterData as CharacterConfig;
|
|
||||||
return progression.map(e => config.training[stat][e[0]][e[1]]);
|
|
||||||
}
|
|
||||||
|
|
@ -745,8 +745,9 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"peoples": [
|
"peoples": {
|
||||||
{
|
"e662m19q590kn4dowvssowi1qf8ia7sk": {
|
||||||
|
"id": "e662m19q590kn4dowvssowi1qf8ia7sk",
|
||||||
"name": "Humain",
|
"name": "Humain",
|
||||||
"description": "Les humains, originaire d'un tout autre monde, ont subit un cataclysme qui les a projeté dans les terres d'Erina. En tant que civilisation dépourvue de magie, ils sont plus specialisés, gagnant moins de statistiques mais pouvant plus tôt ou plus fréquemment obtenir certains bonus.",
|
"description": "Les humains, originaire d'un tout autre monde, ont subit un cataclysme qui les a projeté dans les terres d'Erina. En tant que civilisation dépourvue de magie, ils sont plus specialisés, gagnant moins de statistiques mais pouvant plus tôt ou plus fréquemment obtenir certains bonus.",
|
||||||
"options": {
|
"options": {
|
||||||
|
|
@ -833,8 +834,75 @@
|
||||||
"9q8mf0u06oxxwqltyv58kbavs7qtoouw"
|
"9q8mf0u06oxxwqltyv58kbavs7qtoouw"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"3v3rwsn9bimpyd2fc95ml8wdrrmfsqb0": {
|
||||||
|
"id": "3v3rwsn9bimpyd2fc95ml8wdrrmfsqb0",
|
||||||
|
"name": "Quplothien",
|
||||||
|
"description": "Quploth est la région du monde abritant le plus de marchands et charlatans. Dû à la sur-désertification de leurs terres, ils ont appris à vivre en troquant les richesses. Leurs cités, denses et prospères, sont peu nombreuses et suscitent un tourisme culturel croissant.",
|
||||||
|
"options": {
|
||||||
|
"1": [
|
||||||
|
"bbuzw6awn2mkb05imdoecxfkc5zuj98i"
|
||||||
|
],
|
||||||
|
"2": [
|
||||||
|
"ub2ws6q8xdbngeouip02umvw2oox9r68"
|
||||||
|
],
|
||||||
|
"3": [
|
||||||
|
"5d7u2jvi4u0nnrzesderha3uo8kb3zjq"
|
||||||
|
],
|
||||||
|
"4": [
|
||||||
|
"8w4jthjrn3l8u4trmj46z6t6ab5rbgk3"
|
||||||
|
],
|
||||||
|
"5": [
|
||||||
|
"z9lux6nlhl8pjhcwst6bnhpn6cq6c77w"
|
||||||
|
],
|
||||||
|
"6": [
|
||||||
|
"dx5khvrhwkhhn8fv4b8pecuh8i5wtwij"
|
||||||
|
],
|
||||||
|
"7": [
|
||||||
|
"pfzopr4oyrsgxg0cbva16zzzly3kke9z"
|
||||||
|
],
|
||||||
|
"8": [
|
||||||
|
"fk0wmg94tlq78khq8zot2o5u4nnxr2gb"
|
||||||
|
],
|
||||||
|
"9": [
|
||||||
|
"u9vv3z280jgzab7pjwe9kexqjlpoxvax"
|
||||||
|
],
|
||||||
|
"10": [
|
||||||
|
"fuxn9ndabr5yl0rrdtilldssmjxso24p"
|
||||||
|
],
|
||||||
|
"11": [
|
||||||
|
"7kdxs6b6j9pqhgm3c8ydp8f9o074vp8q"
|
||||||
|
],
|
||||||
|
"12": [
|
||||||
|
"dwvjqspm8l0gnks6y7u9vty0563u20kd"
|
||||||
|
],
|
||||||
|
"13": [
|
||||||
|
"bmh55yfypfw8rezd16m2cuocrx0kkfl4"
|
||||||
|
],
|
||||||
|
"14": [
|
||||||
|
"hatuas1yl3armteqwjwm1gjpsjp3v97x"
|
||||||
|
],
|
||||||
|
"15": [
|
||||||
|
"0fg543b25uppvollu9oxtowyxjjw1x5a"
|
||||||
|
],
|
||||||
|
"16": [
|
||||||
|
"l770gvirwzfbtfgfl29dxhvdh95f1m71"
|
||||||
|
],
|
||||||
|
"17": [
|
||||||
|
"m1zkviiz3ow1g7rwpkyygmyggphvoz8b"
|
||||||
|
],
|
||||||
|
"18": [
|
||||||
|
"uuc8vci5bk5kkx23a7ks1gu778fmu9w1"
|
||||||
|
],
|
||||||
|
"19": [
|
||||||
|
"fd076hnyjagipaez166p9xp3wtlf5sgw"
|
||||||
|
],
|
||||||
|
"20": [
|
||||||
|
"qcp28eysi3l3n438v41kowisdpq4ht61"
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
"training": {
|
"training": {
|
||||||
"strength": {
|
"strength": {
|
||||||
"0": [
|
"0": [
|
||||||
|
|
@ -9452,6 +9520,106 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"id": "9q8mf0u06oxxwqltyv58kbavs7qtoouw"
|
"id": "9q8mf0u06oxxwqltyv58kbavs7qtoouw"
|
||||||
|
},
|
||||||
|
"bbuzw6awn2mkb05imdoecxfkc5zuj98i": {
|
||||||
|
"id": "bbuzw6awn2mkb05imdoecxfkc5zuj98i",
|
||||||
|
"description": "Bonjour",
|
||||||
|
"effect": []
|
||||||
|
},
|
||||||
|
"ub2ws6q8xdbngeouip02umvw2oox9r68": {
|
||||||
|
"id": "ub2ws6q8xdbngeouip02umvw2oox9r68",
|
||||||
|
"description": "je",
|
||||||
|
"effect": []
|
||||||
|
},
|
||||||
|
"5d7u2jvi4u0nnrzesderha3uo8kb3zjq": {
|
||||||
|
"id": "5d7u2jvi4u0nnrzesderha3uo8kb3zjq",
|
||||||
|
"description": "suis",
|
||||||
|
"effect": []
|
||||||
|
},
|
||||||
|
"8w4jthjrn3l8u4trmj46z6t6ab5rbgk3": {
|
||||||
|
"id": "8w4jthjrn3l8u4trmj46z6t6ab5rbgk3",
|
||||||
|
"description": "Nicolas",
|
||||||
|
"effect": []
|
||||||
|
},
|
||||||
|
"z9lux6nlhl8pjhcwst6bnhpn6cq6c77w": {
|
||||||
|
"id": "z9lux6nlhl8pjhcwst6bnhpn6cq6c77w",
|
||||||
|
"description": "Sarkozy",
|
||||||
|
"effect": []
|
||||||
|
},
|
||||||
|
"dx5khvrhwkhhn8fv4b8pecuh8i5wtwij": {
|
||||||
|
"id": "dx5khvrhwkhhn8fv4b8pecuh8i5wtwij",
|
||||||
|
"description": "",
|
||||||
|
"effect": []
|
||||||
|
},
|
||||||
|
"pfzopr4oyrsgxg0cbva16zzzly3kke9z": {
|
||||||
|
"id": "pfzopr4oyrsgxg0cbva16zzzly3kke9z",
|
||||||
|
"description": "",
|
||||||
|
"effect": []
|
||||||
|
},
|
||||||
|
"fk0wmg94tlq78khq8zot2o5u4nnxr2gb": {
|
||||||
|
"id": "fk0wmg94tlq78khq8zot2o5u4nnxr2gb",
|
||||||
|
"description": "",
|
||||||
|
"effect": []
|
||||||
|
},
|
||||||
|
"u9vv3z280jgzab7pjwe9kexqjlpoxvax": {
|
||||||
|
"id": "u9vv3z280jgzab7pjwe9kexqjlpoxvax",
|
||||||
|
"description": "",
|
||||||
|
"effect": []
|
||||||
|
},
|
||||||
|
"fuxn9ndabr5yl0rrdtilldssmjxso24p": {
|
||||||
|
"id": "fuxn9ndabr5yl0rrdtilldssmjxso24p",
|
||||||
|
"description": "",
|
||||||
|
"effect": []
|
||||||
|
},
|
||||||
|
"7kdxs6b6j9pqhgm3c8ydp8f9o074vp8q": {
|
||||||
|
"id": "7kdxs6b6j9pqhgm3c8ydp8f9o074vp8q",
|
||||||
|
"description": "",
|
||||||
|
"effect": []
|
||||||
|
},
|
||||||
|
"dwvjqspm8l0gnks6y7u9vty0563u20kd": {
|
||||||
|
"id": "dwvjqspm8l0gnks6y7u9vty0563u20kd",
|
||||||
|
"description": "",
|
||||||
|
"effect": []
|
||||||
|
},
|
||||||
|
"bmh55yfypfw8rezd16m2cuocrx0kkfl4": {
|
||||||
|
"id": "bmh55yfypfw8rezd16m2cuocrx0kkfl4",
|
||||||
|
"description": "",
|
||||||
|
"effect": []
|
||||||
|
},
|
||||||
|
"hatuas1yl3armteqwjwm1gjpsjp3v97x": {
|
||||||
|
"id": "hatuas1yl3armteqwjwm1gjpsjp3v97x",
|
||||||
|
"description": "",
|
||||||
|
"effect": []
|
||||||
|
},
|
||||||
|
"0fg543b25uppvollu9oxtowyxjjw1x5a": {
|
||||||
|
"id": "0fg543b25uppvollu9oxtowyxjjw1x5a",
|
||||||
|
"description": "",
|
||||||
|
"effect": []
|
||||||
|
},
|
||||||
|
"l770gvirwzfbtfgfl29dxhvdh95f1m71": {
|
||||||
|
"id": "l770gvirwzfbtfgfl29dxhvdh95f1m71",
|
||||||
|
"description": "",
|
||||||
|
"effect": []
|
||||||
|
},
|
||||||
|
"m1zkviiz3ow1g7rwpkyygmyggphvoz8b": {
|
||||||
|
"id": "m1zkviiz3ow1g7rwpkyygmyggphvoz8b",
|
||||||
|
"description": "",
|
||||||
|
"effect": []
|
||||||
|
},
|
||||||
|
"uuc8vci5bk5kkx23a7ks1gu778fmu9w1": {
|
||||||
|
"id": "uuc8vci5bk5kkx23a7ks1gu778fmu9w1",
|
||||||
|
"description": "",
|
||||||
|
"effect": []
|
||||||
|
},
|
||||||
|
"fd076hnyjagipaez166p9xp3wtlf5sgw": {
|
||||||
|
"id": "fd076hnyjagipaez166p9xp3wtlf5sgw",
|
||||||
|
"description": "",
|
||||||
|
"effect": []
|
||||||
|
},
|
||||||
|
"qcp28eysi3l3n438v41kowisdpq4ht61": {
|
||||||
|
"id": "qcp28eysi3l3n438v41kowisdpq4ht61",
|
||||||
|
"description": "",
|
||||||
|
"effect": []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -428,7 +428,7 @@ export class CharacterBuilder extends CharacterCompiler
|
||||||
}
|
}
|
||||||
private render()
|
private render()
|
||||||
{
|
{
|
||||||
/*this._steps = [
|
this._steps = [
|
||||||
PeoplePicker,
|
PeoplePicker,
|
||||||
LevelPicker,
|
LevelPicker,
|
||||||
TrainingPicker,
|
TrainingPicker,
|
||||||
|
|
@ -439,7 +439,7 @@ export class CharacterBuilder extends CharacterCompiler
|
||||||
dom("div", { class: "group flex items-center", }, [
|
dom("div", { class: "group flex items-center", }, [
|
||||||
dom("div", { class: "px-2 py-1 border-b border-transparent hover:border-accent-blue disabled:text-light-50 dark:disabled:text-dark-50 disabled:hover:border-transparent group-data-[state=active]:text-accent-blue cursor-pointer", listeners: { click: () => this.display(i) } }, [text(e.header)]),
|
dom("div", { class: "px-2 py-1 border-b border-transparent hover:border-accent-blue disabled:text-light-50 dark:disabled:text-dark-50 disabled:hover:border-transparent group-data-[state=active]:text-accent-blue cursor-pointer", listeners: { click: () => this.display(i) } }, [text(e.header)]),
|
||||||
])
|
])
|
||||||
);*/
|
);
|
||||||
this._helperText = text("Choisissez un peuple afin de définir la progression de votre personnage au fil des niveaux.")
|
this._helperText = text("Choisissez un peuple afin de définir la progression de votre personnage au fil des niveaux.")
|
||||||
this._content = dom('div', { class: 'flex-1 outline-none max-w-full w-full overflow-y-auto', attributes: { id: 'characterEditorContainer' } });
|
this._content = dom('div', { class: 'flex-1 outline-none max-w-full w-full overflow-y-auto', attributes: { id: 'characterEditorContainer' } });
|
||||||
this._container.appendChild(div('flex flex-1 flex-col justify-start items-center px-8 w-full h-full overflow-y-hidden', [
|
this._container.appendChild(div('flex flex-1 flex-col justify-start items-center px-8 w-full h-full overflow-y-hidden', [
|
||||||
|
|
@ -460,15 +460,15 @@ export class CharacterBuilder extends CharacterCompiler
|
||||||
if(step < 0 || step >= this._stepsHeader.length)
|
if(step < 0 || step >= this._stepsHeader.length)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
//if(step !== 0 && this._steps.slice(0, step).some(e => !e.validate(this)))
|
if(step !== 0 && this._steps.slice(0, step).some(e => !e.validate(this)))
|
||||||
// return;
|
return;
|
||||||
|
|
||||||
//this._stepsHeader.forEach(e => e.setAttribute('data-state', 'inactive'));
|
this._stepsHeader.forEach(e => e.setAttribute('data-state', 'inactive'));
|
||||||
//this._stepsHeader[step]!.setAttribute('data-state', 'active');
|
this._stepsHeader[step]!.setAttribute('data-state', 'active');
|
||||||
|
|
||||||
//this._content?.replaceChildren(...(new this._steps[step]!(this)).dom);
|
this._content?.replaceChildren(...(new this._steps[step]!(this)).dom);
|
||||||
|
|
||||||
//this._helperText.textContent = this._steps[step]!.description;
|
this._helperText.textContent = this._steps[step]!.description;
|
||||||
}
|
}
|
||||||
async save(leave: boolean = true)
|
async save(leave: boolean = true)
|
||||||
{
|
{
|
||||||
|
|
@ -681,7 +681,7 @@ abstract class BuilderTab {
|
||||||
};
|
};
|
||||||
type BuilderTabConstructor = {
|
type BuilderTabConstructor = {
|
||||||
new (builder: CharacterBuilder): BuilderTab;
|
new (builder: CharacterBuilder): BuilderTab;
|
||||||
name: string;
|
header: string;
|
||||||
description: string;
|
description: string;
|
||||||
validate(builder: CharacterBuilder): boolean;
|
validate(builder: CharacterBuilder): boolean;
|
||||||
}
|
}
|
||||||
|
|
@ -691,8 +691,6 @@ class PeoplePicker extends BuilderTab
|
||||||
private _visibilityInput: HTMLDivElement;
|
private _visibilityInput: HTMLDivElement;
|
||||||
private _options: HTMLDivElement[];
|
private _options: HTMLDivElement[];
|
||||||
|
|
||||||
private _activeOption?: HTMLDivElement;
|
|
||||||
|
|
||||||
static override header = 'Peuple';
|
static override header = 'Peuple';
|
||||||
static override description = 'Choisissez un peuple afin de définir la progression de votre personnage au fil des niveaux.';
|
static override description = 'Choisissez un peuple afin de définir la progression de votre personnage au fil des niveaux.';
|
||||||
|
|
||||||
|
|
@ -704,16 +702,15 @@ class PeoplePicker extends BuilderTab
|
||||||
input: (value) => {
|
input: (value) => {
|
||||||
this._builder.character.name = value ?? '';
|
this._builder.character.name = value ?? '';
|
||||||
document.title = `d[any] - Edition de ${this._builder.character.name || 'nouveau personnage'}`;
|
document.title = `d[any] - Edition de ${this._builder.character.name || 'nouveau personnage'}`;
|
||||||
}
|
}, defaultValue: this._builder.character.name
|
||||||
});
|
});
|
||||||
this._visibilityInput = toggle({ defaultValue: this._builder.character.visibility === "private", change: (value) => this._builder.character.visibility = value ? "private" : "public" });
|
this._visibilityInput = toggle({ defaultValue: this._builder.character.visibility === "private", change: (value) => this._builder.character.visibility = value ? "private" : "public" });
|
||||||
|
|
||||||
this._options = config.peoples.map(
|
this._options = Object.values(config.peoples).map(
|
||||||
(people, i) => dom("div", { class: "flex flex-col flex-nowrap gap-2 p-2 border border-light-35 dark:border-dark-35 cursor-pointer hover:border-light-70 dark:hover:border-dark-70 w-[320px]", listeners: { click: () => {
|
(people, i) => dom("div", { class: "flex flex-col flex-nowrap gap-2 p-2 border border-light-35 dark:border-dark-35 cursor-pointer hover:border-light-70 dark:hover:border-dark-70 w-[320px]", listeners: { click: () => {
|
||||||
this._builder.character.people = i;
|
this._builder.character.people = people.id;
|
||||||
"border-accent-blue outline-2 outline outline-accent-blue".split(" ").forEach(e => this._activeOption?.classList.toggle(e, false));
|
"border-accent-blue outline-2 outline outline-accent-blue".split(" ").forEach(e => this._options.forEach(f => f?.classList.toggle(e, false)));
|
||||||
this._activeOption = this._options[i]!;
|
"border-accent-blue outline-2 outline outline-accent-blue".split(" ").forEach(e => this._options[i]?.classList.toggle(e, true));
|
||||||
"border-accent-blue outline-2 outline outline-accent-blue".split(" ").forEach(e => this._activeOption?.classList.toggle(e, true));
|
|
||||||
}
|
}
|
||||||
} }, [div("h-[320px]"), div("text-xl font-bold text-center", [text(people.name)]), div("w-full border-b border-light-50 dark:border-dark-50"), div("text-wrap word-break", [text(people.description)])]),
|
} }, [div("h-[320px]"), div("text-xl font-bold text-center", [text(people.name)]), div("w-full border-b border-light-50 dark:border-dark-50"), div("text-wrap word-break", [text(people.description)])]),
|
||||||
);
|
);
|
||||||
|
|
@ -734,13 +731,6 @@ class PeoplePicker extends BuilderTab
|
||||||
{
|
{
|
||||||
this._nameInput.value = this._builder.character.name;
|
this._nameInput.value = this._builder.character.name;
|
||||||
this._visibilityInput.setAttribute('data-state', this._builder.character.visibility === "private" ? "checked" : "unchecked");
|
this._visibilityInput.setAttribute('data-state', this._builder.character.visibility === "private" ? "checked" : "unchecked");
|
||||||
"border-accent-blue outline-2 outline outline-accent-blue".split(" ").forEach(e => this._activeOption?.classList.toggle(e, false));
|
|
||||||
|
|
||||||
if(this._builder.character.people !== undefined)
|
|
||||||
{
|
|
||||||
this._activeOption = this._options[this._builder.character.people]!;
|
|
||||||
"border-accent-blue outline-2 outline outline-accent-blue".split(" ").forEach(e => this._activeOption?.classList.toggle(e, true));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
static override validate(builder: CharacterBuilder): boolean
|
static override validate(builder: CharacterBuilder): boolean
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -47,10 +47,10 @@ export function select<T extends NonNullable<any>>(options: Array<{ text: string
|
||||||
if(e === undefined)
|
if(e === undefined)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
return dom('div', { listeners: { click: () => {
|
return dom('div', { listeners: { click: (_e) => {
|
||||||
textValue.textContent = e.text;
|
textValue.textContent = e.text;
|
||||||
settings?.change && settings?.change(e.value);
|
settings?.change && settings?.change(e.value);
|
||||||
context && context.close && context.close();
|
context && context.close && !_e.ctrlKey && context.close();
|
||||||
}, mouseenter: (e) => focus(i) }, class: ['data-[focused]:bg-light-30 dark:data-[focused]:bg-dark-30 text-light-70 dark:text-dark-70 data-[focused]:text-light-100 dark:data-[focused]:text-dark-100 py-1 px-2 cursor-pointer', settings?.class?.option] }, [ text(e.text) ]);
|
}, mouseenter: (e) => focus(i) }, class: ['data-[focused]:bg-light-30 dark:data-[focused]:bg-dark-30 text-light-70 dark:text-dark-70 data-[focused]:text-light-100 dark:data-[focused]:text-dark-100 py-1 px-2 cursor-pointer', settings?.class?.option] }, [ text(e.text) ]);
|
||||||
});
|
});
|
||||||
const select = dom('div', { listeners: { click: () => {
|
const select = dom('div', { listeners: { click: () => {
|
||||||
|
|
@ -116,12 +116,12 @@ export function multiselect<T extends NonNullable<any>>(options: Array<{ text: s
|
||||||
if(e === undefined)
|
if(e === undefined)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const element = dom('div', { listeners: { click: () => {
|
const element = dom('div', { listeners: { click: (_e) => {
|
||||||
selection = selection.includes(e.value) ? selection.filter(f => f !== e.value) : [...selection, e.value];
|
selection = selection.includes(e.value) ? selection.filter(f => f !== e.value) : [...selection, e.value];
|
||||||
textValue.textContent = selection.length > 0 ? ((options.find(f => f?.value === selection[0])?.text ?? '') + (selection.length > 1 ? ` +${selection.length - 1}` : '')) : '';
|
textValue.textContent = selection.length > 0 ? ((options.find(f => f?.value === selection[0])?.text ?? '') + (selection.length > 1 ? ` +${selection.length - 1}` : '')) : '';
|
||||||
element.toggleAttribute('data-selected', selection.includes(e.value));
|
element.toggleAttribute('data-selected', selection.includes(e.value));
|
||||||
settings?.change && settings?.change(selection);
|
settings?.change && settings?.change(selection);
|
||||||
context && context.close && context.close();
|
context && context.close && !_e.ctrlKey && context.close();
|
||||||
}, mouseenter: (e) => focus(i) }, class: ['group flex flex-row justify-between items-center data-[focused]:bg-light-30 dark:data-[focused]:bg-dark-30 text-light-70 dark:text-dark-70 data-[focused]:text-light-100 dark:data-[focused]:text-dark-100 py-1 px-2 cursor-pointer', settings?.class?.option], attributes: { 'data-selected': selection.includes(e.value) } }, [ text(e.text), icon('radix-icons:check', { class: 'hidden group-data-[selected]:block', noobserver: true }) ]);
|
}, mouseenter: (e) => focus(i) }, class: ['group flex flex-row justify-between items-center data-[focused]:bg-light-30 dark:data-[focused]:bg-dark-30 text-light-70 dark:text-dark-70 data-[focused]:text-light-100 dark:data-[focused]:text-dark-100 py-1 px-2 cursor-pointer', settings?.class?.option], attributes: { 'data-selected': selection.includes(e.value) } }, [ text(e.text), icon('radix-icons:check', { class: 'hidden group-data-[selected]:block', noobserver: true }) ]);
|
||||||
return element;
|
return element;
|
||||||
});
|
});
|
||||||
|
|
@ -240,11 +240,11 @@ export function combobox<T extends NonNullable<any>>(options: Option<T>[], setti
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return { item: option, dom: dom('div', { listeners: { click: () => {
|
return { item: option, dom: dom('div', { listeners: { click: (_e) => {
|
||||||
select.value = option.text;
|
select.value = option.text;
|
||||||
settings?.change && settings?.change(option.value as T);
|
settings?.change && settings?.change(option.value as T);
|
||||||
selected = true;
|
selected = true;
|
||||||
hide();
|
!_e.ctrlKey && hide();
|
||||||
}, mouseenter: () => focus(option.value) }, class: ['data-[focused]:bg-light-30 dark:data-[focused]:bg-dark-30 text-light-70 dark:text-dark-70 data-[focused]:text-light-100 dark:data-[focused]:text-dark-100 py-1 px-2 cursor-pointer', settings?.class?.option] }, [ option?.render ? option?.render() : text(option.text) ]) };
|
}, mouseenter: () => focus(option.value) }, class: ['data-[focused]:bg-light-30 dark:data-[focused]:bg-dark-30 text-light-70 dark:text-dark-70 data-[focused]:text-light-100 dark:data-[focused]:text-dark-100 py-1 px-2 cursor-pointer', settings?.class?.option] }, [ option?.render ? option?.render() : text(option.text) ]) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -347,10 +347,10 @@ export function numberpicker(settings?: { defaultValue?: number, change?: (value
|
||||||
switch(e.key)
|
switch(e.key)
|
||||||
{
|
{
|
||||||
case "ArrowUp":
|
case "ArrowUp":
|
||||||
validateAndChange(storedValue + (e.shiftKey ? 10 : 1)) && settings?.input && settings.input(storedValue);
|
validateAndChange(storedValue + (e.ctrlKey ? 10 : 1)) && settings?.input && settings.input(storedValue);
|
||||||
break;
|
break;
|
||||||
case "ArrowDown":
|
case "ArrowDown":
|
||||||
validateAndChange(storedValue - (e.shiftKey ? 10 : 1)) && settings?.input && settings.input(storedValue);
|
validateAndChange(storedValue - (e.ctrlKey ? 10 : 1)) && settings?.input && settings.input(storedValue);
|
||||||
break;
|
break;
|
||||||
case "PageUp":
|
case "PageUp":
|
||||||
settings?.max && validateAndChange(settings.max) && settings?.input && settings.input(storedValue);
|
settings?.max && validateAndChange(settings.max) && settings?.input && settings.input(storedValue);
|
||||||
|
|
@ -371,12 +371,23 @@ export function numberpicker(settings?: { defaultValue?: number, change?: (value
|
||||||
return field;
|
return field;
|
||||||
}
|
}
|
||||||
// Open by default
|
// Open by default
|
||||||
export function foldable(content: NodeChildren, title: NodeChildren, settings?: { open?: boolean, class?: { container?: Class, title?: Class, content?: Class, icon?: Class } })
|
export function foldable(content: NodeChildren | (() => NodeChildren), title: NodeChildren, settings?: { open?: boolean, class?: { container?: Class, title?: Class, content?: Class, icon?: Class } })
|
||||||
{
|
{
|
||||||
|
let _content: NodeChildren;
|
||||||
|
const display = (state: boolean) => {
|
||||||
|
if(state && !_content)
|
||||||
|
{
|
||||||
|
_content = typeof content === 'function' ? content() : content;
|
||||||
|
//@ts-ignore
|
||||||
|
contentContainer.replaceChildren(..._content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const contentContainer = div(['hidden group-data-[active]:flex', settings?.class?.content]);
|
||||||
const fold = div(['group flex flex-1 w-full flex-col', settings?.class?.container], [
|
const fold = div(['group flex flex-1 w-full flex-col', settings?.class?.container], [
|
||||||
div('flex', [ dom('div', { listeners: { click: () => fold.toggleAttribute('data-active') }, class: ['flex justify-center items-center', settings?.class?.icon] }, [ icon('radix-icons:caret-right', { class: 'group-data-[active]:rotate-90 origin-center' }) ]), div(['flex-1', settings?.class?.title], title) ]),
|
div('flex', [ dom('div', { listeners: { click: () => { display(fold.toggleAttribute('data-active')) } }, class: ['flex justify-center items-center', settings?.class?.icon] }, [ icon('radix-icons:caret-right', { class: 'group-data-[active]:rotate-90 origin-center', noobserver: true }) ]), div(['flex-1', settings?.class?.title], title) ]),
|
||||||
div(['hidden group-data-[active]:flex', settings?.class?.content], content),
|
contentContainer
|
||||||
]);
|
]);
|
||||||
|
display(settings?.open ?? true);
|
||||||
fold.toggleAttribute('data-active', settings?.open ?? true);
|
fold.toggleAttribute('data-active', settings?.open ?? true);
|
||||||
return fold;
|
return fold;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import type { Ability, AspectConfig, CharacterConfig, Feature, FeatureEffect, FeatureItem, MainStat, Resistance, SpellConfig, TrainingLevel } from "~/types/character";
|
import type { Ability, AspectConfig, CharacterConfig, Feature, FeatureEffect, FeatureItem, Level, MainStat, RaceConfig, Resistance, SpellConfig, TrainingLevel } from "~/types/character";
|
||||||
import { div, dom, icon, text, type NodeChildren } from "#shared/dom.util";
|
import { div, dom, icon, text, type NodeChildren } from "#shared/dom.util";
|
||||||
import { MarkdownEditor } from "#shared/editor.util";
|
import { MarkdownEditor } from "#shared/editor.util";
|
||||||
import { fakeA } from "#shared/proses";
|
import { fakeA } from "#shared/proses";
|
||||||
import { button, combobox, foldable, input, multiselect, numberpicker, select, table, toggle, type Option } from "#shared/components.util";
|
import { button, combobox, foldable, input, multiselect, numberpicker, select, table, toggle, type Option } from "#shared/components.util";
|
||||||
import { confirm, contextmenu, fullblocker, tooltip } from "#shared/floating.util";
|
import { confirm, contextmenu, fullblocker, tooltip } from "#shared/floating.util";
|
||||||
import { ALIGNMENTS, alignmentTexts, elementTexts, MAIN_STATS, mainStatShortTexts, mainStatTexts, SPELL_ELEMENTS, SPELL_TYPES, spellTypeTexts } from "#shared/character.util";
|
import { ALIGNMENTS, alignmentTexts, elementTexts, LEVELS, MAIN_STATS, mainStatShortTexts, mainStatTexts, SPELL_ELEMENTS, SPELL_TYPES, spellTypeTexts } from "#shared/character.util";
|
||||||
import characterConfig from "#shared/character-config.json";
|
import characterConfig from "#shared/character-config.json";
|
||||||
import { getID, ID_SIZE } from "#shared/general.util";
|
import { getID, ID_SIZE } from "#shared/general.util";
|
||||||
import renderMarkdown, { renderText } from "#shared/markdown.util";
|
import renderMarkdown, { renderText } from "#shared/markdown.util";
|
||||||
|
|
@ -100,24 +100,73 @@ abstract class BuilderTab {
|
||||||
};
|
};
|
||||||
class PeopleEditor extends BuilderTab
|
class PeopleEditor extends BuilderTab
|
||||||
{
|
{
|
||||||
private _options: HTMLDivElement[];
|
|
||||||
|
|
||||||
private _activeOption?: HTMLDivElement;
|
|
||||||
|
|
||||||
constructor(builder: HomebrewBuilder, config: CharacterConfig)
|
constructor(builder: HomebrewBuilder, config: CharacterConfig)
|
||||||
{
|
{
|
||||||
super(builder, config);
|
super(builder, config);
|
||||||
|
|
||||||
this._options = config.peoples.map(
|
const add = () => {
|
||||||
(people, i) => dom("div", { class: "flex flex-col flex-nowrap gap-2 p-2 border border-light-35 dark:border-dark-35 cursor-pointer hover:border-light-70 dark:hover:border-dark-70 w-[320px]", listeners: { click: () => {
|
const people: RaceConfig = {
|
||||||
"border-accent-blue outline-2 outline outline-accent-blue".split(" ").forEach(e => this._activeOption?.classList.toggle(e, false));
|
id: getID(ID_SIZE),
|
||||||
this._activeOption = this._options[i]!;
|
name: '',
|
||||||
"border-accent-blue outline-2 outline outline-accent-blue".split(" ").forEach(e => this._activeOption?.classList.toggle(e, true));
|
description: '',
|
||||||
|
options: LEVELS.map(e => {
|
||||||
|
const feature: Feature = {
|
||||||
|
id: getID(ID_SIZE),
|
||||||
|
description: '',
|
||||||
|
effect: [],
|
||||||
|
}
|
||||||
|
config.features[feature.id] = feature;
|
||||||
|
return [e, [feature.id]] as [Level, string[]];
|
||||||
|
}).reduce((p, v) => { p[v[0]] = v[1]; return p }, {} as Record<Level, string[]>)
|
||||||
|
};
|
||||||
|
config.peoples[people.id] = people;
|
||||||
|
(this._content[0] as HTMLDivElement).appendChild(peopleRender(people));
|
||||||
|
}
|
||||||
|
const remove = (people: RaceConfig) => {
|
||||||
|
confirm('Voulez vous vraiment supprimer cet aspect ?').then(e => {
|
||||||
|
if(e)
|
||||||
|
{
|
||||||
|
Object.values(people.options).forEach(e => e.forEach(id => delete config.features[id]));
|
||||||
|
delete config.peoples[people.id];
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
} }, [div("h-[320px]"), div("text-xl font-bold text-center", [text(people.name)]), div("w-full border-b border-light-50 dark:border-dark-50"), div("text-wrap word-break", [text(people.description)])]),
|
})
|
||||||
);
|
}
|
||||||
|
const render = (people: string, level: Level, feature: string) => {
|
||||||
this._content = [ div('flex flex-1 gap-4 p-2 overflow-x-auto justify-center', this._options) ];
|
let element = dom("div", { class: ["border border-light-40 dark:border-dark-40 cursor-pointer px-2 py-1 w-[400px] hover:border-light-50 dark:hover:border-dark-50"], listeners: { click: e => {
|
||||||
|
this._builder.edit(config.features[feature]!).then(e => {
|
||||||
|
element.replaceChildren(markdownUtil(config.features[feature]!.description, undefined, { tags: { a: fakeA } }));
|
||||||
|
});
|
||||||
|
}, contextmenu: (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const context = contextmenu(e.clientX, e.clientY, [
|
||||||
|
dom('div', { class: 'px-2 py-1 border-bottom border-light-35 dark:border-dark-35 cursor-pointer hover:bg-light-40 dark:hover:bg-dark-40 text-light-100 dark:text-dark-100', listeners: { click: () => {
|
||||||
|
context.close();
|
||||||
|
const _feature: Feature = { id: getID(ID_SIZE), description: '', effect: [] };
|
||||||
|
config.features[_feature.id] = _feature;
|
||||||
|
config.peoples[people]!.options[level]!.push(_feature.id);
|
||||||
|
element.parentElement?.appendChild(render(people, level, _feature.id));
|
||||||
|
} } }, [ text('Nouveau') ]),
|
||||||
|
config.peoples[people]!.options[level].length > 1 ? dom('div', { class: 'px-2 py-1 border-bottom border-light-35 dark:border-dark-35 cursor-pointer hover:bg-light-40 dark:hover:bg-dark-40 text-light-100 dark:text-dark-100', listeners: { click: () => {
|
||||||
|
context.close();
|
||||||
|
confirm('Voulez-vous vraiment supprimer cet element ?').then(e => { if(e) {
|
||||||
|
config.peoples[people]!.options[level] = config.peoples[people]!.options[level].filter(e => e !== feature);
|
||||||
|
delete config.features[feature];
|
||||||
|
element.remove();
|
||||||
|
}
|
||||||
|
}) } } }, [ text('Supprimer') ]) : undefined,
|
||||||
|
], { placement: "right-start", priority: false });
|
||||||
|
}}}, [ markdownUtil(config.features[feature]!.description, undefined, { tags: { a: fakeA } }) ]);
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
const peopleRender = (people: RaceConfig) => {
|
||||||
|
return foldable(() => Object.entries(people.options).flatMap(level => [ div("w-full flex h-px", [div("border-t border-dashed border-light-50 dark:border-dark-50 w-full"), dom('span', { class: "relative" }, [ text(level[0]) ])]),
|
||||||
|
div("flex flex-row gap-4 justify-center", level[1].map((option) => render(people.id, parseInt(level[0], 10) as Level, option))),
|
||||||
|
]), [ input('text', { defaultValue: people.name, input: (value) => people.name = value, class: 'w-32' }), input('text', { defaultValue: people.description, input: (value) => people.description = value, class: 'w-full' }) ], { class: { container: 'gap-2 max-h-full', title: 'flex flex-row', content: 'flex flex-shrink-0 flex-col gap-4 relative w-full overflow-y-auto px-8' }, open: false })
|
||||||
|
}
|
||||||
|
const container = div('flex flex-col gap-2', Object.values(config.peoples).map(peopleRender));
|
||||||
|
this._content = [ div('flex flex-col py-2 gap-2', [ div('w-full flex flex-row-reverse', [ button(icon('radix-icons:plus'), add, 'p-1') ]), container ]) ];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
class TrainingEditor extends BuilderTab
|
class TrainingEditor extends BuilderTab
|
||||||
|
|
@ -131,24 +180,37 @@ class TrainingEditor extends BuilderTab
|
||||||
constructor(builder: HomebrewBuilder, config: CharacterConfig)
|
constructor(builder: HomebrewBuilder, config: CharacterConfig)
|
||||||
{
|
{
|
||||||
super(builder, config);
|
super(builder, config);
|
||||||
|
const render = (stat: MainStat, level: TrainingLevel, feature: string) => {
|
||||||
|
let element = dom("div", { class: ["border border-light-40 dark:border-dark-40 cursor-pointer px-2 py-1 w-[400px] hover:border-light-50 dark:hover:border-dark-50"], listeners: { click: e => {
|
||||||
|
this._builder.edit(config.features[feature]!).then(e => {
|
||||||
|
element.replaceChildren(markdownUtil(config.features[feature]!.description, undefined, { tags: { a: fakeA } }));
|
||||||
|
});
|
||||||
|
}, contextmenu: (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const context = contextmenu(e.clientX, e.clientY, [
|
||||||
|
dom('div', { class: 'px-2 py-1 border-bottom border-light-35 dark:border-dark-35 cursor-pointer hover:bg-light-40 dark:hover:bg-dark-40 text-light-100 dark:text-dark-100', listeners: { click: () => {
|
||||||
|
context.close();
|
||||||
|
const _feature: Feature = { id: getID(ID_SIZE), description: '', effect: [] };
|
||||||
|
config.features[_feature.id] = _feature;
|
||||||
|
config.training[stat][level].push(_feature.id);
|
||||||
|
element.parentElement?.appendChild(render(stat, level, _feature.id));
|
||||||
|
} } }, [ text('Nouveau') ]),
|
||||||
|
config.training[stat][level].length > 1 ? dom('div', { class: 'px-2 py-1 border-bottom border-light-35 dark:border-dark-35 cursor-pointer hover:bg-light-40 dark:hover:bg-dark-40 text-light-100 dark:text-dark-100', listeners: { click: () => {
|
||||||
|
context.close();
|
||||||
|
confirm('Voulez-vous vraiment supprimer cet element ?').then(e => { if(e) {
|
||||||
|
config.training[stat][level as any as TrainingLevel] = config.training[stat][level as any as TrainingLevel].filter(e => e !== feature);
|
||||||
|
delete config.features[feature];
|
||||||
|
element.remove();
|
||||||
|
}
|
||||||
|
}) } } }, [ text('Supprimer') ]) : undefined,
|
||||||
|
], { placement: "right-start", priority: false });
|
||||||
|
}}}, [ markdownUtil(config.features[feature]!.description, undefined, { tags: { a: fakeA } }) ]);
|
||||||
|
return element;
|
||||||
|
};
|
||||||
const statRenderBlock = (stat: MainStat) => {
|
const statRenderBlock = (stat: MainStat) => {
|
||||||
return Object.entries(config.training[stat]).map(
|
return Object.entries(config.training[stat]).map(
|
||||||
(level) => [ div("w-full flex h-px", [div("border-t border-dashed border-light-50 dark:border-dark-50 w-full"), dom('span', { class: "relative" }, [ text(level[0]) ])]),
|
(level) => [ div("w-full flex h-px", [div("border-t border-dashed border-light-50 dark:border-dark-50 w-full"), dom('span', { class: "relative" }, [ text(level[0]) ])]),
|
||||||
div("flex flex-row gap-4 justify-center", level[1].map((option, j) => {
|
div("flex flex-row gap-4 justify-center", level[1].map((option) => render(stat, parseInt(level[0], 10) as TrainingLevel, option))),
|
||||||
let element = dom("div", { class: ["border border-light-40 dark:border-dark-40 cursor-pointer px-2 py-1 w-[400px] hover:border-light-50 dark:hover:border-dark-50"], listeners: { click: e => {
|
|
||||||
this._builder.edit(config.features[option]!).then(e => {
|
|
||||||
element.replaceChildren(markdownUtil(config.features[option]!.description, undefined, { tags: { a: fakeA } }));
|
|
||||||
});
|
|
||||||
}, contextmenu: (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
const context = contextmenu(e.clientX, e.clientY, [
|
|
||||||
dom('div', { class: 'px-2 py-1 border-bottom border-light-35 dark:border-dark-35 cursor-pointer hover:bg-light-40 dark:hover:bg-dark-40 text-light-100 dark:text-dark-100', listeners: { click: () => { context.close(); } } }, [ text('Nouveau avant') ]),
|
|
||||||
dom('div', { class: 'px-2 py-1 border-bottom border-light-35 dark:border-dark-35 cursor-pointer hover:bg-light-40 dark:hover:bg-dark-40 text-light-100 dark:text-dark-100', listeners: { click: () => { context.close(); } } }, [ text('Nouveau après') ]),
|
|
||||||
dom('div', { class: 'px-2 py-1 border-bottom border-light-35 dark:border-dark-35 cursor-pointer hover:bg-light-40 dark:hover:bg-dark-40 text-light-100 dark:text-dark-100', listeners: { click: () => { context.close(); confirm('Voulez-vous vraiment supprimer cet element ?').then(e => { if(e) { delete config.training[stat][level[0] as any as TrainingLevel]; /* redraw */ } }) } } }, [ text('Supprimer') ])
|
|
||||||
], { placement: "right-start", priority: false });
|
|
||||||
}}}, [ markdownUtil(config.features[option]!.description, undefined, { tags: { a: fakeA } }) ]);
|
|
||||||
return element;
|
|
||||||
})),
|
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -229,11 +291,16 @@ class AspectEditor extends BuilderTab
|
||||||
content = element;
|
content = element;
|
||||||
};
|
};
|
||||||
const remove = (aspect: AspectConfig) => {
|
const remove = (aspect: AspectConfig) => {
|
||||||
config.aspects = config.aspects.filter(e => e !== aspect);
|
confirm('Voulez vous vraiment supprimer cet aspect ?').then(e => {
|
||||||
|
if(e)
|
||||||
|
{
|
||||||
|
config.aspects = config.aspects.filter(e => e !== aspect);
|
||||||
|
|
||||||
const element = redraw();
|
const element = redraw();
|
||||||
content.parentElement?.replaceChild(element, content);
|
content.parentElement?.replaceChild(element, content);
|
||||||
content = element;
|
content = element;
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
const redraw = () => table(config.aspects.map(render), { name: 'Nom', description: 'Description', stat: 'Buff de stat', alignment: 'Alignement', magic: 'Magie', difficulty: 'Difficulté', physic: 'Physique', mental: 'Mental', personality: 'Caractère', action: 'Actions' }, { class: { table: 'flex-1' } });
|
const redraw = () => table(config.aspects.map(render), { name: 'Nom', description: 'Description', stat: 'Buff de stat', alignment: 'Alignement', magic: 'Magie', difficulty: 'Difficulté', physic: 'Physique', mental: 'Mental', personality: 'Caractère', action: 'Actions' }, { class: { table: 'flex-1' } });
|
||||||
let content = redraw();
|
let content = redraw();
|
||||||
|
|
@ -247,19 +314,15 @@ class SpellEditor extends BuilderTab
|
||||||
super(builder, config);
|
super(builder, config);
|
||||||
|
|
||||||
const render = (spell: SpellConfig) => {
|
const render = (spell: SpellConfig) => {
|
||||||
return {
|
return foldable([
|
||||||
id: spell.id,
|
dom('label', { class: 'flex flex-col items-center justify-start gap-2 flex-1 *:text-center' }, [ text('Rang'), select([{ text: 'Rang 1', value: 1 }, { text: 'Rang 2', value: 2 }, { text: 'Rang 3', value: 3 }, { text: 'Spécial', value: 4 }], { change: (value: 1 | 2 | 3 | 4) => spell.rank = value, defaultValue: spell.rank, class: { container: '!m-0 !h-9 w-full' } }), ]),
|
||||||
name: input('text', { input: (value) => spell.name = value, defaultValue: spell.name, class: '!m-0 w-full' }),
|
dom('label', { class: 'flex flex-col items-center justify-start gap-2 flex-1 *:text-center' }, [ text('Type'), select(SPELL_TYPES.map(f => ({ text: spellTypeTexts[f], value: f })), { change: (value) => spell.type = value, defaultValue: spell.type, class: { container: '!m-0 !h-9 w-full' } }), ]),
|
||||||
rank: select([{ text: 'Rang 1', value: 1 }, { text: 'Rang 2', value: 2 }, { text: 'Rang 3', value: 3 }, { text: 'Spécial', value: 4 }], { change: (value: 1 | 2 | 3 | 4) => spell.rank = value, defaultValue: spell.rank, class: { container: '!m-0 w-full' } }),
|
dom('label', { class: 'flex flex-col items-center justify-start gap-2 flex-1 *:text-center' }, [ text('Coût'), numberpicker({ defaultValue: spell.cost, input: (value) => spell.cost = value, class: '!m-0 w-full' }), ]),
|
||||||
type: select(SPELL_TYPES.map(f => ({ text: spellTypeTexts[f], value: f })), { change: (value) => spell.type = value, defaultValue: spell.type, class: { container: '!m-0 w-full' } }),
|
dom('label', { class: 'flex flex-col items-center justify-start gap-2 flex-1 *:text-center' }, [ text('Incantation'), select<'action' | 'reaction' | number>([{ text: 'Action', value: 'action' }, { text: 'Reaction', value: 'reaction' }, { text: '1 minute', value: 1 }, { text: '10 minutes', value: 10 }], { change: (value) => spell.speed = value, defaultValue: spell.speed, class: { container: '!m-0 !h-9 w-full' } }), ]),
|
||||||
cost: numberpicker({ defaultValue: spell.cost, input: (value) => spell.cost = value, class: '!m-0 w-full' }),
|
dom('label', { class: 'flex flex-col items-center justify-start gap-2 flex-1 *:text-center' }, [ text('Elements'), multiselect(SPELL_ELEMENTS.map(f => ({ text: elementTexts[f].text, value: f })), { change: (value) => spell.elements = value, defaultValue: spell.elements, class: { container: '!m-0 !h-9 w-full' } }), ]),
|
||||||
speed: select<'action' | 'reaction' | number>([{ text: 'Action', value: 'action' }, { text: 'Reaction', value: 'reaction' }, { text: '1 minute', value: 1 }, { text: '10 minutes', value: 10 }], { change: (value) => spell.speed = value, defaultValue: spell.speed, class: { container: '!m-0 w-full' } }),
|
dom('label', { class: 'flex flex-col items-center justify-start gap-2 flex-1 *:text-center' }, [ text('Tags'), multiselect([{ text: 'Dégâts', value: 'damage' }, { text: 'Buff', value: 'buff' }, { text: 'Debuff', value: 'debuff' }, { text: 'Support', value: 'support' }, { text: 'Tank', value: 'tank' }, { text: 'Mouvement', value: 'movement' }, { text: 'Utilitaire', value: 'utilitary' }], { change: (value) => spell.tags = value, defaultValue: spell.tags, class: { container: '!m-0 !h-9 w-full' } }), ]),
|
||||||
elements: multiselect(SPELL_ELEMENTS.map(f => ({ text: elementTexts[f].text, value: f })), { change: (value) => spell.elements = value, defaultValue: spell.elements, class: { container: '!m-0 w-full' } }),
|
dom('label', { class: 'flex flex-col items-center justify-start gap-2 flex-1 *:text-center' }, [ text('Concentration'), toggle({ change: (value) => spell.concentration = value, defaultValue: spell.concentration, class: { container: '!m-0 !flex-none' } }), ]),
|
||||||
effect: input('text', { input: (value) => spell.effect = value, defaultValue: spell.effect, class: '!m-0 w-full' }),
|
], [ div('gap-4 px-4 flex', [ input('text', { input: (value) => spell.name = value, defaultValue: spell.name, class: '!m-0 w-64' }), input('text', { input: (value) => spell.effect = value, defaultValue: spell.effect, class: '!m-0 w-full' }),div('flex flex-row justify-center gap-2', [ button(icon('radix-icons:trash', { noobserver: true }), () => remove(spell), 'p-1') ]) ]) ], { class: { container: 'border-light-35 dark:border-dark-35 py-1', content: 'gap-2 px-4 py-1 flex items-center *:flex-1' }, open: false });
|
||||||
tags: multiselect([{ text: 'Dégâts', value: 'damage' }, { text: 'Buff', value: 'buff' }, { text: 'Debuff', value: 'debuff' }, { text: 'Support', value: 'support' }, { text: 'Tank', value: 'tank' }, { text: 'Mouvement', value: 'movement' }, { text: 'Utilitaire', value: 'utilitary' }], { change: (value) => spell.tags = value, defaultValue: spell.tags, class: { container: '!m-0 w-full' } }),
|
|
||||||
concentration: toggle({ change: (value) => spell.concentration = value, defaultValue: spell.concentration, class: { container: '!m-0' } }),
|
|
||||||
action: div('flex flex-row justify-center gap-2', [ button(icon('radix-icons:trash'), () => remove(spell), 'p-1') ])
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
const add = () => {
|
const add = () => {
|
||||||
config.spells.push({
|
config.spells.push({
|
||||||
|
|
@ -280,13 +343,19 @@ class SpellEditor extends BuilderTab
|
||||||
content = element;
|
content = element;
|
||||||
};
|
};
|
||||||
const remove = (spell: SpellConfig) => {
|
const remove = (spell: SpellConfig) => {
|
||||||
config.spells = config.spells.filter(e => e !== spell);
|
confirm('Voulez vous vraiment supprimer ce sort ?').then(e => {
|
||||||
|
if(e)
|
||||||
|
{
|
||||||
|
config.spells = config.spells.filter(e => e !== spell);
|
||||||
|
|
||||||
const element = redraw();
|
const element = redraw();
|
||||||
content.parentElement?.replaceChild(element, content);
|
content.parentElement?.replaceChild(element, content);
|
||||||
content = element;
|
content = element;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
const redraw = () => table(config.spells.map(render), { id: 'ID', name: 'Nom', rank: 'Rang', type: 'Type', cost: 'Coût', speed: 'Incantation', elements: 'Elements', effect: 'Effet', tags: 'Tag', concentration: 'Concentration', action: 'Actions' }, { class: { table: 'flex-1' } });
|
const redraw = () => div('flex flex-col divide-y', config.spells.map(render));
|
||||||
|
//, { class: { table: 'flex-1' } });
|
||||||
let content = redraw();
|
let content = redraw();
|
||||||
this._content = [ div('flex px-8 py-4 flex-col gap-4', [ div('flex flex-row-reverse', [ button(icon('radix-icons:plus'), add, 'p-1') ]), content ] ) ];
|
this._content = [ div('flex px-8 py-4 flex-col gap-4', [ div('flex flex-row-reverse', [ button(icon('radix-icons:plus'), add, 'p-1') ]), content ] ) ];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ export type Character = {
|
||||||
id: number;
|
id: number;
|
||||||
|
|
||||||
name: string;
|
name: string;
|
||||||
people?: number;
|
people?: string;
|
||||||
level: number;
|
level: number;
|
||||||
aspect?: number;
|
aspect?: number;
|
||||||
notes?: string | null;
|
notes?: string | null;
|
||||||
|
|
@ -44,7 +44,7 @@ export type CharacterVariables = {
|
||||||
equipment: Array<string>;
|
equipment: Array<string>;
|
||||||
};
|
};
|
||||||
export type CharacterConfig = {
|
export type CharacterConfig = {
|
||||||
peoples: RaceConfig[];
|
peoples: Record<string, RaceConfig>;
|
||||||
resistances: Record<Resistance, { name: string, statistic: MainStat }>;
|
resistances: Record<Resistance, { name: string, statistic: MainStat }>;
|
||||||
training: Record<MainStat, Record<TrainingLevel, FeatureID[]>>;
|
training: Record<MainStat, Record<TrainingLevel, FeatureID[]>>;
|
||||||
abilities: Record<Ability, AbilityConfig>;
|
abilities: Record<Ability, AbilityConfig>;
|
||||||
|
|
@ -72,6 +72,7 @@ export type AbilityConfig = {
|
||||||
description: string;
|
description: string;
|
||||||
};
|
};
|
||||||
export type RaceConfig = {
|
export type RaceConfig = {
|
||||||
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
description: string;
|
description: string;
|
||||||
options: Record<Level, FeatureID[]>;
|
options: Record<Level, FeatureID[]>;
|
||||||
|
|
@ -126,7 +127,7 @@ export type CompiledCharacter = {
|
||||||
name: string;
|
name: string;
|
||||||
health: number; //Max
|
health: number; //Max
|
||||||
mana: number; //Max
|
mana: number; //Max
|
||||||
race: number;
|
race: string;
|
||||||
spellslots: number; //Max
|
spellslots: number; //Max
|
||||||
artslots: number; //Max
|
artslots: number; //Max
|
||||||
spellranks: Record<SpellType, 0 | 1 | 2 | 3>;
|
spellranks: Record<SpellType, 0 | 1 | 2 | 3>;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue