# World - contains everything in the game world
package Games::3D::World;
# (C) by Tels
use strict;
use vars qw/$VERSION/;
use Games::3D::Template;
$VERSION = '0.07';
##############################################################################
# protected vars
{
my $id = 1;
sub ID { return $id++;}
sub _reset_ID { $id = 1; }
}
##############################################################################
# methods
sub new
{
# create a new world
my $class = shift;
my $self = bless {}, $class;
$self->{things} = { };
$self->{render} = { };
$self->{thinks} = { };
$self->{templates} = {};
_reset_ID();
if (@_ == 2)
{
$self->load_templates($_[0]);
$self->load_from_file($_[1]);
}
$self->{time} = 0;
$self;
}
sub load_from_file
{
my ($self,$file) = @_;
$self->{file} = $file;
$self->{things} = { };
_reset_ID();
$self;
}
sub load_templates
{
my ($self,$file) = @_;
if (!ref($file))
{
# got filename, so read in data
open FILE, "$file" or die ("Cant read $file: $!");
local $/ = undef; # slurp mode
my $doc = ;
$file = \$doc;
close FILE;
}
$self->{templates} = {};
my @objects = Games::3D::Template::from_string($$file);
if (@objects == 1 && !ref($objects[0]))
{
die($objects[0]);
}
foreach my $o (@objects)
{
if (exists $self->{templates}->{$o->{class}})
{
warn ("Template for class $o->{class} already seen");
}
$self->{templates}->{$o->{class}} = $o;
$o->{_world} = $self;
}
$self;
}
sub templates
{
my $self = shift;
scalar keys %{$self->{templates}};
}
sub save_templates
{
my ($self,$file) = @_;
$self;
}
sub save_to_file
{
my ($self,$file) = @_;
$self->{file} = $file;
$self;
}
sub reload
{
my $self = shift;
$self->load_from_file($self->{file});
}
sub register
{
# register an object with yourself
my ($self,$obj) = @_;
$obj->{id} = ID(); # get it a new ID
$self->{things}->{$obj->{id}} = $obj; # store it
$self->{things}->{_world} = $self; # give thing access to us
if ($obj->{visible})
{
$self->{render}->{$obj->{id}} = $obj; # store it
}
if ($obj->{think_time} != 0)
{
$self->{think}->{$obj->{id}} = $obj; # store it
}
$self;
}
sub unregister
{
# should ONLY be called via $thing->{_world}->unregister($thing) !
my ($self,$thing) = @_;
my $id = $thing->{id};
delete $self->{render}->{$id};
delete $self->{things}->{$id};
# tricky, what happens if called inside update()?
delete $self->{think}->{$id};
$self;
}
sub things
{
# get count of things
my ($self) = @_;
scalar keys %{$self->{things}};
}
sub thinkers
{
# get count of thinking things
my ($self) = @_;
scalar keys %{$self->{think}};
}
sub update
{
my ($self,$now) = @_;
$self->{time} = $now; # cache time
foreach my $id (keys %{$self->{think}})
{
my $thing = $self->{think}->{$id};
if ($thing->{next_think} >= $now)
{
$thing->think($now);
}
# if the thing is in transition between states, let it update itself
$thing->update($now) if $thing->{state_endtime} != 0;
# XXX TODO: does not handle things that no longer want to think()
}
$self;
}
sub time
{
my $self = shift;
$self->{time};
}
sub render
{
my ($self,$now,$callback) = @_;
foreach my $id (keys %{$self->{render}})
{
&$callback ( $now, $self->{render}->{$id} );
}
$self;
}
sub create
{
# create an object based on a template (class name)
my ($self,$class) = @_;
return undef if !exists $self->{templates}->{$class};
$self->{templates}->{$class}->create_thing();
}
sub find_template
{
# given a class name, return the template object for it
my ($self,$class) = @_;
$self->{templates}->{$class};
}
sub id { 0; }
1;
__END__
=pod
=head1 NAME
Games::3D::World - contains all things in the game world
=head1 SYNOPSIS
use Games::3D::World;
# construct world from templates file and level file
my $level = Games::3D::World->new( $templates, $file);
# load the same level again
$level->reload();
# create a new world from sratch:
my $world = Games::3D::World->new();
$world->load_templates( $templates_file );
# add some thing directly
$world->create ( $thing_class );
# create another one
my $thing = Games::3D::Thingy->new( ... );
$thing->visible(1);
$thing->think_time(100);
# and make our world contain it
$world->register($thing);
# save the world
$world->save_to_file();
# foreach frame to render:
while ($not_quit)
{
# other code like user input handling here
...
# update the world with the current frame time:
$world->update( $now );
...
# then let world call $callback for each visible object
$world->render( $now, $callback );
# other drawing code here
...
}
=head1 EXPORTS
Exports nothing on default.
=head1 DESCRIPTION
This class represents the entire in-game object system. It contains
I, e.g. the blue-prints for objects, as well as the objects itself.
=head1 METHODS
=over 2
=item new()
my $world = Games::3D::World->new( templates => $file );
Creates a new game world/level and reads in the templates from C<$file>.
=item load_from_file()
$world->load_from_file( $file );
Load the game world/level from a file, replacing all existing data.
=item load_templates()
$world->load_templates( $templates_file );
Loads the templates from a file. Alternatively, if given a scalar ref,
will I the templates from the contents of the scalar.
=item save_to_file()
my $rc = $world->save_to_file( $file );
Save game world/level to a file, returns undef for success, otherwise
ref to error message.
=item save_templates()
my $rc = $world->save_templates( $file );
Save game world/level to a file, returns undef for success, otherwise
ref to error message.
=item templates()
print "I have ", $world->templates(), " templates\n";
Returns the number of templates the world currently knows about.
=item render()
$world->render();
Calls the method C on all things that want to be rendered.
=item update()
$world->update( $now );
Let's all objects that want to think regulary think, and then updates
objects that need updating. After this call, each object represents the
the state if has at the time C<$now>.
=item time()
$world->time( );
Return the current time. Usefull for objects that want to know the time,
because L might cause some object to send a signal to another
object, and the second one needs to know when the signal arrives.
=item create()
my $object = $world->create($class);
Create an object based on a template (class name) and populate it's settings
with the default values.
=item find_template()
my $template_object = $world->find_template($class);
Given a class name, return the template object for it..
=item id()
my $id = $world->id();
Return the ID of this world, which is always 0.
=item register()
$self->register($thingy);
Register an object with the world.
=item unregister()
This method is automatically called to unregister objects with
the world upon their death.
=item reload()
$world->reload();
Loads the world from the file again, thus resetting it to its
initial state again.
=item things()
my $things = $world->things();
Returns the count of things in this world.
=item thinkers()
my $thinkers = $world->thinkers();
Get the count of thinking things this world has.
=back
=head1 AUTHORS
(c) 2003, 2004, 2006 Tels
=head1 SEE ALSO
L, L, L.
=cut