diff --git a/Build.PL b/Build.PL new file mode 100755 index 0000000..0c6d690 --- /dev/null +++ b/Build.PL @@ -0,0 +1,24 @@ +#!/usr/bin/env perl +use Module::Build; + +my $home = $ENV{HOME}; + +my $build = Module::Build->new( + module_name => 'LasTres', + license => 'AGPLv3', + dist_author => 'Sergio Iglesias ', + dist_abstract => 'Juego de L3TDE.', + requires => { + 'Mojolicious' => 0, + 'Moo' => 0, + 'Params::ValidationCompiler' => 0, + 'Types::Standard' => 0, + 'Crypt::URandom' => 0, + 'Crypt::Bcrypt' => 0, + 'DBIx::Class' => 0, + 'DBIx::Class::DeploymentHandler' => 0, + 'UUID::URandom' => 0, + 'Module::Pluggable' => 0, + }, +); +$build->create_build_script; diff --git a/babel.config.json b/babel.config.json new file mode 100644 index 0000000..30e6bce --- /dev/null +++ b/babel.config.json @@ -0,0 +1,5 @@ +{ + "presets": [ + "@babel/preset-react" + ] +} diff --git a/build_styles.sh b/build_styles.sh new file mode 100644 index 0000000..55263d0 --- /dev/null +++ b/build_styles.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +if which sassc &> /dev/null; then + sassc public/css/styles.scss > public/css/styles.css +else + echo "No sassc" + exit 1 +fi diff --git a/dbicdh/PostgreSQL/deploy/1/000-deploy.sql b/dbicdh/PostgreSQL/deploy/1/000-deploy.sql new file mode 100644 index 0000000..c941cb3 --- /dev/null +++ b/dbicdh/PostgreSQL/deploy/1/000-deploy.sql @@ -0,0 +1 @@ +CREATE EXTENSION "uuid-ossp"; diff --git a/dbicdh/PostgreSQL/deploy/1/001-auto-__VERSION.sql b/dbicdh/PostgreSQL/deploy/1/001-auto-__VERSION.sql new file mode 100644 index 0000000..02e5039 --- /dev/null +++ b/dbicdh/PostgreSQL/deploy/1/001-auto-__VERSION.sql @@ -0,0 +1,18 @@ +-- +-- Created by SQL::Translator::Producer::PostgreSQL +-- Created on Mon May 29 12:19:47 2023 +-- +; +-- +-- Table: dbix_class_deploymenthandler_versions +-- +CREATE TABLE "dbix_class_deploymenthandler_versions" ( + "id" serial NOT NULL, + "version" character varying(50) NOT NULL, + "ddl" text, + "upgrade_sql" text, + PRIMARY KEY ("id"), + CONSTRAINT "dbix_class_deploymenthandler_versions_version" UNIQUE ("version") +); + +; diff --git a/dbicdh/PostgreSQL/deploy/1/001-auto.sql b/dbicdh/PostgreSQL/deploy/1/001-auto.sql new file mode 100644 index 0000000..0fcecf8 --- /dev/null +++ b/dbicdh/PostgreSQL/deploy/1/001-auto.sql @@ -0,0 +1,252 @@ +-- +-- Created by SQL::Translator::Producer::PostgreSQL +-- Created on Mon May 29 12:19:46 2023 +-- +; +-- +-- Table: equipment +-- +CREATE TABLE "equipment" ( + "uuid" uuid NOT NULL, + PRIMARY KEY ("uuid") +); + +; +-- +-- Table: inventories +-- +CREATE TABLE "inventories" ( + "uuid" uuid NOT NULL, + PRIMARY KEY ("uuid") +); + +; +-- +-- Table: players +-- +CREATE TABLE "players" ( + "uuid" uuid NOT NULL, + "username" text NOT NULL, + "encrypted_password" text NOT NULL, + "email" text NOT NULL, + "verified" boolean NOT NULL, + "verification_token" text, + "register_date" timestamp DEFAULT NOW() NOT NULL, + "last_activity" timestamp DEFAULT NOW() NOT NULL, + PRIMARY KEY ("uuid"), + CONSTRAINT "unique_constraint_email" UNIQUE ("email"), + CONSTRAINT "unique_constraint_username" UNIQUE ("username") +); + +; +-- +-- Table: skill_like_lists +-- +CREATE TABLE "skill_like_lists" ( + "uuid" uuid DEFAULT uuid_generate_v4() NOT NULL, + PRIMARY KEY ("uuid") +); + +; +-- +-- Table: stats +-- +CREATE TABLE "stats" ( + "uuid" uuid NOT NULL, + "health" integer NOT NULL, + "mana" integer NOT NULL, + "strength" integer NOT NULL, + "resistance" integer NOT NULL, + "magic" integer NOT NULL, + "speed" integer NOT NULL, + "intelligence" integer NOT NULL, + "charisma" integer NOT NULL, + PRIMARY KEY ("uuid") +); + +; +-- +-- Table: equipment_items +-- +CREATE TABLE "equipment_items" ( + "kind" text NOT NULL, + "equipment" uuid NOT NULL, + "identifier" text NOT NULL, + "quantity" integer NOT NULL, + PRIMARY KEY ("kind", "equipment") +); +CREATE INDEX "equipment_items_idx_equipment" on "equipment_items" ("equipment"); + +; +-- +-- Table: inventory_items +-- +CREATE TABLE "inventory_items" ( + "uuid" uuid DEFAULT uuid_generate_v4() NOT NULL, + "inventory" uuid NOT NULL, + "identifier" text NOT NULL, + "quantity" integer NOT NULL, + PRIMARY KEY ("uuid") +); +CREATE INDEX "inventory_items_idx_inventory" on "inventory_items" ("inventory"); + +; +-- +-- Table: skill_like_items +-- +CREATE TABLE "skill_like_items" ( + "identifier" text NOT NULL, + "owner_list" uuid NOT NULL, + "level" integer DEFAULT 1 NOT NULL, + PRIMARY KEY ("identifier", "owner_list") +); +CREATE INDEX "skill_like_items_idx_owner_list" on "skill_like_items" ("owner_list"); + +; +-- +-- Table: teams +-- +CREATE TABLE "teams" ( + "uuid" uuid NOT NULL, + "leader" uuid NOT NULL, + "name" text NOT NULL, + "planet" text NOT NULL, + "super_area" text NOT NULL, + "area" text NOT NULL, + "location" text NOT NULL, + PRIMARY KEY ("uuid"), + CONSTRAINT "u_name" UNIQUE ("name") +); +CREATE INDEX "teams_idx_leader" on "teams" ("leader"); + +; +-- +-- Table: player_pjs +-- +CREATE TABLE "player_pjs" ( + "uuid" uuid DEFAULT uuid_generate_v4() NOT NULL, + "owner" uuid NOT NULL, + "full_name" text NOT NULL, + "short_name" text NOT NULL, + "nick" text NOT NULL, + "race" text NOT NULL, + "team" uuid NOT NULL, + "creation_date" timestamp DEFAULT NOW() NOT NULL, + "last_activity" timestamp DEFAULT NOW() NOT NULL, + "level" integer DEFAULT 1 NOT NULL, + "exp" integer DEFAULT 1 NOT NULL, + "equipment" uuid NOT NULL, + "stats" uuid NOT NULL, + "skills" uuid NOT NULL, + "spells" uuid NOT NULL, + "inventory" uuid NOT NULL, + PRIMARY KEY ("uuid") +); +CREATE INDEX "player_pjs_idx_equipment" on "player_pjs" ("equipment"); +CREATE INDEX "player_pjs_idx_inventory" on "player_pjs" ("inventory"); +CREATE INDEX "player_pjs_idx_owner" on "player_pjs" ("owner"); +CREATE INDEX "player_pjs_idx_skills" on "player_pjs" ("skills"); +CREATE INDEX "player_pjs_idx_spells" on "player_pjs" ("spells"); +CREATE INDEX "player_pjs_idx_stats" on "player_pjs" ("stats"); +CREATE INDEX "player_pjs_idx_team" on "player_pjs" ("team"); + +; +-- +-- Table: player_companion_npcs +-- +CREATE TABLE "player_companion_npcs" ( + "uuid" uuid DEFAULT uuid_generate_v4() NOT NULL, + "owner" uuid NOT NULL, + "identifier" text NOT NULL, + "nick" text, + "race" text NOT NULL, + "level" integer DEFAULT 1 NOT NULL, + "exp" integer DEFAULT 1 NOT NULL, + "equipment" uuid NOT NULL, + "stats" uuid NOT NULL, + "skills" uuid NOT NULL, + "spells" uuid NOT NULL, + "inventory" uuid NOT NULL, + PRIMARY KEY ("uuid") +); +CREATE INDEX "player_companion_npcs_idx_equipment" on "player_companion_npcs" ("equipment"); +CREATE INDEX "player_companion_npcs_idx_inventory" on "player_companion_npcs" ("inventory"); +CREATE INDEX "player_companion_npcs_idx_owner" on "player_companion_npcs" ("owner"); +CREATE INDEX "player_companion_npcs_idx_skills" on "player_companion_npcs" ("skills"); +CREATE INDEX "player_companion_npcs_idx_spells" on "player_companion_npcs" ("spells"); +CREATE INDEX "player_companion_npcs_idx_stats" on "player_companion_npcs" ("stats"); + +; +-- +-- Foreign Key Definitions +-- + +; +ALTER TABLE "equipment_items" ADD CONSTRAINT "equipment_items_fk_equipment" FOREIGN KEY ("equipment") + REFERENCES "equipment" ("uuid") ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE; + +; +ALTER TABLE "inventory_items" ADD CONSTRAINT "inventory_items_fk_inventory" FOREIGN KEY ("inventory") + REFERENCES "inventories" ("uuid") ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE; + +; +ALTER TABLE "skill_like_items" ADD CONSTRAINT "skill_like_items_fk_owner_list" FOREIGN KEY ("owner_list") + REFERENCES "skill_like_lists" ("uuid") ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE; + +; +ALTER TABLE "teams" ADD CONSTRAINT "teams_fk_leader" FOREIGN KEY ("leader") + REFERENCES "player_pjs" ("uuid") DEFERRABLE; + +; +ALTER TABLE "player_pjs" ADD CONSTRAINT "player_pjs_fk_equipment" FOREIGN KEY ("equipment") + REFERENCES "equipment" ("uuid") DEFERRABLE; + +; +ALTER TABLE "player_pjs" ADD CONSTRAINT "player_pjs_fk_inventory" FOREIGN KEY ("inventory") + REFERENCES "inventories" ("uuid") DEFERRABLE; + +; +ALTER TABLE "player_pjs" ADD CONSTRAINT "player_pjs_fk_owner" FOREIGN KEY ("owner") + REFERENCES "players" ("uuid") DEFERRABLE; + +; +ALTER TABLE "player_pjs" ADD CONSTRAINT "player_pjs_fk_skills" FOREIGN KEY ("skills") + REFERENCES "skill_like_lists" ("uuid") DEFERRABLE; + +; +ALTER TABLE "player_pjs" ADD CONSTRAINT "player_pjs_fk_spells" FOREIGN KEY ("spells") + REFERENCES "skill_like_lists" ("uuid") DEFERRABLE; + +; +ALTER TABLE "player_pjs" ADD CONSTRAINT "player_pjs_fk_stats" FOREIGN KEY ("stats") + REFERENCES "stats" ("uuid") DEFERRABLE; + +; +ALTER TABLE "player_pjs" ADD CONSTRAINT "player_pjs_fk_team" FOREIGN KEY ("team") + REFERENCES "teams" ("uuid") ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE; + +; +ALTER TABLE "player_companion_npcs" ADD CONSTRAINT "player_companion_npcs_fk_equipment" FOREIGN KEY ("equipment") + REFERENCES "equipment" ("uuid") DEFERRABLE; + +; +ALTER TABLE "player_companion_npcs" ADD CONSTRAINT "player_companion_npcs_fk_inventory" FOREIGN KEY ("inventory") + REFERENCES "inventories" ("uuid") DEFERRABLE; + +; +ALTER TABLE "player_companion_npcs" ADD CONSTRAINT "player_companion_npcs_fk_owner" FOREIGN KEY ("owner") + REFERENCES "player_pjs" ("uuid") ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE; + +; +ALTER TABLE "player_companion_npcs" ADD CONSTRAINT "player_companion_npcs_fk_skills" FOREIGN KEY ("skills") + REFERENCES "skill_like_lists" ("uuid") DEFERRABLE; + +; +ALTER TABLE "player_companion_npcs" ADD CONSTRAINT "player_companion_npcs_fk_spells" FOREIGN KEY ("spells") + REFERENCES "skill_like_lists" ("uuid") DEFERRABLE; + +; +ALTER TABLE "player_companion_npcs" ADD CONSTRAINT "player_companion_npcs_fk_stats" FOREIGN KEY ("stats") + REFERENCES "stats" ("uuid") DEFERRABLE; + +; diff --git a/dbicdh/_source/deploy/1/001-auto-__VERSION.yml b/dbicdh/_source/deploy/1/001-auto-__VERSION.yml new file mode 100644 index 0000000..61dfa16 --- /dev/null +++ b/dbicdh/_source/deploy/1/001-auto-__VERSION.yml @@ -0,0 +1,91 @@ +--- +schema: + procedures: {} + tables: + dbix_class_deploymenthandler_versions: + constraints: + - deferrable: 1 + expression: '' + fields: + - id + match_type: '' + name: '' + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: PRIMARY KEY + - deferrable: 1 + expression: '' + fields: + - version + match_type: '' + name: dbix_class_deploymenthandler_versions_version + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: UNIQUE + fields: + ddl: + data_type: text + default_value: ~ + is_nullable: 1 + is_primary_key: 0 + is_unique: 0 + name: ddl + order: 3 + size: + - 0 + id: + data_type: int + default_value: ~ + is_auto_increment: 1 + is_nullable: 0 + is_primary_key: 1 + is_unique: 0 + name: id + order: 1 + size: + - 0 + upgrade_sql: + data_type: text + default_value: ~ + is_nullable: 1 + is_primary_key: 0 + is_unique: 0 + name: upgrade_sql + order: 4 + size: + - 0 + version: + data_type: varchar + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 1 + name: version + order: 2 + size: + - 50 + indices: [] + name: dbix_class_deploymenthandler_versions + options: [] + order: 1 + triggers: {} + views: {} +translator: + add_drop_table: 0 + filename: ~ + no_comments: 0 + parser_args: + sources: + - __VERSION + parser_type: SQL::Translator::Parser::DBIx::Class + producer_args: {} + producer_type: SQL::Translator::Producer::YAML + show_warnings: 0 + trace: 0 + version: 1.63 diff --git a/dbicdh/_source/deploy/1/001-auto.yml b/dbicdh/_source/deploy/1/001-auto.yml new file mode 100644 index 0000000..e267570 --- /dev/null +++ b/dbicdh/_source/deploy/1/001-auto.yml @@ -0,0 +1,1255 @@ +--- +schema: + procedures: {} + tables: + equipment: + constraints: + - deferrable: 1 + expression: '' + fields: + - uuid + match_type: '' + name: '' + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: PRIMARY KEY + fields: + uuid: + data_type: uuid + default_value: ~ + is_nullable: 0 + is_primary_key: 1 + is_unique: 0 + name: uuid + order: 1 + size: + - 0 + indices: [] + name: equipment + options: [] + order: 1 + equipment_items: + constraints: + - deferrable: 1 + expression: '' + fields: + - kind + - equipment + match_type: '' + name: '' + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: PRIMARY KEY + - deferrable: 1 + expression: '' + fields: + - equipment + match_type: '' + name: equipment_items_fk_equipment + on_delete: CASCADE + on_update: CASCADE + options: [] + reference_fields: + - uuid + reference_table: equipment + type: FOREIGN KEY + fields: + equipment: + data_type: uuid + default_value: ~ + is_nullable: 0 + is_primary_key: 1 + is_unique: 0 + name: equipment + order: 2 + size: + - 0 + identifier: + data_type: text + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: identifier + order: 3 + size: + - 0 + kind: + data_type: text + default_value: ~ + is_nullable: 0 + is_primary_key: 1 + is_unique: 0 + name: kind + order: 1 + size: + - 0 + quantity: + data_type: Integer + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: quantity + order: 4 + size: + - 0 + indices: + - fields: + - equipment + name: equipment_items_idx_equipment + options: [] + type: NORMAL + name: equipment_items + options: [] + order: 6 + inventories: + constraints: + - deferrable: 1 + expression: '' + fields: + - uuid + match_type: '' + name: '' + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: PRIMARY KEY + fields: + uuid: + data_type: uuid + default_value: ~ + is_nullable: 0 + is_primary_key: 1 + is_unique: 0 + name: uuid + order: 1 + size: + - 0 + indices: [] + name: inventories + options: [] + order: 2 + inventory_items: + constraints: + - deferrable: 1 + expression: '' + fields: + - uuid + match_type: '' + name: '' + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: PRIMARY KEY + - deferrable: 1 + expression: '' + fields: + - inventory + match_type: '' + name: inventory_items_fk_inventory + on_delete: CASCADE + on_update: CASCADE + options: [] + reference_fields: + - uuid + reference_table: inventories + type: FOREIGN KEY + fields: + identifier: + data_type: text + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: identifier + order: 3 + size: + - 0 + inventory: + data_type: uuid + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: inventory + order: 2 + size: + - 0 + quantity: + data_type: Integer + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: quantity + order: 4 + size: + - 0 + uuid: + data_type: uuid + default_value: !!perl/ref + =: uuid_generate_v4() + is_nullable: 0 + is_primary_key: 1 + is_unique: 0 + name: uuid + order: 1 + size: + - 0 + indices: + - fields: + - inventory + name: inventory_items_idx_inventory + options: [] + type: NORMAL + name: inventory_items + options: [] + order: 7 + player_companion_npcs: + constraints: + - deferrable: 1 + expression: '' + fields: + - uuid + match_type: '' + name: '' + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: PRIMARY KEY + - deferrable: 1 + expression: '' + fields: + - equipment + match_type: '' + name: player_companion_npcs_fk_equipment + on_delete: '' + on_update: '' + options: [] + reference_fields: + - uuid + reference_table: equipment + type: FOREIGN KEY + - deferrable: 1 + expression: '' + fields: + - inventory + match_type: '' + name: player_companion_npcs_fk_inventory + on_delete: '' + on_update: '' + options: [] + reference_fields: + - uuid + reference_table: inventories + type: FOREIGN KEY + - deferrable: 1 + expression: '' + fields: + - owner + match_type: '' + name: player_companion_npcs_fk_owner + on_delete: CASCADE + on_update: CASCADE + options: [] + reference_fields: + - uuid + reference_table: player_pjs + type: FOREIGN KEY + - deferrable: 1 + expression: '' + fields: + - skills + match_type: '' + name: player_companion_npcs_fk_skills + on_delete: '' + on_update: '' + options: [] + reference_fields: + - uuid + reference_table: skill_like_lists + type: FOREIGN KEY + - deferrable: 1 + expression: '' + fields: + - spells + match_type: '' + name: player_companion_npcs_fk_spells + on_delete: '' + on_update: '' + options: [] + reference_fields: + - uuid + reference_table: skill_like_lists + type: FOREIGN KEY + - deferrable: 1 + expression: '' + fields: + - stats + match_type: '' + name: player_companion_npcs_fk_stats + on_delete: '' + on_update: '' + options: [] + reference_fields: + - uuid + reference_table: stats + type: FOREIGN KEY + fields: + equipment: + data_type: uuid + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: equipment + order: 8 + size: + - 0 + exp: + data_type: integer + default_value: !!perl/ref + =: 1 + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: exp + order: 7 + size: + - 0 + identifier: + data_type: text + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: identifier + order: 3 + size: + - 0 + inventory: + data_type: uuid + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: inventory + order: 12 + size: + - 0 + level: + data_type: integer + default_value: !!perl/ref + =: 1 + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: level + order: 6 + size: + - 0 + nick: + data_type: text + default_value: ~ + is_nullable: 1 + is_primary_key: 0 + is_unique: 0 + name: nick + order: 4 + size: + - 0 + owner: + data_type: uuid + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: owner + order: 2 + size: + - 0 + race: + data_type: text + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: race + order: 5 + size: + - 0 + skills: + data_type: uuid + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: skills + order: 10 + size: + - 0 + spells: + data_type: uuid + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: spells + order: 11 + size: + - 0 + stats: + data_type: uuid + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: stats + order: 9 + size: + - 0 + uuid: + data_type: uuid + default_value: !!perl/ref + =: uuid_generate_v4() + is_nullable: 0 + is_primary_key: 1 + is_unique: 0 + name: uuid + order: 1 + size: + - 0 + indices: + - fields: + - equipment + name: player_companion_npcs_idx_equipment + options: [] + type: NORMAL + - fields: + - inventory + name: player_companion_npcs_idx_inventory + options: [] + type: NORMAL + - fields: + - owner + name: player_companion_npcs_idx_owner + options: [] + type: NORMAL + - fields: + - skills + name: player_companion_npcs_idx_skills + options: [] + type: NORMAL + - fields: + - spells + name: player_companion_npcs_idx_spells + options: [] + type: NORMAL + - fields: + - stats + name: player_companion_npcs_idx_stats + options: [] + type: NORMAL + name: player_companion_npcs + options: [] + order: 11 + player_pjs: + constraints: + - deferrable: 1 + expression: '' + fields: + - uuid + match_type: '' + name: '' + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: PRIMARY KEY + - deferrable: 1 + expression: '' + fields: + - equipment + match_type: '' + name: player_pjs_fk_equipment + on_delete: '' + on_update: '' + options: [] + reference_fields: + - uuid + reference_table: equipment + type: FOREIGN KEY + - deferrable: 1 + expression: '' + fields: + - inventory + match_type: '' + name: player_pjs_fk_inventory + on_delete: '' + on_update: '' + options: [] + reference_fields: + - uuid + reference_table: inventories + type: FOREIGN KEY + - deferrable: 1 + expression: '' + fields: + - owner + match_type: '' + name: player_pjs_fk_owner + on_delete: '' + on_update: '' + options: [] + reference_fields: + - uuid + reference_table: players + type: FOREIGN KEY + - deferrable: 1 + expression: '' + fields: + - skills + match_type: '' + name: player_pjs_fk_skills + on_delete: '' + on_update: '' + options: [] + reference_fields: + - uuid + reference_table: skill_like_lists + type: FOREIGN KEY + - deferrable: 1 + expression: '' + fields: + - spells + match_type: '' + name: player_pjs_fk_spells + on_delete: '' + on_update: '' + options: [] + reference_fields: + - uuid + reference_table: skill_like_lists + type: FOREIGN KEY + - deferrable: 1 + expression: '' + fields: + - stats + match_type: '' + name: player_pjs_fk_stats + on_delete: '' + on_update: '' + options: [] + reference_fields: + - uuid + reference_table: stats + type: FOREIGN KEY + - deferrable: 1 + expression: '' + fields: + - team + match_type: '' + name: player_pjs_fk_team + on_delete: CASCADE + on_update: CASCADE + options: [] + reference_fields: + - uuid + reference_table: teams + type: FOREIGN KEY + fields: + creation_date: + data_type: timestamp + default_value: !!perl/ref + =: NOW() + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: creation_date + order: 8 + size: + - 0 + equipment: + data_type: uuid + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: equipment + order: 12 + size: + - 0 + exp: + data_type: integer + default_value: !!perl/ref + =: 1 + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: exp + order: 11 + size: + - 0 + full_name: + data_type: text + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: full_name + order: 3 + size: + - 0 + inventory: + data_type: uuid + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: inventory + order: 16 + size: + - 0 + last_activity: + data_type: timestamp + default_value: !!perl/ref + =: NOW() + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: last_activity + order: 9 + size: + - 0 + level: + data_type: integer + default_value: !!perl/ref + =: 1 + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: level + order: 10 + size: + - 0 + nick: + data_type: text + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: nick + order: 5 + size: + - 0 + owner: + data_type: uuid + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: owner + order: 2 + size: + - 0 + race: + data_type: text + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: race + order: 6 + size: + - 0 + short_name: + data_type: text + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: short_name + order: 4 + size: + - 0 + skills: + data_type: uuid + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: skills + order: 14 + size: + - 0 + spells: + data_type: uuid + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: spells + order: 15 + size: + - 0 + stats: + data_type: uuid + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: stats + order: 13 + size: + - 0 + team: + data_type: uuid + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: team + order: 7 + size: + - 0 + uuid: + data_type: uuid + default_value: !!perl/ref + =: uuid_generate_v4() + is_nullable: 0 + is_primary_key: 1 + is_unique: 0 + name: uuid + order: 1 + size: + - 0 + indices: + - fields: + - equipment + name: player_pjs_idx_equipment + options: [] + type: NORMAL + - fields: + - inventory + name: player_pjs_idx_inventory + options: [] + type: NORMAL + - fields: + - owner + name: player_pjs_idx_owner + options: [] + type: NORMAL + - fields: + - skills + name: player_pjs_idx_skills + options: [] + type: NORMAL + - fields: + - spells + name: player_pjs_idx_spells + options: [] + type: NORMAL + - fields: + - stats + name: player_pjs_idx_stats + options: [] + type: NORMAL + - fields: + - team + name: player_pjs_idx_team + options: [] + type: NORMAL + name: player_pjs + options: [] + order: 10 + players: + constraints: + - deferrable: 1 + expression: '' + fields: + - uuid + match_type: '' + name: '' + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: PRIMARY KEY + - deferrable: 1 + expression: '' + fields: + - email + match_type: '' + name: unique_constraint_email + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: UNIQUE + - deferrable: 1 + expression: '' + fields: + - username + match_type: '' + name: unique_constraint_username + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: UNIQUE + fields: + email: + data_type: text + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 1 + name: email + order: 4 + size: + - 0 + encrypted_password: + data_type: text + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: encrypted_password + order: 3 + size: + - 0 + last_activity: + data_type: timestamp + default_value: !!perl/ref + =: NOW() + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: last_activity + order: 8 + size: + - 0 + register_date: + data_type: timestamp + default_value: !!perl/ref + =: NOW() + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: register_date + order: 7 + size: + - 0 + username: + data_type: text + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 1 + name: username + order: 2 + size: + - 0 + uuid: + data_type: uuid + default_value: ~ + is_nullable: 0 + is_primary_key: 1 + is_unique: 0 + name: uuid + order: 1 + size: + - 0 + verification_token: + data_type: text + default_value: ~ + is_nullable: 1 + is_primary_key: 0 + is_unique: 0 + name: verification_token + order: 6 + size: + - 0 + verified: + data_type: boolean + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: verified + order: 5 + size: + - 0 + indices: [] + name: players + options: [] + order: 3 + skill_like_items: + constraints: + - deferrable: 1 + expression: '' + fields: + - identifier + - owner_list + match_type: '' + name: '' + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: PRIMARY KEY + - deferrable: 1 + expression: '' + fields: + - owner_list + match_type: '' + name: skill_like_items_fk_owner_list + on_delete: CASCADE + on_update: CASCADE + options: [] + reference_fields: + - uuid + reference_table: skill_like_lists + type: FOREIGN KEY + fields: + identifier: + data_type: text + default_value: ~ + is_nullable: 0 + is_primary_key: 1 + is_unique: 0 + name: identifier + order: 1 + size: + - 0 + level: + data_type: integer + default_value: !!perl/ref + =: 1 + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: level + order: 3 + size: + - 0 + owner_list: + data_type: uuid + default_value: ~ + is_nullable: 0 + is_primary_key: 1 + is_unique: 0 + name: owner_list + order: 2 + size: + - 0 + indices: + - fields: + - owner_list + name: skill_like_items_idx_owner_list + options: [] + type: NORMAL + name: skill_like_items + options: [] + order: 8 + skill_like_lists: + constraints: + - deferrable: 1 + expression: '' + fields: + - uuid + match_type: '' + name: '' + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: PRIMARY KEY + fields: + uuid: + data_type: uuid + default_value: !!perl/ref + =: uuid_generate_v4() + is_nullable: 0 + is_primary_key: 1 + is_unique: 0 + name: uuid + order: 1 + size: + - 0 + indices: [] + name: skill_like_lists + options: [] + order: 4 + stats: + constraints: + - deferrable: 1 + expression: '' + fields: + - uuid + match_type: '' + name: '' + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: PRIMARY KEY + fields: + charisma: + data_type: integer + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: charisma + order: 9 + size: + - 0 + health: + data_type: integer + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: health + order: 2 + size: + - 0 + intelligence: + data_type: integer + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: intelligence + order: 8 + size: + - 0 + magic: + data_type: integer + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: magic + order: 6 + size: + - 0 + mana: + data_type: integer + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: mana + order: 3 + size: + - 0 + resistance: + data_type: integer + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: resistance + order: 5 + size: + - 0 + speed: + data_type: integer + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: speed + order: 7 + size: + - 0 + strength: + data_type: integer + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: strength + order: 4 + size: + - 0 + uuid: + data_type: uuid + default_value: ~ + is_nullable: 0 + is_primary_key: 1 + is_unique: 0 + name: uuid + order: 1 + size: + - 0 + indices: [] + name: stats + options: [] + order: 5 + teams: + constraints: + - deferrable: 1 + expression: '' + fields: + - uuid + match_type: '' + name: '' + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: PRIMARY KEY + - deferrable: 1 + expression: '' + fields: + - name + match_type: '' + name: u_name + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: UNIQUE + - deferrable: 1 + expression: '' + fields: + - leader + match_type: '' + name: teams_fk_leader + on_delete: '' + on_update: '' + options: [] + reference_fields: + - uuid + reference_table: player_pjs + type: FOREIGN KEY + fields: + area: + data_type: text + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: area + order: 6 + size: + - 0 + leader: + data_type: uuid + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: leader + order: 2 + size: + - 0 + location: + data_type: text + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: location + order: 7 + size: + - 0 + name: + data_type: text + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 1 + name: name + order: 3 + size: + - 0 + planet: + data_type: text + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: planet + order: 4 + size: + - 0 + super_area: + data_type: text + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: super_area + order: 5 + size: + - 0 + uuid: + data_type: uuid + default_value: ~ + is_nullable: 0 + is_primary_key: 1 + is_unique: 0 + name: uuid + order: 1 + size: + - 0 + indices: + - fields: + - leader + name: teams_idx_leader + options: [] + type: NORMAL + name: teams + options: [] + order: 9 + triggers: {} + views: {} +translator: + add_drop_table: 0 + filename: ~ + no_comments: 0 + parser_args: + sources: + - CompanionNPC + - Equipment + - EquipmentItem + - Inventory + - InventoryItem + - PJ + - Player + - SkillLikeItem + - SkillLikeList + - Stats + - Team + parser_type: SQL::Translator::Parser::DBIx::Class + producer_args: {} + producer_type: SQL::Translator::Producer::YAML + show_warnings: 0 + trace: 0 + version: 1.63 diff --git a/js-src/components/bottom-panel.tsx b/js-src/components/bottom-panel.tsx new file mode 100644 index 0000000..cd6bb5e --- /dev/null +++ b/js-src/components/bottom-panel.tsx @@ -0,0 +1,7 @@ +import * as React from 'react' +export default function BottomPanel (): JSX.Element { + return ( + <> + + ) +} diff --git a/js-src/components/game.tsx b/js-src/components/game.tsx new file mode 100644 index 0000000..d0dcc15 --- /dev/null +++ b/js-src/components/game.tsx @@ -0,0 +1,35 @@ +import * as React from 'react' + +import UpperPanel from '@lastres/components/upper-panel' +import BottomPanel from '@lastres/components/bottom-panel' +import PJSelectionMenu from '@lastres/components/pj-selection-menu' + +export interface GameProps { + setSelectedPJ: (set: string | null) => void + selectedPJ: string | null + userWantsToCreatePJ: boolean + setUserWantsToCreatePJ: (set: boolean) => void + error: string | null + setError: (set: string | null) => void +} + +export default function Game (props: GameProps): JSX.Element { + if (props.selectedPJ === null) { + return ( + <> + + + ) + } + return ( + <> + + + + ) +} diff --git a/js-src/components/login-page.tsx b/js-src/components/login-page.tsx new file mode 100644 index 0000000..28cd67a --- /dev/null +++ b/js-src/components/login-page.tsx @@ -0,0 +1,65 @@ +import * as React from 'react' + +export interface LoginPageProps { + setIsLoggedIn: (set: boolean) => void + setIsAskingForRegistration: (set: boolean) => void + setError: (set: string | null) => void + error: string | null +} + +export default function LoginPage (props: LoginPageProps): JSX.Element { + const userInputRef = React.useRef(null) + const passwordInputRef = React.useRef(null) + const onUserAsksForRegistration = (): void => { + props.setIsAskingForRegistration(true) + } + const login = (): void => { + if (userInputRef.current === null) { + return + } + if (passwordInputRef.current == null) { + return + } + fetch('/player/login', { + method: 'POST', + mode: 'same-origin', + cache: 'no-cache', + body: JSON.stringify({ + username: userInputRef.current.value, + password: passwordInputRef.current.value + }) + }).then(async (response) => { + const statusCode = response.status + const data = await response.json() + if (statusCode !== 200) { + props.setError(data.error) + return + } + props.setError(null) + props.setIsLoggedIn(true) + }).catch((error) => { + console.log(error) + }) + } + return ( + <> +
+
+

Inicia sesión en L3TDE online

+ {(props.error !== null + ? (

{props.error}

) + : (<>) + )} +
+ + +
+ +
+
+

¿Todavía no tienes cuenta? Registrate

+
+
+ + ) +} diff --git a/js-src/components/menu-bar.tsx b/js-src/components/menu-bar.tsx new file mode 100644 index 0000000..8a6886c --- /dev/null +++ b/js-src/components/menu-bar.tsx @@ -0,0 +1,42 @@ +'use strict' +import * as React from 'react' + +export interface MenuBarProps { + isLoggedIn: boolean +}; + +export interface MenuItem { + onClick: () => void + text: string +}; + +export default function MenuBar (props: MenuBarProps): JSX.Element { + const isLoggedIn = props.isLoggedIn + const menuItems: MenuItem[] = [] + fillMenu(menuItems, isLoggedIn) + return ( + + ) +} + +function fillMenu (menuItems: MenuItem[], isLoggedIn: boolean): void { + if (isLoggedIn) { + menuItems.push({ + text: 'Cerrar Sesión', + onClick: () => { alert('logout') } + }) + return + } + menuItems.push({ + text: 'Regístrate', + onClick: () => { alert('register') } + }) + menuItems.push({ + text: 'Inicia Sesión', + onClick: () => { alert('login') } + }) +} diff --git a/js-src/components/page.tsx b/js-src/components/page.tsx new file mode 100644 index 0000000..9003859 --- /dev/null +++ b/js-src/components/page.tsx @@ -0,0 +1,52 @@ +import * as React from 'react' + +import LoginPage from '@lastres/components/login-page' +import Game from '@lastres/components/game' +import { checkLogin } from '@lastres/login' +import RegistrationPage from '@lastres/components/registration-page' +// import Game from '@lastres/components/game' + +export default function Page (): JSX.Element { + const [isLoggedIn, setIsLoggedIn] = React.useState(false) + const [userWantsToCreatePJ, setUserWantsToCreatePJ] = React.useState(false) + const [isAskingForRegistration, setIsAskingForRegistration] = React.useState(false) + const [error, setError] = React.useState(null) + const [selectedPJ, setSelectedPJ] = React.useState(null) + checkLogin(setError, setIsLoggedIn) + if (!isLoggedIn) { + return notLoggedRender(setIsLoggedIn, isAskingForRegistration, setIsAskingForRegistration, error, setError) + } + return ( + + ) +} + +function notLoggedRender (setIsLoggedIn: (a: boolean) => void, + isAskingForRegistration: boolean, + setIsAskingForRegistration: (a: boolean) => void, + error: string | null, + setError: (a: string | null) => void): JSX.Element { + if (isAskingForRegistration) { + return ( + <> + + + ) + } + return ( + <> + + + ) +} diff --git a/js-src/components/pj-creation-menu.tsx b/js-src/components/pj-creation-menu.tsx new file mode 100644 index 0000000..72a0127 --- /dev/null +++ b/js-src/components/pj-creation-menu.tsx @@ -0,0 +1,75 @@ +import * as React from 'react' + +export interface PJCreationMenuProps { + error: string | null + setSelectedPJ: (set: string | null) => void + setUserWantsToCreatePJ: (set: boolean) => void + setError: (set: string | null) => void +} + +export interface Race { + identifier: string; + name_selection: string; + description: string; +} +export interface Races { + [id: string]: Race +} + +export default function PJCreationMenu (props: PJCreationMenuProps): JSX.Element { + const longNameInputRef = React.useRef(null) + const shortNameInputRef = React.useRef(null) + const nickInputRef = React.useRef(null) + const raceSelectRef = React.useRef(null) + const [playableRaces, setPlayableRaces] = React.useState({}) + React.useEffect(() => { + fetch('/races/playable', { + method: 'GET', + mode: 'same-origin', + cache: 'no-cache' + }).then(async (response) => { + const data = await response.json() + setPlayableRaces(data) + }).catch((error) => { + console.log(error) + props.setError('Imposible conectar al servidor para recibir las razas.') + }) + }) + return ( + <> +
+
+

