Game-TextMapper

 view release on metacpan or  search on metacpan

lib/Game/TextMapper/Solo.pm  view on Meta::CPAN

# Copyright (C) 2024  Alex Schroeder <alex@gnu.org>
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option) any
# later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.

=encoding utf8

=head1 NAME

Game::TextMapper::Solo - generate a map generated step by step

=head1 SYNOPSIS

    use Modern::Perl;
    use Game::TextMapper::Solo;
    my $map = Game::TextMapper::Solo->new->generate_map();
    print $map;

=head1 DESCRIPTION

This starts the map and generates all the details directly, for each step,
without knowledge of the rest of the map. The tricky part is to generate
features such that no terrible geographical problems arise.

=cut

package Game::TextMapper::Solo;
use Game::TextMapper::Log;
use Modern::Perl '2018';
use List::Util qw(shuffle all any none);
use Mojo::Base -base;

my $log = Game::TextMapper::Log->get;

=head1 ATTRIBUTES

=head2 rows

The height of the map, defaults to 15.

    use Modern::Perl;
    use Game::TextMapper::Solo;
    my $map = Game::TextMapper::Solo->new(rows => 20)
        ->generate_map;
    print $map;

=head2 cols

The width of the map, defaults to 30.

    use Modern::Perl;
    use Game::TextMapper::Solo;
    my $map = Game::TextMapper::Solo->new(cols => 40)
        ->generate_map;
    print $map;

=cut

has 'rows' => 15;
has 'cols' => 30;
has 'altitudes' => sub{[]}; # these are the altitudes of each hex, a number between 0 (deep ocean) and 10 (ice)
has 'tiles' => sub{[]}; # these are the tiles on the map, an array of arrays of strings
has 'flows' => sub{[]}; # these are the water flow directions on the map, an array of coordinates
has 'rivers' => sub{[]}; # for rendering, the flows are turned into rivers, an array of arrays of coordinates
has 'trails' => sub{[]};
has 'loglevel';

my @tiles = qw(plain rough swamp desert forest hills green-hills forest-hill mountains mountain volcano ice water coastal ocean);
my @no_sources = qw(desert volcano water coastal ocean);
my @settlements = qw(house ruin tower ruined-tower castle ruined-castle);
my @ruins = qw(ruin ruined-tower ruined-castle);

=head1 METHODS

=head2 generate_map

This method takes no arguments. Set the properties of the map using the
attributes.

=cut

sub generate_map {
  my ($self) = @_;
  $log->level($self->loglevel) if $self->loglevel;
  $self->random_walk();
  # my $walks = $self->random_walk();
  # debug random walks
  # my @walks = @$walks;
  # @walks = @walks[0 .. 10];
  # $self->trails(\@walks);
  $self->add_rivers();
  return $self->to_text();
}

sub random_walk {
  my ($self) = @_;
  my %seen;
  my $tile_count = 0;
  my $path_length = 1;
  my $max_tiles = $self->rows * $self->cols;
  my $start = int($self->rows / 2) * $self->cols + int($self->cols / 2);
  $self->altitudes->[$start] = 5;
  my @neighbours = $self->neighbours($start);
  # initial river setup: roll a d6 four destination
  $self->flows->[$start] = $neighbours[int(rand(6))];
  # roll a d6 for source, skip if same as destination
  my $source = $neighbours[int(rand(6))];
  $self->flows->[$source] = $start unless $source == $self->flows->[$start];
  # initial setup: roll for starting region with a village
  $seen{$start} = 1;
  $self->random_tile($start, $start, 'house');



( run in 1.330 second using v1.01-cache-2.11-cpan-75ffa21a3d4 )