/*
* 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 : OSPRITE2.CPP
//Description : Object Sprite
#include
#include
#include
#include
#include
#include
#include
#ifdef DEBUG
static int check_dir1, check_dir2;
/*static int check_dir(int curX, int curY, int destX, int destY)
{
if( destX == curX )
{
if( destY > curY )
return DIR_S;
else
return DIR_N;
}
else if( destX < curX )
{
if( destY == curY )
return DIR_W;
else if( destY > curY )
return DIR_SW;
else
return DIR_NW;
}
else
{
if( destY == curY )
return DIR_E;
else if( destY > curY )
return DIR_SE;
else
return DIR_NE;
}
}*/
#endif
//--- Define no. of pixels per direction move (N, NE, E, SE, S, SW, W, NW) ---//
static short move_x_pixel_array[] = { 0, ZOOM_LOC_WIDTH, ZOOM_LOC_WIDTH, ZOOM_LOC_WIDTH, 0, -ZOOM_LOC_WIDTH, -ZOOM_LOC_WIDTH, -ZOOM_LOC_WIDTH };
static short move_y_pixel_array[] = { -ZOOM_LOC_HEIGHT, -ZOOM_LOC_HEIGHT, 0, ZOOM_LOC_HEIGHT, ZOOM_LOC_HEIGHT, ZOOM_LOC_HEIGHT, 0, -ZOOM_LOC_HEIGHT };
//--------- Begin of function Sprite::sprite_move --------//
//
// desX, desY - the destination coordination
//
void Sprite::sprite_move(int desX, int desY)
{
if( cur_action != SPRITE_MOVE )
{
cur_action = SPRITE_MOVE;
cur_frame = 1;
}
err_when(desX<0 || desY<0 || desX>=MAX_WORLD_X_LOC*ZOOM_LOC_WIDTH || desY>=MAX_WORLD_Y_LOC*ZOOM_LOC_HEIGHT);
#ifdef DEBUG
short vectorX=desX-next_x;
short vectorY=desY-next_y;
if(vectorX && vectorY) // both are non-zero
{
err_if(abs(vectorX) != abs(vectorY))
err_here();
}
#endif
go_x = desX;
go_y = desY;
//----------- determine the movement direction ---------//
set_dir(cur_x, cur_y, go_x, go_y);
//------ set the next tile to move towards -------//
int stepMagn = move_step_magn();
set_next(cur_x+stepMagn*move_x_pixel_array[final_dir], cur_y+stepMagn*move_y_pixel_array[final_dir], -stepMagn);
err_when(cur_action==SPRITE_MOVE && (cur_x!=next_x || cur_y!=next_y) &&
final_dir!=(check_dir1=get_dir(cur_x, cur_y, next_x, next_y)));
}
//---------- End of function Sprite::sprite_move --------//
//------ Begin of function Sprite::match_dir -------//
// return 1 if matched
// return 0 otherwise
//
int Sprite::match_dir()
{
err_when(final_dir<0 || final_dir>MAX_SPRITE_DIR_TYPE);
if(!sprite_info->need_turning)
{
cur_dir = final_dir;
err_when(turn_delay);
return 1;
}
static char turn_amount[10] = {60, 30, 20, 15, 12, 10, 9, 8, 7, 6};
#define HALF_SPRITE_DIR_TYPE MAX_SPRITE_DIR_TYPE/2
#define TURN_REQUIRE_AMOUNT 60
if(cur_dir==final_dir) // same direction
{
turn_delay = 0;
return 1;
}
char turnAmount = turn_amount[sprite_info->need_turning];
if((cur_dir+HALF_SPRITE_DIR_TYPE)%MAX_SPRITE_DIR_TYPE == final_dir) // opposite direction
{
cur_dir += (final_dir%2 ? 1 : -1);
cur_dir = cur_dir%MAX_SPRITE_DIR_TYPE;
}
else
{
err_when(cur_dir==final_dir || ((cur_dir+HALF_SPRITE_DIR_TYPE)%MAX_SPRITE_DIR_TYPE)==final_dir);
UCHAR dirDiff = (final_dir - cur_dir + MAX_SPRITE_DIR_TYPE) % MAX_SPRITE_DIR_TYPE;
if(dirDiff=TURN_REQUIRE_AMOUNT)
{
cur_dir = (cur_dir+1)%MAX_SPRITE_DIR_TYPE;
turn_delay = 0;
}
}
else
{
turn_delay -= turnAmount;
if(turn_delay<=-TURN_REQUIRE_AMOUNT)
{
cur_dir = (cur_dir-1+MAX_SPRITE_DIR_TYPE)%MAX_SPRITE_DIR_TYPE;
turn_delay = 0;
}
}
}
err_when(final_dir<0 || final_dir>MAX_SPRITE_DIR_TYPE);
err_when(cur_dir<0 || cur_dir>MAX_SPRITE_DIR_TYPE);
err_when(final_dir==cur_dir && turn_delay);
return (final_dir==cur_dir);
}
//---------- End of function Sprite::match_dir --------//
//------ Begin of function Sprite::set_dir -------//
void Sprite::set_dir(int curX, int curY, int destX, int destY)
{
UCHAR newDir = get_dir(curX, curY, destX, destY);
if(newDir != final_dir)
{
final_dir = newDir;
turn_delay = 0;
}
if(!sprite_info->need_turning)
cur_dir = final_dir;
else
match_dir(); // start turning
}
//---------- End of function Sprite::set_dir --------//
//------ Begin of function Sprite::set_dir -------//
void Sprite::set_dir(UCHAR newDir)
{
if(newDir != final_dir)
{
final_dir = newDir;
turn_delay = 0;
}
if(!sprite_info->need_turning)
cur_dir = final_dir;
else
match_dir();
}
//---------- End of function Sprite::set_dir --------//
//------ Begin of function Sprite::is_dir_correct -------//
int Sprite::is_dir_correct()
{
//### begin alex 1/8 ###//
//return (cur_dir==final_dir);
return (cur_dir==final_dir && turn_delay==0);
//#### end alex 1/8 ####//
}
//---------- End of function Sprite::is_dir_correct --------//
//------ Begin of function Sprite::get_dir -------//
//
// Compare the current and destination locations and
// return which direction the sprite should move towards or attack at.
//
int Sprite::get_dir(int curX, int curY, int destX, int destY)
{
unsigned xDiff( abs(destX - curX) );
unsigned yDiff( abs(destY - curY) );
unsigned squSize = max(xDiff, yDiff); // the size of the square we consider
if( destX == curX )
{
if( destY > curY )
return DIR_S;
else
return DIR_N;
}
else if( destX < curX )
{
// west side
if( destY > curY )
{ // south west quadrant
if(2*xDiff <= squSize)
return DIR_S;
else if(2*yDiff <= squSize)
return DIR_W;
else
return DIR_SW;
}
else
{ // north west quadrant
if(2*xDiff <= squSize)
return DIR_N;
else if(2*yDiff <= squSize)
return DIR_W;
else
return DIR_NW;
}
}
else // destX > curX
{
// east side
if( destY > curY )
{ // south east quadrant
if(2*xDiff <= squSize)
return DIR_S;
else if(2*yDiff <= squSize)
return DIR_E;
else
return DIR_SE;
}
else
{
// north east quadrant
if(2*xDiff <= squSize)
return DIR_N;
else if(2*yDiff <= squSize)
return DIR_E;
else
return DIR_NE;
}
}
return DIR_N;
}
//------- End of function Sprite::get_dir --------//
//--------- Begin of function Sprite::process_idle --------//
void Sprite::process_idle()
{
//-------- If it's an air unit --------//
// note : most land units do have have stop frame,
// so cur_sprite_stop->frame_count is 0
if( ++cur_frame > cur_sprite_stop()->frame_count )
cur_frame = 1;
}
//---------- End of function Sprite::process_idle ----------//
//--------- Begin of function Sprite::process_move --------//
void Sprite::process_move()
{
#ifdef DEBUG
int debugStepMagn1 = move_step_magn();
err_when(abs(cur_x-next_x)>ZOOM_LOC_WIDTH*debugStepMagn1 || abs(cur_y-next_y)>ZOOM_LOC_HEIGHT*debugStepMagn1);
#endif
//---- for some sprite (e.g. elephant), move one step per a few frames ----//
if( --remain_frames_per_step > 0 )
return;
else
remain_frames_per_step = sprite_info->frames_per_step;
err_when(cur_x < 0 || cur_y < 0 || cur_x >= ZOOM_X_PIXELS || cur_y >= ZOOM_Y_PIXELS);
err_when(cur_x-next_x!=0 && cur_y-next_y!=0 && abs(next_x-cur_x)!=abs(next_y-cur_y));
//----- if the sprite has reach the destintion ----//
if( cur_x==go_x && cur_y==go_y)
{
cur_action = SPRITE_IDLE;
set_next(cur_x, cur_y); //********* BUGHERE
#ifdef DEBUG
char h, w, blocked=0;
short x, y;
for(h=0, y=next_y_loc(); hloc_height&&!blocked; h++, y++)
{
for(w=0, x=next_x_loc(); wloc_width&&!blocked; w++, x++)
blocked = world.get_unit_recno(x,y,mobile_type) != sprite_recno;
}
err_if(blocked)
err_here();
#endif
cur_frame = 1;
return;
}
err_when(cur_x-next_x!=0 && cur_y-next_y!=0 && abs(next_x-cur_x)!=abs(next_y-cur_y));
//---- set the next tile the sprite will be moving towards ---//
static short vector_x_array[] = { 0, 1, 1, 1, 0, -1, -1, -1 }; // default vectors, temporary only
static short vector_y_array[] = { -1, -1, 0, 1, 1, 1, 0, -1 };
short stepX = sprite_info->speed;//abs(vectorX); //********* improve later
short stepY = sprite_info->speed;//abs(vectorY);
if( next_x != go_x || next_y != go_y ) // if next_x==go_x & next_y==go_y, reach destination already, don't move further.
{
if( abs(cur_x-next_x) <= stepX && abs(cur_y-next_y) <= stepY )
{
int stepMagn = move_step_magn();
set_next(next_x+stepMagn*move_x_pixel_array[final_dir], next_y+stepMagn*move_y_pixel_array[final_dir], -stepMagn);
}
}
err_when(cur_x-next_x!=0 && cur_y-next_y!=0 && abs(next_x-cur_x)!=abs(next_y-cur_y));
//---- if the is blocked, cur_action is changed to SPRITE_WAIT, return now ----//
if( cur_action != SPRITE_MOVE )
return;
//-------------- update position -----------------//
//
// If it gets very close to the destination, fit it
// to the destination ingoring the normal vector.
//
//------------------------------------------------//
short vectorX = vector_x_array[final_dir] * sprite_info->speed; // cur_dir may be changed in the above set_next() call
short vectorY = vector_y_array[final_dir] * sprite_info->speed;
if( abs(cur_x-go_x) <= stepX )
cur_x = go_x;
else
cur_x += vectorX;
if( abs(cur_y-go_y) <= stepY )
cur_y = go_y;
else
cur_y += vectorY;
err_when(cur_x-next_x!=0 && cur_y-next_y!=0 && abs(next_x-cur_x)!=abs(next_y-cur_y)); // is a better checking if speed in all direction is equal
#ifdef DEBUG
int debugStepMagn2 = move_step_magn();
err_when(abs(cur_x-next_x)>ZOOM_LOC_WIDTH*debugStepMagn1 || abs(cur_y-next_y)>ZOOM_LOC_HEIGHT*debugStepMagn1);
#endif
//-------- check boundary ---------//
err_when(cur_x < 0 || cur_y < 0 || cur_x >= ZOOM_X_PIXELS || cur_y >= ZOOM_Y_PIXELS);
//------- update frame id. --------//
if( ++cur_frame > cur_sprite_move()->frame_count )
cur_frame = 1;
}
//---------- End of function Sprite::process_move ----------//
//--------- Begin of function Sprite::process_attack --------//
//
// Return: 1 - if the sprite just finished its current attack
// 0 - other statuses - either waiting for next attack
// or is attacking.
//
int Sprite::process_attack()
{
if(remain_attack_delay && cur_frame==1)
return 0;
//------- next attack frame --------//
SpriteAttack* spriteAttack = cur_sprite_attack();
// ------ sound effect --------//
char action[] = "A1";
action[1] += cur_attack;
se_res.sound(cur_x_loc(), cur_y_loc(), cur_frame, 'S', sprite_id, action);
if( ++cur_frame > spriteAttack->frame_count )
{
((Unit *)this)->cycle_eqv_attack(); // assume only unit can attack
cur_frame = 1;
return 1;
}
return 0;
}
//---------- End of function Sprite::process_attack ----------//
//--------- Begin of function Sprite::process_turn --------//
void Sprite::process_turn()
{
err_when(!sprite_info->need_turning);
match_dir();
}
//---------- End of function Sprite::process_turn ----------//
//--------- Begin of function Sprite::process_die --------//
//
// return : 1 - dying animation completes.
// 0 - still dying
//
int Sprite::process_die()
{
//--------- next frame ---------//
if( sys.frame_count%3 == 0 )
{
se_res.sound(cur_x_loc(), cur_y_loc(), cur_frame, 'S',sprite_id,"DIE");
if( ++cur_frame > sprite_info->die.frame_count )
return 1;
}
return 0;
}
//---------- End of function Sprite::process_die ----------//
//--------- Begin of function Sprite::set_remain_attack_delay --------//
void Sprite::set_remain_attack_delay()
{
Unit* unitPtr = (Unit*) this; //**BUGHERE, assuming all Sprite that call process_attack() are Unit
remain_attack_delay = unitPtr->attack_info_array[unitPtr->cur_attack].attack_delay;
}
//---------- End of function Sprite::set_remain_attack_delay ----------//
//--------- Begin of function Sprite::move_step_magn --------//
// return the magnitude of each step in moving
// i.e 1 for unit_land, 2 for unit_sea and unit_air
//
int Sprite::move_step_magn()
{
return (mobile_type==UNIT_LAND) ? 1 : 2;
}
//---------- End of function Sprite::move_step_magn ----------//
//--------- Begin of function Sprite::set_next --------//
//
void Sprite::set_next(int nextX, int nextY, int para, int blockedChecked)
{
err_here(); //**BUGHERE, for now, we don't have any pure Sprites, all functions should call Unit::set_next() instead of Sprite::set_next()
next_x = nextX;
next_y = nextY;
}
//---------- End of function Sprite::set_next ----------//
//--------- Begin of function Sprite::set_guard_on --------//
//
void Sprite::set_guard_on()
{
err_when( !sprite_info->can_stand_guard() && !sprite_info->can_move_guard());
guard_count = 1;
}
//--------- End of function Sprite::set_guard_on --------//
//--------- Begin of function Sprite::set_guard_off --------//
//
void Sprite::set_guard_off()
{
guard_count = 0;
}
//--------- End of function Sprite::set_guard_off --------//