Adding initial actions and some documentation.
This commit is contained in:
parent
f8020241cb
commit
01a098d926
289
README.md
289
README.md
|
@ -1,2 +1,291 @@
|
|||
# LasTres
|
||||
|
||||
## Installation instructions. (Prod)
|
||||
|
||||
This is an example, you can configure the server
|
||||
as you please, use this guide as a reference.
|
||||
|
||||
This guide uses Debian stable 12, this server
|
||||
requires at least a system compatible with
|
||||
Redis such as Linux, Windows is not compatible
|
||||
with Redis, but you can use it virtualized or
|
||||
with WSL2.
|
||||
|
||||
We recommend Linux or at least something Unix-like
|
||||
to deploy both in server and in development this
|
||||
project.
|
||||
|
||||
That said features that improve OS compatibility
|
||||
will always be appreciated.
|
||||
|
||||
### Postgresql notes.
|
||||
|
||||
We are going to use the peer authentication that
|
||||
comes preconfigured in Debian, if you are using
|
||||
other OS you should care of `pg_hba.conf` to
|
||||
be configured as peer for empty address.
|
||||
|
||||
This is how it looks on the Debian I used.
|
||||
|
||||
You can also configure it with password if the database
|
||||
server is not the same host that the webserver, it
|
||||
is supported by the software.
|
||||
|
||||
```
|
||||
local all postgres peer
|
||||
|
||||
# TYPE DATABASE USER ADDRESS METHOD
|
||||
|
||||
# "local" is for Unix domain socket connections only
|
||||
local all all peer
|
||||
# IPv4 local connections:
|
||||
host all all 127.0.0.1/32 scram-sha-256
|
||||
# IPv6 local connections:
|
||||
host all all ::1/128 scram-sha-256
|
||||
# Allow replication connections from localhost, by a user with the
|
||||
# replication privilege.
|
||||
local replication all peer
|
||||
host replication all 127.0.0.1/32 scram-sha-256
|
||||
host replication all ::1/128 scram-sha-256
|
||||
```
|
||||
|
||||
### Creating user
|
||||
|
||||
```shell
|
||||
sudo useradd -m las_tres -d /var/lib/las_tres
|
||||
```
|
||||
|
||||
### Installing dependencies.
|
||||
|
||||
```shell
|
||||
sudo apt update && \
|
||||
sudo apt install git postgresql redis nginx pwgen liblocal-lib-perl libpq-dev
|
||||
```
|
||||
|
||||
### Starting an enable dependency services
|
||||
|
||||
```shell
|
||||
sudo systemctl daemon-reload && \
|
||||
sudo systemctl enable redis-server && \
|
||||
sudo systemctl start redis-server && \
|
||||
sudo systemctl enable postgresql && \
|
||||
sudo systemctl start postgresql && \
|
||||
sudo systemctl enable nginx && \
|
||||
sudo systemctl start nginx
|
||||
```
|
||||
|
||||
### Configuring the database
|
||||
|
||||
```shell
|
||||
( cat << 'EOF'
|
||||
create user "las_tres";
|
||||
create database "las_tres";
|
||||
grant all privileges on database "las_tres" to "las_tres";
|
||||
alter database "las_tres" owner to "las_tres";
|
||||
EOF
|
||||
) | sudo -u postgres psql
|
||||
```
|
||||
|
||||
### Cloning project.
|
||||
|
||||
```shell
|
||||
sudo -u las_tres bash -c "$(cat <<'EOF'
|
||||
cd && \
|
||||
git clone https://git.owlcode.tech/sergiotarxz/LasTres
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
### If you are going to change the JS. (Only once.)
|
||||
|
||||
```shell
|
||||
sudo apt update && sudo apt install nodejs npm && \
|
||||
sudo -u las_tres bash -c "$(cat <<'EOF'
|
||||
cd ~/LasTres && \
|
||||
npm install
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
### Deploying your js changes
|
||||
|
||||
```shell
|
||||
sudo -u las_tres bash -c "$(cat <<'EOF'
|
||||
cd ~/LasTres && \
|
||||
npx webpack
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
### If you are going to change the css (Only once.)
|
||||
|
||||
```shell
|
||||
sudo apt update && sudo apt install sassc
|
||||
```
|
||||
|
||||
### Deploying the css changes
|
||||
|
||||
```shell
|
||||
sudo -u las_tres bash -c "$(cat <<'EOF'
|
||||
cd ~/LasTres && \
|
||||
bash build_styles.sh
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
|
||||
### Configuring NGINX.
|
||||
|
||||
#### (TLS is out of the scope of this tutorial, but you must do it if you want to deploy this server to the public world.)
|
||||
|
||||
```shell
|
||||
( cat << 'EOF'
|
||||
map $http_upgrade $connection_upgrade {
|
||||
default upgrade;
|
||||
'' close;
|
||||
}
|
||||
upstream backend_las_tres {
|
||||
server 127.0.0.1:3000 fail_timeout=0;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name las_tres.example.com;
|
||||
location /.well-known/acme-challenge/ { allow all; }
|
||||
|
||||
keepalive_timeout 70;
|
||||
sendfile on;
|
||||
client_max_body_size 80m;
|
||||
|
||||
gzip on;
|
||||
gzip_disable "msie6";
|
||||
gzip_vary on;
|
||||
gzip_proxied any;
|
||||
gzip_comp_level 6;
|
||||
gzip_buffers 16 8k;
|
||||
gzip_http_version 1.1;
|
||||
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml image/x-icon;
|
||||
|
||||
location / {
|
||||
add_header Strict-Transport-Security "max-age=31536000" always;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header Proxy "";
|
||||
proxy_pass_header Server;
|
||||
|
||||
proxy_pass http://backend_las_tres;
|
||||
proxy_buffering on;
|
||||
proxy_redirect off;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
tcp_nodelay on;
|
||||
}
|
||||
|
||||
}
|
||||
EOF
|
||||
) | sudo tee /etc/nginx/sites-enabled/las_tres.conf && \
|
||||
sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
### Configuring the app
|
||||
|
||||
For non standard configurations you should peek the
|
||||
config in `las_tres.example.yml` copy to `las_tres.yml`
|
||||
and modify the copy until it suits your needs, patches
|
||||
and issues are welcome if you need more options than
|
||||
example allows you to do.
|
||||
|
||||
```shell
|
||||
sudo -u las_tres bash -c "$( cat << 'EOF'
|
||||
cd ~/LasTres && \
|
||||
cat << EOF1 > las_tres.yml
|
||||
secrets:
|
||||
- $(pwgen -s 512 1)
|
||||
database:
|
||||
dbname: las_tres
|
||||
hypnotoad:
|
||||
listen:
|
||||
# Here we have changed it to only listen localhost.
|
||||
- http://127.0.0.1:3000
|
||||
EOF1
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
|
||||
### Installing Perl deps
|
||||
|
||||
```shell
|
||||
sudo -u las_tres bash -c "$( cat << EOF
|
||||
source ~/.profile && \
|
||||
if ! grep PERL5LIB ~/.profile; then
|
||||
perl -Mlocal::lib 2>/dev/null >> ~/.profile ;
|
||||
fi && \
|
||||
rm -fr ~/.cpan && \
|
||||
( echo y && echo reload cpan && echo o conf commit ) | perl -MCPAN -Mlocal::lib -e shell && \
|
||||
cd ~/LasTres && \
|
||||
perl Build.PL && \
|
||||
./Build installdeps
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
### Creating Systemd service
|
||||
|
||||
```shell
|
||||
( cat << 'EOF'
|
||||
[Unit]
|
||||
Description=LasTres the web text game
|
||||
After=network.target postgresql.service redis.service
|
||||
|
||||
[Service]
|
||||
User=las_tres
|
||||
Group=las_tres
|
||||
WorkingDirectory=/var/lib/las_tres/LasTres/
|
||||
ExecStart=/bin/bash -c "source /var/lib/las_tres/.profile && hypnotoad -f script/las_tres"
|
||||
User=las_tres
|
||||
Group=las_tres
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
Alias=las_tres.service
|
||||
EOF
|
||||
) | sudo tee /etc/systemd/system/las_tres.service
|
||||
```
|
||||
|
||||
### Deploying the database
|
||||
|
||||
#### If you are installing for the first time.
|
||||
|
||||
```shell
|
||||
sudo -u las_tres bash -c "$( cat << EOF
|
||||
cd ~/LasTres && \
|
||||
source ~/.profile && \
|
||||
perl script/install.pl
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
#### If you are migrating from a older version
|
||||
|
||||
```shell
|
||||
sudo -u las_tres bash -c "$( cat << EOF
|
||||
cd ~/LasTres && \
|
||||
source ~/.profile && \
|
||||
perl script/upgrade.pl
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
### Enabling and starting service
|
||||
|
||||
```shell
|
||||
sudo systemctl daemon-reload && \
|
||||
sudo systemctl restart las_tres && \
|
||||
sudo systemctl enable las_tres
|
||||
```
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
export interface Action {
|
||||
name: string
|
||||
identifier: string
|
||||
icon: string | null
|
||||
is_disabled: boolean
|
||||
disabled_reason: string | null
|
||||
};
|
||||
export type ActionHash = Record<string, Action>
|
|
@ -1,18 +1,72 @@
|
|||
import * as React from 'react'
|
||||
|
||||
import type { Action, ActionHash } from '@lastres/action'
|
||||
|
||||
import PresentationItem from '@lastres/components/presentation-item'
|
||||
import Presentation from '@lastres/components/presentation'
|
||||
|
||||
export interface BottomPanelProps {
|
||||
websocket: WebSocket | null
|
||||
actionHash: ActionHash | null
|
||||
|
||||
}
|
||||
|
||||
export default function BottomPanel (): JSX.Element {
|
||||
export interface Style {
|
||||
background?: string
|
||||
}
|
||||
|
||||
export default function BottomPanel (props: BottomPanelProps): JSX.Element {
|
||||
const actionHash = props.actionHash
|
||||
function printListActions(): JSX.Element {
|
||||
if (actionHash === null) {
|
||||
return <></>
|
||||
}
|
||||
const listOfActionKeys = Object.keys(actionHash).sort((a, b) => {
|
||||
const isDisabledComparisionValue: number = +actionHash[a].is_disabled - +actionHash[b].is_disabled
|
||||
if (isDisabledComparisionValue !== 0) {
|
||||
return isDisabledComparisionValue
|
||||
}
|
||||
if (actionHash[a].name < actionHash[b].name) {
|
||||
return -1
|
||||
}
|
||||
if (actionHash[a].name > actionHash[b].name) {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
})
|
||||
function printDisabledReason (action: Action): JSX.Element {
|
||||
if (!action.is_disabled || action.disabled_reason === null) {
|
||||
return <></>
|
||||
}
|
||||
return (
|
||||
<p className="disabled-reason" style={{ color: 'red' }}>{action.disabled_reason}</p>
|
||||
)
|
||||
}
|
||||
return <>
|
||||
<p>Acciones disponibles.</p>
|
||||
{
|
||||
listOfActionKeys.map((key) => {
|
||||
const style: Style = {}
|
||||
const action = actionHash[key]
|
||||
if (action.is_disabled) {
|
||||
style.background = 'lightgray'
|
||||
}
|
||||
return <div className="action" style={style} key={action.identifier}>
|
||||
<p>{action.name}</p>
|
||||
{
|
||||
printDisabledReason(action)
|
||||
}
|
||||
</div>
|
||||
})
|
||||
}
|
||||
</>
|
||||
}
|
||||
return (
|
||||
<Presentation>
|
||||
<PresentationItem>
|
||||
</PresentationItem>
|
||||
<PresentationItem>
|
||||
{
|
||||
printListActions()
|
||||
}
|
||||
</PresentationItem>
|
||||
<PresentationItem>
|
||||
</PresentationItem>
|
||||
|
|
|
@ -3,6 +3,7 @@ import * as React from 'react'
|
|||
import type { PJ } from '@lastres/pj'
|
||||
import type { Location } from '@lastres/location'
|
||||
import type { LogLine } from '@lastres/log-line'
|
||||
import type { ActionHash } from '@lastres/action'
|
||||
|
||||
import UpperPanel from '@lastres/components/upper-panel'
|
||||
import BottomPanel from '@lastres/components/bottom-panel'
|
||||
|
@ -47,6 +48,7 @@ export default function Game (props: GameProps): JSX.Element {
|
|||
const [scrollLog, setScrollLog] = React.useState<number | null>(null)
|
||||
const [movingTo, setMovingTo] = React.useState<Location | null>(null)
|
||||
const [remainingFrames, setRemainingFrames] = React.useState<number | null>(null)
|
||||
const [actionHash, setActionHash] = React.useState<ActionHash | null>(null)
|
||||
const logPresentationRef = React.useRef<HTMLDivElement>(null)
|
||||
const websocket = props.websocket
|
||||
const setWebsocket = props.setWebsocket
|
||||
|
@ -79,7 +81,8 @@ export default function Game (props: GameProps): JSX.Element {
|
|||
setCurrentLocation, setConnectedLocations,
|
||||
logLines, setLogLines, setError,
|
||||
setScrollLog, logPresentationRef,
|
||||
setMovingTo, setRemainingFrames)
|
||||
setMovingTo, setRemainingFrames,
|
||||
setActionHash)
|
||||
webSocket.onmessage = (event) => {
|
||||
const packet = JSON.parse(event.data)
|
||||
inputPackets.handle(packet)
|
||||
|
@ -108,7 +111,7 @@ export default function Game (props: GameProps): JSX.Element {
|
|||
logPresentationRef={logPresentationRef}
|
||||
movingTo={movingTo}
|
||||
remainingFrames={remainingFrames}/>
|
||||
<BottomPanel/>
|
||||
<BottomPanel actionHash={actionHash} websocket={websocket}/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import type { PJ } from '@lastres/pj'
|
|||
import type { Location } from '@lastres/location'
|
||||
import type InputPacket from '@lastres/input-packet'
|
||||
import type { LogLine } from '@lastres/log-line'
|
||||
import type { ActionHash } from '@lastres/action'
|
||||
import InputPacketInfo from '@lastres/input-packet/info'
|
||||
import InputPacketPong from '@lastres/input-packet/pong'
|
||||
|
||||
|
@ -19,6 +20,7 @@ type SetScrollLog = (set: number | null | SetScrollLogCallback) => void
|
|||
type LogPresentationRef = React.RefObject<HTMLDivElement>
|
||||
type SetMovingTo = (set: Location | null) => void
|
||||
type SetRemainingFrames = (set: number | null) => void
|
||||
type SetActionHash = (set: ActionHash | null) => void
|
||||
|
||||
interface Packet {
|
||||
command: string
|
||||
|
@ -41,6 +43,7 @@ export default class InputPackets {
|
|||
logPresentationRef: LogPresentationRef
|
||||
setMovingTo: SetMovingTo
|
||||
setRemainingFrames: SetRemainingFrames
|
||||
setActionHash: SetActionHash
|
||||
constructor (setTeamPJs: SetTeamPJs,
|
||||
setEnemyTeamPJs: SetEnemyTeamPJs,
|
||||
setIsBattling: SetIsBattling,
|
||||
|
@ -52,7 +55,8 @@ export default class InputPackets {
|
|||
setScrollLog: SetScrollLog,
|
||||
logPresentationRef: LogPresentationRef,
|
||||
setMovingTo: SetMovingTo,
|
||||
setRemainingFrames: SetRemainingFrames) {
|
||||
setRemainingFrames: SetRemainingFrames,
|
||||
setActionHash: SetActionHash) {
|
||||
this.setTeamPJs = setTeamPJs
|
||||
this.setEnemyTeamPJs = setEnemyTeamPJs
|
||||
this.setCurrentLocation = setCurrentLocation
|
||||
|
@ -65,6 +69,7 @@ export default class InputPackets {
|
|||
this.setMovingTo = setMovingTo
|
||||
this.setRemainingFrames = setRemainingFrames
|
||||
this.setIsBattling = setIsBattling
|
||||
this.setActionHash = setActionHash
|
||||
}
|
||||
|
||||
handle (packet: Packet): void {
|
||||
|
@ -164,6 +169,9 @@ export default class InputPackets {
|
|||
applyScroll()
|
||||
}, 10)
|
||||
}
|
||||
if (data.available_actions !== undefined) {
|
||||
this.setActionHash(data.available_actions)
|
||||
}
|
||||
})
|
||||
this.cachedArray = [infoPacket, pongPacket]
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
---
|
||||
secrets:
|
||||
- change_me_for_a_proper_secret_generated_with_pwgen
|
||||
database:
|
||||
|
|
|
@ -74,10 +74,11 @@ sub handle ( $self, $ws, $session, $data ) {
|
|||
LasTres::Controller::Websocket::OutputPacket::Info->new(
|
||||
set_log => [ $pj->last_50_log ],
|
||||
$self->_location_data($pj),
|
||||
team_pjs => $team->combat_members_serializable($pj),
|
||||
team_pjs => $team->combat_members_serializable($pj),
|
||||
is_battling => defined $team->battle,
|
||||
$self->_enemy_team_pjs($session),
|
||||
clear => $JSON::true,
|
||||
$self->_available_actions($pj),
|
||||
);
|
||||
$info_packet_to_send->send($ws);
|
||||
my $redis = LasTres::Redis->new;
|
||||
|
@ -155,11 +156,20 @@ sub _on_redis_event ( $self, $ws, $session, $message, $topic, $topics ) {
|
|||
if ( $data->{command} eq 'update-team-sprites' ) {
|
||||
my $team = $pj->team;
|
||||
LasTres::Controller::Websocket::OutputPacket::Info->new(
|
||||
team_pjs => $team->combat_members_serializable($pj),
|
||||
team_pjs => $team->combat_members_serializable($pj),
|
||||
is_battling => defined $team->battle,
|
||||
$self->_enemy_team_pjs($session)
|
||||
)->send($ws);
|
||||
}
|
||||
if ( $data->{command} eq 'update-actions' ) {
|
||||
LasTres::Controller::Websocket::OutputPacket::Info->new(
|
||||
$self->_available_actions($pj) )->send($ws);
|
||||
}
|
||||
}
|
||||
|
||||
sub _available_actions ($self, $pj) {
|
||||
return ( available_actions =>
|
||||
{ map { $_->identifier => $_->hash($pj) } $pj->actions->@* }, );
|
||||
}
|
||||
|
||||
sub _get_connected_places ( $self, $pj ) {
|
||||
|
|
|
@ -25,21 +25,23 @@ has set_log => ( is => 'rw', );
|
|||
has append_log => ( is => 'rw' );
|
||||
has is_battling => ( is => 'rw' );
|
||||
|
||||
has remaining_frames => ( is => 'rw' );
|
||||
has remaining_frames => ( is => 'rw' );
|
||||
has available_actions => ( is => 'rw' );
|
||||
|
||||
sub identifier {
|
||||
return 'info';
|
||||
}
|
||||
|
||||
sub data ($self) {
|
||||
my $clear = $self->clear;
|
||||
my $team_pjs = $self->team_pjs;
|
||||
my $enemy_team_pjs = $self->enemy_team_pjs;
|
||||
my $location_data = $self->location_data;
|
||||
my $set_log = $self->set_log;
|
||||
my $append_log = $self->append_log;
|
||||
my $remaining_frames = $self->remaining_frames;
|
||||
my $is_battling = $self->is_battling;
|
||||
my $clear = $self->clear;
|
||||
my $team_pjs = $self->team_pjs;
|
||||
my $enemy_team_pjs = $self->enemy_team_pjs;
|
||||
my $location_data = $self->location_data;
|
||||
my $set_log = $self->set_log;
|
||||
my $append_log = $self->append_log;
|
||||
my $remaining_frames = $self->remaining_frames;
|
||||
my $is_battling = $self->is_battling;
|
||||
my $available_actions = $self->available_actions;
|
||||
|
||||
if ( defined $is_battling ) {
|
||||
$is_battling = $is_battling ? $JSON::true : $JSON::false;
|
||||
|
@ -74,7 +76,13 @@ sub data ($self) {
|
|||
( defined $remaining_frames )
|
||||
? ( remaining_frames => $remaining_frames )
|
||||
: ()
|
||||
)
|
||||
),
|
||||
(
|
||||
( defined $available_actions )
|
||||
? ( available_actions => $available_actions )
|
||||
: ()
|
||||
),
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -102,8 +102,8 @@ sub on_pj_arrival ( $self, $pj ) {
|
|||
$pj->set_known_location($self);
|
||||
}
|
||||
$self->show_intro($pj);
|
||||
$redis->publish( $redis->pj_subscription($pj),
|
||||
to_json( { command => 'update-location' } ) );
|
||||
$pj->update_location;
|
||||
$pj->update_actions;
|
||||
}
|
||||
|
||||
## DO NOT EXTEND NOT SUPPORTED.
|
||||
|
@ -168,8 +168,8 @@ sub on_pj_moving ( $self, $pj ) {
|
|||
{ color => 'green', text => $self->name($pj) },
|
||||
]
|
||||
);
|
||||
$redis->publish( $redis->pj_subscription($pj),
|
||||
to_json( { command => 'update-location' } ) );
|
||||
$pj->update_location;
|
||||
$pj->update_actions;
|
||||
}
|
||||
|
||||
## DO NOT EXTEND NOT SUPPORTED.
|
||||
|
|
|
@ -6,19 +6,31 @@ use warnings;
|
|||
|
||||
use feature 'signatures';
|
||||
|
||||
use JSON qw/to_json from_json/;
|
||||
use Moo::Role;
|
||||
|
||||
requires( 'identifier', 'callback', 'name' );
|
||||
|
||||
## IMPLEMENTORS MUST IMPLEMENT
|
||||
#
|
||||
# sub callback($self, $pj);
|
||||
# What to do when this action is invoked by the PJ.
|
||||
#
|
||||
# sub identifier;
|
||||
# The unique identifier of this action in the game.
|
||||
#
|
||||
# sub name($self, $pj);
|
||||
# The name of the action, possibly variable for diferent PJs.
|
||||
|
||||
## OVERRIDE
|
||||
# This should be a square icon, ideally of 400x400px
|
||||
# If not set the frontend should attempt to show the
|
||||
# action with as much dignity as possible.
|
||||
#
|
||||
# Should return nothing or a string that is a absolute url
|
||||
# Should return undef or a string that is a absolute url
|
||||
# to the resource.
|
||||
sub icon ( $self, $pj ) {
|
||||
return;
|
||||
return undef;
|
||||
}
|
||||
|
||||
## OVERRIDE
|
||||
|
@ -38,20 +50,20 @@ sub is_disabled ( $self, $pj ) {
|
|||
# the pj cannot use this action but they still see it as
|
||||
# something possible to be done.
|
||||
#
|
||||
# Should return nothing if the pj can do the action and a string
|
||||
# Should return undef if the pj can do the action and a string
|
||||
# with the reason otherwise.
|
||||
sub disabled_reason ( $self, $pj ) {
|
||||
return;
|
||||
return undef;
|
||||
}
|
||||
|
||||
## DO NOT EXTEND NOT SUPPORTED.
|
||||
sub hash ( $self, $pj ) {
|
||||
return {
|
||||
identifier => $self->identifier,
|
||||
icon => $self->icon,
|
||||
name => $self->name,
|
||||
is_disabled => $self->is_disabled,
|
||||
disabled_reason => $self->disabled_reason,
|
||||
icon => $self->icon($pj),
|
||||
name => $self->name($pj),
|
||||
is_disabled => $self->is_disabled($pj) ? $JSON::true : $JSON::false,
|
||||
disabled_reason => $self->disabled_reason($pj),
|
||||
};
|
||||
}
|
||||
1;
|
||||
|
|
|
@ -4,6 +4,7 @@ use v5.36.0;
|
|||
|
||||
use strict;
|
||||
use warnings;
|
||||
use utf8;
|
||||
|
||||
use Moo;
|
||||
|
||||
|
@ -40,7 +41,7 @@ sub disabled_reason($self, $pj) {
|
|||
if (!$self->_check_if_someone_needs_to_restore($pj)) {
|
||||
return 'Todo tu equipo esta lleno de vitalidad y listo para la aventura.';
|
||||
}
|
||||
return;
|
||||
return undef;
|
||||
}
|
||||
|
||||
sub _check_if_someone_needs_to_restore($self, $pj) {
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
package LasTres::Action::GolpearArbolCentralTribuDeLaLima;
|
||||
package LasTres::PJAction::GolpearArbolCentralTribuDeLaLima;
|
||||
|
||||
use v5.36.0;
|
||||
use strict;
|
||||
use warnings;
|
||||
use utf8;
|
||||
|
||||
use feature 'signatures';
|
||||
|
||||
|
@ -31,7 +32,7 @@ sub disabled_reason ( $self, $pj ) {
|
|||
if ($pj->health < 1) {
|
||||
return "Estás debilitado.";
|
||||
}
|
||||
return;
|
||||
return undef;
|
||||
}
|
||||
|
||||
sub _chance_enemy ( $self, $pj ) {
|
||||
|
|
|
@ -12,6 +12,7 @@ use utf8;
|
|||
use Moo;
|
||||
|
||||
use LasTres::Planet::Bahdder::BosqueDelHeroe::TribuDeLaLima;
|
||||
use LasTres::PJAction::GolpearArbolCentralTribuDeLaLima;
|
||||
|
||||
with 'LasTres::Location';
|
||||
|
||||
|
@ -32,7 +33,9 @@ sub parent {
|
|||
}
|
||||
|
||||
sub actions {
|
||||
return [];
|
||||
return [
|
||||
LasTres::PJAction::GolpearArbolCentralTribuDeLaLima->new
|
||||
];
|
||||
}
|
||||
|
||||
sub npcs {
|
||||
|
|
|
@ -13,8 +13,8 @@ use LasTres::Schema;
|
|||
our $VERSION = $LasTres::Schema::VERSION;
|
||||
|
||||
{
|
||||
my $self;
|
||||
sub new {
|
||||
my $self;
|
||||
my $class = shift;
|
||||
if (!defined $self) {
|
||||
$self = $class->SUPER::new(@_);
|
||||
|
|
|
@ -470,6 +470,13 @@ sub update_location ($self) {
|
|||
to_json( { command => 'update-location' } ) );
|
||||
}
|
||||
|
||||
sub update_actions($self) {
|
||||
require LasTres::Redis;
|
||||
my $redis = LasTres::Redis->new;
|
||||
$redis->publish( $redis->pj_subscription($self),
|
||||
to_json( { command => 'update-actions' } ) );
|
||||
}
|
||||
|
||||
sub actions ($self) {
|
||||
my @actions;
|
||||
$self = $self->get_from_storage;
|
||||
|
@ -477,13 +484,13 @@ sub actions ($self) {
|
|||
my $location = $team->location;
|
||||
if ( defined $team->battle ) {
|
||||
|
||||
# There should go the battle actions.
|
||||
return;
|
||||
# Here should go the battle actions.
|
||||
return [];
|
||||
}
|
||||
if ( $team->is_moving ) {
|
||||
|
||||
# Probably there should go the actions still doable when moving.
|
||||
return;
|
||||
return [];
|
||||
}
|
||||
|
||||
# TODO: Handle explore when implemented.
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
body {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
min-height: 100%;
|
||||
height: 100vh;
|
||||
background: ghostwhite; }
|
||||
body label.bar-container {
|
||||
width: 90%; }
|
||||
|
@ -20,6 +20,18 @@ body {
|
|||
body label.bar-container div.bar div.filled {
|
||||
background: lightgreen;
|
||||
height: 100%; }
|
||||
body div.action {
|
||||
border: solid 1px black;
|
||||
text-decoration: underline;
|
||||
display: flex;
|
||||
min-height: 100px;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
width: 100%; }
|
||||
body div.action div.disabled-reason {
|
||||
text-decoration: none; }
|
||||
body div.pj-list-item {
|
||||
display: flex;
|
||||
align-items: center; }
|
||||
|
@ -64,7 +76,7 @@ body {
|
|||
body div.width-max-content {
|
||||
width: max-content; }
|
||||
body div#game-container {
|
||||
min-height: 100%; }
|
||||
height: 100vh; }
|
||||
body div#game-container nav.menu-bar {
|
||||
width: 100%;
|
||||
background: grey;
|
||||
|
|
|
@ -10,6 +10,10 @@
|
|||
}
|
||||
}
|
||||
body {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
height: 100vh;
|
||||
background: ghostwhite;
|
||||
label.bar-container {
|
||||
width: 90%;
|
||||
div.bar {
|
||||
|
@ -22,6 +26,20 @@ body {
|
|||
}
|
||||
}
|
||||
}
|
||||
div.action {
|
||||
border: solid 1px black;
|
||||
text-decoration: underline;
|
||||
display: flex;
|
||||
min-height: 100px;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
div.disabled-reason {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
div.pj-list-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
@ -75,15 +93,11 @@ body {
|
|||
height: 50vh;
|
||||
}
|
||||
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
min-height: 100%;
|
||||
background: ghostwhite;
|
||||
div.width-max-content {
|
||||
width: max-content;
|
||||
}
|
||||
div#game-container {
|
||||
min-height: 100%;
|
||||
height: 100vh;
|
||||
nav.menu-bar {
|
||||
width: 100%;
|
||||
background: grey;
|
||||
|
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue