##########################################################################
# This file is part of Vacuum Magic
# Copyright (C) 2008 by UPi <upi at sourceforge.net>
##########################################################################

use strict;

package Sprite;
package main;


sub InitSprites {
  %Sprites = (
    'pangguy_fire'    => new Sprite('enemy', 256, 64,  48,  64 ),
    'pangguy_fall'    => new Sprite('enemy', 192, 64, -64,  64 ),
    'pangguy_walk'    => SpriteSequence('enemy', [0, 64, 128, 64], 64, -64, 64),
    
    'mothercloud'     => SpriteSequence('enemy', [0, 96, 192], 384, 96, 48 ),
    'horrormoon'      => SpriteSequence('enemy', [0, 112, 224, 336], 224, 112, 96 ),
    'bossvitality'    => [ map { new Sprite('bossvitality', $_ % 10 * 51, int($_ / 10) * 51, 50, 50 ) } ( 0 .. 45) ],
    swirl             => new Sprite('swirl', 0, 0, 128, 128),
    engineexhaust     => new Sprite('enemy', 384, 528, 107, 26),
    laser             => new Sprite('enemy', 304, 160, 64, 32),
    food              => new Sprite('enemy', 112, 848, 64, 64),
    poison            => new Sprite('enemy', 176, 848, 64, 64),
    food2             => new Sprite('enemy', 240, 848, 64, 64),
    poison2           => new Sprite('enemy', 304, 848, 64, 64),
    flameshield       => new OffsetSprite('bonus', 416, 48, 96, 64, 16, 16, 70, 39),
    particle          => new Sprite('particle', 0, 0, 32, 32),
    koules            => new Sprite('enemy', 368, 848, 64, 64),
    papakoules        => new Sprite('papakoules', 0, 0, 256, 256),
  );

  $Sprites{reddragon_fly}->[0] = new OffsetSprite('reddragon', 0, 0, 161, 156, 96, 64, 5, 31);
  $Sprites{reddragon_fly}->[1] = new OffsetSprite('reddragon', 160, 0, 172, 91, 96, 64, 19, 29);
  $Sprites{reddragon_fly}->[2] = new OffsetSprite('reddragon', 352, 0, 140, 107, 96, 64, 7, 43);
  $Sprites{reddragon_fly}->[3] = new OffsetSprite('reddragon', 512, 0, 143, 112, 96, 64, 8, 46);
  $Sprites{reddragon_fly}->[4] = new OffsetSprite('reddragon', 674, 0, 158, 98, 96, 64, 15, 27);
  $Sprites{reddragon_fly}->[5] = new OffsetSprite('reddragon', 0, 160, 167, 138, 96, 64, -2, 29);
  $Sprites{reddragon_attack1}->[0] = new OffsetSprite('reddragon', 192, 96, 161, 157, 96, 64, 5, 32);
  $Sprites{reddragon_attack1}->[1] = new OffsetSprite('reddragon', 832, 0, 172, 96, 96, 64, 19, 30);
  $Sprites{reddragon_attack1}->[2] = new OffsetSprite('reddragon', 352, 128, 205, 112, 96, 64, 72, 50);
  $Sprites{reddragon_attack1}->[3] = new OffsetSprite('reddragon', 576, 128, 248, 116, 96, 64, 107, 52);
  $Sprites{reddragon_attack1}->[4] = new OffsetSprite('reddragon', 0, 320, 261, 120, 96, 64, 112, 50);
  $Sprites{reddragon_attack1}->[5] = new OffsetSprite('reddragon', 288, 256, 286, 158, 96, 64, 117, 49);
  $Sprites{reddragon_attack1}->[6] = new OffsetSprite('reddragon', 576, 256, 262, 173, 96, 64, 106, 48);
  $Sprites{reddragon_attack1}->[7] = new OffsetSprite('reddragon', 832, 96, 172, 92, 96, 64, 19, 30);
  $Sprites{reddragon_attack1}->[8] = new OffsetSprite('reddragon', 864, 192, 140, 107, 96, 64, 7, 43);
  $Sprites{reddragon_attack1}->[9] = new OffsetSprite('reddragon', 864, 320, 149, 112, 96, 64, 8, 46);
  $Sprites{reddragon_attack1}->[10] = new OffsetSprite('reddragon', 0, 448, 164, 100, 96, 64, 15, 29);
  $Sprites{reddragon_attack1}->[11] = new OffsetSprite('reddragon', 192, 448, 167, 139, 96, 64, -2, 30);
  $Sprites{reddragon_attack2}->[0] = new OffsetSprite('reddragon', 383, 416, 161, 157, 96, 64, 5, 32);
  $Sprites{reddragon_attack2}->[1] = new OffsetSprite('reddragon', 544, 448, 208, 166, 96, 64, 55, 54);
  $Sprites{reddragon_attack2}->[2] = new OffsetSprite('reddragon', 768, 448, 206, 166, 96, 64, 58, 53);
  $Sprites{reddragon_attack2}->[3] = new OffsetSprite('reddragon', 0, 608, 205, 168, 96, 64, 55, 55);
  $Sprites{reddragon_attack2}->[4] = new OffsetSprite('reddragon', 224, 608, 206, 170, 96, 64, 53, 56);
  $Sprites{reddragon_attack2}->[5] = new OffsetSprite('reddragon', 448, 640, 225, 161, 96, 64, 56, 51);
  $Sprites{reddragon_hit1}->[0] = new OffsetSprite('reddragon', 672, 640, 174, 167, 96, 64, -11, 33);
  $Sprites{reddragon_die1}->[0] = new OffsetSprite('reddragon', 863, 640, 161, 157, 96, 64, -9, 23);
  $Sprites{reddragon_die1}->[1] = new OffsetSprite('reddragon', 0, 800, 172, 92, 96, 64, 5, 25);
  $Sprites{reddragon_die1}->[2] = new OffsetSprite('reddragon', 192, 800, 140, 107, 96, 64, -7, 44);
  $Sprites{reddragon_die1}->[3] = new OffsetSprite('reddragon', 352, 832, 149, 112, 96, 64, -6, 52);
  $Sprites{reddragon_die1}->[4] = new OffsetSprite('reddragon', 512, 832, 149, 112, 96, 64, -6, 57);

  $Sprites{bluedragon_fly}->[0] = new OffsetSprite('bluedragon', 326, 364, 161, 156, 96, 64, 5, 31);
  $Sprites{bluedragon_fly}->[1] = new OffsetSprite('bluedragon', 348, 907, 172, 91, 96, 64, 19, 29);
  $Sprites{bluedragon_fly}->[2] = new OffsetSprite('bluedragon', 303, 793, 140, 107, 96, 64, 7, 43);
  $Sprites{bluedragon_fly}->[3] = new OffsetSprite('bluedragon', 0, 793, 143, 112, 96, 64, 8, 46);
  $Sprites{bluedragon_fly}->[4] = new OffsetSprite('bluedragon', 694, 793, 158, 98, 96, 64, 15, 27);
  $Sprites{bluedragon_fly}->[5] = new OffsetSprite('bluedragon', 793, 364, 167, 138, 96, 64, -2, 29);
  $Sprites{bluedragon_attack1_info_hit}->[0] = new OffsetSprite('bluedragon', 962, 364, 50, 57, 96, 64, -69, 27);
  $Sprites{bluedragon_attack1_info_hit}->[1] = new OffsetSprite('bluedragon', 854, 793, 96, 98, 96, 64, -35, 33);
  $Sprites{bluedragon_attack1_info_hit}->[2] = new OffsetSprite('bluedragon', 587, 793, 105, 104, 96, 64, -25, 31);
  $Sprites{bluedragon_attack1}->[0] = new OffsetSprite('bluedragon', 821, 195, 161, 157, 96, 64, 5, 32);
  $Sprites{bluedragon_attack1}->[1] = new OffsetSprite('bluedragon', 0, 907, 172, 96, 96, 64, 19, 30);
  $Sprites{bluedragon_attack1}->[2] = new OffsetSprite('bluedragon', 145, 793, 156, 108, 96, 64, 23, 46);
  $Sprites{bluedragon_attack1}->[3] = new OffsetSprite('bluedragon', 274, 660, 246, 113, 96, 64, 105, 49);
  $Sprites{bluedragon_attack1}->[4] = new OffsetSprite('bluedragon', 0, 660, 272, 131, 96, 64, 123, 61);
  $Sprites{bluedragon_attack1}->[5] = new OffsetSprite('bluedragon', 297, 0, 301, 173, 96, 64, 132, 64);
  $Sprites{bluedragon_attack1}->[6] = new OffsetSprite('bluedragon', 0, 0, 295, 193, 96, 64, 139, 68);
  $Sprites{bluedragon_attack1}->[7] = new OffsetSprite('bluedragon', 547, 523, 289, 133, 96, 64, 136, 71);
  $Sprites{bluedragon_attack1}->[8] = new OffsetSprite('bluedragon', 0, 523, 269, 135, 96, 64, 136, 71);
  $Sprites{bluedragon_attack1}->[9] = new OffsetSprite('bluedragon', 271, 523, 274, 134, 96, 64, 133, 68);
  $Sprites{bluedragon_attack1}->[10] = new OffsetSprite('bluedragon', 522, 660, 282, 112, 96, 64, 133, 41);
  $Sprites{bluedragon_attack1}->[11] = new OffsetSprite('bluedragon', 489, 364, 302, 150, 96, 64, 133, 41);
  $Sprites{bluedragon_attack2}->[0] = new OffsetSprite('bluedragon', 0, 364, 161, 157, 96, 64, 5, 32);
  $Sprites{bluedragon_attack2}->[1] = new OffsetSprite('bluedragon', 176, 195, 208, 166, 96, 64, 55, 54);
  $Sprites{bluedragon_attack2}->[2] = new OffsetSprite('bluedragon', 386, 195, 206, 166, 96, 64, 58, 53);
  $Sprites{bluedragon_attack2}->[3] = new OffsetSprite('bluedragon', 808, 0, 205, 168, 96, 64, 55, 55);
  $Sprites{bluedragon_attack2}->[4] = new OffsetSprite('bluedragon', 600, 0, 206, 170, 96, 64, 53, 56);
  $Sprites{bluedragon_attack2}->[5] = new OffsetSprite('bluedragon', 594, 195, 225, 161, 96, 64, 56, 51);
  $Sprites{bluedragon_hit1}->[0] = new OffsetSprite('bluedragon', 0, 195, 174, 167, 96, 64, -11, 33);
  $Sprites{bluedragon_die1}->[0] = new OffsetSprite('bluedragon', 163, 364, 161, 157, 96, 64, -9, 23);
  $Sprites{bluedragon_die1}->[1] = new OffsetSprite('bluedragon', 174, 907, 172, 92, 96, 64, 5, 25);
  $Sprites{bluedragon_die1}->[2] = new OffsetSprite('bluedragon', 445, 793, 140, 107, 96, 64, -7, 44);
  $Sprites{bluedragon_die1}->[3] = new OffsetSprite('bluedragon', 838, 523, 149, 112, 96, 64, -6, 52);
  $Sprites{bluedragon_die1}->[4] = new OffsetSprite('bluedragon', 806, 660, 149, 112, 96, 64, -6, 57);

  $Sprites{bluedragon_attack2_info_hit}->[0] = new OffsetSprite('bluedragon', 609, 907, 76, 80, 2, 2, 38, 43);
  $Sprites{bluedragon_attack2_info_hit}->[1] = new OffsetSprite('bluedragon', 522, 907, 85, 87, 2, 2, 43, 45);
  $Sprites{bluedragon_attack2_info_hit}->[2] = new OffsetSprite('bluedragon', 687, 907, 83, 75, 2, 2, 29, 41);
  $Sprites{bluedragon_attack2_info_hit}->[3] = new OffsetSprite('bluedragon', 952, 793, 71, 72, 2, 2, 34, 44);
  
  $Sprites{robowitch}->[0] = new Sprite('enemy', 0, 432, 112, 64);
  $Sprites{robowitch}->[1] = new Sprite('enemy', 112, 432, 112, 64);
  $Sprites{robowitch}->[2] = new Sprite('enemy', 224, 448, 64, 48);
  $Sprites{robowitch}->[3] = new Sprite('enemy', 288, 432, 32, 32);
  $Sprites{rubble} = &SpriteSequence('enemy', 336, [432, 448, 464, 480], 16, 16);
  $Sprites{firetrail} = &SpriteSequence('enemy', 352, [432, 448, 464, 480], 48, 16);
  
  $Sprites{eaten}->[0] = new OffsetSprite('enemy',   0, 560,  20, 34, 128, 80, -50, -50);
  $Sprites{eaten}->[1] = new OffsetSprite('enemy',   0, 601,  31, 39, 128, 80, -44, -45);
  $Sprites{eaten}->[2] = new OffsetSprite('enemy',  32, 560,  71, 79, 128, 80, -34,   3);
  $Sprites{eaten}->[3] = new OffsetSprite('enemy', 112, 560, 117, 81, 128, 80,  -7,   3);
  $Sprites{eaten}->[4] = new OffsetSprite('enemy', 240, 560, 125, 85, 128, 80,  -1,   6);
  $Sprites{eaten}->[5] = new OffsetSprite('enemy', 368, 560, 127, 79, 128, 80,   0,   0);
  $Sprites{eaten}->[6] = new OffsetSprite('enemy',   0, 640, 118, 67, 128, 80,  -3,  -2);
  
  $Sprites{slurped}->[0] = new OffsetSprite('enemy', 128, 640, 52, 51, 52, 52,   0,  0);  # 6230601
  $Sprites{slurped}->[1] = new OffsetSprite('enemy', 192, 640, 81, 52, 52, 52,  15, -4);
  $Sprites{slurped}->[2] = new OffsetSprite('enemy', 272, 640, 94, 45, 52, 52,  16, -2);
  $Sprites{slurped}->[3] = new OffsetSprite('enemy', 368, 640, 85, 50, 52, 52,   7, -2);
  
  $Sprites{seeker_move}->[0] = new OffsetSprite('enemy',   0, 720, 36, 36,  36, 36,  0, 0 );
  $Sprites{seeker_move}->[1] = new OffsetSprite('enemy',  38, 720, 36, 36,  36, 36,  0, 0 );
  $Sprites{seeker_move}->[2] = new OffsetSprite('enemy',  76, 720, 36, 36,  36, 36,  0, 0 );
  $Sprites{seeker_move}->[3] = new OffsetSprite('enemy', 114, 720, 36, 36,  36, 36,  0, 0 );
  $Sprites{flare} = new Sprite('enemy', 320, 496, 64, 64);
 
  $Sprites{spiderweb} = new Sprite('enemy', 304, 128, 64, 32);
  $Sprites{spider}->[0] = new Sprite('enemy', 369, 128, 36, 32);
  $Sprites{spider}->[1] = new Sprite('enemy', 406, 128, 36, 32);
  
  $Sprites{icedart} = new Sprite('enemy', 448, 160, 51, 128);
  $Sprites{icecube} = new Sprite('enemy', 0, 848, 112, 96);
  
  $Sprites{bomb} = new Sprite('enemy', 448, 288, 48, 48);
  $Sprites{bombspark}->[0] = new OffsetSprite('enemy', 416, 320, 32, 16,  16, 16, 6, 0);
  $Sprites{bombspark}->[1] = new OffsetSprite('enemy', 416, 336, 16, 16,  16, 16, 0, 0);
  $Sprites{bombspark}->[2] = new NullSprite();
#  my ($textureName, $tx, $ty, $tw, $th, $logicalWidth, $logicalHeight, $offsetX, $offsetY) = @_;
  
#   $offsetX =   $imageData{x} + $spriteOffsetX;                        0 =  25       -25
#   $offsetY = - $imageData{y} + $imageData{th} + $spriteOffsetY;       0 = -56+51     +5
  
  $Sprites{bonusbox} = new Sprite('enemy', 384, 332, 32, 20);
  $Sprites{wings} = &SpriteSequence('enemy', 400, [432, 448, 464, 480], 16, 16);
  $Sprites{bonusitem}->{largeslurp} = new Sprite('bonus', 192, 176, 32, 32);
  $Sprites{bonusitem}->{slurppower} = new Sprite('bonus', 224, 176, 32, 32);
  $Sprites{bonusitem}->{spitpower} = new Sprite('bonus', 256, 176, 32, 32);
  $Sprites{bonusitem}->{speed} = new Sprite('bonus', 288, 176, 32, 32);
  $Sprites{bonusitem}->{life} = new Sprite('bonus', 320, 176, 32, 32);
  $Sprites{bonusitem}->{50000} = new Sprite('bonus', 352, 176, 32, 32);
  $Sprites{bonusitem}->{timeeffect} = new Sprite('bonus', 384, 176, 32, 32);
  $Sprites{bonusitem}->{fireshield} = new Sprite('bonus', 416, 176, 32, 32);
  
  $Sprites{bullseye} = new Sprite('enemy', 432, 432, 80, 80);
  $Sprites{rock1}->[0] = new Sprite('rock', 0, 0, 48, 48);
  $Sprites{rock1}->[1] = new Sprite('rock', 48, 0, 48, 48);
  $Sprites{rock1}->[2] = new Sprite('rock', 96, 0, 48, 48);
  $Sprites{rock1}->[3] = new Sprite('rock', 144, 0, 48, 48);
  $Sprites{rock1}->[4] = new Sprite('rock', 0, 48, 48, 48);
  $Sprites{rock1}->[5] = new Sprite('rock', 48, 48, 48, 48);
  $Sprites{rock1}->[6] = new Sprite('rock', 96, 48, 48, 48);
  $Sprites{rock1}->[7] = new Sprite('rock', 144, 48, 48, 48);
  
  $Sprites{pangball} = new Sprite('enemy', 0, 768, 96, 80);
  $Sprites{pangpop} = &SpriteSequence('enemy', [96, 192, 288, 384], 768, 96, 80);
  
  $Sprites{bomber}->[0] = new OffsetSprite('bomber', 0,   0, 256, 73, 256, 50, 0,  9 );
  $Sprites{bomber}->[1] = new OffsetSprite('bomber', 0,  74, 256, 66, 256, 50, 0,  2 );
  $Sprites{bomber}->[2] = new OffsetSprite('bomber', 0, 141, 256, 61, 256, 50, 0,  0 );
  $Sprites{bomber}->[3] = new OffsetSprite('bomber', 0, 203, 256, 64, 256, 50, 0,  6 );
  $Sprites{bomber}->[4] = new OffsetSprite('bomber', 0, 268, 256, 68, 256, 50, 0, 10 );
  $Sprites{bomber}->[5] = new OffsetSprite('bomber', 0, 337, 256, 77, 256, 50, 0, 18 );
  $Sprites{bomber}->[6] = new OffsetSprite('bomber', 0, 415, 256, 86, 256, 50, 0, 21 );
}

