Adding enemy sprites in combat.

This commit is contained in:
Sergiotarxz 2023-06-27 07:10:56 +02:00
parent fd0017eb0d
commit bf81629197
15 changed files with 216 additions and 54 deletions

View File

@ -38,6 +38,8 @@ export default function Game (props: GameProps): JSX.Element {
) )
} }
const [teamPJs, setTeamPJs] = React.useState<PJ[] | null>(null) const [teamPJs, setTeamPJs] = React.useState<PJ[] | null>(null)
const [enemyTeamPJs, setEnemyTeamPJs] = React.useState<PJ[] | null>(null)
const [isBattling, setIsBattling] = React.useState<boolean | null>(null)
const [currentLocation, setCurrentLocation] = React.useState<Location | null>(null) const [currentLocation, setCurrentLocation] = React.useState<Location | null>(null)
const [connectedLocations, setConnectedLocations] = React.useState<Location[] | null>(null) const [connectedLocations, setConnectedLocations] = React.useState<Location[] | null>(null)
const [logLines, setLogLines] = React.useState<LogLine[] | null>(null) const [logLines, setLogLines] = React.useState<LogLine[] | null>(null)
@ -69,9 +71,11 @@ export default function Game (props: GameProps): JSX.Element {
}, 250000) }, 250000)
} }
const inputPackets = new InputPackets(setTeamPJs, const inputPackets = new InputPackets(setTeamPJs,
setEnemyTeamPJs, setIsBattling,
setCurrentLocation, setConnectedLocations, setCurrentLocation, setConnectedLocations,
logLines, setLogLines, setError, setScrollLog, logLines, setLogLines, setError,
logPresentationRef, setMovingTo, setRemainingFrames) setScrollLog, logPresentationRef,
setMovingTo, setRemainingFrames)
webSocket.onmessage = (event) => { webSocket.onmessage = (event) => {
const packet = JSON.parse(event.data) const packet = JSON.parse(event.data)
inputPackets.handle(packet) inputPackets.handle(packet)
@ -91,6 +95,8 @@ export default function Game (props: GameProps): JSX.Element {
return ( return (
<> <>
<UpperPanel teamPJs={teamPJs} <UpperPanel teamPJs={teamPJs}
enemyTeamPJs={enemyTeamPJs}
isBattling={isBattling}
currentLocation={currentLocation} currentLocation={currentLocation}
connectedLocations={connectedLocations} connectedLocations={connectedLocations}
logLines={logLines} logLines={logLines}

View File

@ -12,6 +12,15 @@ export default function PJListItem (props: PJListItemProps): JSX.Element {
} }
return <><img src={pj.image}/><div className="shadow"/></> return <><img src={pj.image}/><div className="shadow"/></>
} }
function printExperience (): JSX.Element {
if (pj.experience_to_next_level_current === undefined || pj.experience_to_next_level_complete === undefined) {
return <></>
}
return (
<p>Experiencia: <span style={{ color: 'red' }}>{pj.experience_to_next_level_current}</span>
/<span style={{ color: 'green' }}>{pj.experience_to_next_level_complete}</span></p>
)
}
return ( return (
<div className="pj-list-item"> <div className="pj-list-item">
<div className="avatar"> <div className="avatar">
@ -21,8 +30,9 @@ export default function PJListItem (props: PJListItemProps): JSX.Element {
</div> </div>
<div className="data"> <div className="data">
<p>{pj.nick} Nivel <span style={{ color: 'red' }}>{pj.level}.</span></p> <p>{pj.nick} Nivel <span style={{ color: 'red' }}>{pj.level}.</span></p>
<p>Experiencia: <span style={{ color: 'red' }}>{pj.experience_to_next_level_current}</span> {
/<span style={{ color: 'green' }}>{pj.experience_to_next_level_complete}</span></p> printExperience()
}
<label className="bar-container"> <label className="bar-container">
Salud Salud
<PJHealthLikeBar value={pj.health} max={pj.max_health}/> <PJHealthLikeBar value={pj.health} max={pj.max_health}/>

View File

@ -11,6 +11,8 @@ import MoveToPacket from '@lastres/output-packet/move-to'
interface UpperPanelProps { interface UpperPanelProps {
connectedLocations: Location[] | null connectedLocations: Location[] | null
teamPJs: PJ[] | null teamPJs: PJ[] | null
enemyTeamPJs: PJ[] | null
isBattling: boolean | null
currentLocation: Location | null currentLocation: Location | null
logLines: LogLine[] | null logLines: LogLine[] | null
websocket: WebSocket | null websocket: WebSocket | null
@ -26,7 +28,9 @@ export default function UpperPanel (props: UpperPanelProps): JSX.Element {
const logLines = props.logLines const logLines = props.logLines
const websocket = props.websocket const websocket = props.websocket
const logPresentationRef = props.logPresentationRef const logPresentationRef = props.logPresentationRef
if (!(teamPJs !== null && currentLocation !== null && connectedLocations !== null)) { const isBattling = props.isBattling
const enemyTeamPJs = props.enemyTeamPJs
if (!(teamPJs !== null && currentLocation !== null && connectedLocations !== null && isBattling != null)) {
return ( return (
<> <>
<p>Esperando datos...</p> <p>Esperando datos...</p>
@ -94,6 +98,33 @@ export default function UpperPanel (props: UpperPanelProps): JSX.Element {
) )
} }
function showThirdPanel (): JSX.Element {
if (isBattling === null || !isBattling || enemyTeamPJs === null) {
return (
<>
{
showLocationMenuHeaderText()
}
{
showRemainingFrames()
}
{
availableLocationsToMove()
}
</>
)
}
return (
<>
{
enemyTeamPJs.map((item, i) => {
return <PJListItem key={i} pj={item}/>
})
}
</>
)
}
return ( return (
<Presentation> <Presentation>
<PresentationItem> <PresentationItem>
@ -108,13 +139,7 @@ export default function UpperPanel (props: UpperPanelProps): JSX.Element {
</PresentationItem> </PresentationItem>
<PresentationItem> <PresentationItem>
{ {
showLocationMenuHeaderText() showThirdPanel()
}
{
showRemainingFrames()
}
{
availableLocationsToMove()
} }
</PresentationItem> </PresentationItem>
</Presentation> </Presentation>

View File

@ -6,6 +6,8 @@ import InputPacketInfo from '@lastres/input-packet/info'
import InputPacketPong from '@lastres/input-packet/pong' import InputPacketPong from '@lastres/input-packet/pong'
type SetTeamPJs = (set: PJ[] | null) => void type SetTeamPJs = (set: PJ[] | null) => void
type SetIsBattling = (set: boolean | null) => void
type SetEnemyTeamPJs = (set: PJ[] | null) => void
type SetCurrentLocation = (set: Location | null) => void type SetCurrentLocation = (set: Location | null) => void
type SetConnectedLocations = (set: Location[] | null) => void type SetConnectedLocations = (set: Location[] | null) => void
type DispatchHash = Record<string, any> type DispatchHash = Record<string, any>
@ -26,6 +28,8 @@ type LogHash = Record<string, LogLine>
export default class InputPackets { export default class InputPackets {
setTeamPJs: SetTeamPJs setTeamPJs: SetTeamPJs
setEnemyTeamPJs: SetEnemyTeamPJs
setIsBattling: SetIsBattling
setCurrentLocation: SetCurrentLocation setCurrentLocation: SetCurrentLocation
setConnectedLocations: SetConnectedLocations setConnectedLocations: SetConnectedLocations
setLogLines: SetLogLines setLogLines: SetLogLines
@ -38,6 +42,8 @@ export default class InputPackets {
setMovingTo: SetMovingTo setMovingTo: SetMovingTo
setRemainingFrames: SetRemainingFrames setRemainingFrames: SetRemainingFrames
constructor (setTeamPJs: SetTeamPJs, constructor (setTeamPJs: SetTeamPJs,
setEnemyTeamPJs: SetEnemyTeamPJs,
setIsBattling: SetIsBattling,
setCurrentLocation: SetCurrentLocation, setCurrentLocation: SetCurrentLocation,
setConnectedLocations: SetConnectedLocations, setConnectedLocations: SetConnectedLocations,
logLines: LogLine[] | null, logLines: LogLine[] | null,
@ -48,6 +54,7 @@ export default class InputPackets {
setMovingTo: SetMovingTo, setMovingTo: SetMovingTo,
setRemainingFrames: SetRemainingFrames) { setRemainingFrames: SetRemainingFrames) {
this.setTeamPJs = setTeamPJs this.setTeamPJs = setTeamPJs
this.setEnemyTeamPJs = setEnemyTeamPJs
this.setCurrentLocation = setCurrentLocation this.setCurrentLocation = setCurrentLocation
this.setConnectedLocations = setConnectedLocations this.setConnectedLocations = setConnectedLocations
this.logLines = logLines this.logLines = logLines
@ -57,6 +64,7 @@ export default class InputPackets {
this.logPresentationRef = logPresentationRef this.logPresentationRef = logPresentationRef
this.setMovingTo = setMovingTo this.setMovingTo = setMovingTo
this.setRemainingFrames = setRemainingFrames this.setRemainingFrames = setRemainingFrames
this.setIsBattling = setIsBattling
} }
handle (packet: Packet): void { handle (packet: Packet): void {
@ -106,6 +114,12 @@ export default class InputPackets {
if (data.team_pjs !== undefined) { if (data.team_pjs !== undefined) {
this.setTeamPJs(data.team_pjs) this.setTeamPJs(data.team_pjs)
} }
if (data.enemy_team_pjs !== undefined) {
this.setEnemyTeamPJs(data.enemy_team_pjs)
}
if (data.is_battling !== undefined) {
this.setIsBattling(data.is_battling)
}
if (data.location_data !== undefined) { if (data.location_data !== undefined) {
console.log(data.location_data) console.log(data.location_data)
if (data.location_data.connected_places !== undefined) { if (data.location_data.connected_places !== undefined) {

View File

@ -1,6 +1,6 @@
export interface PJ { export interface PJ {
full_name: string full_name?: string
short_name: string short_name?: string
nick: string nick: string
health: number health: number
mana: number mana: number
@ -9,9 +9,9 @@ export interface PJ {
race: string race: string
uuid: string uuid: string
image?: string image?: string
experience_to_next_level_complete?: number
experience_to_next_level_current?: number
level: number level: number
experience_to_next_level_complete: number
experience_to_next_level_current: number
} }
export async function fetchMyPjs (setError: (set: string | null) => void): Promise<PJ[]> { export async function fetchMyPjs (setError: (set: string | null) => void): Promise<PJ[]> {

View File

@ -40,17 +40,17 @@ sub has_movement_auto_combat ($self) {
} }
## OVERRIDE ## OVERRIDE
sub min_enemies_per_head ($self) { sub min_enemies_per_head ($self, $pj = undef) {
return 1; return 1;
} }
## OVERRIDE ## OVERRIDE
sub max_enemies_per_head ($self) { sub max_enemies_per_head ($self, $pj = undef) {
return 1; return 1;
} }
## OVERRIDE ## OVERRIDE
sub list_of_enemies ( $self, $pj ) { sub list_of_enemies ( $self, $pj = undef ) {
return []; return [];
} }
@ -115,10 +115,10 @@ sub _try_to_start_battle ( $self, $team ) {
} }
sub _generate_enemies_team ($self, $enemy_team, $enemy_members_array, $team) { sub _generate_enemies_team ($self, $enemy_team, $enemy_members_array, $team) {
for my $pj ( $team->combat_members ) { for my $pj ( @{$team->combat_members} ) {
my $number_of_enemies = my $number_of_enemies =
LasTres::Util::rand_range_int( $self->min_enemies_per_head, LasTres::Util::rand_range_int( $self->min_enemies_per_head($pj),
$self->max_enemies_per_head ); $self->max_enemies_per_head($pj) );
$self->_generate_n_enemies_pj( $enemy_team, $number_of_enemies, $enemy_members_array, $pj ); $self->_generate_n_enemies_pj( $enemy_team, $number_of_enemies, $enemy_members_array, $pj );
} }
} }
@ -126,7 +126,7 @@ sub _generate_enemies_team ($self, $enemy_team, $enemy_members_array, $team) {
sub _generate_n_enemies_pj ( $self, $enemy_team, $number_of_enemies, sub _generate_n_enemies_pj ( $self, $enemy_team, $number_of_enemies,
$enemy_members_array, $pj ) $enemy_members_array, $pj )
{ {
my @list_possible_enemies = @{ $self->list_of_enemies }; my @list_possible_enemies = @{ $self->list_of_enemies($pj) };
if ( !scalar @list_possible_enemies ) { if ( !scalar @list_possible_enemies ) {
warn "$self does not have defined enemies."; warn "$self does not have defined enemies.";
return; return;

View File

@ -60,6 +60,7 @@ sub try_turn ($self, $entity) {
my $combat_target = $entity->combat_target; my $combat_target = $entity->combat_target;
my $enemy_team = $self->get_enemy_team($entity->team); my $enemy_team = $self->get_enemy_team($entity->team);
my @combat_members_enemy = @{$enemy_team->combat_members}; my @combat_members_enemy = @{$enemy_team->combat_members};
@combat_members_enemy = grep { $_->health > 0 } @combat_members_enemy;
if ($entity->health <= 0) { if ($entity->health <= 0) {
# This entity is out of the battle. # This entity is out of the battle.
return; return;
@ -101,6 +102,8 @@ sub loser ($self) {
sub end ($self) { sub end ($self) {
require LasTres::Redis; require LasTres::Redis;
my $redis = LasTres::Redis->new; my $redis = LasTres::Redis->new;
$self->team1->on_end_combat;
$self->team2->on_end_combat;
$self->team1->current_battle(undef); $self->team1->current_battle(undef);
$self->team1->update; $self->team1->update;
if ( defined $self->team2 ) { if ( defined $self->team2 ) {

View File

@ -9,9 +9,30 @@ use feature 'signatures';
use Moo::Role; use Moo::Role;
requires qw/uuid combat_members current_battle to_serializable from_serializable update is_defeated on_win_combat on_lose_combat update on_end_combat/; requires
qw/uuid combat_members current_battle to_serializable from_serializable update is_defeated on_win_combat on_lose_combat on_end_combat combat_members_serializable/;
sub get_from_storage($self) { sub get_from_storage ($self) {
return $self; return $self;
} }
sub battle ($self) {
my $current_battle = $self->current_battle;
if ( !defined $current_battle ) {
return;
}
my $battle = LasTres::Battle->get_redis($current_battle);
if ( !defined $battle ) {
return;
}
return $battle;
}
sub enemy_team ($self) {
my $battle = $self->battle;
return if !defined $battle;
my $enemy_team = $battle->get_enemy_team($self);
return $enemy_team;
}
1; 1;

View File

@ -44,8 +44,8 @@ sub handle ( $self, $ws, $session, $data ) {
to_json( { error => 'You are not the owner of this pj.' } ) ); to_json( { error => 'You are not the owner of this pj.' } ) );
} }
$session->{pj} = $pj; $session->{pj} = $pj;
my $team = $pj->team; my $team = $pj->team;
my $location = $team->location; my $location = $team->location;
$pj->append_log_line( $pj->append_log_line(
[ [
@ -72,10 +72,12 @@ sub handle ( $self, $ws, $session, $data ) {
my $info_packet_to_send = my $info_packet_to_send =
LasTres::Controller::Websocket::OutputPacket::Info->new( LasTres::Controller::Websocket::OutputPacket::Info->new(
set_log => [ $pj->last_50_log ], set_log => [ $pj->last_50_log ],
$self->_location_data($pj), $self->_location_data($pj),
team_pjs => $team->combat_members_serializable($pj), team_pjs => $team->combat_members_serializable($pj),
clear => $JSON::true, is_battling => defined $team->battle,
$self->_enemy_team_pjs($session),
clear => $JSON::true,
); );
$info_packet_to_send->send($ws); $info_packet_to_send->send($ws);
my $redis = LasTres::Redis->new; my $redis = LasTres::Redis->new;
@ -89,21 +91,37 @@ sub handle ( $self, $ws, $session, $data ) {
$session->{redis} = $redis; $session->{redis} = $redis;
} }
sub _location_data($self, $pj) { sub _enemy_team_pjs ( $self, $session ) {
$session->{pj} = $session->{pj}->get_from_storage;
my $pj = $session->{pj};
my $team = $pj->team;
my $enemy_team = $team->enemy_team;
return (
( defined $enemy_team )
? ( enemy_team_pjs => $enemy_team->combat_members_serializable )
: ()
);
}
sub _location_data ( $self, $pj ) {
my $connected_places = $self->_get_connected_places($pj); my $connected_places = $self->_get_connected_places($pj);
my $team = $pj->team->get_from_storage; my $team = $pj->team->get_from_storage;
my $location = $team->location; my $location = $team->location;
return ( return (
location_data => { location_data => {
current => $location->hash, current => $location->hash,
( (
( $team->is_moving ) ( $team->is_moving )
? ( moving_to => LasTres::Location::get(@{from_json( $team->moving_to )})->hash ) ? (
moving_to => LasTres::Location::get(
@{ from_json( $team->moving_to ) }
)->hash
)
: () : ()
), ),
connected_places => $connected_places, connected_places => $connected_places,
}, },
); );
} }
sub _on_redis_event ( $self, $ws, $session, $message, $topic, $topics ) { sub _on_redis_event ( $self, $ws, $session, $message, $topic, $topics ) {
@ -120,26 +138,26 @@ sub _on_redis_event ( $self, $ws, $session, $message, $topic, $topics ) {
if ( $data->{command} eq 'update-location' ) { if ( $data->{command} eq 'update-location' ) {
my $info_packet_to_send = my $info_packet_to_send =
LasTres::Controller::Websocket::OutputPacket::Info->new( LasTres::Controller::Websocket::OutputPacket::Info->new(
$self->_location_data($pj) $self->_location_data($pj) );
);
$info_packet_to_send->send($ws); $info_packet_to_send->send($ws);
return; return;
} }
if ($data->{command} eq 'show-frame') { if ( $data->{command} eq 'show-frame' ) {
my $current_frame = $pj->team->action_frame; my $current_frame = $pj->team->action_frame;
if ($pj->team->is_moving) { if ( $pj->team->is_moving ) {
my $last_frame = $pj->team->location->parent->frames_to_move; my $last_frame = $pj->team->location->parent->frames_to_move;
LasTres::Controller::Websocket::OutputPacket::Info->new( LasTres::Controller::Websocket::OutputPacket::Info->new(
remaining_frames => $last_frame - $current_frame remaining_frames => $last_frame - $current_frame )->send($ws);
)->send($ws);
} }
return; return;
} }
if ($data->{command} eq 'update-team-sprites') { if ( $data->{command} eq 'update-team-sprites' ) {
my $team = $pj->team; my $team = $pj->team;
LasTres::Controller::Websocket::OutputPacket::Info->new( 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); )->send($ws);
} }
} }

View File

@ -10,17 +10,20 @@ use feature 'signatures';
use Data::Dumper; use Data::Dumper;
use Moo; use Moo;
use JSON;
with 'LasTres::Controller::Websocket::OutputPacket'; with 'LasTres::Controller::Websocket::OutputPacket';
has clear => ( is => 'rw', ); has clear => ( is => 'rw', );
has team_pjs => ( is => 'rw', ); has team_pjs => ( is => 'rw', );
has enemy_team_pjs => ( is => 'rw' );
has location_data => ( is => 'rw', ); has location_data => ( is => 'rw', );
has set_log => ( is => 'rw', ); has set_log => ( is => 'rw', );
has append_log => ( is => 'rw' ); has append_log => ( is => 'rw' );
has is_battling => ( is => 'rw' );
has remaining_frames => ( is => 'rw' ); has remaining_frames => ( is => 'rw' );
@ -31,10 +34,16 @@ sub identifier {
sub data ($self) { sub data ($self) {
my $clear = $self->clear; my $clear = $self->clear;
my $team_pjs = $self->team_pjs; my $team_pjs = $self->team_pjs;
my $enemy_team_pjs = $self->enemy_team_pjs;
my $location_data = $self->location_data; my $location_data = $self->location_data;
my $set_log = $self->set_log; my $set_log = $self->set_log;
my $append_log = $self->append_log; my $append_log = $self->append_log;
my $remaining_frames = $self->remaining_frames; my $remaining_frames = $self->remaining_frames;
my $is_battling = $self->is_battling;
if ( defined $is_battling ) {
$is_battling = $is_battling ? $JSON::true : $JSON::false;
}
return { return {
( (
( defined $clear ) ? ( clear => $clear ) ( defined $clear ) ? ( clear => $clear )
@ -44,6 +53,10 @@ sub data ($self) {
( defined $team_pjs ) ? ( team_pjs => $team_pjs ) ( defined $team_pjs ) ? ( team_pjs => $team_pjs )
: () : ()
), ),
(
( defined $enemy_team_pjs ) ? ( enemy_team_pjs => $enemy_team_pjs )
: ()
),
( (
( defined $location_data ) ? ( location_data => $location_data ) ( defined $location_data ) ? ( location_data => $location_data )
: () : ()
@ -56,6 +69,7 @@ sub data ($self) {
( defined $append_log ) ? ( append_log => $append_log ) ( defined $append_log ) ? ( append_log => $append_log )
: () : ()
), ),
( ( defined $is_battling ) ? ( is_battling => $is_battling ) : () ),
( (
( defined $remaining_frames ) ( defined $remaining_frames )
? ( remaining_frames => $remaining_frames ) ? ( remaining_frames => $remaining_frames )

View File

@ -136,6 +136,29 @@ sub combat_target ( $self, @extra ) {
return $self->_combat_target; return $self->_combat_target;
} }
sub to_hash_display ($self) {
my $race = $self->race;
my $image;
if ( $race->can('image') ) {
$image = $race->image;
}
return {
uuid => $self->uuid,
nick => $self->nick,
race => $self->race_string,
health => $self->health,
max_health => $self->max_health,
mana => $self->mana,
max_mana => $self->max_mana,
level => $self->level,
(
( defined $image )
? ( image => $image )
: ()
),
};
}
sub gain_experience { sub gain_experience {
} }

View File

@ -48,6 +48,11 @@ sub combat_members ($self) {
return $self->members; return $self->members;
} }
sub combat_members_serializable($self) {
my @members_hash = map { $_->to_hash_display } $self->members->@*;
return \@members_hash;
}
sub to_serializable ($self) { sub to_serializable ($self) {
return { return {
members => [ map { $_->to_hash } @{ $self->members } ], members => [ map { $_->to_hash } @{ $self->members } ],

View File

@ -37,14 +37,31 @@ sub has_movement_auto_combat ($self) {
return 1; return 1;
} }
sub list_of_enemies ($self) { sub max_enemies_per_head ( $self, $pj = undef ) {
return [ if ( defined $pj && $pj->level >= 7 ) {
LasTres::EnemyArea->new( return 2;
}
return 1;
}
sub list_of_enemies ( $self, $pj = undef ) {
my @enemies;
push @enemies,
LasTres::EnemyArea->new(
race => 'conejo',
nick => 'Conejo rabioso',
level => 2,
);
if ( defined $pj && $pj->level >= 6 ) {
push @enemies,
LasTres::EnemyArea->new(
race => 'conejo', race => 'conejo',
nick => 'Conejo rabioso', nick => 'Conejo rabioso',
level => 3, level => 3,
), );
]; }
return \@enemies;
} }
sub identifier { sub identifier {

View File

@ -189,6 +189,12 @@ sub combat_members ($self) {
} }
sub on_end_combat($self) { sub on_end_combat($self) {
require LasTres::Redis;
my $redis = LasTres::Redis->new;
for my $pj ( $self->members ) {
$redis->publish( $redis->pj_subscription($pj),
to_json( { command => 'update-team-sprites' } ) );
}
} }
sub to_serializable ($self) { sub to_serializable ($self) {

File diff suppressed because one or more lines are too long