Crea tu personaje.

+ {(props.error !== null + ? (

{props.error}

) + : (<>) + )} +
+ + + + + + + + +
+ +
+
+
+
+ + ) +} diff --git a/js-src/components/pj-selection-menu.tsx b/js-src/components/pj-selection-menu.tsx new file mode 100644 index 0000000..c81d8fe --- /dev/null +++ b/js-src/components/pj-selection-menu.tsx @@ -0,0 +1,36 @@ +import * as React from 'react' +import PJCreationMenu from '@lastres/components/pj-creation-menu' + +export interface PJSelectionMenuProps { + setSelectedPJ: (set: string | null) => void + setUserWantsToCreatePJ: (set: boolean) => void + userWantsToCreatePJ: boolean + error: string | null + setError: (set: string | null) => void +} + +export default function PJSelectionMenu (props: PJSelectionMenuProps): JSX.Element { + const createPJ = (): void => { + props.setUserWantsToCreatePJ(true) + } + if (props.userWantsToCreatePJ) { + return ( + + ) + } + return ( +
+
+

L3TDE

+

Selecciona un Personaje

+ +
+
+ ) +} diff --git a/js-src/components/registration-page.tsx b/js-src/components/registration-page.tsx new file mode 100644 index 0000000..64a6012 --- /dev/null +++ b/js-src/components/registration-page.tsx @@ -0,0 +1,76 @@ +import * as React from 'react' + +export interface RegistrationPageProps { + setIsLoggedIn: (set: boolean) => void + setIsAskingForRegistration: (set: boolean) => void + error: string | null + setError: (a: string|null) => void +} + +export default function RegistrationPage (props: RegistrationPageProps): JSX.Element { + const userInputRef = React.useRef(null) + const passwordInputRef = React.useRef(null) + const repeatPasswordInputRef = React.useRef(null) + const emailInputRef = React.useRef(null) + const onUserAsksForLogin = (): void => { + props.setIsAskingForRegistration(false) + } + function registerUser (): void { + if (userInputRef.current === null) { + return + } + if (passwordInputRef.current === null) { + return + } + if (repeatPasswordInputRef.current === null) { + return + } + if (emailInputRef.current === null) { + return + } + fetch('/player/register', { + method: 'POST', + mode: 'same-origin', + cache: 'no-cache', + body: JSON.stringify({ + username: userInputRef.current.value, + password: passwordInputRef.current.value, + repeat_password: repeatPasswordInputRef.current.value, + email: emailInputRef.current.value + }) + }).then(async (response) => { + const statusCode = response.status + const data = await response.json() + if (statusCode !== 200) { + props.setError(data.error) + return + } + props.setError(null) + }).catch((error) => { + console.log(error) + }) + } + return ( + <> +
+
+

Inicia sesión en L3TDE online

+ {(props.error !== null + ? (

{props.error}

) + : (<>) + )} +
+ + + + +
+ +
+
+

¿Ya tienes cuenta? Inicia sesión.

+
+
+ + ) +} diff --git a/js-src/components/upper-panel.tsx b/js-src/components/upper-panel.tsx new file mode 100644 index 0000000..46ccdfc --- /dev/null +++ b/js-src/components/upper-panel.tsx @@ -0,0 +1,7 @@ +import * as React from 'react' +export default function UpperPanel (): JSX.Element { + return ( + <> + + ) +} diff --git a/js-src/index.tsx b/js-src/index.tsx new file mode 100644 index 0000000..c5324bb --- /dev/null +++ b/js-src/index.tsx @@ -0,0 +1,11 @@ +'use strict' +import * as React from 'react' +import * as ReactDOMClient from 'react-dom/client' + +import Page from '@lastres/components/page' + +const gameContainer = document.querySelector('#game-container') +if (gameContainer !== null) { + const root = ReactDOMClient.createRoot(gameContainer) + root.render() +} diff --git a/js-src/login.ts b/js-src/login.ts new file mode 100644 index 0000000..8c8da09 --- /dev/null +++ b/js-src/login.ts @@ -0,0 +1,15 @@ +export function checkLogin (setError: (set: string | null) => void, + setIsLoggedIn: (set: boolean) => void): void { + fetch('/player/check_login', { + method: 'POST', + mode: 'same-origin', + cache: 'no-cache' + }).then(async (response) => { + const data = await response.json() + if (data.is_login === 1) { + setIsLoggedIn(true) + } + }).catch((error) => { + console.log(error) + }) +} diff --git a/lib/LasTres.pm b/lib/LasTres.pm new file mode 100644 index 0000000..7e8970c --- /dev/null +++ b/lib/LasTres.pm @@ -0,0 +1,48 @@ +package LasTres; + +use Mojo::Base 'Mojolicious', -signatures; + +# This method will run once at server start +sub startup ($self) { + + # Load configuration from config file + my $config = $self->plugin('NotYAMLConfig'); + + # Configure the application + $self->secrets( $config->{secrets} ); + + # Router + my $r = $self->routes; + + $self->helper(user => sub($self) { + require LasTres::DAO::Players; + my $result_set_players = LasTres::DAO::Players->ResultSet; + my $uuid = $self->session->{uuid}; + if (!defined $uuid) { + return; + } + my @players = $result_set_players->search({ + uuid => $uuid, + }); + my $player = $players[0]; + if (!defined $player) { + delete $self->session->{uuid}; + return; + } + return $player; + }); + + my $sessions = Mojolicious::Sessions->new; + $sessions->cookie_name('LasTres'); + $sessions->default_expiration(86400); + + # Normal route to controller + $r->get('/')->to('Root#index'); + $r->post('/player/register')->to('Player#register'); + $r->post('/player/login')->to('Player#login'); + $r->post('/player/check_login')->to('Player#check_login'); + $r->post('/pj/create')->to('PJ#create');; + $r->websocket('/ws')->to('Websocket#ws'); +} + +1; diff --git a/lib/LasTres/Area.pm b/lib/LasTres/Area.pm new file mode 100644 index 0000000..7466927 --- /dev/null +++ b/lib/LasTres/Area.pm @@ -0,0 +1,11 @@ +package LasTres::Area; + +use v5.36.0; + +use strict; +use warnings; + +use Moo::Role; + +requires qw/identifier locations name description parent/; +1; diff --git a/lib/LasTres/Controller/Example.pm b/lib/LasTres/Controller/Example.pm new file mode 100644 index 0000000..cd392ad --- /dev/null +++ b/lib/LasTres/Controller/Example.pm @@ -0,0 +1,11 @@ +package LasTres::Controller::Example; +use Mojo::Base 'Mojolicious::Controller', -signatures; + +# This action will render a template +sub welcome ($self) { + + # Render template "example/welcome.html.ep" with message + $self->render(msg => 'Welcome to the Mojolicious real-time web framework!'); +} + +1; diff --git a/lib/LasTres/Controller/PJ.pm b/lib/LasTres/Controller/PJ.pm new file mode 100644 index 0000000..32a56bd --- /dev/null +++ b/lib/LasTres/Controller/PJ.pm @@ -0,0 +1,57 @@ +package LasTres::Controller::PJ; + +use v5.36.0; + +use strict; +use warnings; + +use UUID::URandom qw/create_uuid_string/; + +use LasTres::DAO::Players; + +use Mojo::Base 'Mojolicious::Controller', -signatures; + +my $result_set_pjs = LasTres::DAO::PJ->ResultSet; + +sub create($self) { + my %params = %{ $self->req->json }; + my $user = $self->user; + if (!defined $user) { + return $self->render( + status => 401, + json => { error => 'You must login first.' } + ); + } + my @pjs = $user->pjs; + if (scalar @pjs >= 0) { + return $self->render( + status => 401, + json => { error => 'You reached the limit of free pjs, delete one or update to premium.', } + ); + } + my $uuid = create_uuid_string(); + my $owner = $user; + my $full_name = $param{full_name}; + if (!defined $full_name || !length $full_name > 3) { + return $self->render( + status => 400, + json => { error => 'The full_name is too short.', } + ); + } + my $short_name = $param{full_name}; + if (!defined $short_name || !length $full_name > 3) { + return $self->render( + status => 400, + json => { error => 'The short_name is too short.', } + ); + } + my $nick = $param{'nick'}; + if (!defined $nick || !length $nick > 0) { + return $self->render( + status => 400, + json => { error => 'You must set a nick.', } + ); + } + my $race = $param{'race'}; +} +1; diff --git a/lib/LasTres/Controller/Player.pm b/lib/LasTres/Controller/Player.pm new file mode 100644 index 0000000..71697e4 --- /dev/null +++ b/lib/LasTres/Controller/Player.pm @@ -0,0 +1,177 @@ +package LasTres::Controller::Player; + +use v5.36.0; + +use strict; +use warnings; + +use Crypt::URandom qw/urandom/; +use Crypt::Bcrypt qw/bcrypt bcrypt_check/; +use UUID::URandom qw/create_uuid_string/; + +use LasTres::DAO::Players; + +use Mojo::Base 'Mojolicious::Controller', -signatures; + +my $result_set_players = LasTres::DAO::Players->ResultSet; + +sub check_login($self) { + my $user = $self->user; + my $return_hash = { + is_login => 0, + is_verified => 0, + }; + if (!defined $user) { + return $self->render(status => 200, json => $return_hash); + } + $return_hash->{is_login} = 1; + if ($user->verified) { + $return_hash->{is_verified} = 1; + } + return $self->render(status => 200, json => $return_hash); +} + +sub login ($self) { + my %params = %{ $self->req->json }; + my $username = $params{username}; + my $password = $params{password}; + my @players = $result_set_players->search({ + -or => [ + username => $username, + email => $username, + ] + }); + my $player = $players[0]; + if (!defined $player) { + return $self->return_incorrect_username_or_password(); + } + if (!bcrypt_check($password, $player->encrypted_password)) { + return $self->return_incorrect_username_or_password(); + } + $self->session->{uuid} = $player->uuid; + return $self->render( + status => 200, + json => { + info => 'Login sucessful', + } + ); +} + +sub return_incorrect_username_or_password($self) { + return $self->render( + status => 400, + json => { + error => 'Incorrect username or password.', + } + ); +} + +sub register ($self) { + my %params = %{ $self->req->json }; + my $username = $params{username}; + my $password = $params{password}; + my $repeat_password = $params{repeat_password}; + my $email = $params{email}; + if ( !defined $username || length $username < 5 ) { + return $self->render( + status => 400, + json => { error => 'Username too short, minimum 5 characters.', }, + ); + } + if ($username =~ /@/) { + return $self->render( + status => 400, + json => { error => 'The character @ is forbidden in usernames.', }, + ); + } + if ( !defined $email || $email !~ /@/ ) { + return $self->render( + status => 400, + json => { error => 'This is not a valid mail.', }, + ); + } + if ( $password ne $repeat_password ) { + return $self->render( + status => 400, + json => { error => 'Passwords do not match.' } + ); + } + if ( $password =~ /^\d+$/ ) { + return $self->render( + status => 400, + json => { + error => 'Numeric only password are too unsafe to be allowed.' + } + ); + } + if ( length $password < 12 ) { + return $self->render( + status => 400, + json => { + error => 'The password should be at least 12 characters long.' + } + ); + } + my $new_salt = urandom(16); + my $encrypted_password = bcrypt $password, '2b', 12, $new_salt; + my $uuid = create_uuid_string(); + eval { + $result_set_players->create( + { + uuid => $uuid, + username => $username, + encrypted_password => $encrypted_password, + email => $email, + verified => 0, + } + ); + }; + if ($@) { + if ( $@ =~ /Key \((.*?)\)=\((.*?)\) already exists\./ ) { + return $self->render( + status => 400, + json => { + error => "The key $1 ($2) already exists in the database.", + } + ); + } + say STDERR $@; + return $self->render( + status => 500, + json => { + error => 'Unhandled database error.', + } + ); + } + + my @players = $result_set_players->search({ + uuid => $uuid, + }); + my $player = $players[0]; + + if (!defined $player) { + return $self->render( + status => 500, + json => { + error => 'Unknown error creating entity player.', + } + ); + } + + undef($@); + + $self->session->{uuid} = $player->uuid; + + return $self->render( + status => 200, + json => { + user => { + username => $username, + email => $email, + verified => 0, + }, + info => 'User sucessfully created', + } + ); +} +1; diff --git a/lib/LasTres/Controller/Root.pm b/lib/LasTres/Controller/Root.pm new file mode 100644 index 0000000..c7b34de --- /dev/null +++ b/lib/LasTres/Controller/Root.pm @@ -0,0 +1,13 @@ +package LasTres::Controller::Root; + +use v5.36.0; + +use strict; +use warnings; + +use Mojo::Base 'Mojolicious::Controller', -signatures; + +sub index($self) { + return $self->render; +} +1; diff --git a/lib/LasTres/Controller/Websocket.pm b/lib/LasTres/Controller/Websocket.pm new file mode 100644 index 0000000..85ef8d1 --- /dev/null +++ b/lib/LasTres/Controller/Websocket.pm @@ -0,0 +1,32 @@ +package LasTres::Controller::Websocket; + +use v5.36.0; + +use strict; +use warnings; + +use Mojo::Base 'Mojolicious::Controller', -signatures; + +sub ws($self) { + my $user = $self->user; + if (!defined $user) { + return $self->render( + status => 401, + json => { + error => 'You are not logged in.', + } + ); + } + if (!$user->verified) { + return $self->render( + status => 401, + json => { + error => 'Your user is not verified.', + } + ); + } + $self->on(json => sub($self, $hash) { + $self->_handle_packet($user, $hash); + }); +} +1; diff --git a/lib/LasTres/DAO/Players.pm b/lib/LasTres/DAO/Players.pm new file mode 100644 index 0000000..75633d5 --- /dev/null +++ b/lib/LasTres/DAO/Players.pm @@ -0,0 +1,23 @@ +package LasTres::DAO::Players; + +use v5.36.0; + +use strict; +use warnings; + +use feature 'signatures'; + +use Moo; + +use Params::ValidationCompiler qw/validation_for/; +use Types::Standard qw/Str Bool/; + +use LasTres::Schema; + +my $schema = LasTres::Schema->Schema; +my $result_set = $schema->resultset('Player'); + +sub ResultSet { + return $result_set; +} +1; diff --git a/lib/LasTres/Location.pm b/lib/LasTres/Location.pm new file mode 100644 index 0000000..026f961 --- /dev/null +++ b/lib/LasTres/Location.pm @@ -0,0 +1,11 @@ +package LasTres::Location; + +use v5.36.0; + +use strict; +use warnings; + +use Moo::Role; + +requires qw/identifier name description parent actions npcs/; +1; diff --git a/lib/LasTres/Planet.pm b/lib/LasTres/Planet.pm new file mode 100644 index 0000000..bd1ac02 --- /dev/null +++ b/lib/LasTres/Planet.pm @@ -0,0 +1,11 @@ +package LasTres::Planet; + +use v5.36.0; + +use strict; +use warnings; + +use Moo::Role; + +requires qw/identifier super_areas name description/; +1; diff --git a/lib/LasTres/Planet/Bahdder.pm b/lib/LasTres/Planet/Bahdder.pm new file mode 100644 index 0000000..eda0d24 --- /dev/null +++ b/lib/LasTres/Planet/Bahdder.pm @@ -0,0 +1,41 @@ +package LasTres::Planet::Bahdder; + +use v5.36.0; + +use strict; +use warnings; + +use Moo; + +with 'LasTres::Planet'; + +use Module::Pluggable search_path => ['LasTres::Planet::Bahdder']; + +has super_areas => ( + is => 'lazy' +); +sub identifier { + return 'bahdder', +} + +sub _build_super_areas { + my $self = shift; + my $hash = {}; + my @super_areas = $self->plugins(); + for $super_area (@super_areas) { + $hash->{$super_area->identifier} = $super_area; + } + return $hash; +} + +sub name { + return 'Bahdder'; +} + +sub description { + return 'Archivo de la Patrulla Galáctica: Bahdder es uno de los planetas con mayor población de la galaxia con una población estimada ' + . 'de tres mil millones de individuos pertenecientes a especies consideradas inteligentes. ' + . 'Es conocido como el planeta origen de los Yaren; no obstante la presencia de los mismos en este planeta es actualmente simbólica. ' + . 'Su población actual esta mayormente compuesta por razas locales como los Áldimor. '; +} +1; diff --git a/lib/LasTres/Planet/Bahdder/BosqueDelHeroe.pm b/lib/LasTres/Planet/Bahdder/BosqueDelHeroe.pm new file mode 100644 index 0000000..eb20309 --- /dev/null +++ b/lib/LasTres/Planet/Bahdder/BosqueDelHeroe.pm @@ -0,0 +1,47 @@ +package LasTres::Planet::Bahdder::BosqueDelHeroe; + +use v5.36.0; + +use strict; +use warnings; + +use Moo; + +with 'LasTres::SuperArea'; + +use Module::Pluggable search_path => ['LasTres::Planet::Bahdder::BosqueDelHeroe']; + +has areas => ( + is => 'lazy' +); + +sub identifier { + return 'bosque_del_heroe'; +} + +sub _build_areas { + my $self = shift; + my $hash = {}; + my @areas = $self->plugins(); + for $area (@areas) { + $hash->{$area->identifier} = $area; + } + return $hash; +} + +sub name { + return 'Bosque del Héroe'; +} + +sub description { + return 'El Bosque del Héroe es el pulmón del planeta Bahdder. ' + . 'Se cree que solo una pequeña parte de las especies que viven en el mismo han sido catalogadas. ' + . 'Los áldimor viven en este bosque en armonía con la naturaleza. ' + . 'En este bosque se encuentra la Torre de hechicería Áldimor, lugar donde los áldimor más talentosos aprenden ' + . 'a usar la magia para la guerra. '; +} + +sub parent { + return LasTres::Planet::Bahdder->new; +} +1; diff --git a/lib/LasTres/Planet/Bahdder/BosqueDelHeroe/BosqueDelHeroeI.pm b/lib/LasTres/Planet/Bahdder/BosqueDelHeroe/BosqueDelHeroeI.pm new file mode 100644 index 0000000..83504c9 --- /dev/null +++ b/lib/LasTres/Planet/Bahdder/BosqueDelHeroe/BosqueDelHeroeI.pm @@ -0,0 +1,37 @@ +package LasTres::Planet::Bahdder::BosqueDelHeroe::BosqueDelHeroeI; + +use v5.36.0; + +use strict; +use warnings; + +with 'LasTres::Area'; + +use Module::Pluggable search_path => ['LasTres::Planet::Bahdder::BosqueDelHeroe::BosqueDelHeroeI']; + +has locations => ( + is => 'lazy' +); + +sub identifier { + return 'bosque_del_heroe_i'; +} + +sub _build_locations { + my $self = shift; + my $hash = {}; + my @locations = $self->plugins(); + for $location (@locations) { + $hash->{$location->identifier} = $location; + } + return $hash; +} + +sub name { + return 'Bosque del Héroe (I)'; +} + +sub parent { + return LasTres::Planet::Bahdder::BosqueDelHeroe->new; +} +1; diff --git a/lib/LasTres/Planet/Bahdder/BosqueDelHeroe/BosqueDelHeroeI/TribuDeLaLima.pm b/lib/LasTres/Planet/Bahdder/BosqueDelHeroe/BosqueDelHeroeI/TribuDeLaLima.pm new file mode 100644 index 0000000..cc78241 --- /dev/null +++ b/lib/LasTres/Planet/Bahdder/BosqueDelHeroe/BosqueDelHeroeI/TribuDeLaLima.pm @@ -0,0 +1,35 @@ +package LasTres::Planet::Bahdder::BosqueDelHeroe::BosqueDelHeroeI::TribuDeLaLima; + +use v5.36.0; + +use strict; +use warnings; + +with 'LasTres::Location'; + +sub identifier { + return 'tribu_de_la_lima'; +} + +sub name { + return 'Tribu de la Lima (Exterior)'; +} + +sub description { + return 'La Tribu de la Lima se siente como un hogar seas o no de aquí. ' + . 'Las casitas están improvisadas con paja que los aldeanos intercambian con otras tribus. ' + . 'Los cultivos de Lima están siempre buscando trabajadores, el sueldo es una parte de lo cosechado. '; +} + +sub parent { + return LasTres::Planet::Bahdder::BosqueDelHeroe::BosqueDelHeroeI->new; +} + +sub actions { + return []; +} + +sub npcs { + return []; +} +1; diff --git a/lib/LasTres/Race.pm b/lib/LasTres/Race.pm new file mode 100644 index 0000000..f5bfa0a --- /dev/null +++ b/lib/LasTres/Race.pm @@ -0,0 +1,23 @@ +package LasTres::Race; + +use v5.36.0; + +use strict; +use warnings; + +use feature 'signatures'; + +use Moo::Role; + +requires qw/spawn identifier name name_selection description is_playable/; + +sub hash($self) { + return { + identifier => $self->{identifier}, + name => $self->{name}, + name_selection => $self->{name_selection}, + description => $self->{description}, + is_playable => $self->{is_playable}, + } +} +1; diff --git a/lib/LasTres/Race/Aldimor.pm b/lib/LasTres/Race/Aldimor.pm new file mode 100644 index 0000000..6bde2b9 --- /dev/null +++ b/lib/LasTres/Race/Aldimor.pm @@ -0,0 +1,35 @@ +package LasTres::Race::Aldimor; + +use v5.36.0; + +use strict; +use warnings; + +use Moo; + +with 'LasTres::Race'; + +sub spawn { + return LasTres::Planet::Bahdder::BosqueDelHeroe::BosqueDelHeroeI::TribuDeLaLima->new; +} + +sub identifier { + return 'aldimor'; +} + +sub name { + return 'Aldimor'; +} + +sub name_selection { + return 'Aldimor del Bosque del Héroe.'; +} + +sub description { + return 'La raza de la naturaleza y la magia.'; +} + +sub is_playable { + return 1; +} +1; diff --git a/lib/LasTres/Races.pm b/lib/LasTres/Races.pm new file mode 100644 index 0000000..e6ed8c4 --- /dev/null +++ b/lib/LasTres/Races.pm @@ -0,0 +1,77 @@ +package LasTres::Races; + +use v5.36.0; + +use strict; +use warnings; + +use feature 'signatures'; + +use Moo; +use Module::Pluggable search_path => ['LasTres::Race']; + +has hash => ( + is => 'lazy', +); + +has hash_playable => ( + is => 'lazy', +) + +has hash_all => ( + is => 'lazy', +) + +sub _build_hash_all($self) { + return { map { $_ => $self->hash->{$_}->hash } (keys %{$self->hash}) }; +} + +sub _build_hash_all_playable($self) { + my %hash = %{$self->hash}; + for my $race_key (keys %hash) { + my $race = $hash{$race_key}; + if (!$race->is_playable) { + delete $hash{$race_key}; + } + $hash{$race_key} = $race->hash; + } + return \%hash; +} + +sub _build_hash { + my $self = shift; + my $hash = {}; + my @races = $self->plugins(); + for my $race (@races) { + $hash{$race->identifier} = $race; + } + return $hash; +} + +sub _build_hash_playable { + my $self = shift; + my $hash = {@{$self->hash}}; + for my $identifier_race (keys %$hash) { + my $race = $hash->{$identifier_race}; + if (!$race->is_playable) { + delete $hash->{$identifier_race}; + } + } + return $hash; +} + +sub get($self, $race_identifier) { + return $self->hash->{$race_identifier}; +} + +sub get_playable($self, $race_identifier) { + my $race = $self->hash->{$race_identifier}; + if (!defined $race) { + return undef; + } + if (!$race->is_playable) { + return undef; + } + return $race; +} +1; diff --git a/lib/LasTres/Schema.pm b/lib/LasTres/Schema.pm new file mode 100644 index 0000000..66edaac --- /dev/null +++ b/lib/LasTres/Schema.pm @@ -0,0 +1,40 @@ +package LasTres::Schema; +our $VERSION = 1; + +use v5.36.0; + +use strict; +use warnings; + +use feature 'signatures'; + +use LasTres; + +use parent 'DBIx::Class::Schema'; + +__PACKAGE__->load_namespaces(); + +sub Schema($class) { + my $app = LasTres->new; + my $config = $app->{config}; + my $database_config = $config->{database}; + my $dbname = $database_config->{dbname}; + my $host = $database_config->{host}; + my $port = $database_config->{port}; + my $user = $database_config->{user}; + my $password = $database_config->{password}; + my $dsn = 'dbi:Pg:'; + if (!defined $dbname) { + die "The key database/dbname must be configured."; + } + $dsn .= "dbname=$dbname"; + if (defined $host) { + $dsn .= ";host=$host"; + } + if (defined $port) { + $dsn .= ";port=$port"; + } + # Undef is perfectly fine for username and password. + return $class->connect($dsn, $user, $password); +} +1; diff --git a/lib/LasTres/Schema/Result/CompanionNPC.pm b/lib/LasTres/Schema/Result/CompanionNPC.pm new file mode 100644 index 0000000..dc5a04a --- /dev/null +++ b/lib/LasTres/Schema/Result/CompanionNPC.pm @@ -0,0 +1,79 @@ +package LasTres::Schema::Result::CompanionNPC; + +use v5.36.0; + +use strict; +use warnings; + +use parent 'DBIx::Class::Core'; + +__PACKAGE__->table('player_companion_npcs'); + +__PACKAGE__->add_columns( + uuid => { + data_type => 'uuid', + default_value => \'uuid_generate_v4()', + is_nullable => 0, + }, + owner => { + data_type => 'uuid', + is_nullable => 0, + is_foreign_key => 1, + }, + identifier => { + data_type => 'text', + is_nullable => 0, + }, + nick => { + data_type => 'text', + is_nullable => 1, + }, + race => { + data_type => 'text', + is_nullable => 0, + }, + level => { + data_type => 'integer', + default_value => \'1', + is_nullable => 0, + }, + exp => { + data_type => 'integer', + default_value => \'1', + is_nullable => 0, + }, + equipment => { + data_type => 'uuid', + is_nullable => 0, + is_foreign_key => 1, + }, + stats => { + data_type => 'uuid', + is_nullable => 0, + is_foreign_key => 1, + }, + skills => { + data_type => 'uuid', + is_nullable => 0, + is_foreign_key => 1, + }, + spells => { + data_type => 'uuid', + is_nullable => 0, + is_foreign_key => 1, + }, + inventory => { + data_type => 'uuid', + is_nullable => 0, + } + +); +__PACKAGE__->set_primary_key('uuid'); + +__PACKAGE__->belongs_to('stats', 'LasTres::Schema::Result::Stats'); +__PACKAGE__->belongs_to('inventory', 'LasTres::Schema::Result::Inventory'); +__PACKAGE__->belongs_to('equipment', 'LasTres::Schema::Result::Equipment'); +__PACKAGE__->belongs_to('skills', 'LasTres::Schema::Result::SkillLikeList'); +__PACKAGE__->belongs_to('spells', 'LasTres::Schema::Result::SkillLikeList'); +__PACKAGE__->belongs_to('owner', 'LasTres::Schema::Result::PJ'); +1; diff --git a/lib/LasTres/Schema/Result/Equipment.pm b/lib/LasTres/Schema/Result/Equipment.pm new file mode 100644 index 0000000..f9b5ba3 --- /dev/null +++ b/lib/LasTres/Schema/Result/Equipment.pm @@ -0,0 +1,21 @@ +package LasTres::Schema::Result::Equipment; + +use v5.36.0; + +use strict; +use warnings; + +use parent 'DBIx::Class::Core'; + +__PACKAGE__->table('equipment'); + +__PACKAGE__->add_columns( + uuid => { + data_type => 'uuid', + is_nullable => 0, + }, +); + +__PACKAGE__->set_primary_key('uuid'); +__PACKAGE__->has_many('items', 'LasTres::Schema::Result::EquipmentItem', 'equipment'); +1; diff --git a/lib/LasTres/Schema/Result/EquipmentItem.pm b/lib/LasTres/Schema/Result/EquipmentItem.pm new file mode 100644 index 0000000..f699e4e --- /dev/null +++ b/lib/LasTres/Schema/Result/EquipmentItem.pm @@ -0,0 +1,34 @@ +package LasTres::Schema::Result::EquipmentItem; + +use v5.36.0; + +use strict; +use warnings; + +use parent 'DBIx::Class::Core'; + +__PACKAGE__->table('equipment_items'); + +__PACKAGE__->add_columns( + kind => { + data_type => 'text', + is_nullable => 0, + }, + equipment => { + data_type => 'uuid', + is_nullable => 0, + is_foreign_key => 1, + }, + identifier => { + data_type => 'text', + is_nullable => 0, + }, + quantity => { + data_type => 'Integer', + is_nullable => 0, + }, +); + +__PACKAGE__->set_primary_key('kind', 'equipment'); +__PACKAGE__->belongs_to('equipment', 'LasTres::Schema::Result::Equipment'); +1; diff --git a/lib/LasTres/Schema/Result/Inventory.pm b/lib/LasTres/Schema/Result/Inventory.pm new file mode 100644 index 0000000..297dbd0 --- /dev/null +++ b/lib/LasTres/Schema/Result/Inventory.pm @@ -0,0 +1,20 @@ +package LasTres::Schema::Result::Inventory; + +use v5.36.0; + +use strict; +use warnings; + +use parent 'DBIx::Class::Core'; + +__PACKAGE__->table('inventories'); + +__PACKAGE__->add_columns( + uuid => { + data_type => 'uuid', + is_nullable => 0, + }, +); +__PACKAGE__->set_primary_key('uuid'); +__PACKAGE__->has_many('items', 'LasTres::Schema::Result::InventoryItem', 'inventory'); +1; diff --git a/lib/LasTres/Schema/Result/InventoryItem.pm b/lib/LasTres/Schema/Result/InventoryItem.pm new file mode 100644 index 0000000..5ec21ae --- /dev/null +++ b/lib/LasTres/Schema/Result/InventoryItem.pm @@ -0,0 +1,35 @@ +package LasTres::Schema::Result::InventoryItem; + +use v5.36.0; + +use strict; +use warnings; + +use parent 'DBIx::Class::Core'; + +__PACKAGE__->table('inventory_items'); + +__PACKAGE__->add_columns( + uuid => { + data_type => 'uuid', + is_nullable => 0, + default_value => \'uuid_generate_v4()', + }, + inventory => { + data_type => 'uuid', + is_nullable => 0, + is_foreign_key => 1, + }, + identifier => { + data_type => 'text', + is_nullable => 0, + }, + quantity => { + data_type => 'Integer', + is_nullable => 0, + }, +); + +__PACKAGE__->set_primary_key('uuid'); +__PACKAGE__->belongs_to('inventory', 'LasTres::Schema::Result::Inventory'); +1; diff --git a/lib/LasTres/Schema/Result/PJ.pm b/lib/LasTres/Schema/Result/PJ.pm new file mode 100644 index 0000000..00cc596 --- /dev/null +++ b/lib/LasTres/Schema/Result/PJ.pm @@ -0,0 +1,99 @@ +package LasTres::Schema::Result::PJ; + +use v5.36.0; + +use strict; +use warnings; + +use parent 'DBIx::Class::Core'; + +__PACKAGE__->table('player_pjs'); + +__PACKAGE__->add_columns( + uuid => { + data_type => 'uuid', + default_value => \'uuid_generate_v4()', + is_nullable => 0, + }, + owner => { + data_type => 'uuid', + is_nullable => 0, + is_foreign_key => 1, + }, + full_name => { + data_type => 'text', + is_nullable => 0, + }, + short_name => { + data_type => 'text', + is_nullable => 0, + }, + nick => { + data_type => 'text', + is_nullable => 0, + }, + race => { + data_type => 'text', + is_nullable => 0, + }, + team => { + data_type => 'uuid', + is_nullable => 0, + }, + creation_date => { + data_type => 'timestamp', + default_value => \'NOW()', + is_nullable => 0, + }, + last_activity => { + data_type => 'timestamp', + default_value => \'NOW()', + is_nullable => 0, + }, + level => { + data_type => 'integer', + default_value => \'1', + is_nullable => 0, + }, + exp => { + data_type => 'integer', + default_value => \'1', + is_nullable => 0, + }, + equipment => { + data_type => 'uuid', + is_nullable => 0, + is_foreign_key => 1, + }, + stats => { + data_type => 'uuid', + is_foreign_key => 1, + is_nullable => 0, + }, + skills => { + data_type => 'uuid', + is_nullable => 0, + is_foreign_key => 1, + }, + spells => { + data_type => 'uuid', + is_nullable => 0, + is_foreign_key => 1, + }, + inventory => { + data_type => 'uuid', + is_nullable => 0, + } + +); +__PACKAGE__->set_primary_key('uuid'); + +__PACKAGE__->has_many('npcs', 'LasTres::Schema::Result::CompanionNPC', 'owner'); +__PACKAGE__->belongs_to('stats', 'LasTres::Schema::Result::Stats'); +__PACKAGE__->belongs_to('inventory', 'LasTres::Schema::Result::Inventory'); +__PACKAGE__->belongs_to('skills', 'LasTres::Schema::Result::SkillLikeList'); +__PACKAGE__->belongs_to('spells', 'LasTres::Schema::Result::SkillLikeList'); +__PACKAGE__->belongs_to('equipment', 'LasTres::Schema::Result::Equipment'); +__PACKAGE__->belongs_to('team', 'LasTres::Schema::Result::Team'); +__PACKAGE__->belongs_to('owner', 'LasTres::Schema::Result::Player'); +1; diff --git a/lib/LasTres/Schema/Result/Player.pm b/lib/LasTres/Schema/Result/Player.pm new file mode 100644 index 0000000..9c2b273 --- /dev/null +++ b/lib/LasTres/Schema/Result/Player.pm @@ -0,0 +1,52 @@ +package LasTres::Schema::Result::Player; + +use v5.36.0; + +use strict; +use warnings; + +use parent 'DBIx::Class::Core'; + +__PACKAGE__->table('players'); + +__PACKAGE__->add_columns( + uuid => { + data_type => 'uuid', + is_nullable => 0, + }, + username => { + data_type => 'text', + is_nullable => 0, + }, + encrypted_password => { + data_type => 'text', + is_nullable => 0, + }, + email => { + data_type => 'text', + is_nullable => 0, + }, + verified => { + data_type => 'boolean', + is_nullable => 0, + }, + verification_token => { + data_type => 'text', + is_nullable => 1, + }, + register_date => { + data_type => 'timestamp', + is_nullable => 0, + default_value => \'NOW()', + }, + last_activity => { + data_type => 'timestamp', + is_nullable => 0, + default_value => \'NOW()', + }, +); +__PACKAGE__->set_primary_key('uuid'); +__PACKAGE__->has_many('pjs', 'LasTres::Schema::Result::PJ', 'owner'); +__PACKAGE__->add_unique_constraint("unique_constraint_username", ['username']); +__PACKAGE__->add_unique_constraint("unique_constraint_email", ["email"]); +1; diff --git a/lib/LasTres/Schema/Result/SkillLikeItem.pm b/lib/LasTres/Schema/Result/SkillLikeItem.pm new file mode 100644 index 0000000..358c197 --- /dev/null +++ b/lib/LasTres/Schema/Result/SkillLikeItem.pm @@ -0,0 +1,29 @@ +package LasTres::Schema::Result::SkillLikeItem; + +use v5.36.0; + +use strict; +use warnings; + +use parent 'DBIx::Class::Core'; + +__PACKAGE__->table('skill_like_items'); + +__PACKAGE__->add_columns( + identifier => { + data_type => 'text', + is_nullable => 0, + }, + owner_list => { + data_type => 'uuid', + is_nullable => 0, + }, + level => { + data_type => 'integer', + is_nullable => 0, + default_value => \'1', + } +); +__PACKAGE__->set_primary_key('identifier', 'owner_list'); +__PACKAGE__->belongs_to('owner_list', 'LasTres::Schema::Result::SkillLikeList'); +1; diff --git a/lib/LasTres/Schema/Result/SkillLikeList.pm b/lib/LasTres/Schema/Result/SkillLikeList.pm new file mode 100644 index 0000000..124e11c --- /dev/null +++ b/lib/LasTres/Schema/Result/SkillLikeList.pm @@ -0,0 +1,21 @@ +package LasTres::Schema::Result::SkillLikeList; + +use v5.36.0; + +use strict; +use warnings; + +use parent 'DBIx::Class::Core'; + +__PACKAGE__->table('skill_like_lists'); + +__PACKAGE__->add_columns( + uuid => { + data_type => 'uuid', + default_value => \'uuid_generate_v4()', + is_nullable => 0, + }, +); +__PACKAGE__->set_primary_key('uuid'); +__PACKAGE__->has_many('items', 'LasTres::Schema::Result::SkillLikeItem', 'owner_list'); +1; diff --git a/lib/LasTres/Schema/Result/Stats.pm b/lib/LasTres/Schema/Result/Stats.pm new file mode 100644 index 0000000..b1da671 --- /dev/null +++ b/lib/LasTres/Schema/Result/Stats.pm @@ -0,0 +1,51 @@ +package LasTres::Schema::Result::Stats; + +use v5.36.0; + +use strict; +use warnings; + +use parent 'DBIx::Class::Core'; + +__PACKAGE__->table('stats'); + +__PACKAGE__->add_columns( + uuid => { + data_type => 'uuid', + is_nullable => 0, + }, + health => { + data_type => 'integer', + is_nullable => 0, + }, + mana => { + data_type => 'integer', + is_nullable => 0, + }, + strength => { + data_type => 'integer', + is_nullable => 0, + }, + resistance => { + data_type => 'integer', + is_nullable => 0, + }, + magic => { + data_type => 'integer', + is_nullable => 0, + }, + speed => { + data_type => 'integer', + is_nullable => 0, + }, + intelligence => { + data_type => 'integer', + is_nullable => 0, + }, + charisma => { + data_type => 'integer', + is_nullable => 0, + }, +); +__PACKAGE__->set_primary_key('uuid'); +1; diff --git a/lib/LasTres/Schema/Result/Team.pm b/lib/LasTres/Schema/Result/Team.pm new file mode 100644 index 0000000..a2b63a1 --- /dev/null +++ b/lib/LasTres/Schema/Result/Team.pm @@ -0,0 +1,49 @@ +package LasTres::Schema::Result::Team; + +use v5.36.0; + +use strict; +use warnings; + +use parent 'DBIx::Class::Core'; + +__PACKAGE__->table('teams'); + +__PACKAGE__->add_columns( + uuid => { + data_type => 'uuid', + is_nullable => 0, + }, + leader => { + data_type => 'uuid', + is_nullable => 0, + is_foreign_key => 1, + }, + name => { + data_type => 'text', + is_nullable => 0, + }, + planet => { + data_type => 'text', + is_nullable => 0, + }, + super_area => { + data_type => 'text', + is_nullable => 0, + }, + area => { + data_type => 'text', + is_nullable => 0, + }, + location => { + data_type => 'text', + is_nullable => 0, + }, +); + + +__PACKAGE__->set_primary_key('uuid'); +__PACKAGE__->add_unique_constraint(u_name => ['name']); +__PACKAGE__->has_many('members', 'LasTres::Schema::Result::PJ', 'team'); +__PACKAGE__->belongs_to('leader', 'LasTres::Schema::Result::PJ'); +1; diff --git a/lib/LasTres/SuperArea.pm b/lib/LasTres/SuperArea.pm new file mode 100644 index 0000000..05fa7cd --- /dev/null +++ b/lib/LasTres/SuperArea.pm @@ -0,0 +1,11 @@ +package LasTres::SuperArea; + +use v5.36.0; + +use strict; +use warnings; + +use Moo::Role; + +requires qw/identifier areas name description parent/; +1; diff --git a/package.json b/package.json new file mode 100644 index 0000000..1ebd620 --- /dev/null +++ b/package.json @@ -0,0 +1,39 @@ +{ + "name": "LasTres", + "version": "0.1.1", + "description": "", + "private": true, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "MIT", + "devDependencies": { + "@babel/preset-react": "^7.18.6", + "@types/react": "^18.0.28", + "@types/react-dom": "^18.0.11", + "@typescript-eslint/eslint-plugin": "^5.57.0", + "babel-loader": "^9.1.2", + "eslint": "^8.36.0", + "eslint-config-standard-with-typescript": "^34.0.1", + "eslint-import-resolver-typescript": "^3.5.3", + "eslint-plugin-import": "^2.27.5", + "eslint-plugin-n": "^15.6.1", + "eslint-plugin-no-relative-import-paths": "^1.5.2", + "eslint-plugin-promise": "^6.1.1", + "eslint-plugin-react": "^7.32.2", + "file-loader": "^6.2.0", + "ts-loader": "^9.4.2", + "typescript": "^5.0.2", + "typescript-transform-paths": "^3.4.6", + "url-loader": "^4.1.1", + "webpack": "^5.38.1", + "webpack-cli": "^4.7.2" + }, + "dependencies": { + "babel-preset-react": "^6.24.1", + "react": "^18.2.0", + "react-dom": "^18.2.0" + } +} diff --git a/public/css/styles.css b/public/css/styles.css new file mode 100644 index 0000000..af78ffc --- /dev/null +++ b/public/css/styles.css @@ -0,0 +1,103 @@ +body { + margin: 0px; + padding: 0px; + min-height: 100%; } + body div.width-max-content { + width: max-content; } + body div#game-container { + min-height: 100%; } + body div#game-container nav.menu-bar { + width: 100%; + background: grey; + height: 3rem; + display: flex; + justify-content: end; + align-items: center; } + body div#game-container nav.menu-bar a { + font-size: 1.7rem; + display: inline-block; + background: chocolate; + border-radius: 0.3rem; + color: ghostwhite; + text-decoration: none; + margin-right: 0.7rem; + margin-left: 0.7rem; + height: 100%; } + body div#game-container nav.menu-bar a:last-child { + margin-right: 0px; } + body div#game-container nav.menu-bar a:hover { + background: ghostwhite; + color: black; } + body div.pj-selection-menu { + display: flex; + justify-content: center; + align-items: center; + min-height: 100%; + background: ghostwhite; + flex-direction: column; } + body div.pj-selection-menu div.pj-selection-menu-container { + background: ghostwhite; + color: black; + padding: 30px; + display: flex; + align-items: center; + min-width: 80%; + min-height: 100%; + flex-direction: column; + overflow: scroll; } + body div.pj-selection-menu div.pj-selection-menu-container div.pj-list { + background: ghostwhite; + width: 90%; + padding: 10px; + display: flex; + flex-wrap: wrap; + flex-direction: row; + align-content: center; + justify-content: center; + align-items: center; + flex-grow: 1px; + gap: 8px; } + body div.pj-selection-menu div.pj-selection-menu-container div.pj-list a { + width: 10em; + height: 10em; + background: white; + border: solid 1px black; + box-shadow: 3px 1px 3px 3px #333; + color: blue; + display: flex; + align-items: center; + justify-content: center; + text-align: center; + text-decoration: none; } + body div.pj-selection-menu div.pj-selection-menu-container div.pj-list a:hover { + color: yellow; + background: gray; } + body div.login-container { + display: flex; + justify-content: center; + align-items: center; + height: 100%; + background: url("/img/wallpaper.jpg") no-repeat center black; + flex-direction: column; } + body div.login-container div.login-contained { + background: #001e8b; + color: azure; + padding: 30px; + border: solid 1px black; + display: flex; + align-items: center; + flex-direction: column; } + body div.login-container div.login-contained input { + display: block; } + body div.login-container div.login-contained button { + display: block; } + body div.login-container div.login-contained div.login-form { + display: flex; + flex-direction: column; + width: max-content; } + body div.login-container div.login-contained div.login-form div.align-self-end { + align-self: end; } + body div.login-container div.login-contained a { + color: aquamarine; } + body div.login-container div.login-contained a:hover { + color: yellow; } diff --git a/public/css/styles.scss b/public/css/styles.scss new file mode 100644 index 0000000..2fa37d2 --- /dev/null +++ b/public/css/styles.scss @@ -0,0 +1,125 @@ +body { + margin: 0px; + padding: 0px; + min-height: 100%; + div.width-max-content { + width: max-content; + } + div#game-container { + min-height: 100%; + nav.menu-bar { + width: 100%; + background: grey; + height: 3rem; + display: flex; + justify-content: end; + align-items: center; + a { + font-size: 1.7rem; + display: inline-block; + background: chocolate; + border-radius: 0.3rem; + color: ghostwhite; + text-decoration: none; + margin-right: 0.7rem; + margin-left: 0.7rem; + height: 100%; + &:last-child { + margin-right: 0px; + } + &:hover { + background: ghostwhite; + color: black; + + } + } + } + } + div.pj-selection-menu { + display: flex; + justify-content: center; + align-items: center; + min-height: 100%; + background: ghostwhite; + flex-direction: column; + div.pj-selection-menu-container { + background: ghostwhite; + color: black; + padding: 30px; + display: flex; + align-items: center; + min-width: 80%; + min-height: 100%; + flex-direction: column; + overflow: scroll; + div.pj-list { + background: ghostwhite; + width: 90%; + padding: 10px; + display: flex; + flex-wrap: wrap; + flex-direction: row; + align-content: center; + justify-content: center; + align-items: center; + flex-grow: 1px; + gap: 8px; + + a { + width: 10em; + height: 10em; + background: white; + border: solid 1px black; + box-shadow: 3px 1px 3px 3px #333; + color: blue; + display: flex; + align-items: center; + justify-content: center; + text-align: center; + &:hover { + color: yellow; + background: gray; + } + text-decoration: none; + } + } + } + } + div.login-container { + display: flex; + justify-content: center; + align-items: center; + height: 100%; + background: url('/img/wallpaper.jpg') no-repeat center black; + flex-direction: column; + div.login-contained { + background: rgba(0, 30, 139, 1); + color: azure; + padding: 30px; + border: solid 1px black; + display: flex; + align-items: center; + flex-direction: column; + input { + display: block; + } + button { + display: block; + } + div.login-form { + display: flex; + flex-direction: column; + width: max-content; + div.align-self-end { + align-self: end; + } + } + a { + color: aquamarine; + &:hover { + color: yellow; + } + } + } + } +} diff --git a/public/img/wallpaper.jpg b/public/img/wallpaper.jpg new file mode 100644 index 0000000..73e1f75 Binary files /dev/null and b/public/img/wallpaper.jpg differ diff --git a/public/js/bundle.js b/public/js/bundle.js new file mode 100644 index 0000000..94ddd7a --- /dev/null +++ b/public/js/bundle.js @@ -0,0 +1,270 @@ +/* + * ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development"). + * This devtool is neither made for production nor for readable output files. + * It uses "eval()" calls to create a separate source file in the browser devtools. + * If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/) + * or disable the default devtool with "devtool: false". + * If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/). + */ +/******/ (() => { // webpackBootstrap +/******/ "use strict"; +/******/ var __webpack_modules__ = ({ + +/***/ "./node_modules/react-dom/cjs/react-dom.development.js": +/*!*************************************************************!*\ + !*** ./node_modules/react-dom/cjs/react-dom.development.js ***! + \*************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +eval("/**\n * @license React\n * react-dom.development.js\n *\n * Copyright (c) Facebook, Inc. and its affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n\n\nif (true) {\n (function() {\n\n 'use strict';\n\n/* global __REACT_DEVTOOLS_GLOBAL_HOOK__ */\nif (\n typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined' &&\n typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart ===\n 'function'\n) {\n __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(new Error());\n}\n var React = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\nvar Scheduler = __webpack_require__(/*! scheduler */ \"./node_modules/scheduler/index.js\");\n\nvar ReactSharedInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;\n\nvar suppressWarning = false;\nfunction setSuppressWarning(newSuppressWarning) {\n {\n suppressWarning = newSuppressWarning;\n }\n} // In DEV, calls to console.warn and console.error get replaced\n// by calls to these methods by a Babel plugin.\n//\n// In PROD (or in packages without access to React internals),\n// they are left as they are instead.\n\nfunction warn(format) {\n {\n if (!suppressWarning) {\n for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {\n args[_key - 1] = arguments[_key];\n }\n\n printWarning('warn', format, args);\n }\n }\n}\nfunction error(format) {\n {\n if (!suppressWarning) {\n for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {\n args[_key2 - 1] = arguments[_key2];\n }\n\n printWarning('error', format, args);\n }\n }\n}\n\nfunction printWarning(level, format, args) {\n // When changing this logic, you might want to also\n // update consoleWithStackDev.www.js as well.\n {\n var ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame;\n var stack = ReactDebugCurrentFrame.getStackAddendum();\n\n if (stack !== '') {\n format += '%s';\n args = args.concat([stack]);\n } // eslint-disable-next-line react-internal/safe-string-coercion\n\n\n var argsWithFormat = args.map(function (item) {\n return String(item);\n }); // Careful: RN currently depends on this prefix\n\n argsWithFormat.unshift('Warning: ' + format); // We intentionally don't use spread (or .apply) directly because it\n // breaks IE9: https://github.com/facebook/react/issues/13610\n // eslint-disable-next-line react-internal/no-production-logging\n\n Function.prototype.apply.call(console[level], console, argsWithFormat);\n }\n}\n\nvar FunctionComponent = 0;\nvar ClassComponent = 1;\nvar IndeterminateComponent = 2; // Before we know whether it is function or class\n\nvar HostRoot = 3; // Root of a host tree. Could be nested inside another node.\n\nvar HostPortal = 4; // A subtree. Could be an entry point to a different renderer.\n\nvar HostComponent = 5;\nvar HostText = 6;\nvar Fragment = 7;\nvar Mode = 8;\nvar ContextConsumer = 9;\nvar ContextProvider = 10;\nvar ForwardRef = 11;\nvar Profiler = 12;\nvar SuspenseComponent = 13;\nvar MemoComponent = 14;\nvar SimpleMemoComponent = 15;\nvar LazyComponent = 16;\nvar IncompleteClassComponent = 17;\nvar DehydratedFragment = 18;\nvar SuspenseListComponent = 19;\nvar ScopeComponent = 21;\nvar OffscreenComponent = 22;\nvar LegacyHiddenComponent = 23;\nvar CacheComponent = 24;\nvar TracingMarkerComponent = 25;\n\n// -----------------------------------------------------------------------------\n\nvar enableClientRenderFallbackOnTextMismatch = true; // TODO: Need to review this code one more time before landing\n// the react-reconciler package.\n\nvar enableNewReconciler = false; // Support legacy Primer support on internal FB www\n\nvar enableLazyContextPropagation = false; // FB-only usage. The new API has different semantics.\n\nvar enableLegacyHidden = false; // Enables unstable_avoidThisFallback feature in Fiber\n\nvar enableSuspenseAvoidThisFallback = false; // Enables unstable_avoidThisFallback feature in Fizz\n// React DOM Chopping Block\n//\n// Similar to main Chopping Block but only flags related to React DOM. These are\n// grouped because we will likely batch all of them into a single major release.\n// -----------------------------------------------------------------------------\n// Disable support for comment nodes as React DOM containers. Already disabled\n// in open source, but www codebase still relies on it. Need to remove.\n\nvar disableCommentsAsDOMContainers = true; // Disable javascript: URL strings in href for XSS protection.\n// and client rendering, mostly to allow JSX attributes to apply to the custom\n// element's object properties instead of only HTML attributes.\n// https://github.com/facebook/react/issues/11347\n\nvar enableCustomElementPropertySupport = false; // Disables children for