sub SpriteSequence {
  my ($textureName, $xSequence, $ySequence, $wSequence, $hSequence) = @_;
  my ($i, @result, $x, $y, $w, $h);
  
  Carp::confess("SpriteSequence: not a sequence")  unless ref($xSequence) || ref($ySequence) || ref($wSequence) || ref($hSequence);
  for ($i = 0; 1; ++$i) {
    $x = (ref $xSequence) ? $xSequence->[$i] : $xSequence;
    $y = (ref $ySequence) ? $ySequence->[$i] : $ySequence;
    $w = (ref $wSequence) ? $wSequence->[$i] : $wSequence;
    $h = (ref $hSequence) ? $hSequence->[$i] : $hSequence;
    last  unless defined($x) && defined($y) && defined($w) && defined($h);
    push @result, new Sprite($textureName, $x, $y, $w, $h);
  }
  return \@result;
}

# ========================================================================
package Sprite;
# ========================================================================

sub new {
  my $class = shift;
  my ($textureName, $tx, $ty, $tw, $th) = @_;
  my ($textureObject, $self);
  
  $textureObject = $::Textures{$textureName};
  die "Missing texture: $textureName"  unless $textureObject;
  $self = {
    texture => $textureObject,
    tx => $tx,
    ty => $ty,
    tw => $tw,
    th => $th,
    textureParams => [ $tx, $ty, $tw, $th ],
  };
  bless $self, $class;
}

