Addding initial support for listing enemies.

This commit is contained in:
Sergiotarxz 2024-01-22 01:08:28 +01:00
parent 08f539bf42
commit 58c30c1059
12 changed files with 484 additions and 1 deletions

View File

@ -100,6 +100,7 @@ sub startup ($self) {
$r->get('/conquer/teams')->to('ConquerTeam#getAll');
$r->put('/conquer/node')->to('ConquerNode#create');
$r->get('/conquer/node/near')->to('ConquerNode#nearbyNodes');
$r->get('/conquer/user/enemies/global')->to('ConquerUserCurrentEnemy#listEnemiesGlobal');
$r->get('/conquer/node/<uuid>')->to('ConquerNode#get');
$r->post('/conquer/node/<uuid>/try-conquer')->to('ConquerNode#tryConquer');
$r->get('/conquer/user')->to('UserConquer#get_self');

View File

@ -0,0 +1,103 @@
package BurguillosInfo::Controller::ConquerUserCurrentEnemy;
use v5.34.1;
use strict;
use warnings;
use utf8;
use Mojo::Base 'Mojolicious::Controller', '-signatures';
use UUID::URandom qw/create_uuid_string/;
use JSON;
use BurguillosInfo::Schema;
use BurguillosInfo::Species;
sub listEnemiesGlobal ($self) {
my $user = $self->current_user;
if ( !defined $user ) {
return $self->render(
status => 401,
json => {
error => 'Debes estar autenticado.',
}
);
}
my $current_enemies = $self->_get_enemies($user);
if ( scalar @$current_enemies ) {
return $self->_return_enemies($current_enemies);
}
$self->_generate_enemies_global($user);
$current_enemies = $self->_get_enemies($user);
return $self->_return_enemies($current_enemies);
}
sub _return_enemies ( $self, $current_enemies ) {
return $self->render( json => [ map { $_->serialize } @$current_enemies ] );
}
sub _get_enemies ( $self, $user ) {
my $resultset_current_enemies =
BurguillosInfo::Schema->Schema->resultset('ConquerUserCurrentEnemy');
my @current_enemies = $resultset_current_enemies->search(
{
'user_object.uuid' => $user->uuid,
},
{
join => 'user_object',
}
);
return \@current_enemies;
}
sub _generate_enemies_global ( $self, $user ) {
my $minimum_number_enemies = 2;
my $maximum_number_enemies = 6;
my $number_enemies =
$self->_calculate_number_of_enemies( $minimum_number_enemies,
$maximum_number_enemies );
for ( my $i = 0 ; $i < $number_enemies ; $i++ ) {
$self->_generate_enemy_global($user);
}
}
sub _generate_enemy_global ( $self, $user ) {
my $resultset_current_enemies =
BurguillosInfo::Schema->Schema->resultset('ConquerUserCurrentEnemy');
my $uuid = create_uuid_string();
my $species = BurguillosInfo::Species->new;
my @species = @{ $species->list_can_be_global };
my $selected_species = $species[int( rand( scalar @species ) )];
my $enemy = $resultset_current_enemies->new(
{
uuid => $uuid,
species => $selected_species->id,
level => $self->_get_level_enemy($user),
user => $user->uuid,
}
);
$enemy->insert;
}
sub _get_level_enemy ( $self, $user ) {
my $max_enemy_level = int( $user->level / 2 ) + 1;
if ( $max_enemy_level < $user->level - 10 ) {
$max_enemy_level = $user->level - 10;
}
if ( $max_enemy_level < 3 ) {
$max_enemy_level = 3;
}
my $min_enemy_level = $max_enemy_level - 5;
if ( $min_enemy_level < 2 ) {
$min_enemy_level = 2;
}
return $min_enemy_level +
int( rand( $max_enemy_level - $min_enemy_level + 1 ) );
}
sub _calculate_number_of_enemies ( $self, $min, $max ) {
return $min + int( rand( $max - $min + 1 ) );
}
1;

View File

@ -88,6 +88,17 @@ sub MIGRATIONS {
'ALTER TABLE conquer_node ALTER COLUMN geometry SET NOT NULL;',
'ALTER TABLE conquer_node DROP COLUMN coordinate_1;',
'ALTER TABLE conquer_node DROP COLUMN coordinate_2;',
'ALTER TABLE conquer_user ADD COLUMN experience INTEGER NOT NULL DEFAULT 125;',
'ALTER TABLE conquer_user ADD COLUMN current_hp INTEGER NOT NULL DEFAULT 999;',
'CREATE TABLE conquer_user_current_enemy (
uuid UUID NOT NULL PRIMARY KEY,
"user" UUID NOT NULL REFERENCES conquer_user(uuid),
species INTEGER NOT NULL,
is_battled BOOLEAN DEFAULT false,
is_selected_to_battle BOOLEAN DEFAULT false,
level INTEGER NOT NULL
);',
'ALTER TABLE conquer_user_current_enemy ALTER COLUMN species TYPE TEXT;',
);
}

View File

@ -54,7 +54,8 @@ sub Schema ($class) {
shift->do('set timezone = UTC');
return;
}
}
},
quote_char => '"',
}
);
}

View File

@ -56,8 +56,72 @@ __PACKAGE__->add_columns(
is_nullable => 0,
default_value => \'0',
},
experience => {
data_type => 'integer',
is_nullable => 0,
default_value => \'125',
},
current_hp => {
data_type => 'integer',
is_nullable => 0,
default_value => \'999',
}
);
sub max_health($self) {
$self = $self->get_from_storage();
my $base = 50;
my $born_value = 31;
return int(
(($base * 2 + $born_value) * $self->level)
/ 100 + $self->level + 10
);
}
sub level($self) {
$self = $self->get_from_storage();
return int($self->experience ** (1/3) + 0.0000000000001);
}
sub attack($self) {
$self = $self->get_from_storage();
my $base = 50;
my $born_value = 31;
return int(
(($base * 2 + $self->level)*$self->level)
/100
);
}
sub defense($self) {
$self = $self->get_from_storage();
my $base = 50;
my $born_value = 31;
return int(
(($base * 2 + $self->level)*$self->level)
/100
);
}
sub health($self, $health = undef) {
$self = $self->get_from_storage();
my $hp = $self->current_hp;
if ($hp > $self->max_health) {
$self->current_hp($self->max_health);
$self->update;
$self = $self->get_from_storage();
}
if (defined $health) {
if ($health > $self->max_health) {
$health = $self->max_health;
}
$self->current_hp($health);
$self->update;
$self = $self->get_from_storage();
}
return $self->current_hp;
}
sub coordinates ( $self, $coordinates = undef ) {
if ( defined $coordinates ) {
if ( ref $coordinates ne 'ARRAY' || scalar $coordinates->@* != 2 ) {

View File

@ -0,0 +1,101 @@
package BurguillosInfo::Schema::Result::ConquerUserCurrentEnemy;
use v5.36.0;
use strict;
use warnings;
use parent 'DBIx::Class::Core';
use feature 'signatures';
use JSON;
use BurguillosInfo::Species;
__PACKAGE__->table('conquer_user_current_enemy');
__PACKAGE__->load_components("TimeStamp");
__PACKAGE__->add_columns(
uuid => {
data_type => 'uuid',
is_nullable => 0,
},
user => {
data_type => 'uuid',
is_nullable => 0,
},
species => {
data_type => 'text',
is_nullable => 0,
},
is_selected_to_battle => {
data_type => 'boolean',
is_nullable => 0,
default_value => \'false',
},
is_battled => {
data_type => 'boolean',
is_nullable => 0,
default_value => \'false',
},
level => {
data_type => 'integer',
is_nullable => 0,
},
);
sub serialize ($self) {
my $species = BurguillosInfo::Species->new;
my $specie = $species->get( $self->species );
return {
uuid => $self->uuid,
species => $specie->serialize,
level => $self->level,
max_health => $self->max_health,
};
}
sub max_health ($self) {
$self = $self->get_from_storage();
my $base = 50;
my $born_value = 31;
return
int( ( ( $base * 2 + $born_value ) * $self->level ) / 100 +
$self->level +
10 );
}
sub experience_drop ($self) {
$self = $self->get_from_storage();
return int( $self->level / 7 * 179 );
}
sub experience ($self) {
$self = $self->get_from_storage();
return int( $self->level**(3) );
}
sub attack ($self) {
$self = $self->get_from_storage();
my $base = 50;
my $born_value = 31;
return int( ( ( $base * 2 + $self->level ) * $self->level ) / 100 );
}
sub defense ($self) {
$self = $self->get_from_storage();
my $base = 50;
my $born_value = 31;
return int( ( ( $base * 2 + $self->level ) * $self->level ) / 100 );
}
sub health ($self) {
# Combat result is decided from the start of battle.
return $self->max_health;
}
__PACKAGE__->set_primary_key('uuid');
__PACKAGE__->belongs_to( 'user_object',
'BurguillosInfo::Schema::Result::ConquerUser', 'user' );
1;

View File

@ -0,0 +1,24 @@
package BurguillosInfo::Specie;
use v5.36.0;
use strict;
use warnings;
use feature 'signatures';
use Moo::Role;
sub can_be_global {
return 0;
}
sub serialize ($self) {
return {
id => $self->id,
name => $self->name,
image => $self->image,
};
}
requires 'id name image';
1;

View File

@ -0,0 +1,58 @@
package BurguillosInfo::Species;
use v5.36.0;
use strict;
use warnings;
use Moo;
use Module::Pluggable
search_path => ['BurguillosInfo::Species'],
instantiate => 'new',
on_require_error => sub ( $plugin, $error ) {
die $error;
};
{
my %hash_species;
sub _hash ($self) {
if ( !scalar keys %hash_species ) {
$self->_populate_hash;
}
return {%hash_species};
}
sub _populate_hash ($self) {
my @species = $self->plugins();
print Data::Dumper::Dumper \@species;
for my $specie (@species) {
$self->_check_specie_valid($specie);
if (exists $hash_species{$specie->id}) {
die "Duplicated species id @{[$specie->id]}.";
}
$hash_species{$specie->id} = $specie;
}
}
}
sub _check_specie_valid ( $self, $specie ) {
if ( !$specie->does('BurguillosInfo::Specie') ) {
die "$specie does not implement BurguillosInfo::Specie.";
}
}
sub get($self, $id) {
return $self->_hash->{$id};
}
sub list($self) {
my @species_keys = keys %{$self->_hash};
my $species = [ sort { $a->id cmp $b->id } map { $self->_hash->{$_} } @species_keys ];
return $species;
}
sub list_can_be_global($self) {
return [ grep { $_->can_be_global } $self->list->@* ];
}
1;

View File

@ -0,0 +1,28 @@
package BurguillosInfo::Species::Murcielago;
use v5.34.1;
use strict;
use warnings;
use Moo;
use parent 'BurguillosInfo::Specie';
sub id {
return 'murcielago';
}
sub name {
return 'Murcielago';
}
sub image {
return '/img/conquer/species/murcielago.png';
}
sub can_be_global {
return 1;
}
1;

View File

@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="180"
height="180"
viewBox="0 0 180 180"
version="1.1"
id="svg5"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
sodipodi:docname="arrow-player-killed.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="true"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
showgrid="false"
inkscape:zoom="2.1722222"
inkscape:cx="114.39898"
inkscape:cy="58.925831"
inkscape:window-width="1499"
inkscape:window-height="991"
inkscape:window-x="26"
inkscape:window-y="23"
inkscape:window-maximized="0"
inkscape:current-layer="layer2" />
<defs
id="defs2" />
<g
inkscape:groupmode="layer"
id="layer3"
inkscape:label="Layer 3"
style="stroke:none;stroke-opacity:1;fill:#000000;fill-opacity:0.99906439">
<ellipse
style="display:inline;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:3.22896;stroke-dasharray:none;stroke-opacity:1"
id="path1514"
cy="90"
cx="90"
rx="87.179558"
ry="88.385521" />
</g>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="Layer 2"
style="stroke:none;stroke-opacity:1;fill:#000000;fill-opacity:0.99906439">
<path
style="display:inline;fill:#000000;fill-opacity:0.999064;stroke:none;stroke-width:3.42195;stroke-dasharray:none;stroke-opacity:1"
d="m 27.1872,145.48336 c -0.325678,6.59573 4.86944,13.81584 14.628956,9.61774 38.996707,-20.08168 63.312444,-21.05373 94.835564,1.87477 9.98034,4.08129 14.61469,-3.036 15.90751,-6.76847 C 153.79696,107.06667 118.11757,6.5875149 90,1.6144791 63.092475,7.5716541 26.88641,91.183358 27.1872,145.48336 Z"
id="path1682"
sodipodi:nodetypes="cccccc" />
</g>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
style="display:inline">
<path
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:2.805;stroke-dasharray:none;stroke-opacity:0"
d="m 109.99321,42.936903 5.25546,0.250702 -11.87761,18.116623 -5.190631,-0.233775 z"
id="path16212"
sodipodi:nodetypes="ccccc" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:2.805;stroke-dasharray:none;stroke-opacity:0"
d="m 98.180429,45.18702 4.515381,-0.518513 12.68756,15.571862 -5.93341,0.608601 z"
id="path16538"
sodipodi:nodetypes="ccccc" />
<path
style="display:inline;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:2.805;stroke-dasharray:none;stroke-opacity:0"
d="m 75.170413,42.936903 5.25546,0.250702 -11.87761,18.116623 -5.190631,-0.233775 z"
id="path16212-5"
sodipodi:nodetypes="ccccc" />
<path
style="display:inline;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:2.805;stroke-dasharray:none;stroke-opacity:0"
d="m 63.357632,45.18702 4.515381,-0.518513 12.68756,15.571862 -5.93341,0.608601 z"
id="path16538-3"
sodipodi:nodetypes="ccccc" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:2.39555;stroke-dasharray:none;stroke-opacity:0.997527"
d="m 60.222542,111.59197 2.432165,2.37521 7.776952,-12.17573 5.48651,12.67675 7.484611,-13.49323 6.616088,13.42642 5.917787,-12.95044 6.241525,15.681 7.16239,-16.32398 3.56976,17.01614 7.0569,-13.92744 -1.49286,-2.20634 -5.24564,9.43495 -3.04735,-15.193902 -7.75217,14.476702 -6.68575,-13.508063 -5.724592,11.749843 -6.278623,-12.941158 -7.663829,12.631268 -5.33589,-11.255318 z"
id="path16739" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 956 B

BIN
public/img/missingicon.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 B