/*
* 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 : OU_MARI.CPP
// Description : sea unit
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef DEBUG2
#include
#endif
//------- Define constant ----------//
const int WAVE_CYCLE = 8;
//------- Begin of function UnitMarine::UnitMarine -------//
UnitMarine::UnitMarine()
{
menu_mode = 0;
extra_move_in_beach = NO_EXTRA_MOVE;
in_beach = 0;
selected_unit_id = 0;
//------- transporting units vars ---------//
unit_count = 0;
memset(unit_recno_array, 0, sizeof(short)*MAX_UNIT_IN_SHIP);
//------- transporting goods vars ---------//
memset( stop_array, 0, MAX_STOP_FOR_SHIP * sizeof(ShipStop) );
journey_status = ON_WAY_TO_FIRM;
dest_stop_id = 0;
stop_defined_num = 0;
wait_count = 0;
stop_x_loc = 0;
stop_y_loc = 0;
memset(raw_qty_array, 0, sizeof(short)*MAX_RAW);
memset(product_raw_qty_array, 0, sizeof(short)*MAX_PRODUCT);
auto_mode = 1; // there should be no button to toggle it if the ship is only for trading
cur_firm_recno = 0;
}
//------- End of function UnitMarine::UnitMarine -------//
//------- Begin of function UnitMarine::~UnitMarine -------//
UnitMarine::~UnitMarine()
{
//-------- del those units in the ship -------//
for(int i=0; iget_sub_sprite_info(1)->sprite_id;
splash.init( spriteId, cur_x_loc(), cur_y_loc() );
splash.cur_frame = 1;
//------- set carry_goods_capacity -------//
carry_goods_capacity = unit_res[unitId]->carry_goods_capacity;
//------- set menu mode of the unit -------//
UnitInfo* unitInfo = unit_res[unitId];
if( unitInfo->carry_unit_capacity==0 && unitInfo->carry_goods_capacity>0 ) // if this ship only carries goods
menu_mode = SHIP_MENU_GOODS;
else
menu_mode = SHIP_MENU_UNIT;
}
//------- End of function UnitMarine::init -------//
//------- Begin of function UnitMarine::update_abs_pos ------//
void UnitMarine::update_abs_pos(SpriteFrame *spriteFrame)
{
Unit::update_abs_pos(spriteFrame);
short h = wave_height(6);
abs_y1 -= h;
abs_y2 -= h;
}
//------- End of function UnitMarine::update_abs_pos -------//
//------- Begin of function UnitMarine::draw -------//
void UnitMarine::draw()
{
// -------- update splash parameter --------//
// ###### begin Gilbert 8/9 #######//
char oldSplashAction = splash.cur_action;
switch(cur_action)
{
case SPRITE_MOVE:
splash.cur_action = SPRITE_MOVE;
if( splash.cur_action != oldSplashAction)
splash.cur_frame = 1;
else
{
++splash.cur_frame;
if( splash.cur_frame < 1 || splash.cur_frame > splash.cur_sprite_move()->frame_count )
splash.cur_frame = 1;
}
break;
default:
splash.cur_action = SPRITE_IDLE;
if( splash.cur_action != oldSplashAction)
splash.cur_frame = 1;
else
{
++splash.cur_frame;
if( splash.cur_frame < 1 || splash.cur_frame > splash.cur_sprite_stop()->frame_count)
splash.cur_frame = 1;
}
break;
}
// ###### end Gilbert 8/9 #######//
splash.cur_x = cur_x;
splash.cur_y = cur_y - wave_height(7);
splash.cur_dir = cur_dir;
splash.final_dir = final_dir;
splash.turn_delay = turn_delay;
// --------- draw splash and then the unit --------//
// ###### begin Gilbert 24/9 #######//
if( cur_action != SPRITE_DIE )
splash.draw();
// ###### end Gilbert 24/9 #######//
Unit::draw();
#ifdef DEBUG2
if(selected_flag && 0)
{
vga_util.d3_panel_up( INFO_X1, INFO_Y1+144, INFO_X2, INFO_Y1+144+87 );
int x=INFO_X1+4, y=INFO_Y1+200, refreshFlag=INFO_REPAINT;
font_san.field( x, y, " " , x+2, sprite_recno, 1, INFO_X2-2, refreshFlag);
font_san.field( x+20, y, " " , x+22, next_x_loc(), 1, INFO_X2-2, refreshFlag);
font_san.field( x+50, y, " " , x+52, next_y_loc(), 1, INFO_X2-2, refreshFlag);
font_san.field( x+70, y, " " , x+72, nation_recno, 1, INFO_X2-2, refreshFlag);
font_san.field( x+100, y, " " , x+102, action_mode, 1, INFO_X2-2, refreshFlag);
font_san.field( x+120, y, " " , x+122, action_para, 1, INFO_X2-2, refreshFlag);
font_san.field( x+140, y, " " , x+142, action_x_loc, 1, INFO_X2-2, refreshFlag);
font_san.field( x+160, y, " " , x+162, action_y_loc, 1, INFO_X2-2, refreshFlag);
y-=20;
font_san.field( x+100, y, " " , x+102, action_mode2, 1, INFO_X2-2, refreshFlag);
font_san.field( x+120, y, " " , x+122, action_para2, 1, INFO_X2-2, refreshFlag);
font_san.field( x+140, y, " " , x+142, action_x_loc2, 1, INFO_X2-2, refreshFlag);
font_san.field( x+160, y, " " , x+162, action_y_loc2, 1, INFO_X2-2, refreshFlag);
y-=20;
font_san.field( x+160, y, " " , x+162, cur_action, 1, INFO_X2-2, refreshFlag);
}
#endif
}
//------- End of function UnitMarine::draw -------//
//------- Begin of function UnitMarine::draw_outlined -------//
void UnitMarine::draw_outlined()
{
// -------- update splash parameter --------//
// ###### begin Gilbert 8/9 #######//
char oldSplashAction = splash.cur_action;
switch(cur_action)
{
case SPRITE_MOVE:
splash.cur_action = SPRITE_MOVE;
if( splash.cur_action != oldSplashAction)
splash.cur_frame = 1;
else
{
++splash.cur_frame;
if( splash.cur_frame < 1 || splash.cur_frame > splash.cur_sprite_move()->frame_count )
splash.cur_frame = 1;
}
break;
default:
splash.cur_action = SPRITE_IDLE;
if( splash.cur_action != oldSplashAction)
splash.cur_frame = 1;
else
{
++splash.cur_frame;
if( splash.cur_frame < 1 || splash.cur_frame > splash.cur_sprite_stop()->frame_count)
splash.cur_frame = 1;
}
break;
}
// ###### end Gilbert 8/9 #######//
splash.cur_x = cur_x;
splash.cur_y = cur_y - wave_height(7);
splash.cur_dir = cur_dir;
splash.final_dir = final_dir;
splash.turn_delay = turn_delay;
// --------- draw splash and then the unit --------//
// ###### begin Gilbert 24/9 #######//
if( cur_action != SPRITE_DIE )
splash.draw();
// ###### end Gilbert 24/9 #######//
Unit::draw_outlined();
#ifdef DEBUG2
if(selected_flag && 0)
{
vga_util.d3_panel_up( INFO_X1, INFO_Y1+144, INFO_X2, INFO_Y1+144+87 );
int x=INFO_X1+4, y=INFO_Y1+200, refreshFlag=INFO_REPAINT;
font_san.field( x, y, " " , x+2, sprite_recno, 1, INFO_X2-2, refreshFlag);
font_san.field( x+20, y, " " , x+22, next_x_loc(), 1, INFO_X2-2, refreshFlag);
font_san.field( x+50, y, " " , x+52, next_y_loc(), 1, INFO_X2-2, refreshFlag);
font_san.field( x+70, y, " " , x+72, nation_recno, 1, INFO_X2-2, refreshFlag);
font_san.field( x+100, y, " " , x+102, action_mode, 1, INFO_X2-2, refreshFlag);
font_san.field( x+120, y, " " , x+122, action_para, 1, INFO_X2-2, refreshFlag);
font_san.field( x+140, y, " " , x+142, action_x_loc, 1, INFO_X2-2, refreshFlag);
font_san.field( x+160, y, " " , x+162, action_y_loc, 1, INFO_X2-2, refreshFlag);
y-=20;
font_san.field( x+100, y, " " , x+102, action_mode2, 1, INFO_X2-2, refreshFlag);
font_san.field( x+120, y, " " , x+122, action_para2, 1, INFO_X2-2, refreshFlag);
font_san.field( x+140, y, " " , x+142, action_x_loc2, 1, INFO_X2-2, refreshFlag);
font_san.field( x+160, y, " " , x+162, action_y_loc2, 1, INFO_X2-2, refreshFlag);
y-=20;
font_san.field( x+160, y, " " , x+162, cur_action, 1, INFO_X2-2, refreshFlag);
}
#endif
}
//------- End of function UnitMarine::draw_outlined -------//
//------- Begin of function UnitMarine::wave_height -------//
short UnitMarine::wave_height(int phase)
{
err_when( phase < 0);
static short height[WAVE_CYCLE] = { 4,3,2,1,0,1,2,3 };
return height[((sys.frame_count /4) + phase) % WAVE_CYCLE];
}
//------- End of function UnitMarine::wave_height -------//
//--------- Begin of function UnitMarine::del_unit ---------//
//
// Delete a unit from the ship. This function is called by
// Unit::deinit() when the unit is killed.
//
// unitRecno - recno of the unit to be loaded.
//
void UnitMarine::del_unit(int unitRecno)
{
for( int i=0 ; i MAX_UNIT_IN_SHIP );
misc.del_array_rec(unit_recno_array, unit_count, sizeof(unit_recno_array[0]), i+1);
return;
}
}
err_here();
}
//----------- End of function UnitMarine::del_unit -----------//
//--------- Begin of function UnitMarine::load_unit ---------//
//
// Load an unit to the ship.
//
// unitRecno - recno of the unit to be loaded.
//
void UnitMarine::load_unit(int unitRecno)
{
if(unit_array.is_deleted(unitRecno))
return;
Unit *unitPtr = unit_array[unitRecno];
if(unitPtr->hit_points<=0 || unitPtr->cur_action==SPRITE_DIE || unitPtr->action_mode2==ACTION_DIE)
return;
if( unit_count == MAX_UNIT_IN_SHIP )
return;
unit_recno_array[unit_count++] = unitRecno;
unitPtr->set_mode(UNIT_MODE_ON_SHIP, sprite_recno); // set unit mode
if(unitPtr->selected_flag)
{
unitPtr->selected_flag = 0;
unit_array.selected_count--;
}
unitPtr->deinit_sprite();
//--- if this marine unit is currently selected ---//
if(unit_array.selected_recno==sprite_recno)
{
if(!remote.is_enable() || nation_recno==nation_array.player_recno || config.show_ai_info)
disp_info(INFO_UPDATE);
}
}
//----------- End of function UnitMarine::load_unit -----------//
//--------- Begin of function UnitMarine::unload_unit ---------//
//
// Unload an unit from the ship.
//
// unitSeqId - sequence id. of the unit in unit_recno_array[]
//
void UnitMarine::unload_unit(int unitSeqId, char remoteAction)
{
err_when(unitSeqId > unit_count);
if(!remoteAction && remote.is_enable() )
{
// packet structure :
short *shortPtr = (short *)remote.new_send_queue_msg(MSG_U_SHIP_UNLOAD_UNIT, 2*sizeof(short));
*shortPtr = sprite_recno;
shortPtr[1] = unitSeqId;
return;
}
//-------- unload unit now -------//
if( unloading_unit(0, unitSeqId-1) ) // unit is unloaded
{
err_when( unit_count+1 > MAX_UNIT_IN_SHIP );
misc.del_array_rec(unit_recno_array, unit_count+1, sizeof(unit_recno_array[0]), unitSeqId);
}
}
//----------- End of function UnitMarine::unload_unit -----------//
//--------- Begin of function UnitMarine::unload_all_units ---------//
void UnitMarine::unload_all_units(char remoteAction)
{
if(!remoteAction && remote.is_enable() )
{
// packet structure :
short *shortPtr = (short *)remote.new_send_queue_msg(MSG_U_SHIP_UNLOAD_ALL_UNITS, sizeof(short));
*shortPtr = sprite_recno;
return;
}
unloading_unit(1); // unload all units
}
//----------- End of function UnitMarine::unload_all_units -----------//
//--------- Begin of function UnitMarine::unloading_unit ---------//
//
// isAll - 1 for unload all the units
// - otherwise 0
// unitSeqId - if(isAll==0) unitSeqId+1 is the recno of the
// selected unit in unit_recno_array[]
//
int UnitMarine::unloading_unit(int isAll, int unitSeqId)
{
if( !is_on_coast() )
return 0;
//-------------------------------------------------------------------------//
// return if no territory is nearby the ship
//-------------------------------------------------------------------------//
int curXLoc = next_x_loc(); // ship location
int curYLoc = next_y_loc();
int unprocess = isAll ? unit_count : 1;
Unit *unitPtr = isAll ? unit_array[unit_recno_array[unprocess-1]] : unit_array[unit_recno_array[unitSeqId]];
Location *locPtr;
int xShift, yShift, checkXLoc, checkYLoc;
int regionId = 0; // unload all the units in the same territory
int found = 0, i = 2;
int sqtSize = 5, sqtArea = sqtSize*sqtSize;
#ifdef DEBUG
long debugCount = 0L;
#endif
if(isAll && nation_recno == nation_array.player_recno ) // for player's camp, patrol() can only be called when the player presses the button.
power.reset_selection();
while(unprocess) // using the calculated 'i' to reduce useless calculation
{
err_when(debugCount++ > 4*long(MAX_WORLD_X_LOC*MAX_WORLD_Y_LOC));
misc.cal_move_around_a_point(i, MAX_WORLD_X_LOC, MAX_WORLD_Y_LOC, xShift, yShift);
checkXLoc = curXLoc+xShift;
checkYLoc = curYLoc+yShift;
if(checkXLoc<0 || checkXLoc>=MAX_WORLD_X_LOC || checkYLoc<0 || checkYLoc>=MAX_WORLD_Y_LOC)
{
i++;
continue;
}
locPtr = world.get_loc(checkXLoc, checkYLoc);
//-------------------------------------------------------------------------//
// check for space to unload the unit
//-------------------------------------------------------------------------//
if(!regionId || locPtr->region_id == regionId)
{
if(locPtr->walkable())
found = 1;
if(locPtr->can_move(UNIT_LAND))//unitPtr->mobile_type))
{
regionId = locPtr->region_id;
unitPtr->init_sprite(checkXLoc, checkYLoc);
unitPtr->set_mode(0);
if( isAll && nation_recno == nation_array.player_recno ) // for player's camp, patrol() can only be called when the player presses the button.
{
unitPtr->selected_flag = 1; // mark selected if unload all
unit_array.selected_count++;
if( !unit_array.selected_recno )
unit_array.selected_recno = unitPtr->sprite_recno;
}
unprocess--;
unit_count--;
if(unprocess)
unitPtr = unit_array[unit_recno_array[unprocess-1]]; // point to next unit
else
break; // finished, all have been unloaded
}
}
//-------------------------------------------------------------------------//
// stop checking if there is totally bouned by unacessible location
//-------------------------------------------------------------------------//
if(i==sqtArea)
{
if(found)
{
found = 0; // reset found
sqtSize += 2;
sqtArea = sqtSize*sqtSize;
}
else // no continuous location for the unit to unload, some units can't be unloaded
return 0;
}
i++;
}
//-------- display info --------//
if( nation_recno == nation_array.player_recno ) // for player's camp, patrol() can only be called when the player presses the button.
info.disp();
return 1;
}
//----------- End of function UnitMarine::unloading_unit -----------//
//--------- Begin of function UnitMarine::is_on_coast ---------//
//
// Return whether the unit is on the coast and ready for unloading.
//
int UnitMarine::is_on_coast()
{
Location *locPtr;
int xShift, yShift, checkXLoc, checkYLoc, found=0;
int curXLoc = next_x_loc(); // ship location
int curYLoc = next_y_loc();
for(int i=2; i<=9; i++) // checking for the surrouding location
{
misc.cal_move_around_a_point(i, 3, 3, xShift, yShift);
checkXLoc = curXLoc + xShift;
checkYLoc = curYLoc + yShift;
if(checkXLoc<0 || checkXLoc>=MAX_WORLD_X_LOC || checkYLoc<0 || checkYLoc>=MAX_WORLD_Y_LOC)
continue;
locPtr = world.get_loc(checkXLoc, checkYLoc);
if(terrain_res[locPtr->terrain_id]->average_type!=TERRAIN_OCEAN && // a territory nearby
locPtr->walkable())
{
return 1;
}
}
return 0;
}
//----------- End of function UnitMarine::is_on_coast -----------//
//--------- Begin of function UnitMarine::extra_move ---------//
void UnitMarine::extra_move()
{
static char offset[3] = {0, 1, -1};
int curXLoc = next_x_loc();
int curYLoc = next_y_loc();
int vecX = action_x_loc2 - curXLoc;
int vecY = action_y_loc2 - curYLoc;
int checkXLoc, checkYLoc, i, found=0;
if(vecX==0 || vecY==0)
{
if(vecX==0)
{
vecY /= abs(vecY);
checkYLoc = curYLoc + vecY;
}
else // vecY==0
{
vecX /= abs(vecX);
checkXLoc = curXLoc + vecX;
}
for(i=0; i<3; i++)
{
if(vecX==0)
checkXLoc = curXLoc + offset[i];
else
checkYLoc = curYLoc + offset[i];
if(checkXLoc<0 || checkXLoc>=MAX_WORLD_X_LOC || checkYLoc<0 || checkYLoc>=MAX_WORLD_Y_LOC)
continue;
if(world.get_loc(checkXLoc, checkYLoc)->can_move(mobile_type))
{
found++;
break;
}
}
}
else
{
vecX /= abs(vecX);
vecY /= abs(vecY);
checkXLoc = curXLoc + vecX;
checkYLoc = curYLoc + vecY;
if(world.get_loc(checkXLoc, checkYLoc)->can_move(mobile_type))
found++;
}
if(!found)
return;
set_dir(curXLoc, curYLoc, checkXLoc, checkYLoc);
cur_action = SPRITE_SHIP_EXTRA_MOVE;
go_x = checkXLoc*ZOOM_LOC_WIDTH;
go_y = checkYLoc*ZOOM_LOC_HEIGHT;
err_when(cur_x==go_x && cur_y==go_y);
//extra_move_in_beach = EXTRA_MOVING_IN;
}
//----------- End of function UnitMarine::extra_move -----------//
//------- Begin of function UnitMarine::process_extra_move ------//
void UnitMarine::process_extra_move()
{
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};
if(!match_dir()) // process turning
return;
if(cur_x!=go_x || cur_y!=go_y)
{
//------------------------------------------------------------------------//
// set cargo_recno, extra_move_in_beach
//------------------------------------------------------------------------//
if(cur_x==next_x && cur_y==next_y)
{
int goXLoc = go_x>>ZOOM_X_SHIFT_COUNT;
int goYLoc = go_y>>ZOOM_Y_SHIFT_COUNT;
if(!world.get_loc(goXLoc, goYLoc)->can_move(mobile_type))
{
go_x = next_x;
go_y = next_y;
return;
}
int curXLoc = next_x_loc();
int curYLoc = next_y_loc();
world.set_unit_recno(curXLoc, curYLoc, mobile_type, 0);
world.set_unit_recno(goXLoc, goYLoc, mobile_type, sprite_recno);
next_x = go_x;
next_y = go_y;
err_when( ((curXLoc%2)|(curYLoc%2)) + ((goXLoc%2)|(goYLoc%2)) != 1); // one pair location must be even, another is not even
in_beach = !(curXLoc%2 || curYLoc%2);
if(goXLoc%2 || goYLoc%2) // not even location
extra_move_in_beach = EXTRA_MOVING_IN;
else // even location
extra_move_in_beach = EXTRA_MOVING_OUT;
}
//else
// int debug = 0;
//---------- process moving -----------//
short stepX = sprite_info->speed;
short stepY = sprite_info->speed;
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(extra_move_in_beach!=EXTRA_MOVING_IN && extra_move_in_beach!=EXTRA_MOVING_OUT);
err_when(cur_action!=SPRITE_SHIP_EXTRA_MOVE);
}
if(cur_x==go_x && cur_y==go_y)
{
if(result_node_array==NULL)
{
cur_action = SPRITE_IDLE;
cur_frame = 1;
move_to_x_loc = next_x_loc();
move_to_y_loc = next_y_loc();
}
else
{
cur_action = SPRITE_MOVE;
next_move();
}
if(in_beach)
{
extra_move_in_beach = EXTRA_MOVE_FINISH;
err_when(move_to_x_loc%2==0 && move_to_y_loc%2==0);
err_when(result_node_array || result_path_dist);
}
else
{
extra_move_in_beach = NO_EXTRA_MOVE;
err_when(move_to_x_loc%2 || move_to_y_loc%2);
err_when(result_node_array || result_path_dist);
}
err_when(cur_action==SPRITE_IDLE && (extra_move_in_beach==EXTRA_MOVING_IN || extra_move_in_beach==EXTRA_MOVING_OUT));
}
}
//----------- End of function UnitMarine::process_extra_move -----------//
//------------ Begin of function UnitMarine::actual_damage --------------//
//
// This function returns the actual hit damage this unit can do to a target.
//
float UnitMarine::actual_damage()
{
float attackDamage = Unit::actual_damage();
//-----------------------------------------//
//
// If there is units on the ship, the units
// leadership will increase the attacking damage.
//
// actual damage = normal damage X (100+highest leadership unit on the ship) / 100
//
//-----------------------------------------//
Unit* unitPtr;
int t, highestLeadership=0;
for( int i=0 ; iskill.skill_id == SKILL_LEADING )
{
if( (t=unitPtr->skill.skill_level) > highestLeadership )
highestLeadership = t;
}
}
return attackDamage * (100+highestLeadership) / 100;
}
//------------ End of function UnitMarine::actual_damage --------------//
//------- Begin of function UnitMarine::total_carried_goods -------//
int UnitMarine::total_carried_goods()
{
int totalQty=0;
for( int i=0 ; i