sub Blit {
  my $self = shift;
  # my ($screenX, $screenY, $screenW, $screenH) = @_;
  $self->{texture}->Blit(@_, @{$self->{textureParams}});
}

sub RotoBlit {
  my ($self, $screenX, $screenY, $screenW, $screenH, $rotation) = @_;
  
  ::glLoadIdentity();
  ::glTranslate($screenX + $screenW/2, $screenY + $screenH/2, 0);
  ::glRotate($rotation, 0, 0, 1);
  $self->{texture}->Blit(-$screenW/2, -$screenH/2, $screenW, $screenH, @{$self->{textureParams}});
  ::glLoadIdentity();
}


# ========================================================================
package OffsetSprite;
# ========================================================================

=comment

Imagine a sprite that has "spokes" that are larger than the logical width
of the game object. In this case the texture size is larger than the
logical object size. That is what OffsetSprite is for.

                            texture width
                          ------------------
                        |         X
                        |         X
          texture height|         X
                        |         X
                        |       XXXXX        |
                        | XXXXXXXXXXXXXXXXXX |logical height      
                        |       XXXXX        |
                        |         X        |
                        |         X        |offsetY
                        |         X        |
                                -----
                                lwidth
                          ------
                          offsetX

=cut

sub new {
  my $class = shift;
  my ($textureName, $tx, $ty, $tw, $th, $logicalWidth, $logicalHeight, $offsetX, $offsetY) = @_;
  my ($textureObject, $self);
  
  $textureObject = $::Textures{$textureName};
  die  unless $textureObject;
  $self = {
    texture => $textureObject,
    tx => $tx,
    ty => $ty,
    tw => $tw,
    th => $th,
    lw => $logicalWidth,
    lh => $logicalHeight,
    offsetX => $offsetX,
    offsetY => $offsetY,
    textureParams => [ $tx, $ty, $tw, $th ],
    mirroredTextureParams => [ $tx, $ty, -$tw, $th ],
  };
  bless $self, $class;
}

