Skip to content

Commit

Permalink
Initial module and character for Root RPG (#1757)
Browse files Browse the repository at this point in the history
Initial module and character for Root RPG (Starts on #1754)

* Add initial character sheet and number roll for Root
* Adds for issue #1754:
  * Move models
  * Nature models
  * Playbook models (partial)
  * Start of a Root character sheet (at least from the quickstart)
  * Initial API
* Also, validated that Sushi is pretty awesome for this usecase, so closes issue #1801.
  • Loading branch information
omnicolor authored Dec 16, 2024
1 parent 6151cf7 commit e42450e
Show file tree
Hide file tree
Showing 42 changed files with 3,292 additions and 83 deletions.
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ CYBERPUNKRED_DATA_PATH=../Modules/CyberpunkRed/data/
DND5E_DATA_PATH=../Modules/Dnd5e/data/
EXPANSE_DATA_PATH=../Modules/Expanse/data/
LEGENDOFTHEFIVERINGS4E_DATA_PATH=../Modules/Legendofthefiverings4e/data/
ROOT_DATA_PATH=../Modules/Root/data/
SHADOWRUN5E_DATA_PATH=../Modules/Shadowrun5e/data/
SHADOWRUN6E_DATA_PATH=../Modules/Shadowrun6e/data/
SHADOWRUNANARCHY_DATA_PATH=../Modules/Shadowrunanarchy/data/
Expand Down
42 changes: 42 additions & 0 deletions Modules/Root/app/Casts/AttributeCast.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

declare(strict_types=1);

namespace Modules\Root\Casts;

use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Illuminate\Database\Eloquent\Model;
use Modules\Root\ValueObjects\Attribute;

/**
* @implements CastsAttributes<Attribute, mixed>
*/
class AttributeCast implements CastsAttributes
{
/**
* @param array<string, mixed> $attributes
*/
public function get(
Model $model,
string $key,
mixed $value,
array $attributes,
): Attribute {
return new Attribute((int)$value);
}

/**
* @param array<string, mixed> $attributes
*/
public function set(
Model $model,
string $key,
mixed $value,
array $attributes
): int {
if ($value instanceof Attribute) {
return $value->value;
}
return $value;
}
}
63 changes: 63 additions & 0 deletions Modules/Root/app/Http/Controllers/CharactersController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php

declare(strict_types=1);

namespace Modules\Root\Http\Controllers;

use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Http\Response;
use Illuminate\View\View;
use Modules\Root\Http\Resources\CharacterResource;
use Modules\Root\Models\Character;
use Modules\Root\Models\Move;

use function abort_if;
use function view;

class CharactersController extends Controller
{
public function index(Request $request): JsonResource
{
/** @var User */
$user = $request->user();
return CharacterResource::collection(
Character::where('owner', $user->email)->get()
);
}

public function show(Request $request, Character $character): JsonResource
{
/** @var User */
$user = $request->user();
$campaign = $character->campaign();
abort_if(
$user->email !== $character->owner
&& (null === $campaign || $user->isNot($campaign->gamemaster)),
Response::HTTP_NOT_FOUND
);
return new CharacterResource($character);
}

public function view(Request $request, Character $character): View
{
$starting_weapon_skills = $character->playbook
->starting_weapon_moves;
// @phpstan-ignore larastan.noUnnecessaryCollectionCall
$weapon_skills = Move::weapon()
->get()
->diff($starting_weapon_skills);
return view(
'root::character',
[
'character' => $character,
'natures' => $character->playbook->natures,
'weapon_skills_starting' => $starting_weapon_skills,
'weapon_skills' => $weapon_skills,
'user' => $request->user(),
]
);
}
}
106 changes: 106 additions & 0 deletions Modules/Root/app/Http/Resources/CharacterResource.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<?php

declare(strict_types=1);

namespace Modules\Root\Http\Resources;

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Http\Resources\MissingValue;
use Modules\Root\Models\Character;
use Modules\Root\Models\Move;

/**
* @mixin Character
*/
class CharacterResource extends JsonResource
{
/**
* @return array{
* name: string,
* meta: array{
* look: string,
* species: string
* },
* stats: array{
* charm: int,
* cunning: int,
* finesse: int,
* luck: int,
* might: int
* },
* moves: AnonymousResourceCollection<Move>,
* nature: NatureResource,
* playbook: PlaybookResource,
* tracks: array{
* decay: int<0, 5>,
* decay_max: int<0, 5>,
* exhaustion: int<0, 5>,
* exhaustion_max: int<0, 5>,
* injury: int<0, 5>,
* injury_max: int<0, 5>,
* },
* id: string,
* campaign_id: MissingValue|integer,
* system: string,
* owner: array{
* id: integer,
* name: string
* },
* links: array{
* self: string,
* campaign: MissingValue|string
* }
* }
*/
public function toArray(Request $request): array
{
return [
'name' => $this->name,
'meta' => [
'look' => $this->look,
'species' => $this->species,
],
'stats' => [
'charm' => $this->charm->value,
'cunning' => $this->cunning->value,
'finesse' => $this->finesse->value,
'luck' => $this->luck->value,
'might' => $this->might->value,
],
'moves' => MoveResource::collection($this->moves),
'nature' => new NatureResource($this->nature),
'playbook' => new PlaybookResource($this->playbook),
'tracks' => [
'decay' => $this->decay,
'decay_max' => $this->decay_max,
'exhaustion' => $this->exhaustion,
'exhaustion_max' => $this->exhaustion_max,
'injury' => $this->injury,
'injury_max' => $this->injury_max,
],
'id' => $this->id,
'campaign_id' => $this->when(
null !== $this->campaign_id,
$this->campaign_id,
),
'system' => $this->system,
'owner' => [
// @phpstan-ignore staticMethod.dynamicCall
'id' => $this->user()->id,
// @phpstan-ignore staticMethod.dynamicCall
'name' => $this->user()->name,
],
'links' => [
'self' => route('alien.characters.show', $this->id),
'campaign' => $this->when(
null !== $this->campaign_id,
null !== $this->campaign_id
? route('campaigns.show', $this->campaign_id)
: null,
),
],
];
}
}
48 changes: 48 additions & 0 deletions Modules/Root/app/Http/Resources/MoveResource.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

declare(strict_types=1);

namespace Modules\Root\Http\Resources;

use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Http\Resources\MissingValue;
use Modules\Root\Models\Move;

/**
* @mixin Move
*/
class MoveResource extends JsonResource
{
/**
* @return array{
* description: MissingValue|string,
* effects: null|object,
* id: string,
* name: string,
* weapon_move: bool,
* links: array{
* self: string
* }
* }
*/
public function toArray(Request $request): array
{
/** @var User */
$user = $request->user();
return [
'description' => $this->when(
$user->hasPermissionTo('view data'),
$this->description,
),
'effects' => $this->effects,
'id' => $this->id,
'name' => $this->name,
'weapon_move' => $this->weapon_move,
'links' => [
'self' => route('root.moves.show', $this->id),
],
];
}
}
44 changes: 44 additions & 0 deletions Modules/Root/app/Http/Resources/NatureResource.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

declare(strict_types=1);

namespace Modules\Root\Http\Resources;

use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Http\Resources\MissingValue;
use Modules\Root\Models\Nature;

/**
* @mixin Nature
*/
class NatureResource extends JsonResource
{
/**
* @return array{
* description: MissingValue|string,
* id: string,
* name: string,
* links: array{
* self: string
* }
* }
*/
public function toArray(Request $request): array
{
/** @var User */
$user = $request->user();
return [
'description' => $this->when(
$user->hasPermissionTo('view data'),
$this->description,
),
'id' => $this->id,
'name' => $this->name,
'links' => [
'self' => route('root.natures.show', $this->id),
],
];
}
}
72 changes: 72 additions & 0 deletions Modules/Root/app/Http/Resources/PlaybookResource.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php

declare(strict_types=1);

namespace Modules\Root\Http\Resources;

use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Http\Resources\MissingValue;
use Modules\Root\Models\Move;
use Modules\Root\Models\Nature;
use Modules\Root\Models\Playbook;

/**
* @mixin Playbook
*/
class PlaybookResource extends JsonResource
{
/**
* @return array{
* id: string,
* name: string,
* description_long: MissingValue|string,
* description_short: MissingValue|string,
* moves: AnonymousResourceCollection<Move>,
* natures: AnonymousResourceCollection<Nature>,
* stats: array{
* charm: int,
* cunning: int,
* finesse: int,
* luck: int,
* might: int,
* },
* starting_weapon_moves: AnonymousResourceCollection<Move>,
* links: array{
* self: string
* }
* }
*/
public function toArray(Request $request): array
{
/** @var User */
$user = $request->user();
return [
'id' => $this->id,
'name' => $this->name,
'description_long' => $this->when(
$user->hasPermissionTo('view data'),
$this->description_long,
),
'description_short' => $this->when(
$user->hasPermissionTo('view data'),
$this->description_short,
),
'moves' => MoveResource::collection($this->moves),
'natures' => NatureResource::collection($this->natures),
'stats' => [
'charm' => $this->charm->value,
'cunning' => $this->cunning->value,
'finesse' => $this->finesse->value,
'luck' => $this->finesse->value,
'might' => $this->might->value,
],
'starting_weapon_moves' => MoveResource::collection($this->starting_weapon_moves),
'links' => [
'self' => route('root.playbooks.show', $this->id),
],
];
}
}
Loading

0 comments on commit e42450e

Please sign in to comment.