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 )