sub Blit {
  my ($self, $screenX, $screenY, $screenW, $screenH) = @_;
  my ($zoomX, $zoomY);
  
  $zoomX = $screenW / $self->{lw};
  $zoomY = $screenH / $self->{lh};
  if ($screenW >= 0) {
    $self->{texture}->Blit(
      $screenX - $self->{offsetX} * $zoomX, $screenY - $self->{offsetY} * $zoomY,
      $self->{tw} * $zoomX, $self->{th} * $zoomY,
      @{$self->{textureParams}}
    );
  } else {
    $self->{texture}->Blit(
      $screenX + ($self->{tw} - $self->{lw} - $self->{offsetX}) * $zoomX, $screenY - $self->{offsetY} * $zoomY,
      -$self->{tw} * $zoomX, $self->{th} * $zoomY,
      @{$self->{mirroredTextureParams}} 
    );
  }
}

sub RotoBlit {
  my ($self, $screenX, $screenY, $screenW, $screenH, $rotation) = @_;
  
  ::glLoadIdentity();
  ::glTranslate($screenX + $screenW/2, $screenY + $screenH/2, 0);
  ::glRotate($rotation, 0, 0, 1);
  $self->Blit(-$screenW/2, -$screenH/2, $screenW, $screenH);
  ::glLoadIdentity();
}


# ========================================================================
package NullSprite;
# ========================================================================

sub new {
  my $class = shift;
  my $self = {};
  bless $self, $class;
}

sub Blit {}

sub RotoBlit {}

1;
