/*
* Seven Kingdoms: Ancient Adversaries
*
* Copyright 1997,1998 Enlight Software Ltd.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
*/
//Filename : OSPRITE.CPP
//Description : Object Sprite
#include
#include
#include
#include
#include
#include
#include
#include
//----------- Define static class member variables -----------//
short Sprite::abs_x1, Sprite::abs_y1; // the absolute postion, taking in account of sprite offset
short Sprite::abs_x2, Sprite::abs_y2;
//-------- Begin of function Sprite::Sprite --------//
Sprite::Sprite()
{
// preserve virtual pointer
memset( (char *)this + sizeof(void *), 0, sizeof(Sprite) - sizeof(void *) );
}
//--------- End of function Sprite::Sprite --------//
//-------- Begin of function Sprite::~Sprite --------//
Sprite::~Sprite()
{
deinit();
}
//--------- End of function Sprite::~Sprite --------//
//--------- Begin of function Sprite::init ---------//
//
void Sprite::init(short spriteId, short startXLoc, short startYLoc)
{
sprite_id = spriteId;
cur_x = startXLoc * ZOOM_LOC_WIDTH;
cur_y = startYLoc * ZOOM_LOC_HEIGHT;
go_x = next_x = cur_x;
go_y = next_y = cur_y;
cur_attack = 0;
cur_action = SPRITE_IDLE;
cur_dir = m.random(MAX_SPRITE_DIR_TYPE); // facing any of the eight directions
cur_frame = 1;
final_dir = cur_dir;
//----- clone vars from sprite_res for fast access -----//
sprite_info = sprite_res[sprite_id];
sprite_info->load_bitmap_res();
//------------- init other vars --------------//
remain_attack_delay = 0;
remain_frames_per_step = sprite_info->frames_per_step;
}
//----------- End of function Sprite::init -----------//
//--------- Begin of function Sprite::deinit ---------//
//
void Sprite::deinit()
{
if( sprite_id && cur_x >= 0 )
{
sprite_info->free_bitmap_res();
sprite_id = 0;
}
}
//----------- End of function Sprite::deinit -----------//
//--------- Begin of function Sprite::cur_sprite_frame ---------//
//
// Return the current frame of the sprite
//
SpriteFrame* Sprite::cur_sprite_frame(int *needMirror)
{
UCHAR curDir = display_dir();
if( needMirror)
*needMirror = need_mirror(curDir);
// do not update cur_dir as curDir
err_when(curDir<0 || curDir>=3*MAX_SPRITE_DIR_TYPE);
switch( cur_action )
{
case SPRITE_MOVE:
//### begin alex 14/4 ###//
case SPRITE_SHIP_EXTRA_MOVE:
//#### end alex 14/4 ####//
if( guard_count)
{
if( curDir >= MAX_SPRITE_DIR_TYPE)
{
err_here();
curDir %= MAX_SPRITE_DIR_TYPE;
}
return sprite_frame_res[sprite_info->guard_move_array[curDir].first_frame_recno+cur_frame-1];
}
else
return sprite_frame_res[sprite_info->move_array[curDir].first_frame_recno+cur_frame-1];
case SPRITE_ATTACK:
err_when(curDir<0 || curDir>=MAX_SPRITE_DIR_TYPE);
if( guard_count )
{
SpriteGuardStop *guardStopAction = sprite_info->guard_stop_array + curDir;
return sprite_frame_res[guardStopAction->first_frame_recno+
min(guard_count,guardStopAction->frame_count)-1];
}
else
return sprite_frame_res[sprite_info->attack_array[cur_attack][curDir].first_frame_recno+cur_frame-1];
case SPRITE_TURN:
case SPRITE_IDLE:
case SPRITE_WAIT:
// air unit needs it own stop frames to float on air
{
if( guard_count )
{
if( curDir >= MAX_SPRITE_DIR_TYPE)
{
// if the sprite is turning, adjust direction to next
if( turn_delay > 0)
curDir ++;
curDir %= MAX_SPRITE_DIR_TYPE;
}
SpriteGuardStop *guardStopAction = sprite_info->guard_stop_array + curDir;
return sprite_frame_res[guardStopAction->first_frame_recno+
min(guard_count,guardStopAction->frame_count)-1];
}
else
{
SpriteStop *stopAction= sprite_info->stop_array +curDir;
if(cur_frame > stopAction->frame_count)
return sprite_frame_res[stopAction->frame_recno]; // first frame
else // only few sprite has stopAction->frame_count > 1
return sprite_frame_res[stopAction->frame_recno+cur_frame-1];
}
}
case SPRITE_DIE:
if(sprite_info->die.first_frame_recno) // only if this sprite has dying frame
{
if( needMirror)
*needMirror = 0; // no need to mirror at any direction
return sprite_frame_res[sprite_info->die.first_frame_recno+cur_frame-1];
}
default:
return sprite_frame_res[sprite_info->move_array[curDir].first_frame_recno+cur_frame-1];
}
}
//----------- End of function Sprite::cur_sprite_frame -----------//
//--------- Begin of function Sprite::update_abs_pos ---------//
//
// Update the cur_width & cur_height vars of the sprite for later faster access.
//
// [SpriteFrame *] spriteFrame pointer to the current (default : NULL)
void Sprite::update_abs_pos(SpriteFrame *spriteFrame)
{
if( !spriteFrame )
spriteFrame = cur_sprite_frame();
abs_x1 = cur_x + spriteFrame->offset_x; // absolute position
abs_y1 = cur_y + spriteFrame->offset_y;
abs_x2 = abs_x1 + spriteFrame->width - 1;
abs_y2 = abs_y1 + spriteFrame->height - 1;
}
//----------- End of function Sprite::update_abs_pos -----------//
//--------- Begin of function Sprite::draw ---------//
//
void Sprite::draw()
{
//--------- draw sprite on the zoom window ---------//
int needMirror;
SpriteFrame* spriteFrame = cur_sprite_frame(&needMirror);
update_abs_pos(spriteFrame);
err_when( !sprite_info->res_bitmap.init_flag );
char* bitmapPtr = sprite_info->res_bitmap.read_imported(spriteFrame->bitmap_offset);
//-------- check if the sprite is inside the view area --------//
int x1 = abs_x1-World::view_top_x; // the sprite's position in the view window
if( x1 <= -spriteFrame->width || x1 >= ZOOM_WIDTH ) // out of the view area, not even a slight part of it appears in the view area
return;
int y1 = abs_y1-World::view_top_y;
if( y1 <= -spriteFrame->height || y1 >= ZOOM_HEIGHT )
return;
//------- decide which approach to use for displaying -----//
int x2 = abs_x2-World::view_top_x;
int y2 = abs_y2-World::view_top_y;
//---- only portion of the sprite is inside the view area ------//
if( x1 < 0 || x2 >= ZOOM_WIDTH || y1 < 0 || y2 >= ZOOM_HEIGHT )
{
if( needMirror ) // if this direction needed to be mirrored
{
if( !sprite_info->remap_bitmap_flag )
{
vga_back.put_bitmap_area_trans_decompress_hmirror( x1+ZOOM_X1, y1+ZOOM_Y1, bitmapPtr,
max(0,x1)-x1, max(0,y1)-y1, min(ZOOM_WIDTH-1,x2)-x1, min(ZOOM_HEIGHT-1,y2)-y1 );
}
else
{
vga_back.remap_bitmap_area_hmirror( x1+ZOOM_X1, y1+ZOOM_Y1, bitmapPtr,
vga.vga_color_table->get_table_array(),
max(0,x1)-x1, max(0,y1)-y1, min(ZOOM_WIDTH-1,x2)-x1, min(ZOOM_HEIGHT-1,y2)-y1 );
}
}
else
{
if( !sprite_info->remap_bitmap_flag )
{
vga_back.put_bitmap_area_trans_decompress( x1+ZOOM_X1, y1+ZOOM_Y1, bitmapPtr,
max(0,x1)-x1, max(0,y1)-y1, min(ZOOM_WIDTH-1,x2)-x1, min(ZOOM_HEIGHT-1,y2)-y1 );
}
else
{
vga_back.remap_bitmap_area( x1+ZOOM_X1, y1+ZOOM_Y1, bitmapPtr,
vga.vga_color_table->get_table_array(),
max(0,x1)-x1, max(0,y1)-y1, min(ZOOM_WIDTH-1,x2)-x1, min(ZOOM_HEIGHT-1,y2)-y1 );
}
}
}
//---- the whole sprite is inside the view area ------//
else
{
//------ mirror-bilting for certain directions ------//
if( needMirror ) // if this direction needed to be mirrored
{
if( !sprite_info->remap_bitmap_flag )
{
vga_back.put_bitmap_trans_decompress_hmirror( x1+ZOOM_X1, y1+ZOOM_Y1,
bitmapPtr );
}
else
{
vga_back.remap_bitmap_hmirror( x1+ZOOM_X1, y1+ZOOM_Y1,
bitmapPtr, vga.vga_color_table->get_table_array() );
}
}
else
{
if( !sprite_info->remap_bitmap_flag )
{
vga_back.put_bitmap_trans_decompress( x1+ZOOM_X1, y1+ZOOM_Y1,
bitmapPtr );
}
else
{
vga_back.remap_bitmap( x1+ZOOM_X1, y1+ZOOM_Y1,
bitmapPtr, vga.vga_color_table->get_table_array() );
}
}
}
}
//----------- End of function Sprite::draw -----------//
// ---------- Begin of function Sprite::display_dir ---------//
UCHAR Sprite::display_dir()
{
UCHAR curDir = cur_dir;
switch( sprite_info->turn_resolution)
{
case 0: // fall through
case 1:
curDir &= ~7; // direction less, remain upward or downard, but set to north
break;
case 8:
// cur_dir can be 0 to 3*MAX_SPRITE_DIR_TYPE-1, such as projectile;
// curDir = cur_dir;
break;
case 16:
err_when(curDir<0 || curDir>=MAX_SPRITE_DIR_TYPE);
// curDir should be (from due north, clockwisely) { 0,8,1,9,2,10,3,11,4,12,5,13,6,14,7,15 }
if( turn_delay <= -30)
{
curDir = ((curDir+7) & 7) + 8;
}
else if( turn_delay >= 30)
{
curDir += 8;
}
break;
case 24:
err_when(curDir<0 || curDir>=MAX_SPRITE_DIR_TYPE);
// curDir should be (from due north, clockwisely)
// { 0,8,16,1,9,17,2,10,18,3,11,19,4,12,20,5,13,21,6,14,22,7,15,23 }
if( turn_delay <= -20)
{
if( turn_delay <= -40)
curDir = ((curDir+7) & 7) + 8;
else
curDir = ((curDir+7) & 7) + 16;
}
else if( turn_delay >= 20)
{
if( turn_delay >= 40 )
curDir += 16;
else
curDir += 8;
}
break;
default:
err_here();
}
return curDir;
}
// ---------- End of function Sprite::display_dir ---------//
// ---------- Begin of function Sprite::need_mirror --------//
int Sprite::need_mirror(UCHAR dispDir)
{
return (dispDir < 8 || sprite_info->turn_resolution <= 8) ? (dispDir & 7) >= 5 : (dispDir & 7) >= 4;
}
// ---------- End of function Sprite::need_mirror --------//
// ---------- Begin of function Sprite::is_shealth --------//
int Sprite::is_shealth()
{
// if the visibility of location is just explored, consider shealth
return config.fog_of_war && world.get_loc(cur_x_loc(), cur_y_loc())->visibility() <= EXPLORED_VISIBILITY;
}
// ---------- End of function Sprite::is_shealth --------//