# ABSTRACT: FFI bindings for libtcod
package TCOD;
use strict;
use warnings;
use Encode ();
use FFI::C;
use FFI::CheckLib ();
use FFI::Platypus 1.00;
use FFI::Platypus::Buffer ();
use Ref::Util;
use TCOD::SDL2;
use TCOD::Event;
sub import {
use Import::Into;
TCOD::Event->import::into( scalar caller );
}
our $VERSION = '0.009';
my $bundle = FFI::Platypus->new( api => 1 );
$bundle->bundle('TCOD');
my $ffi;
BEGIN {
$ffi = FFI::Platypus->new( api => 1 );
$ffi->lib( FFI::CheckLib::find_lib_or_exit lib => 'tcod' );
FFI::C->ffi($ffi);
}
$ffi->load_custom_type( '::WideString' => 'wstring', access => 'read' );
$ffi->attach( [ TCOD_set_error => 'set_error' ] => ['string'] => 'int' );
$ffi->attach( [ TCOD_get_error => 'get_error' ] => [ ] => 'string' );
sub enum {
my %enums = @_;
while ( my ( $name, $values ) = each %enums ) {
require constant;
constant->import($values);
my $variable = __PACKAGE__ . '::' . $name;
no strict 'refs';
%{$variable} = ( %{$variable}, reverse %$values );
}
}
use constant {
NOISE_MAX_OCTAVES => 128,
NOISE_MAX_DIMENSIONS => 4,
NOISE_DEFAULT_HURST => 0.5,
NOISE_DEFAULT_LACUNARITY => 2,
};
BEGIN {
enum Distribution => {
DISTRIBUTION_LINEAR => 0,
DISTRIBUTION_GAUSSIAN => 1,
DISTRIBUTION_GAUSSIAN_RANGE => 2,
DISTRIBUTION_GAUSSIAN_INVERSE => 3,
DISTRIBUTION_GAUSSIAN_RANGE_INVERSE => 4,
},
Charmap => {
CHARMAP_CP437 => [
0x0000, 0x263A, 0x263B, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022,
0x25D8, 0x25CB, 0x25D9, 0x2642, 0x2640, 0x266A, 0x266B, 0x263C,
0x25BA, 0x25C4, 0x2195, 0x203C, 0x00B6, 0x00A7, 0x25AC, 0x21A8,
0x2191, 0x2193, 0x2192, 0x2190, 0x221F, 0x2194, 0x25B2, 0x25BC,
0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7,
0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9,
0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192,
0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA,
0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,
0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,
0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4,
0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248,
0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0,
],
CHARMAP_TCOD => [
0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
0x0040, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, 0x0060, 0x007B,
0x007C, 0x007D, 0x007E, 0x2591, 0x2592, 0x2593, 0x2502, 0x2500,
0x253C, 0x2524, 0x2534, 0x251C, 0x252C, 0x2514, 0x250C, 0x2510,
0x2518, 0x2598, 0x259D, 0x2580, 0x2596, 0x259A, 0x2590, 0x2597,
0x2191, 0x2193, 0x2190, 0x2192, 0x25B2, 0x25BC, 0x25C4, 0x25BA,
0x2195, 0x2194, 0x2610, 0x2611, 0x25CB, 0x25C9, 0x2551, 0x2550,
0x256C, 0x2563, 0x2569, 0x2560, 0x2566, 0x255A, 0x2554, 0x2557,
0x255D, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048,
0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050,
0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058,
0x0059, 0x005A, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068,
0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070,
0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078,
0x0079, 0x007A, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
],
},
Alignment => {
LEFT => 0,
RIGHT => 1,
CENTER => 2,
},
Error => {
E_OK => 0,
E_ERROR => -1,
E_INVALID_ARGUMENT => -2,
E_OUT_OF_MEMORY => -3,
E_REQUIRES_ATTENTION => -4,
E_WARN => 1,
},
Renderer => {
RENDERER_GLSL => 0,
RENDERER_OPENGL => 1,
RENDERER_SDL => 2,
RENDERER_SDL2 => 3,
RENDERER_OPENGL2 => 4,
NB_RENDERERS => 5,
},
BackgroundFlag => {
BKGND_NONE => 0,
BKGND_SET => 1,
BKGND_MULTIPLY => 2,
BKGND_LIGHTEN => 3,
BKGND_DARKEN => 4,
BKGND_SCREEN => 5,
BKGND_COLOR_DODGE => 6,
BKGND_COLOR_BURN => 7,
BKGND_ADD => 8,
BKGND_ADDA => 9,
BKGND_BURN => 10,
BKGND_OVERLAY => 11,
BKGND_ALPH => 12,
BKGND_DEFAULT => 13,
},
ColorControl => {
COLCTRL_1 => 1,
COLCTRL_2 => 2,
COLCTRL_3 => 3,
COLCTRL_4 => 4,
COLCTRL_5 => 5,
COLCTRL_NUMBER => 5, # Repeated!
COLCTRL_FORE_RGB => 6,
COLCTRL_BACK_RGB => 7,
COLCTRL_STOP => 8,
},
Keycode => {
K_NONE => 0,
K_ESCAPE => 1,
K_BACKSPACE => 2,
K_TAB => 3,
K_ENTER => 4,
K_SHIFT => 5,
K_CONTROL => 6,
K_ALT => 7,
K_PAUSE => 8,
K_CAPSLOCK => 9,
K_PAGEUP => 10,
K_PAGEDOWN => 11,
K_END => 12,
K_HOME => 13,
K_UP => 14,
K_LEFT => 15,
K_RIGHT => 16,
K_DOWN => 17,
K_PRINTSCREEN => 18,
K_INSERT => 19,
K_DELETE => 20,
K_LWIN => 21,
K_RWIN => 22,
K_APPS => 23,
K_0 => 24,
K_1 => 25,
K_2 => 26,
K_3 => 27,
K_4 => 28,
K_5 => 29,
K_6 => 30,
K_7 => 31,
K_8 => 32,
K_9 => 33,
K_KP0 => 34,
K_KP1 => 35,
K_KP2 => 36,
K_KP3 => 37,
K_KP4 => 38,
K_KP5 => 39,
K_KP6 => 40,
K_KP7 => 41,
K_KP8 => 42,
K_KP9 => 43,
K_KPADD => 44,
K_KPSUB => 45,
K_KPDIV => 46,
K_KPMUL => 47,
K_KPDEC => 48,
K_KPENTER => 49,
K_F1 => 50,
K_F2 => 51,
K_F3 => 52,
K_F4 => 53,
K_F5 => 54,
K_F6 => 55,
K_F7 => 56,
K_F8 => 57,
K_F9 => 58,
K_F10 => 59,
K_F11 => 60,
K_F12 => 61,
K_NUMLOCK => 62,
K_SCROLLLOCK => 63,
K_SPACE => 64,
K_CHAR => 65,
K_TEXT => 66,
},
Char => {
CHAR_HLINE => 196,
CHAR_VLINE => 179,
CHAR_NE => 191,
CHAR_NW => 218,
CHAR_SE => 217,
CHAR_SW => 192,
CHAR_TEEW => 180,
CHAR_TEEE => 195,
CHAR_TEEN => 193,
CHAR_TEES => 194,
CHAR_CROSS => 197,
# double walls
CHAR_DHLINE => 205,
CHAR_DVLINE => 186,
CHAR_DNE => 187,
CHAR_DNW => 201,
CHAR_DSE => 188,
CHAR_DSW => 200,
CHAR_DTEEW => 185,
CHAR_DTEEE => 204,
CHAR_DTEEN => 202,
CHAR_DTEES => 203,
CHAR_DCROSS => 206,
# blocks
CHAR_BLOCK1 => 176,
CHAR_BLOCK2 => 177,
CHAR_BLOCK3 => 178,
# arrows
CHAR_ARROW_N => 24,
CHAR_ARROW_S => 25,
CHAR_ARROW_E => 26,
CHAR_ARROW_W => 27,
# arrows without tail
CHAR_ARROW2_N => 30,
CHAR_ARROW2_S => 31,
CHAR_ARROW2_E => 16,
CHAR_ARROW2_W => 17,
# double arrows
CHAR_DARROW_H => 29,
CHAR_DARROW_V => 18,
# GUI stuff
CHAR_CHECKBOX_UNSET => 224,
CHAR_CHECKBOX_SET => 225,
CHAR_RADIO_UNSET => 9,
CHAR_RADIO_SET => 10,
# sub-pixel resoluti on kit
CHAR_SUBP_NW => 226,
CHAR_SUBP_NE => 227,
CHAR_SUBP_N => 228,
CHAR_SUBP_SE => 229,
CHAR_SUBP_DIAG => 230,
CHAR_SUBP_E => 231,
CHAR_SUBP_SW => 232,
# miscellaneous
CHAR_SMILIE => 1,
CHAR_SMILIE_INV => 2,
CHAR_HEART => 3,
CHAR_DIAMOND => 4,
CHAR_CLUB => 5,
CHAR_SPADE => 6,
CHAR_BULLET => 7,
CHAR_BULLET_INV => 8,
CHAR_MALE => 11,
CHAR_FEMALE => 12,
CHAR_NOTE => 13,
CHAR_NOTE_DOUBLE => 14,
CHAR_LIGHT => 15,
CHAR_EXCLAM_DOUBLE => 19,
CHAR_PILCROW => 20,
CHAR_SECTION => 21,
CHAR_POUND => 156,
CHAR_MULTIPLICATION => 158,
CHAR_FUNCTION => 159,
CHAR_RESERVED => 169,
CHAR_HALF => 171,
CHAR_ONE_QUARTER => 172,
CHAR_COPYRIGHT => 184,
CHAR_CENT => 189,
CHAR_YEN => 190,
CHAR_CURRENCY => 207,
CHAR_THREE_QUARTERS => 243,
CHAR_DIVISION => 246,
CHAR_GRADE => 248,
CHAR_UMLAUT => 249,
CHAR_POW1 => 251,
CHAR_POW3 => 252,
CHAR_POW2 => 253,
CHAR_BULLET_SQUARE => 254,
},
FontFlag => {
FONT_LAYOUT_ASCII_INCOL => 1,
FONT_LAYOUT_ASCII_INROW => 2,
FONT_TYPE_GREYSCALE => 4,
FONT_TYPE_GRAYSCALE => 4,
FONT_LAYOUT_TCOD => 8,
FONT_LAYOUT_CP437 => 16,
},
FOV => {
FOV_BASIC => 0,
FOV_DIAMOND => 1,
FOV_SHADOW => 2,
FOV_PERMISSIVE_0 => 3,
FOV_PERMISSIVE_1 => 4,
FOV_PERMISSIVE_2 => 5,
FOV_PERMISSIVE_3 => 6,
FOV_PERMISSIVE_4 => 7,
FOV_PERMISSIVE_5 => 8,
FOV_PERMISSIVE_6 => 9,
FOV_PERMISSIVE_7 => 10,
FOV_PERMISSIVE_8 => 11,
FOV_RESTRICTIVE => 12,
FOV_SYMMETRIC_SHADOWCAST => 13,
NB_FOV_ALGORITHMS => 14,
},
RandomAlgo => {
RNG_MT => 0,
RNG_CMWC => 1,
},
NoiseType => {
NOISE_PERLIN => 1,
NOISE_SIMPLEX => 2,
NOISE_WAVELET => 4,
NOISE_DEFAULT => 0,
},
Event => {
EVENT_NONE => 0,
EVENT_KEY_PRESS => 1,
EVENT_KEY_RELEASE => 2,
EVENT_MOUSE_MOVE => 4,
EVENT_MOUSE_PRESS => 8,
EVENT_MOUSE_RELEASE => 16,
# Continued below
},
}
BEGIN {
enum Event => {
# Continued above and below
EVENT_KEY => EVENT_KEY_PRESS | EVENT_KEY_RELEASE,
EVENT_MOUSE => EVENT_MOUSE_MOVE | EVENT_MOUSE_PRESS | EVENT_MOUSE_RELEASE,
},
}
BEGIN {
enum Event => {
EVENT_ANY => EVENT_KEY | EVENT_MOUSE,
},
}
$ffi->type( int => 'TCOD_renderer' );
$ffi->type( int => 'TCOD_keycode' );
$ffi->type( int => 'TCOD_error' );
$ffi->type( opaque => 'TCOD_event' );
$ffi->type( '(int, int, int, int, opaque )->float' => 'TCOD_path_func' );
$ffi->type( '(int,int)->bool' => 'TCOD_line_listener' );
# Custom blessed opaque types
for my $name (qw( image console map path dijkstra random noise context )) {
my $type = {
native_type => 'opaque',
perl_to_native => sub { $_[0] ? ${ $_[0] } : undef },
native_to_perl => sub {
return unless $_[0];
bless \$_[0], 'TCOD::' . ucfirst $name;
},
};
$ffi->custom_type( "TCOD_$name" => $type );
$bundle->custom_type( "TCOD_$name" => $type );
}
package TCOD::Key {
use FFI::Platypus::Record;
record_layout_1(
int => 'vk',
'string(1)' => 'c',
'string(32)' => 'text',
bool => 'pressed',
bool => 'lalt',
bool => 'lctrl',
bool => 'lmeta',
bool => 'ralt',
bool => 'rctrl',
bool => 'rmeta',
bool => 'shift',
);
$ffi->type( 'record(TCOD::Key)' => 'TCOD_key' );
}
package TCOD::Mouse {
use FFI::Platypus::Record;
record_layout_1(
int => 'x',
int => 'y',
int => 'dx',
int => 'dy',
int => 'cx',
int => 'cy',
int => 'dcx',
int => 'dcy',
bool => 'lbutton',
bool => 'rbutton',
bool => 'mbutton',
bool => 'lbutton_pressed',
bool => 'rbutton_pressed',
bool => 'mbutton_pressed',
bool => 'wheel_up',
bool => 'wheel_down',
);
$ffi->type( 'record(TCOD::Mouse)' => 'TCOD_mouse' );
$ffi->mangler( sub { 'TCOD_mouse_' . shift } );
$ffi->attach( is_cursor_visible => [ ] => 'bool' );
$ffi->attach( move => [qw( int int )] => 'void' );
$ffi->attach( show_cursor => [qw( bool )] => 'void' );
}
package TCOD::Color {
use Carp ();
use Scalar::Util ();
use overload fallback => 1,
'+' => sub {
my ( $self, $other, $swap ) = @_;
Carp::croak 'TCOD::Color addition only supports colors'
unless Scalar::Util::blessed $other && $other->isa('TCOD::Color');
$self->add($other);
},
'-' => sub {
my ( $self, $other, $swap ) = @_;
Carp::croak 'TCOD::Color subtraction only supports colors'
unless Scalar::Util::blessed $other && $other->isa('TCOD::Color');
$self->subtract($other);
},
'*' => sub {
my ( $self, $other, $swap ) = @_;
return $self->multiply_scalar($other) unless ref $other;
Carp::croak 'TCOD::Color multiplication supports colors or scalars'
unless Scalar::Util::blessed $other && $other->isa('TCOD::Color');
$self->multiply($other);
},
'""' => sub {
my $self = shift;
sprintf '#%02x%02x%02x', $self->r, $self->g, $self->b;
},
'!=' => sub { !shift->equals(shift) },
'==' => sub {
my ( $self, $other, $swap ) = @_;
Carp::croak 'TCOD::Color equality only supports colors'
unless Scalar::Util::blessed $other && $other->isa('TCOD::Color');
$self->equals($other);
};
$ffi->mangler( sub { 'TCOD_color_' . shift } );
use FFI::Platypus::Record;
record_layout_1( uint8 => 'r', uint8 => 'g', uint8 => 'b' );
$ffi->type( 'record(TCOD::Color)' => 'TCOD_color' );
$bundle->type( 'record(TCOD::Color)' => 'TCOD_color' );
{
# Give color a positional constructor
no strict 'refs';
no warnings 'redefine';
my $old = __PACKAGE__->can('new') or die;
my $name = __PACKAGE__ . '::new';
require Sub::Util;
*{$name} = Sub::Util::set_subname $name => sub {
my $class = shift;
$class->$old({ r => shift, g => shift, b => shift });
};
}
$ffi->attach( equals => [qw( TCOD_color TCOD_color )] => 'bool' );
$ffi->attach( add => [qw( TCOD_color TCOD_color )] => 'TCOD_color' );
$ffi->attach( subtract => [qw( TCOD_color TCOD_color )] => 'TCOD_color' );
$ffi->attach( multiply => [qw( TCOD_color TCOD_color )] => 'TCOD_color' );
$ffi->attach( multiply_scalar => [qw( TCOD_color float )] => 'TCOD_color' );
$ffi->attach( lerp => [qw( TCOD_color TCOD_color float )] => 'TCOD_color' );
$ffi->attach( get_hue => [qw( TCOD_color )] => 'float' );
$ffi->attach( set_hue => [qw( TCOD_color* float )] => 'void' );
$ffi->attach( get_saturation => [qw( TCOD_color )] => 'float' );
$ffi->attach( set_saturation => [qw( TCOD_color* float )] => 'void' );
$ffi->attach( get_value => [qw( TCOD_color )] => 'float' );
$ffi->attach( set_value => [qw( TCOD_color* float )] => 'void' );
$ffi->attach( shift_hue => [qw( TCOD_color* float )] => 'void' );
$ffi->attach( scale_HSV => [qw( TCOD_color* float float )] => 'void' );
$ffi->attach( set_HSV => [qw( TCOD_color* float float float )] => 'void' );
$ffi->attach( get_HSV => [qw( TCOD_color float* float* float* )] => 'void' => sub {
$_[0]->( $_[1], \my $h, \my $s, \my $v );
return ( $h, $s, $v );
});
sub gen_map {
shift if @_ % 2; # Discard first arg if called like $color->gen_map( %map )
my ( $this, @rest ) = List::Util::pairs @_;
my @map;
for my $next (@rest) {
my ( $start, $end ) = ( $this->[1], $next->[1] );
for my $i ( $start .. $end ) {
$map[$i] = $this->[0]->lerp( $next->[0], ( $i - $start ) / ( $end - $start ) );
}
$this = $next;
}
@map;
}
}
package TCOD::ColorRGBA {
use FFI::Platypus::Record;
record_layout_1( uint8 => 'r', uint8 => 'g', uint8 => 'b', uint8 => 'a' );
$ffi->type( 'record(TCOD::ColorRGBA)' => 'TCOD_colorRGBA' );
$bundle->type( 'record(TCOD::ColorRGBA)' => 'TCOD_colorRGBA' );
{
# Give color a positional constructor
no strict 'refs';
no warnings 'redefine';
my $old = __PACKAGE__->can('new') or die;
my $name = __PACKAGE__ . '::new';
require Sub::Util;
*{$name} = Sub::Util::set_subname $name => sub {
my $class = shift;
$class->$old({ r => shift, g => shift, b => shift, a => shift });
};
}
}
package TCOD::Map {
$ffi->mangler( sub { 'TCOD_map_' . shift } );
$ffi->attach( new => [qw( int int )] => 'TCOD_map' => sub { $_[0]->( @_[ 2 .. $#_ ] ) } );
$ffi->attach( set_properties => [qw( TCOD_map int int bool bool )] => 'void' );
$ffi->attach( clear => [qw( TCOD_map bool bool )] => 'void' );
$ffi->attach( copy => [qw( TCOD_map TCOD_map )] => 'void' );
$ffi->attach( compute_fov => [qw( TCOD_map int int int bool int )] => 'void' );
$ffi->attach( is_in_fov => [qw( TCOD_map int int )] => 'bool' );
$ffi->attach( is_transparent => [qw( TCOD_map int int )] => 'bool' );
$ffi->attach( is_walkable => [qw( TCOD_map int int )] => 'bool' );
$ffi->attach( get_width => [qw( TCOD_map )] => 'int' );
$ffi->attach( get_height => [qw( TCOD_map )] => 'int' );
}
package TCOD::Console {
$ffi->mangler( sub { 'TCOD_console_' . shift } );
$bundle->mangler( sub { 'PERL_console_' . shift } );
$ffi->attach( [ delete => 'DESTROY' ] => [qw( TCOD_console )] => 'void' );
# Constructors
$ffi->attach( new => [qw( int int )] => 'TCOD_console' => sub { $_[0]->( @_[ 2 .. $#_ ] ) } );
$ffi->attach( from_file => [qw( string )] => 'TCOD_console' => sub { $_[0]->( $_[ 2 ] ) } );
$ffi->attach( load_asc => [qw( string )] => 'TCOD_console' => sub { $_[0]->( $_[ 2 ] ) } );
$ffi->attach( load_apf => [qw( string )] => 'TCOD_console' => sub { $_[0]->( $_[ 2 ] ) } );
# Printing methods
my $blit_with_key_color = $ffi->function( blit_key_color => [qw(
TCOD_console int int int int
TCOD_console int int
float float
TCOD_color
)]);
$ffi->attach( blit => [qw(
TCOD_console int int int int
TCOD_console int int
float float
)] => void => sub {
my ( $xsub, $console, %args ) = @_;
my @args = (
$console,
$args{src_x} // 0,
$args{src_y} // 0,
$args{width} // 0,
$args{height} // 0,
$args{dest},
$args{dest_x} // 0,
$args{dest_y} // 0,
$args{fg_alpha} // $args{alpha} // 1,
$args{bg_alpha} // $args{alpha} // 1,
);
return $blit_with_key_color->( @args, $args{key_color} ) if $args{key_color};
$xsub->(@args);
});
$ffi->attach( [ printn => 'print' ] => [qw(
TCOD_console
int
int
size_t
string
TCOD_color*
TCOD_color*
int
int
)] => TCOD_error => sub {
my ( $xsub, $console, %args ) = ( shift, shift );
# Accommodate $con->print( $x, $y, $string, %rest );
%args = ( x => shift, y => shift, string => shift ) if @_ % 2;
%args = ( %args, @_ );
my $string = Encode::encode( 'UTF-8', $args{string}, Encode::FB_CROAK );
$xsub->(
$console, @args{qw( x y )},
length($string), $string,
@args{qw( fg bg )},
$args{bg_blend} // TCOD::BKGND_SET,
$args{alignment} // TCOD::LEFT,
);
});
$ffi->attach( [ printn_rect => 'print_box' ] => [qw(
TCOD_console
int
int
int
int
size_t
string
TCOD_color*
TCOD_color*
int
int
)] => TCOD_error => sub {
my ( $xsub, $console, %args ) = @_;
my $string = Encode::encode( 'UTF-8', $args{string}, Encode::FB_CROAK );
$xsub->(
$console, @args{qw( x y width height )},
length($string), $string,
@args{qw( fg bg )},
$args{bg_blend} // TCOD::BKGND_SET,
$args{alignment} // TCOD::LEFT,
);
});
$ffi->attach( [ draw_frame_rgb => 'draw_frame' ] => [qw(
TCOD_console
int
int
int
int
int[]
TCOD_color*
TCOD_color*
int
bool
)] => TCOD_error => sub {
my ( $xsub, $console, %args ) = @_;
Carp::croak 'The title parameter is not supported' if exists $args{title};
my $decoration = $args{decoration} // [ '┌', '─', '┐', '│', ' ', '│', '└', '─', '┘' ];
$decoration = [ map ord, split //, $decoration ]
unless Ref::Util::is_arrayref $decoration;
Carp::croak 'Frame decoration must have a length of 9. It has a length of ' . @$decoration
if @$decoration != 9;
$xsub->(
$console, @args{qw( x y width height )},
$decoration,
@args{qw( fg bg )},
$args{bg_blend} // TCOD::BKGND_SET,
$args{clear} // 1,
);
});
$ffi->attach( [ draw_rect_rgb => 'draw_rect' ] => [qw(
TCOD_console
int
int
int
int
int
TCOD_color*
TCOD_color*
int
bool
)] => TCOD_error => sub {
my ( $xsub, $console, %args ) = @_;
$xsub->(
$console,
@args{qw( x y width height ch fg bg )},
$args{bg_blend} // TCOD::BKGND_SET,
);
});
# Methods
my $clear = $bundle->function( clear => [qw( TCOD_console TCOD_color* TCOD_color* int )] );
$ffi->attach( clear => [qw( TCOD_console )] => 'void' => sub {
my ( $xsub, $console, %args ) = @_;
return $xsub->($console) unless %args;
$clear->( $console, $args{fg}, $args{bg}, $args{ch} // TCOD::Event::K_SPACE );
});
$ffi->attach( save_asc => [qw( TCOD_console string )] => 'bool' );
$ffi->attach( save_apf => [qw( TCOD_console string )] => 'bool' );
$ffi->attach( set_char => [qw( TCOD_console int int int )] => 'void' );
$ffi->attach( get_char => [qw( TCOD_console int int )] => 'int' );
$ffi->attach( put_char => [qw( TCOD_console int int int int )] => 'void' );
$ffi->attach( put_char_ex => [qw( TCOD_console int int int TCOD_color TCOD_color )] => 'void' );
$ffi->attach( get_width => [qw( TCOD_console )] => 'int' );
$ffi->attach( get_height => [qw( TCOD_console )] => 'int' );
$ffi->attach( set_background_flag => [qw( TCOD_console int )] => 'void' );
$ffi->attach( get_background_flag => [qw( TCOD_console )] => 'int' );
$ffi->attach( set_alignment => [qw( TCOD_console int )] => 'void' );
$ffi->attach( get_alignment => [qw( TCOD_console )] => 'int' );
$ffi->attach( set_char_background => [qw( TCOD_console int int TCOD_color int )] => 'void' );
$ffi->attach( get_char_background => [qw( TCOD_console int int )] => 'TCOD_color' );
$ffi->attach( set_char_foreground => [qw( TCOD_console int int TCOD_color )] => 'void' );
$ffi->attach( get_char_foreground => [qw( TCOD_console int int )] => 'TCOD_color' );
$ffi->attach( set_default_background => [qw( TCOD_console TCOD_color )] => 'void' );
$ffi->attach( get_default_background => [qw( TCOD_console )] => 'TCOD_color' );
$ffi->attach( set_default_foreground => [qw( TCOD_console TCOD_color )] => 'void' );
$ffi->attach( get_default_foreground => [qw( TCOD_console )] => 'TCOD_color' );
# Root console functions
$ffi->attach( init_root => [qw( int int string int bool )] => 'void' );
$ffi->attach( set_custom_font => [qw( string int int int )] => 'void' );
$ffi->attach( map_ascii_code_to_font => [qw( int int int )] => 'void' );
$ffi->attach( map_ascii_codes_to_font => [qw( int int int int )] => 'void' );
$ffi->attach( map_string_to_font => [qw( string int int )] => 'void' );
$ffi->attach( is_fullscreen => [ ] => 'bool' );
$ffi->attach( set_fullscreen => [qw( bool )] => 'void' );
$ffi->attach( set_window_title => [qw( string )] => 'void' );
$ffi->attach( is_window_closed => [ ] => 'bool' );
$ffi->attach( has_mouse_focus => [ ] => 'bool' );
$ffi->attach( is_active => [ ] => 'bool' );
$ffi->attach( credits => [ ] => 'void' );
$ffi->attach( credits_render => [qw( int int bool )] => 'bool' );
$ffi->attach( credits_reset => [ ] => 'void' );
$ffi->attach( flush => [ ] => 'void' );
$ffi->attach( set_fade => [qw( uint8 TCOD_color )] => 'void' );
$ffi->attach( get_fade => [ ] => 'uint8' );
$ffi->attach( get_fading_color => [ ] => 'TCOD_color' );
$ffi->attach( set_color_control => [qw( int TCOD_color TCOD_color )] => 'void' );
$ffi->attach( wait_for_keypress => ['bool'] => 'TCOD_key' );
$ffi->attach( check_for_keypress => ['int' ] => 'TCOD_key' );
$ffi->attach( is_key_pressed => ['int' ] => 'bool' );
}
package TCOD::Sys {
$ffi->mangler( sub { 'TCOD_sys_' . shift } );
$ffi->attach( wait_for_event => [qw( int TCOD_key* TCOD_mouse* bool )] => 'TCOD_event' );
$ffi->attach( check_for_event => [qw( int TCOD_key* TCOD_mouse* )] => 'TCOD_event' );
$ffi->attach( save_screenshot => [qw( string )] => 'void' );
$ffi->attach( set_fps => [qw( int )] => 'void' );
$ffi->attach( get_fps => [ ] => 'int' );
$ffi->attach( sleep_milli => [qw( uint32 )] => 'void' );
$ffi->attach( elapsed_milli => [ ] => 'uint32' );
$ffi->attach( elapsed_seconds => [ ] => 'float' );
$ffi->attach( get_last_frame_length => [ ] => 'float' );
$ffi->attach( update_char => [qw( int int int TCOD_image int int )] => 'void' );
$ffi->attach( set_renderer => [qw( TCOD_renderer )] => 'void' );
$ffi->attach( get_renderer => [ ] => 'TCOD_renderer' );
# Deprecated
# $ffi->attach( create_directory => [qw( string )] => 'bool' );
# $ffi->attach( delete_directory => [qw( string )] => 'bool' );
# $ffi->attach( delete_file => [qw( string )] => 'bool' );
# $ffi->attach( is_directory => [qw( string )] => 'bool' );
# $ffi->attach( file_exists => [qw( string )] => 'bool' );
# $ffi->attach( clipboard_set => [qw( string )] => 'bool' );
# $ffi->attach( clipboard_get => [ ] => 'string' );
$ffi->attach( get_char_size => [qw( int* int* )] => 'void' => sub { $_[0]->( \my $w, \my $h ); ( $w, $h ) });
$ffi->attach( get_current_resolution => [qw( int* int* )] => 'void' => sub { $_[0]->( \my $w, \my $h ); ( $w, $h ) });
# $ffi->attach( get_fullscreen_offset => [qw( int* int* )] => 'void' => sub { $_[0]->( \my $x, \my $y ); ( $x, $y ) });
$ffi->attach( register_SDL_renderer => [qw( (opaque)->void )] => 'void' );
# $ffi->attach( set_dirty => [qw( int int int int )] => 'void' );
$ffi->attach( force_fullscreen_resolution => [qw( int int )] => 'void' );
}
package TCOD::Path {
$ffi->mangler( sub { 'TCOD_path_' . shift } );
my $new = sub {
my $sub = shift;
my $class = ref $_[0] || $_[0];
shift if $class eq __PACKAGE__;
$sub->(@_);
};
# Constructors
$ffi->attach( new_using_map => [qw( TCOD_map float )] => 'TCOD_path' => $new );
$ffi->attach( new_using_function => [qw( int int TCOD_path_func opaque float )] => 'TCOD_path' => $new );
$ffi->attach( compute => [qw( TCOD_path int int int int )] => 'bool' );
$ffi->attach( reverse => [qw( TCOD_path )] => 'void' );
$ffi->attach( is_empty => [qw( TCOD_path )] => 'bool' );
$ffi->attach( size => [qw( TCOD_path )] => 'int' );
$ffi->attach( get_origin => [qw( TCOD_path int* int* )] => 'void' => sub {
$_[0]->( $_[1], \my $x, \my $y );
return ( $x, $y );
});
$ffi->attach( get_destination => [qw( TCOD_path int* int* )] => 'void' => sub {
$_[0]->( $_[1], \my $x, \my $y );
return ( $x, $y );
});
$ffi->attach( get => [qw( TCOD_path int int* int* )] => 'void' => sub {
$_[0]->( @_[ 1, 2 ], \my $x, \my $y );
return ( $x, $y );
});
$ffi->attach( walk => [qw( TCOD_path int* int* bool )] => 'bool' => sub {
$_[0]->( $_[1], \my $x, \my $y, $_[2] ) or return;
return ( $x, $y );
});
$ffi->mangler( sub { shift } );
$ffi->attach( [ TCOD_path_delete => 'DESTROY' ] => ['TCOD_path'] => 'void' );
}
package TCOD::Dijkstra {
$ffi->mangler( sub { 'TCOD_dijkstra_' . shift } );
my $new = sub {
my $sub = shift;
my $class = ref $_[0] || $_[0];
shift if $class eq __PACKAGE__;
$sub->(@_);
};
# Constructors
$ffi->attach( new => [qw( TCOD_map float )] => 'TCOD_dijkstra' => $new );
$ffi->attach( new_using_function => [qw( int int TCOD_path_func opaque float )] => 'TCOD_dijkstra' => $new );
$ffi->attach( compute => [qw( TCOD_dijkstra int int )] => 'void' );
$ffi->attach( path_set => [qw( TCOD_dijkstra int int )] => 'bool' );
$ffi->attach( reverse => [qw( TCOD_dijkstra )] => 'void' );
$ffi->attach( is_empty => [qw( TCOD_dijkstra )] => 'bool' );
$ffi->attach( size => [qw( TCOD_dijkstra )] => 'int' );
$ffi->attach( get_distance => [qw( TCOD_dijkstra int int )] => 'float' );
$ffi->attach( get => [qw( TCOD_dijkstra int int* int* )] => 'void' => sub {
$_[0]->( @_[ 1, 2 ], \my $x, \my $y );
return ( $x, $y );
});
$ffi->attach( [ path_walk => 'walk' ] => [qw( TCOD_dijkstra int* int* )] => 'bool' => sub {
$_[0]->( $_[1], \my $x, \my $y, $_[2] ) or return;
return ( $x, $y );
});
$ffi->mangler( sub { shift } );
$ffi->attach( [ TCOD_dijkstra_delete => 'DESTROY' ] => ['TCOD_dijkstra'] => 'void' );
}
package TCOD::Image {
$ffi->mangler( sub { 'TCOD_image_' . shift } );
$ffi->attach( new => [qw( int int )] => 'TCOD_image' => sub { $_[0]->( @_[ 2 .. $#_ ] ) } );
$ffi->attach( load => [qw( string )] => 'TCOD_image' );
$ffi->attach( from_console => [qw( TCOD_console )] => 'TCOD_image' );
$ffi->attach( save => [qw( TCOD_image string )] => 'void' );
$ffi->attach( refresh_console => [qw( TCOD_image TCOD_console )] => 'void' );
$ffi->attach( put_pixel => [qw( TCOD_image int int TCOD_color )] => 'void' );
$ffi->attach( scale => [qw( TCOD_image int int )] => 'void' );
$ffi->attach( get_pixel => [qw( TCOD_image int int )] => 'TCOD_color' );
$ffi->attach( get_alpha => [qw( TCOD_image int int )] => 'int' );
# $ffi->attach( pixel_is_transparent => [qw( TCOD_image int int )] => 'bool' );
$ffi->attach( get_mipmap_pixel => [qw( TCOD_image float float float float )] => 'TCOD_color' );
$ffi->attach( rotate90 => [qw( TCOD_image int )] => 'void' );
$ffi->attach( invert => [qw( TCOD_image )] => 'void' );
$ffi->attach( vflip => [qw( TCOD_image )] => 'void' );
$ffi->attach( hflip => [qw( TCOD_image )] => 'void' );
$ffi->attach( clear => [qw( TCOD_image TCOD_color )] => 'void' );
$ffi->attach( set_key_color => [qw( TCOD_image TCOD_color )] => 'void' );
$ffi->attach( blit => [qw( TCOD_image TCOD_console int int int float float float )] => 'void' );
$ffi->attach( blit_2x => [qw( TCOD_image TCOD_console int int int int int int )] => 'void' );
$ffi->attach( blit_rect => [qw( TCOD_image TCOD_console int int int int int )] => 'void' );
$ffi->attach( get_size => [qw( TCOD_image int* int* )] => 'void' => sub {
$_[0]->( @_[ 1, 2 ], \my $w, \my $h );
return ( $w, $h );
});
}
package TCOD::Random {
my $instance;
$ffi->mangler( sub { 'TCOD_random_' . shift } );
$ffi->attach( new => [qw( int )] => 'TCOD_random' => sub { $_[0]->( @_[ 2 .. $#$_ ] ) } );
$ffi->attach( new_from_seed => [qw( int int )] => 'TCOD_random' => sub { $_[0]->( @_[ 2 .. $#$_ ] ) } );
$ffi->attach( get_instance => [ ] => 'TCOD_random' => sub {
$instance //= $_[0]->(); # Prevent the global RNG from being destroyed
});
$ffi->attach( save => [qw( TCOD_random )] => 'TCOD_random' );
$ffi->attach( restore => [qw( TCOD_random TCOD_random )] => 'void' );
$ffi->attach( set_distribution => [qw( TCOD_random int )] => 'void' );
$ffi->attach( get_int => [qw( TCOD_random int int )] => 'int' );
$ffi->attach( get_int_mean => [qw( TCOD_random int int int )] => 'int' );
$ffi->attach( get_float => [qw( TCOD_random float float )] => 'float' );
$ffi->attach( get_float_mean => [qw( TCOD_random float float float )] => 'float' );
$ffi->attach( get_double => [qw( TCOD_random double double )] => 'double' );
$ffi->attach( get_double_mean => [qw( TCOD_random double double double )] => 'double' );
$ffi->mangler( sub { shift } );
$ffi->attach( [ TCOD_random_delete => 'DESTROY' ] => ['TCOD_random'] => 'void' );
}
package TCOD::Noise {
$ffi->mangler( sub { 'TCOD_noise_' . shift } );
$ffi->attach( new => [qw( int float float TCOD_random )] => 'TCOD_noise' => sub { $_[0]->( @_[ 2 .. $#_ ] ) } );
$ffi->attach( set_type => [qw( TCOD_noise int )] => 'void' );
$ffi->attach( get => [qw( TCOD_noise float[] )] => 'float' );
$ffi->attach( get_ex => [qw( TCOD_noise float[] int )] => 'float' );
$ffi->attach( get_fbm => [qw( TCOD_noise float[] float )] => 'float' );
$ffi->attach( get_fbm_ex => [qw( TCOD_noise float[] float int )] => 'float' );
$ffi->attach( get_turbulence => [qw( TCOD_noise float[] float )] => 'float' );
$ffi->attach( get_turbulence_ex => [qw( TCOD_noise float[] float int )] => 'float' );
$ffi->mangler( sub { shift } );
$ffi->attach( [ TCOD_noise_delete => 'DESTROY' ] => ['TCOD_noise'] => 'void' );
}
package TCOD::Tileset {
$ffi->mangler( sub { shift } );
FFI::C->struct( TCOD_tileset => [
tile_width => 'int',
tile_height => 'int',
tile_length => 'int',
tiles_capacity => 'int',
tiles_count => 'int',
pixels => 'opaque', # TCOD_ColorRGBA*
character_map_length => 'int',
character_map => 'opaque',
observer_list => 'opaque', # TCOD_TilesetObserver
virtual_columns => 'int',
ref_count => 'int',
]);
sub tile_shape {
my $self = shift;
( $self->tile_width, $self->tile_height );
}
$ffi->attach( [ TCOD_load_bdf => 'load_bdf' ] => [qw( string )] => 'TCOD_tileset' => sub {
my ( $xsub, $self, $path ) = @_;
Carp::croak "Cannot load tilesheet from $path: no such file"
unless -f $path;
$xsub->( $self, $path );
});
$ffi->attach( [ TCOD_tileset_load => 'load_tilesheet' ] => [qw( string int int int int[] )] => 'TCOD_tileset' => sub {
my ( $xsub, undef, %args ) = ( shift, shift );
%args = @_ != 4 ? @_ : (
path => shift,
columns => shift,
rows => shift,
charmap => shift,
);
Carp::croak "Cannot load tilesheet from $args{path}: no such file"
unless -f $args{path};
$xsub->(
@args{qw( path columns rows )},
scalar @{ $args{charmap} }, $args{charmap},
);
});
$ffi->attach( [ TCOD_tileset_get_tile_ => 'get_tile' ] => [qw( TCOD_tileset int )] => 'int' => sub {
$_[0]->( $_[1], $_[2], my $color = TCOD::ColorRGBA->new );
$color;
});
$ffi->attach( [ TCOD_tileset_set_tile_ => 'set_tile' ] => [qw( TCOD_tileset int TCOD_colorRGBA* )] => 'int' );
$ffi->attach( [ TCOD_tileset_assign_tile => 'remap' ] => [qw( TCOD_tileset int int int )] => 'int' => sub {
my ( $xsub, $self, $x, $y, $codepoint ) = @_;
my $i = $x + $y * $self->tileset->virtual_columns;
Carp::croak "Tile $i is non-existent and can't be assigned"
if $i < 0 || $i >= $self->tileset->tiles_count;
$xsub->( $self, $i, $codepoint );
});
$ffi->attach( [ TCOD_tileset_render_to_surface => 'render' ] => [qw( TCOD_console TCOD_console opaque )] => 'int' => sub {
my ( $xsub, $self, $console ) = @_;
my $width = $console->width * $self->tile_width;
my $height = $console->height * $self->tile_height;
my ($pixels) = FFI::Platypus::Buffer::scalar_to_buffer( pack 'S*', (0) x ( $height * $width ) );
my $surface = TCOD::SDL2::CreateRGBSurfaceWithFormatFrom(
$pixels,
$height, $width, 32, 4,
TCOD::SDL2::PIXELFORMAT_RGBA32,
) or die TCOD::SDL2::GetError();
my $pointer = $ffi->cast( opaque => 'opaque*' => $surface );
$xsub->( $self, $console, undef, $pointer );
});
# $ffi->attach( [ TCOD_tileset_delete => 'DESTROY' ] => ['TCOD_tileset'] => 'void' );
}
package
TCOD::ContextParams {
FFI::C->struct( TCOD_context_params => [
tcod_version => 'int',
window_x => 'int',
window_y => 'int',
pixel_width => 'int',
pixel_height => 'int',
columns => 'int',
rows => 'int',
renderer_type => 'int',
_tileset => 'opaque',
vsync => 'int',
sdl_window_flags => 'int',
window_title => 'opaque',
argc => 'int',
argv => 'opaque',
cli_output => 'opaque',
cli_userdata => 'opaque',
window_xy_defined => 'bool',
]);
sub tileset { $ffi->cast( opaque => TCOD_tileset => shift->_tileset ) }
}
package
TCOD::ViewportOptions {
FFI::C->struct( TCOD_viewport_options => [
tcod_version => 'int',
keep_aspect => 'bool',
integer_scaling => 'bool',
_clear_color_r => 'uint8',
_clear_color_g => 'uint8',
_clear_color_b => 'uint8',
_clear_color_a => 'uint8',
align_x => 'float',
align_y => 'float',
]);
}
package TCOD::Context {
$ffi->mangler( sub { 'TCOD_context_' . shift } );
$ffi->attach( new => [qw( TCOD_context_params opaque* )] => 'TCOD_error' => sub {
my ( $xsub, undef, %args ) = @_;
for (
[ x => 'window_x' ],
[ y => 'window_y' ],
[ width => 'pixel_width' ],
[ height => 'pixel_height' ],
[ renderer => 'renderer_type' ],
[ title => 'window_title' => 'string' ],
[ tileset => '_tileset' => 'TCOD_tileset' ],
) {
my ( $in, $out, $cast ) = @$_;
next unless exists $args{$in};
$args{$out} = delete $args{$in};
$args{$out} = $ffi->cast( $cast => opaque => $args{$out} ) if $cast;
}
$args{vsync} //= 1;
$args{sdl_window_flags} //= TCOD::SDL2::WINDOW_RESIZABLE();
if ( TCOD::SDL2::InitSubSystem( TCOD::SDL2::INIT_VIDEO ) ) {
TCOD::set_error( TCOD::SDL2::GetError() );
return;
}
my $err = $xsub->( TCOD::ContextParams->new(\%args), \my $ctx );
return if $err < 0;
bless \$ctx, 'TCOD::Context';
});
{
my $sub = $ffi->function( recommended_console_size => [qw( TCOD_context float int* int* )] => 'TCOD_error' => sub {
my ( $xsub, $self, $magnification, $min_cols, $min_rows ) = @_;
my $err = $xsub->( $self, $magnification, \my $w, \my $h );
Carp::croak TCOD::get_error() if $err < 0;
$w = $min_cols if $min_cols > $w;
$h = $min_rows if $min_rows > $h;
( $w, $h );
});
sub recommended_console_size {
my ( $self, $min_cols, $min_rows ) = @_;
$sub->( $self, 1, $min_cols // 1, $min_rows // 1 );
}
sub new_console {
my ( $self, %args ) = ( shift );
# Accommodate $ctx->new_console( $cols, $rows, $magnification );
%args = ( min_columns => shift, min_rows => shift, magnification => shift ) if @_ % 2;
%args = ( %args, @_ );
$args{min_columns} //= 1;
$args{min_rows} //= 1;
$args{magnification} //= 1;
Carp::croak "Magnification must be greater than zero (got $args{magnification}"
if $args{magnification} < 0;
my ( $cols, $rows )
= $sub->( $self, $args{magnification}, @args{qw( min_columns min_rows )} );
my ( $width, $height ) = @args{qw( min_columns min_rows )};
$width = $cols if $cols > $width;
$height = $rows if $rows > $height;
TCOD::Console->new( $width, $height );
}
}
$ffi->attach( present => [qw( TCOD_context TCOD_console TCOD_viewport_options )] => 'TCOD_error' => sub {
my ( $xsub, $self, %args ) = ( shift, shift );
# Accommodate $ctx->present( $console, %rest );
%args = ( console => shift ) if @_ % 2;
%args = ( %args, @_ );
@args{qw( align_x align_y )} = @{ delete $args{align} // [ 0.5, 0.5 ] };
my $c = delete $args{clear_color} // TCOD::BLACK();
$args{_clear_color_r} = $c->r;
$args{_clear_color_g} = $c->g;
$args{_clear_color_b} = $c->b;
$args{_clear_color_a} = 0xFF;
my $err = $xsub->( $self, delete $args{console}, TCOD::ViewportOptions->new(\%args) );
Carp::croak TCOD::get_error() if $err < 0;
return;
});
sub convert_event {
my ( $self, $event ) = @_;
if ( $event->isa('TCOD::Event::Mouse') && !$event->isa('TCOD::Event::MouseWheel') ) {
$event->{tilexy} = [ $self->pixel_to_tile( @{ $event->{xy} } ) ];
}
if ( $event->isa('TCOD::Event::MouseMotion') ) {
my ( $xx, $yy ) = $self->pixel_to_tile(
$event->{xy}[0] - $event->{dxy}[0],
$event->{xy}[1] - $event->{dxy}[1],
);
$event->{tiledxy} = [
$event->{tilexy}[0] - $xx,
$event->{tilexy}[1] - $yy,
];
}
}
$ffi->attach( [ screen_pixel_to_tile_i => 'pixel_to_tile' ] => [qw( TCOD_context int* int* )] => 'TCOD_error' => sub {
my ( $xsub, $self, $x, $y ) = @_;
my ( $xx, $yy ) = ( $x, $y );
my $err = $xsub->( $self, \$xx, \$yy );
Carp::croak TCOD::get_error() if $err < 0;
( $xx, $yy );
});
$ffi->attach( [ screen_pixel_to_tile_d => 'pixel_to_subtile' ] => [qw( TCOD_context double* double* )] => 'TCOD_error' => sub {
my ( $xsub, $self, $x, $y ) = @_;
my ( $xx, $yy ) = ( $x, $y );
my $err = $xsub->( $self, \$xx, \$yy );
Carp::croak TCOD::get_error() if $err < 0;
( $xx, $yy );
});
$ffi->mangler( sub { shift } );
$ffi->attach( [ TCOD_context_delete => 'DESTROY' ] => ['TCOD_context'] => 'void' );
}
package TCOD::Line {
$ffi->mangler( sub { shift } );
$ffi->attach( [ TCOD_line => 'bresenham' ] => [qw( int int int int TCOD_line_listener )] => bool => sub {
my ( $xsub, $x1, $y1, $x2, $y2, $cb ) = @_;
my @out;
$cb //= sub { push @out, [ @_ ]; 1 };
my $closure = $ffi->closure( $cb );
$xsub->( $x1, $y1, $x2, $y2, $closure );
@out;
});
}
for (
# color values
[ BLACK => [ 0, 0, 0 ] ],
[ DARKEST_GREY => [ 31, 31, 31 ] ],
[ DARKER_GREY => [ 63, 63, 63 ] ],
[ DARK_GREY => [ 95, 95, 95 ] ],
[ GREY => [ 127, 127, 127 ] ],
[ LIGHT_GREY => [ 159, 159, 159 ] ],
[ LIGHTER_GREY => [ 191, 191, 191 ] ],
[ LIGHTEST_GREY => [ 223, 223, 223 ] ],
[ WHITE => [ 255, 255, 255 ] ],
[ DARKEST_SEPIA => [ 31, 24, 15 ] ],
[ DARKER_SEPIA => [ 63, 50, 31 ] ],
[ DARK_SEPIA => [ 94, 75, 47 ] ],
[ SEPIA => [ 127, 101, 63 ] ],
[ LIGHT_SEPIA => [ 158, 134, 100 ] ],
[ LIGHTER_SEPIA => [ 191, 171, 143 ] ],
[ LIGHTEST_SEPIA => [ 222, 211, 195 ] ],
# desaturated
[ DESATURATED_RED => [ 127, 63, 63 ] ],
[ DESATURATED_FLAME => [ 127, 79, 63 ] ],
[ DESATURATED_ORANGE => [ 127, 95, 63 ] ],
[ DESATURATED_AMBER => [ 127, 111, 63 ] ],
[ DESATURATED_YELLOW => [ 127, 127, 63 ] ],
[ DESATURATED_LIME => [ 111, 127, 63 ] ],
[ DESATURATED_CHARTREUSE => [ 95, 127, 63 ] ],
[ DESATURATED_GREEN => [ 63, 127, 63 ] ],
[ DESATURATED_SEA => [ 63, 127, 95 ] ],
[ DESATURATED_TURQUOISE => [ 63, 127, 111 ] ],
[ DESATURATED_CYAN => [ 63, 127, 127 ] ],
[ DESATURATED_SKY => [ 63, 111, 127 ] ],
[ DESATURATED_AZURE => [ 63, 95, 127 ] ],
[ DESATURATED_BLUE => [ 63, 63, 127 ] ],
[ DESATURATED_HAN => [ 79, 63, 127 ] ],
[ DESATURATED_VIOLET => [ 95, 63, 127 ] ],
[ DESATURATED_PURPLE => [ 111, 63, 127 ] ],
[ DESATURATED_FUCHSIA => [ 127, 63, 127 ] ],
[ DESATURATED_MAGENTA => [ 127, 63, 111 ] ],
[ DESATURATED_PINK => [ 127, 63, 95 ] ],
[ DESATURATED_CRIMSON => [ 127, 63, 79 ] ],
# lightest
[ LIGHTEST_RED => [ 255, 191, 191 ] ],
[ LIGHTEST_FLAME => [ 255, 207, 191 ] ],
[ LIGHTEST_ORANGE => [ 255, 223, 191 ] ],
[ LIGHTEST_AMBER => [ 255, 239, 191 ] ],
[ LIGHTEST_YELLOW => [ 255, 255, 191 ] ],
[ LIGHTEST_LIME => [ 239, 255, 191 ] ],
[ LIGHTEST_CHARTREUSE => [ 223, 255, 191 ] ],
[ LIGHTEST_GREEN => [ 191, 255, 191 ] ],
[ LIGHTEST_SEA => [ 191, 255, 223 ] ],
[ LIGHTEST_TURQUOISE => [ 191, 255, 239 ] ],
[ LIGHTEST_CYAN => [ 191, 255, 255 ] ],
[ LIGHTEST_SKY => [ 191, 239, 255 ] ],
[ LIGHTEST_AZURE => [ 191, 223, 255 ] ],
[ LIGHTEST_BLUE => [ 191, 191, 255 ] ],
[ LIGHTEST_HAN => [ 207, 191, 255 ] ],
[ LIGHTEST_VIOLET => [ 223, 191, 255 ] ],
[ LIGHTEST_PURPLE => [ 239, 191, 255 ] ],
[ LIGHTEST_FUCHSIA => [ 255, 191, 255 ] ],
[ LIGHTEST_MAGENTA => [ 255, 191, 239 ] ],
[ LIGHTEST_PINK => [ 255, 191, 223 ] ],
[ LIGHTEST_CRIMSON => [ 255, 191, 207 ] ],
# lighter
[ LIGHTER_RED => [ 255, 127, 127 ] ],
[ LIGHTER_FLAME => [ 255, 159, 127 ] ],
[ LIGHTER_ORANGE => [ 255, 191, 127 ] ],
[ LIGHTER_AMBER => [ 255, 223, 127 ] ],
[ LIGHTER_YELLOW => [ 255, 255, 127 ] ],
[ LIGHTER_LIME => [ 223, 255, 127 ] ],
[ LIGHTER_CHARTREUSE => [ 191, 255, 127 ] ],
[ LIGHTER_GREEN => [ 127, 255, 127 ] ],
[ LIGHTER_SEA => [ 127, 255, 191 ] ],
[ LIGHTER_TURQUOISE => [ 127, 255, 223 ] ],
[ LIGHTER_CYAN => [ 127, 255, 255 ] ],
[ LIGHTER_SKY => [ 127, 223, 255 ] ],
[ LIGHTER_AZURE => [ 127, 191, 255 ] ],
[ LIGHTER_BLUE => [ 127, 127, 255 ] ],
[ LIGHTER_HAN => [ 159, 127, 255 ] ],
[ LIGHTER_VIOLET => [ 191, 127, 255 ] ],
[ LIGHTER_PURPLE => [ 223, 127, 255 ] ],
[ LIGHTER_FUCHSIA => [ 255, 127, 255 ] ],
[ LIGHTER_MAGENTA => [ 255, 127, 223 ] ],
[ LIGHTER_PINK => [ 255, 127, 191 ] ],
[ LIGHTER_CRIMSON => [ 255, 127, 159 ] ],
# light
[ LIGHT_RED => [ 255, 63, 63 ] ],
[ LIGHT_FLAME => [ 255, 111, 63 ] ],
[ LIGHT_ORANGE => [ 255, 159, 63 ] ],
[ LIGHT_AMBER => [ 255, 207, 63 ] ],
[ LIGHT_YELLOW => [ 255, 255, 63 ] ],
[ LIGHT_LIME => [ 207, 255, 63 ] ],
[ LIGHT_CHARTREUSE => [ 159, 255, 63 ] ],
[ LIGHT_GREEN => [ 63, 255, 63 ] ],
[ LIGHT_SEA => [ 63, 255, 159 ] ],
[ LIGHT_TURQUOISE => [ 63, 255, 207 ] ],
[ LIGHT_CYAN => [ 63, 255, 255 ] ],
[ LIGHT_SKY => [ 63, 207, 255 ] ],
[ LIGHT_AZURE => [ 63, 159, 255 ] ],
[ LIGHT_BLUE => [ 63, 63, 255 ] ],
[ LIGHT_HAN => [ 111, 63, 255 ] ],
[ LIGHT_VIOLET => [ 159, 63, 255 ] ],
[ LIGHT_PURPLE => [ 207, 63, 255 ] ],
[ LIGHT_FUCHSIA => [ 255, 63, 255 ] ],
[ LIGHT_MAGENTA => [ 255, 63, 207 ] ],
[ LIGHT_PINK => [ 255, 63, 159 ] ],
[ LIGHT_CRIMSON => [ 255, 63, 111 ] ],
# normal
[ RED => [ 255, 0, 0 ] ],
[ FLAME => [ 255, 63, 0 ] ],
[ ORANGE => [ 255, 127, 0 ] ],
[ AMBER => [ 255, 191, 0 ] ],
[ YELLOW => [ 255, 255, 0 ] ],
[ LIME => [ 191, 255, 0 ] ],
[ CHARTREUSE => [ 127, 255, 0 ] ],
[ GREEN => [ 0, 255, 0 ] ],
[ SEA => [ 0, 255, 127 ] ],
[ TURQUOISE => [ 0, 255, 191 ] ],
[ CYAN => [ 0, 255, 255 ] ],
[ SKY => [ 0, 191, 255 ] ],
[ AZURE => [ 0, 127, 255 ] ],
[ BLUE => [ 0, 0, 255 ] ],
[ HAN => [ 63, 0, 255 ] ],
[ VIOLET => [ 127, 0, 255 ] ],
[ PURPLE => [ 191, 0, 255 ] ],
[ FUCHSIA => [ 255, 0, 255 ] ],
[ MAGENTA => [ 255, 0, 191 ] ],
[ PINK => [ 255, 0, 127 ] ],
[ CRIMSON => [ 255, 0, 63 ] ],
# dark
[ DARK_RED => [ 191, 0, 0 ] ],
[ DARK_FLAME => [ 191, 47, 0 ] ],
[ DARK_ORANGE => [ 191, 95, 0 ] ],
[ DARK_AMBER => [ 191, 143, 0 ] ],
[ DARK_YELLOW => [ 191, 191, 0 ] ],
[ DARK_LIME => [ 143, 191, 0 ] ],
[ DARK_CHARTREUSE => [ 95, 191, 0 ] ],
[ DARK_GREEN => [ 0, 191, 0 ] ],
[ DARK_SEA => [ 0, 191, 95 ] ],
[ DARK_TURQUOISE => [ 0, 191, 143 ] ],
[ DARK_CYAN => [ 0, 191, 191 ] ],
[ DARK_SKY => [ 0, 143, 191 ] ],
[ DARK_AZURE => [ 0, 95, 191 ] ],
[ DARK_BLUE => [ 0, 0, 191 ] ],
[ DARK_HAN => [ 47, 0, 191 ] ],
[ DARK_VIOLET => [ 95, 0, 191 ] ],
[ DARK_PURPLE => [ 143, 0, 191 ] ],
[ DARK_FUCHSIA => [ 191, 0, 191 ] ],
[ DARK_MAGENTA => [ 191, 0, 143 ] ],
[ DARK_PINK => [ 191, 0, 95 ] ],
[ DARK_CRIMSON => [ 191, 0, 47 ] ],
# darker
[ DARKER_RED => [ 127, 0, 0 ] ],
[ DARKER_FLAME => [ 127, 31, 0 ] ],
[ DARKER_ORANGE => [ 127, 63, 0 ] ],
[ DARKER_AMBER => [ 127, 95, 0 ] ],
[ DARKER_YELLOW => [ 127, 127, 0 ] ],
[ DARKER_LIME => [ 95, 127, 0 ] ],
[ DARKER_CHARTREUSE => [ 63, 127, 0 ] ],
[ DARKER_GREEN => [ 0, 127, 0 ] ],
[ DARKER_SEA => [ 0, 127, 63 ] ],
[ DARKER_TURQUOISE => [ 0, 127, 95 ] ],
[ DARKER_CYAN => [ 0, 127, 127 ] ],
[ DARKER_SKY => [ 0, 95, 127 ] ],
[ DARKER_AZURE => [ 0, 63, 127 ] ],
[ DARKER_BLUE => [ 0, 0, 127 ] ],
[ DARKER_HAN => [ 31, 0, 127 ] ],
[ DARKER_VIOLET => [ 63, 0, 127 ] ],
[ DARKER_PURPLE => [ 95, 0, 127 ] ],
[ DARKER_FUCHSIA => [ 127, 0, 127 ] ],
[ DARKER_MAGENTA => [ 127, 0, 95 ] ],
[ DARKER_PINK => [ 127, 0, 63 ] ],
[ DARKER_CRIMSON => [ 127, 0, 31 ] ],
# darkest
[ DARKEST_RED => [ 63, 0, 0 ] ],
[ DARKEST_FLAME => [ 63, 15, 0 ] ],
[ DARKEST_ORANGE => [ 63, 31, 0 ] ],
[ DARKEST_AMBER => [ 63, 47, 0 ] ],
[ DARKEST_YELLOW => [ 63, 63, 0 ] ],
[ DARKEST_LIME => [ 47, 63, 0 ] ],
[ DARKEST_CHARTREUSE => [ 31, 63, 0 ] ],
[ DARKEST_GREEN => [ 0, 63, 0 ] ],
[ DARKEST_SEA => [ 0, 63, 31 ] ],
[ DARKEST_TURQUOISE => [ 0, 63, 47 ] ],
[ DARKEST_CYAN => [ 0, 63, 63 ] ],
[ DARKEST_SKY => [ 0, 47, 63 ] ],
[ DARKEST_AZURE => [ 0, 31, 63 ] ],
[ DARKEST_BLUE => [ 0, 0, 63 ] ],
[ DARKEST_HAN => [ 15, 0, 63 ] ],
[ DARKEST_VIOLET => [ 31, 0, 63 ] ],
[ DARKEST_PURPLE => [ 47, 0, 63 ] ],
[ DARKEST_FUCHSIA => [ 63, 0, 63 ] ],
[ DARKEST_MAGENTA => [ 63, 0, 47 ] ],
[ DARKEST_PINK => [ 63, 0, 31 ] ],
[ DARKEST_CRIMSON => [ 63, 0, 15 ] ],
# metallic
[ BRASS => [ 191, 151, 96 ] ],
[ COPPER => [ 197, 136, 124 ] ],
[ GOLD => [ 229, 191, 0 ] ],
[ SILVER => [ 203, 203, 203 ] ],
# miscellaneous
[ CELADON => [ 172, 255, 175 ] ],
[ PINKEACH => [ 255, 159, 127 ] ],
) {
no strict 'refs';
my ( $name, @args ) = ( $_->[0], @{ $_->[1] } );
*{ __PACKAGE__ . '::' . $name } = sub () { TCOD::Color->new( @args ) };
}
# Macros
sub BKGND_ALPHA { BKGND_ALPH | int( $_[0] * 255 ) << 8 }
sub BKGND_ADDALPHA { BKGND_ADDA | int( $_[0] * 255 ) << 8 }
# Delete helper functions
delete $TCOD::{$_} for qw( enum );
1;