/*
* 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 : OUNITS.CPP
//Description : Object Unit(Ship) functions
//Owner : Alex
#include
#include
#include
#ifdef NO_DEBUG_UNIT
#undef err_when
#undef err_here
#undef err_if
#undef err_else
#undef err_now
#define err_when(cond)
#define err_here()
#define err_if(cond)
#define err_else
#define err_now(msg)
#undef DEBUG
#endif
//--------- Begin of function Unit::assign_to_ship ---------//
// assigns units to ship
//
// destX, destY - the location for the ship to load the unit
// shipRecno - the recno of the ship
// miscNo - used to cal the offset location (default: 0)
//
void Unit::assign_to_ship(int destX, int destY, short shipRecno, int miscNo)
{
err_when(destX<0 || destX>=MAX_WORLD_X_LOC || destY<0 || destY>=MAX_WORLD_Y_LOC || shipRecno<=0);
err_when(sprite_info->loc_width>1 || sprite_info->loc_height>1);
//----------------------------------------------------------------//
// return if the unit is dead
//----------------------------------------------------------------//
if(hit_points<=0 || action_mode==ACTION_DIE || cur_action==SPRITE_DIE)
return;
if(unit_array.is_deleted(shipRecno))
return;
#ifdef DEBUG
int debugShipXLoc = unit_array[shipRecno]->next_x_loc();
int debugShipYLoc = unit_array[shipRecno]->next_y_loc();
err_when(terrain_res[world.get_loc(debugShipXLoc, debugShipYLoc)->terrain_id]->average_type!=TERRAIN_OCEAN);
int debugCheckXLoc, debugCheckYLoc;
#endif
//----------------------------------------------------------------//
// action_mode2: checking for equal action or idle action
//----------------------------------------------------------------//
if(action_mode2==ACTION_ASSIGN_TO_SHIP && action_para2==shipRecno && action_x_loc2==destX && action_y_loc2==destY)
{
if(cur_action!=SPRITE_IDLE)
return;
}
else
{
//----------------------------------------------------------------//
// action_mode2: store new order
//----------------------------------------------------------------//
action_mode2 = ACTION_ASSIGN_TO_SHIP;
action_para2 = shipRecno;
action_x_loc2 = destX;
action_y_loc2 = destY;
}
//----- order the sprite to stop as soon as possible -----//
stop(); // new order
Unit *shipPtr = unit_array[shipRecno];
int shipXLoc = shipPtr->next_x_loc();
int shipYLoc = shipPtr->next_y_loc();
int resultXLoc, resultYLoc;
int xShift, yShift;
if(!miscNo)
{
//-------- find a suitable location since no offset location is given ---------//
if(abs(shipXLoc-action_x_loc2)<=1 && abs(shipYLoc-action_y_loc2)<=1)
{
int checkXLoc, checkYLoc;
Location *locPtr = world.get_loc(next_x_loc(), next_y_loc());
UCHAR regionId = locPtr->region_id;
for(int i=2; i<=9; i++)
{
m.cal_move_around_a_point(i, 3, 3, xShift, yShift);
checkXLoc = shipXLoc+xShift;
checkYLoc = shipYLoc+yShift;
if(checkXLoc<0 || checkXLoc>=MAX_WORLD_X_LOC || checkYLoc<0 || checkYLoc>=MAX_WORLD_Y_LOC)
continue;
locPtr = world.get_loc(checkXLoc, checkYLoc);
if(locPtr->region_id!=regionId)
continue;
resultXLoc = checkXLoc;
resultYLoc = checkYLoc;
#ifdef DEBUG
debugCheckXLoc = checkXLoc;
debugCheckYLoc = checkYLoc;
#endif
break;
}
}
else
{
resultXLoc = action_x_loc2;
resultYLoc = action_y_loc2;
}
err_when(resultXLoc<0 || resultXLoc>=MAX_WORLD_X_LOC || resultYLoc<0 || resultYLoc>=MAX_WORLD_Y_LOC);
}
else
{
//------------ offset location is given, move there directly ----------//
m.cal_move_around_a_point(miscNo, MAX_WORLD_X_LOC, MAX_WORLD_Y_LOC, xShift, yShift);
resultXLoc = destX+xShift;
resultYLoc = destY+yShift;
err_when(resultXLoc<0 || resultXLoc>=MAX_WORLD_X_LOC || resultYLoc<0 || resultYLoc>=MAX_WORLD_Y_LOC);
}
//--------- start searching ----------//
int curXLoc = next_x_loc();
int curYLoc = next_y_loc();
if((curXLoc!=destX || curYLoc!=destY) && (abs(shipXLoc-curXLoc)>1 || abs(shipYLoc-curYLoc)>1))
search(resultXLoc, resultYLoc, 1);
//-------- set action parameters ----------//
action_mode = ACTION_ASSIGN_TO_SHIP;
action_para = shipRecno;
action_x_loc = destX;
action_y_loc = destY;
}
//----------- End of function Unit::assign_to_ship -----------//
//--------- Begin of function Unit::process_assign_to_ship ---------//
// process unit action of assigning units to ship
//
void Unit::process_assign_to_ship()
{
err_when(sprite_info->loc_width>1 || sprite_info->loc_height>1);
//---------------------------------------------------------------------------//
// clear unit's action if situation is changed
//---------------------------------------------------------------------------//
UnitMarine *shipPtr;
if(unit_array.is_deleted(action_para2))
{
stop2();
return; // stop the unit as the ship is deleted
}
else
{
shipPtr = (UnitMarine*) unit_array[action_para2];
if(shipPtr->nation_recno != nation_recno)
{
stop2();
return; // stop the unit as the ship's nation_recno != the unit's nation_recno
}
}
if(shipPtr->action_mode2!=ACTION_SHIP_TO_BEACH)
{
stop2(); // the ship has changed its action
return;
}
int curXLoc = next_x_loc();
int curYLoc = next_y_loc();
int shipXLoc = shipPtr->next_x_loc();
int shipYLoc = shipPtr->next_y_loc();
if(shipPtr->cur_x==shipPtr->next_x && shipPtr->cur_y==shipPtr->next_y &&
abs(shipXLoc-curXLoc)<=1 && abs(shipYLoc-curYLoc)<=1)
{
//----------- assign the unit now -----------//
if(abs(cur_x-next_x)speed && abs(cur_y-next_y)speed)
{
if(ai_action_id)
nation_array[nation_recno]->action_finished(ai_action_id, sprite_recno);
stop2();
set_dir(curXLoc, curYLoc, shipXLoc, shipYLoc);
shipPtr->load_unit(sprite_recno);
return;
}
}
else if(cur_action==SPRITE_IDLE)
set_dir(curXLoc, curYLoc, shipPtr->move_to_x_loc, shipPtr->move_to_y_loc);
//---------------------------------------------------------------------------//
// update location to embark
//---------------------------------------------------------------------------//
int shipActionXLoc = shipPtr->action_x_loc2;
int shipActionYLoc = shipPtr->action_y_loc2;
if(abs(shipActionXLoc-action_x_loc2)>1 || abs(shipActionYLoc-action_y_loc2)>1)
{
if(shipActionXLoc!=action_x_loc2 || shipActionYLoc!=action_y_loc2)
{
Location *unitLocPtr = world.get_loc(curXLoc, curYLoc);
Location *shipActionLocPtr = world.get_loc(shipActionXLoc, shipActionYLoc);
if(unitLocPtr->region_id != shipActionLocPtr->region_id)
{
stop2();
return;
}
assign_to_ship(shipActionXLoc, shipActionYLoc, action_para2);
return;
}
}
}
//----------- End of function Unit::process_assign_to_ship -----------//
//--------- Begin of function Unit::ship_to_beach ---------//
// destX, destY - the location in land planned for LAND_UNIT to embark
// finalDestX, finalDestY - reference to final location used to embark
//
// This function is only for ship
// move the ship to the coast
//
//
void Unit::ship_to_beach(int destX, int destY, int& finalDestX, int& finalDestY)
{
err_when(unit_res[unit_id]->unit_class!=UNIT_CLASS_SHIP);
err_when(destX<0 || destX>=MAX_WORLD_X_LOC || destY<0 || destY>=MAX_WORLD_Y_LOC);
err_when(terrain_res[world.get_loc(destX, destY)->terrain_id]->average_type == TERRAIN_OCEAN);
//----------------------------------------------------------------//
// return if the unit is dead
//----------------------------------------------------------------//
if(hit_points<=0 || action_mode==ACTION_DIE || cur_action==SPRITE_DIE)
return;
//----------------------------------------------------------------//
// change to move_to if the ship cannot carry units
//----------------------------------------------------------------//
if(unit_res[unit_id]->carry_unit_capacity<=0)
{
move_to(destX, destY, 1);
finalDestX = finalDestY = -1;
return;
}
//----------------------------------------------------------------//
// calculate new destination
//----------------------------------------------------------------//
#ifdef DEBUG
UnitMarine *debugShipPtr = (UnitMarine*) this; //****** for testing
#endif
int curXLoc = next_x_loc();
int curYLoc = next_y_loc();
int resultXLoc, resultYLoc;
stop();
err_when(cur_action==SPRITE_MOVE && cur_x==move_to_x_loc*ZOOM_LOC_WIDTH && cur_y==move_to_y_loc*ZOOM_LOC_HEIGHT);
if(abs(destX-curXLoc)>1 || abs(destY-curYLoc)>1)
{
//-----------------------------------------------------------------------------//
// get a suitable location in the territory as a reference location
//-----------------------------------------------------------------------------//
Location *locPtr = world.get_loc(destX, destY);
UCHAR regionId = locPtr->region_id;
int xStep = curXLoc-destX;
int yStep = curYLoc-destY;
int absXStep = abs(xStep);
int absYStep = abs(yStep);
int count = (absXStep>=absYStep) ? absXStep : absYStep;
int x, y;
long int sameTerr = 0;
for(long int i=1; i<=count; i++)
{
x = destX + int((i*xStep)/count);
y = destY + int((i*yStep)/count);
locPtr = world.get_loc(x, y);
if(locPtr->region_id==regionId)
{
if(locPtr->walkable())
sameTerr = i;
}
}
if(sameTerr)
{
resultXLoc = destX + int((sameTerr*xStep)/count);
resultYLoc = destY + int((sameTerr*yStep)/count);
}
else
{
resultXLoc = destX;
resultYLoc = destY;
}
//------------------------------------------------------------------------------//
// find the path from the ship location in the ocean to the reference location
// in the territory
//------------------------------------------------------------------------------//
if(!ship_to_beach_path_edit(resultXLoc, resultYLoc, regionId))
{
finalDestX = finalDestY = -1;
return; // calling move_to() instead
}
}
else
{
resultXLoc = destX;
resultYLoc = destY;
}
err_when(cur_action==SPRITE_MOVE && cur_x==next_x && cur_y==next_y &&
cur_x==move_to_x_loc*ZOOM_LOC_WIDTH && cur_y==move_to_y_loc*ZOOM_LOC_HEIGHT);
err_when(result_node_array==NULL && result_path_dist);
action_mode = action_mode2 = ACTION_SHIP_TO_BEACH;
action_para = action_para2 = 0;
finalDestX = action_x_loc = action_x_loc2 = resultXLoc;
finalDestY = action_y_loc = action_y_loc2 = resultYLoc;
}
//----------- End of function Unit::ship_to_beach -----------//
//--------- Begin of function Unit::ship_to_beach_path_edit ---------//
// find a path to the beach
//
// resultXLoc - reference to return final x location the ship move to
// resultYLoc - reference to return final y location the ship move to
// regionId - region id of the destination location
//
// return 1 if normal execution
// return 0 if calling move_to() instead
//
int Unit::ship_to_beach_path_edit(int& resultXLoc, int& resultYLoc, UCHAR regionId)
{
int curXLoc = next_x_loc();
int curYLoc = next_y_loc();
if(abs(curXLoc-resultXLoc)<=1 && abs(curYLoc-resultYLoc)<=1)
return 1;
//--------------- find a path to land area -------------------//
UnitMarine *shipPtr = (UnitMarine*) this;
int result = search(resultXLoc, resultYLoc, 1, SEARCH_MODE_TO_LAND_FOR_SHIP, regionId);
if(!result)
return 1;
//----------- update cur location --------//
curXLoc = next_x_loc();
curYLoc = next_y_loc();
err_when(shipPtr->extra_move_in_beach!=NO_EXTRA_MOVE);
//------------------------------------------------------------------------------//
// edit the result path to get a location for embarking
//------------------------------------------------------------------------------//
err_when(result_node_array==NULL && result_node_count);
if(result_node_array && result_node_count)
{
err_when(result_node_count<2);
ResultNode *curNodePtr = result_node_array;
ResultNode *nextNodePtr = curNodePtr + 1;
int moveScale = move_step_magn();
int nodeCount = result_node_count;
Location *locPtr;
int i, j, found, pathDist;
int preXLoc, preYLoc;
int checkXLoc = curXLoc;
int checkYLoc = curYLoc;
int hasMoveStep = 0;
if(checkXLoc!=curNodePtr->node_x || checkYLoc!=curNodePtr->node_y)
{
err_when(abs(checkXLoc-curNodePtr->node_x)>moveScale || abs(checkYLoc-curNodePtr->node_y)>moveScale);
hasMoveStep += moveScale;
checkXLoc = curNodePtr->node_x;
checkYLoc = curNodePtr->node_y;
}
//-----------------------------------------------------------------//
// find the pair of points that one is in ocean and one in land
//-----------------------------------------------------------------//
err_when(terrain_res[world.get_loc(curNodePtr->node_x, curNodePtr->node_y)->terrain_id]->average_type!=TERRAIN_OCEAN);
int vecX, vecY, xMagn, yMagn, magn;
#ifdef DEBUG
int debugLoop1=0, debugLoop2=0;
#endif
for(pathDist=0, found=0, i=1; i10000);
#endif
vecX = nextNodePtr->node_x - curNodePtr->node_x;
vecY = nextNodePtr->node_y - curNodePtr->node_y;
magn = ((xMagn=abs(vecX)) > (yMagn=abs(vecY))) ? xMagn : yMagn;
if(xMagn) { vecX /= xMagn; vecX *= moveScale; }
if(yMagn) { vecY /= yMagn; vecY *= moveScale; }
err_when(abs(vecX)>moveScale && abs(vecY)>moveScale);
//------------- check each location bewteen editNode1 and editNode2 -------------//
for(j=0; j10000);
#endif
preXLoc = checkXLoc;
preYLoc = checkYLoc;
checkXLoc += vecX;
checkYLoc += vecY;
locPtr = world.get_loc(checkXLoc, checkYLoc);
if(terrain_res[locPtr->terrain_id]->average_type!=TERRAIN_OCEAN) // found
{
found++;
break;
}
}
if(found)
{
//------------ a soln is found ---------------//
if(!j) // end node should be curNodePtr pointed at
{
pathDist -= hasMoveStep;
result_node_count = i;
result_path_dist = pathDist;
}
else
{
nextNodePtr->node_x = checkXLoc;
nextNodePtr->node_y = checkYLoc;
if(i==1) // first editing
{
ResultNode *firstNodePtr = result_node_array;
if(cur_x==firstNodePtr->node_x*ZOOM_LOC_WIDTH && cur_y==firstNodePtr->node_y*ZOOM_LOC_HEIGHT)
{
go_x = checkXLoc * ZOOM_LOC_WIDTH;
go_y = checkYLoc * ZOOM_LOC_HEIGHT;
}
}
pathDist += (j+moveScale);
pathDist -= hasMoveStep;
result_node_count = i+1;
result_path_dist = pathDist;
}
move_to_x_loc = preXLoc;
move_to_y_loc = preYLoc;
locPtr = world.get_loc((preXLoc+checkXLoc)/2, (preYLoc+checkYLoc)/2);
if(terrain_res[locPtr->terrain_id]->average_type!=TERRAIN_OCEAN)
{
resultXLoc = (preXLoc+checkXLoc)/2;
resultYLoc = (preYLoc+checkYLoc)/2;
}
else
{
resultXLoc = checkXLoc;
resultYLoc = checkYLoc;
}
break;
}
else
pathDist += magn;
}
if(!found)
{
ResultNode *endNodePtr = result_node_array + result_node_count - 1;
if(abs(endNodePtr->node_x-resultXLoc)>1 || abs(endNodePtr->node_y-resultYLoc)>1)
{
move_to(resultXLoc, resultYLoc, -1);
return 0;
}
}
}
else
{
//------------- scan for the surrounding for a land location -----------//
int xShift, yShift, checkXLoc, checkYLoc;
Location *locPtr;
for(int i=2; i<=9; ++i)
{
m.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(locPtr->region_id!=regionId)
continue;
if(terrain_res[locPtr->terrain_id]->average_type!=TERRAIN_OCEAN && locPtr->can_move(UNIT_LAND))
{
resultXLoc = checkXLoc;
resultYLoc = checkYLoc;
return 1;
}
}
return 0;
}
return 1;
}
//----------- End of function Unit::ship_to_beach_path_edit -----------//
//--------- Begin of function Unit::process_ship_to_beach ---------//
// process unit action SHIP_TO_BEACH
//
void Unit::process_ship_to_beach()
{
//----- action_mode never clear, in_beach to skip idle checking
if(cur_action==SPRITE_IDLE)
{
int shipXLoc = next_x_loc();
int shipYLoc = next_y_loc();
if(shipXLoc==move_to_x_loc && shipYLoc==move_to_y_loc)
{
if(abs(move_to_x_loc-action_x_loc2)<=2 && abs(move_to_y_loc-action_y_loc2)<=2)
{
UnitMarine *shipPtr = (UnitMarine*) this;
//------------------------------------------------------------------------------//
// determine whether extra_move is required
//------------------------------------------------------------------------------//
switch(shipPtr->extra_move_in_beach)
{
case NO_EXTRA_MOVE:
if(abs(shipXLoc-action_x_loc2)>1 || abs(shipYLoc-action_y_loc2)>1)
{
err_when(abs(shipXLoc-action_x_loc2)>2 || abs(shipYLoc-action_y_loc2)>2);
err_when(result_node_array);
shipPtr->extra_move();
}
else
{
//shipPtr->in_beach = 1;
shipPtr->extra_move_in_beach = NO_EXTRA_MOVE;
//#### trevor 23/10 #####//
if(ai_action_id)
nation_array[nation_recno]->action_finished(ai_action_id, sprite_recno);
//#### trevor 23/10 #####//
}
break;
case EXTRA_MOVING_IN:
case EXTRA_MOVING_OUT:
//err_when(cur_action!=SPRITE_SHIP_EXTRA_MOVE);
break;
//#### trevor 23/10 #####//
case EXTRA_MOVE_FINISH:
if(ai_action_id)
nation_array[nation_recno]->action_finished(ai_action_id, sprite_recno);
break;
//#### trevor 23/10 #####//
default: err_here();
break;
}
}
}
else
reset_action_para();
}
else if(cur_action==SPRITE_TURN && is_dir_correct())
set_move();
}
//----------- End of function Unit::process_ship_to_beach -----------//
//--------- Begin of function Unit::ship_leave_beach ---------//
void Unit::ship_leave_beach(int shipOldXLoc, int shipOldYLoc)
{
err_when(cur_x!=next_x || cur_y!=next_y);
UnitMarine *shipPtr = (UnitMarine*) this;
err_when(!shipPtr->in_beach && shipPtr->extra_move_in_beach==EXTRA_MOVE_FINISH);
//--------------------------------------------------------------------------------//
// scan for location to leave the beach
//--------------------------------------------------------------------------------//
int curXLoc = next_x_loc();
int curYLoc = next_y_loc();
int xShift, yShift, checkXLoc, checkYLoc, found=0;
Location *locPtr;
//------------- find a location to leave the beach ------------//
for(int i=2; i<=9; i++)
{
m.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;
if(checkXLoc%2 || checkYLoc%2)
continue;
locPtr = world.get_loc(checkXLoc, checkYLoc);
if(terrain_res[locPtr->terrain_id]->average_type==TERRAIN_OCEAN &&
locPtr->can_move(mobile_type))
{
found++;
break;
}
}
if(!found)
return; // no suitable location, wait until finding suitable location
//---------------- leave now --------------------//
set_dir(shipOldXLoc, shipOldYLoc, checkXLoc, checkYLoc);
set_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);
}
//----------- End of function Unit::ship_leave_beach -----------//