Game-Entities

 view release on metacpan or  search on metacpan

lib/Game/Entities.pm  view on Meta::CPAN

    $index | ( $self->{entities}[$index]->$version << ENTITY_SHIFT )
};

my $generate_guid = sub ($self) {
    Carp::croak 'Exceeded maximum number of entities'
        if @{ $self->{entities} } >= ENTITY_MASK;

    my $guid = @{ $self->{entities} };
    push @{ $self->{entities} }, $guid;

    return $guid;
};

my $recycle_guid = sub ($self) {
    my $next = $self->{available};

    Carp::croak 'Cannot recycle GUID if none has been released'
        if $next->$is_null;

    my $ver = $self->{entities}[$next]->$version;

    $self->{available} = $self->{entities}[$next]->$entity;

    return $self->{entities}[$next] = $next | ( $ver << ENTITY_SHIFT );
};

my $maybe_prefix = sub ( $self, $name ) {
    $$name =~ s/^:([^:])/$self->{prefix}::$1/ if $self->{prefix};
};

my $get = sub ( $self, $unsafe, $guid, @types ) {
    my $index = $guid->$entity;

    if ( $self->{prefix} ) {
        s/^:([^:])/$self->{prefix}::$1/ for @types;
    }

    my @got = map {
        my $set    = $self->{components}{"$_"};
        my $sparse = $set->[SPARSE][$index];

        defined($sparse) && ( $unsafe || $self->check( $guid, $_ ) )
            ? $set->[COMPONENTS][$sparse] : undef
    } @types;

    return $got[0] if @types == 1;
    return @got;
};

## Public methods

sub new ( $class, %args ) {
    my $self = bless { %args{prefix} }, $class;
    $self->clear;
}

sub created ($self) { scalar @{ $self->{entities} } - 1 }

# Get the number of created entities that are still valid; that is, that have
# not been deleted.
sub alive ($self) {
    my $size = @{ $self->{entities} } - 1;
    my $current = $self->{available};

    until ( $current->$is_null ) {
        $size--;
        $current = $self->{entities}[ $current->$entity ];
    }

    return $size;
}

# Reset the registry internal storage. All entities will be deleted, and all
# entity IDs will be made available.
sub clear ($self) {
    delete $self->{view_cache};

    # Keys in this hash are component type names (ie. the result of ref),
    # and values are sparse sets of entities that "have" that component.
    delete $self->{components};

    # Parameters used for recycling entity GUIDs
    # See https://skypjack.github.io/2019-05-06-ecs-baf-part-3
    $self->{entities} = [ undef ];
    $self->{available} = NULL_ENTITY;

    return $self;
}

# Create a new entity
sub create ( $self, @components ) {
    Carp::croak 'Component must be a reference'
        if List::Util::any { !ref } @components;

    my $guid = $self->{available}->$is_null
        ? $self->$generate_guid : $self->$recycle_guid;

    $self->add( $guid, @components );

    return $guid;
}

sub check ( $self, $guid, $type ) {
    Carp::croak 'GUID must be defined' unless defined $guid;
    Carp::croak 'Component name must be defined and not a reference'
        if ! defined $type || ref $type;

    $self->$maybe_prefix(\$type);

    $self->{components}{$type}->$contains( $guid->$entity );
}

# Add or replace a component for an entity
sub add ( $self, $guid, @components ) {
    Carp::croak 'GUID must be defined' unless defined $guid;

    my $index = $guid->$entity;
    for my $component (@components) {
        my $name = ref($component) || Carp::croak 'Component must be a reference';

        #                                SPARSE  DENSE   COMPONENTS



( run in 3.420 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )