/*******************************************************************************
* UNITMOVE.C *
* - Declarations and functions for unit movement *
* *
* FREE SPACE COLONISATION *
* (c)2002 - 2017 Paul Mueller <muellerp61@bluewin.ch> *
* *
* 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 Library General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
*******************************************************************************/
/*******************************************************************************
* INCLUDES *
*******************************************************************************/
#include <string.h>
#include "fscmap.h" /* Movement on map, info about map */
#include "fsctool.h"
#include "game.h"
#include "msg.h" /* Send message, if something happened */
#include "starsys.h"
#include "unit.h" /* Handling of units */
#include "unitinfo.h"
#include "unittype.h"
#include "colonize.h"
/* -- Own header -- */
#include "unitmove.h"
/*******************************************************************************
* DEFINES *
*******************************************************************************/
#define UNITMOVE_GOTO_STUCK 0
#define UNITMOVE_GOTO_MOVING 1
#define UNITMOVE_GOTO_REACHED 2
/*******************************************************************************
* DATA *
*******************************************************************************/
/*******************************************************************************
* CODE *
*******************************************************************************/
/*
* Name:
* unitmove_update_info
* Description:
* Does an update on the management info based on the actual chosen unit
* Input:
* unit_no: For this unit
* punit_info *: Info struct to fill
*/
static void unitmove_update_info(int unit_no, UNIT_INFO_T *punit_info)
{
UNIT_T *punit;
UNITTYPE_T *punit_type;
int i;
punit = unit_get(unit_no);
/* -- First get its name -- */
strncpy(punit_info->name, punit->name, 20);
punit_info->name[20] = 0;
for(i = 0; i < 2; i++)
{
punit_info->hp[i] = punit->hp[i];
punit_info->moves[i] = punit->moves[i];
}
punit_info->attack = punit->attack;
punit_info->defense = punit->defense; /* Attack and defense values */
punit_info->sensor_range = punit->sensor_range; /* Sensorrange for this unit */
/* ------- Save data about actual chosen unit ---------------- */
punit_info->unit_no = unit_no;
punit_info->map_pos = punit->pos;
punit_info->owner_no = punit->owner;
punit_info->type = punit->type;
punit_info->order_no = punit->order.what;
/* Additional info about the units type and abilities */
punit_type = unittype_get(punit_info->type, NULL);
punit_info->ability = punit_type->ability;
punit_info->range_no = punit_type->range;
punit_info->numpos_reachable =
fscmap_get_adjacent(punit_info->owner_no,
punit_info->map_pos,
punit_info->adjposlist,
punit_info->range_no);
/*
/ * The adjacent tiles for this unit * /
int num_adj;
int adjposlist[10];
*/
/* int cargo_type,
cargo_load; */ /* Number of settlers, pduction-points */
/* Has to be filled, if planet is chosen with unit */
/* ==> char special_actions[10]; */
}
/*
* Name:
* unitmove_explore_stars
* Description:
* Get an exploration target for given unit
* Input:
* player_no: For this nation
* start_pos: Calculate distances from here
* range_no: For this range
* Output:
* Nearest target not explored
*/
static int unitmove_explore_stars(int player_no, int start_pos, char range_no)
{
FSCMAP_TILEINFO_T ti[3];
int num_star;
num_star = fscmap_get_rangeinfo(player_no, start_pos, range_no, ti, 2, FSCMAP_FINFO_STAR);
return ti[0].pos;
}
/*
* Name:
* unitmove_pos_pos
* Description:
* Moves the unit from the units pos to 'destpos'. 'destpos' must be
* adjacent to the units position.
* Adjusts the moves left for the unit.
* Adjusts the 'known' flags on map
* Don't moves unit, if unit has no moves left.
* Input:
* punit_info *: Command to handle
* punit *: Pointer on unit
* dest_pos: Where to move from actual unit position
*/
static void unitmove_pos_pos(UNIT_INFO_T *punit_info, UNIT_T *punit, int dest_pos)
{
MSG_INFO_T msg;
int unit_no;
if(punit->moves[UI_VALUE_ACT] > 0)
{
/* There are moves left, move it */
unit_no = punit_info->unit_no;
/* ----- Save the info for display of movement */
msg_prepare(0, punit->owner, MSG_TYPE_INFO, &msg);
msg.unit_no = unit_no;
msg.pos = punit->pos;
msg.args[0] = dest_pos;
fscmap_unit_remove(unit_no, punit->pos);
fscmap_unit_add(unit_no, dest_pos);
punit->moves[UI_VALUE_ACT]--;
msg.args[1] = punit->moves[UI_VALUE_ACT];
/* Could move -- Tell the game-player it happened */
msg.num = GRES_UNIT_MOVED;
msg_send(&msg, 1);
}
}
/*
* Name:
* unitmove_goto
* Description:
* Does the 'goto' moves for given unit. If the goto was stopped for
* whatever reason, the movement result is set.
* Input:
* punit_info *: Pointer on command to fill with result
* punit *: Pointer on unit to execute goto for.
* dest_pos: Where to move
* Output:
* Result: UNITMOVE_GOTO_*
* Last change:
* 2010-01-30 bitnapper
*/
static int unitmove_goto(UNIT_INFO_T *punit_info, UNIT_T *punit, int dest_pos)
{
int i;
if(dest_pos < 0)
{
/* Invalid position could be sent by human player */
/* TODO: Send player a message that he cant move into this direction */
return UNITMOVE_GOTO_STUCK;
}
punit_info->numpos_reachable =
fscmap_get_adjacent(punit_info->owner_no,
punit_info->owner_no,
punit_info->adjposlist,
punit_info->range_no);
for(i = 0; i < 8; i++)
{
if(punit_info->adjposlist[i] == dest_pos)
{
/* Move to adjacent field, order is done */
unitmove_pos_pos(punit_info, punit, dest_pos);
return UNITMOVE_GOTO_REACHED;
}
}
/* ------ Otherwise get facing and goto into direction found ---- */
punit->facing = fscmap_get_long_heading(punit->goto_start, punit->pos, dest_pos);
/* Try to move the unit in given direction */
dest_pos = punit_info->adjposlist[(int)punit->facing];
if(dest_pos >= 0)
{
/* ------ Move to valid position ---- */
unitmove_pos_pos(punit_info, punit, dest_pos);
return UNITMOVE_GOTO_MOVING;
}
return UNITMOVE_GOTO_STUCK;
}
/*
* Name:
* unitmove_range_border
* Description:
* Moves along the maximum range of given unit. If not at border,
* moves into actual direction given by 'facing'
* Input:
* punit_info *: Pointer on command struct to handle
* punit *: Pointer on unit to handle
*/
static void unitmove_range_border(UNIT_INFO_T *punit_info, UNIT_T *punit)
{
int dir_count;
int facing;
char turn_dir;
int ahead_pos, left_pos, right_pos, dest_pos;
/* 1; Check if we can move at all */
if(! punit_info->numpos_reachable)
{
msg_send_short(0, punit->owner, MSG_TYPE_INFO, MSG_UNIT_STUCKMAP);
return;
}
/* 2: Check, if we are at range border TODO: Use 'SetOrder' and 'ExecuteOrder' */
facing = punit->facing;
dest_pos = punit_info->adjposlist[facing];
if(punit_info->numpos_reachable < 8)
{
/* We are at our ranges border */
/* Default: Move ahead */
ahead_pos = punit_info->adjposlist[facing];
left_pos = punit_info->adjposlist[(facing - 1) & 0x07];
right_pos = punit_info->adjposlist[(facing + 1) & 0x07];
if(ahead_pos < 0)
{
/* Can't move into actual direction */
if(left_pos > 0 && right_pos < 0)
{
turn_dir = -1;
}
else if(left_pos < 0 && right_pos > 0)
{
turn_dir = +1;
}
else
{
/* Both sides oder none blocked, choose by chance */
turn_dir = -1;
if(fsctool_rand_no(2) == 2)
{
turn_dir = 1;
}
}
if(dest_pos < 0)
{
/* Look for new position into given turn direction */
for(dir_count = 0; dir_count < 8; dir_count++)
{
facing += turn_dir;
facing &= 0x07;
dest_pos = punit_info->adjposlist[facing];
if(dest_pos > 0)
{
break;
}
}
}
}
}
/* Move ahead, border not reached yet */
if(dest_pos > 0)
{
punit->facing = facing;
unitmove_pos_pos(punit_info, punit, dest_pos);
}
}
/*
* Name:
* unitmove_patrol
* Description:
* Does
* Checks if an alien unit is within units sensor range. If true,
* the given unit is set to ORDER_NONE.
* Input:
* punit_info *: Pointer on command to fill with result
* punit *: Pointer on unit to handle
*/
static void unitmove_patrol(UNIT_INFO_T *punit_info, UNIT_T *punit)
{
/* FIXME: Add proper code */
unitmove_range_border(punit_info, punit);
}
/*
* Name:
* unitmove_explore
* Description:
* Searches for unexplored tiles and moves toward them.
* Input:
* punit_info *: Pointer on command to fill with result
* punit *: Pointer on unit to handle
*/
static void unitmove_explore(UNIT_INFO_T *punit_info, UNIT_T *punit)
{
int result;
if(! punit->order.target)
{
punit->order.target = unitmove_explore_stars(punit->owner, punit->pos, punit_info->range_no);
punit->goto_start = punit->pos; /* _MUST_ be set for 'GOTO' */
/* TODO: Set my own 'GOAL_T' */
}
if(punit->order.target > 0)
{
result = unitmove_goto(punit_info, punit, punit->order.target);
if(result == UNITMOVE_GOTO_REACHED)
{
/* ------- Explore target is reached ----- */
punit->order.target = 0;
}
else if(result == UNITMOVE_GOTO_STUCK)
{
punit->order.target = 0;
unitmove_range_border(punit_info, punit);
}
}
else
{
/* TODO: Do this code too, if the actual goto_dest ==> 'punit->order.target' is off range */
unitmove_range_border(punit_info, punit);
}
}
/*
* Name:
* unitmove_sentry
* Description:
* Checks if an alien unit is within units sensor range. If true,
* the given sends a message, that an alien is seen.
* Input:
* punit *: Pointer on unit to handle
* Last change:
* 2010-01-28 / bitnapper
*/
static void unitmove_sentry(UNIT_T *punit)
{
FSCMAP_TILEINFO_T ti;
int has_alien;
MSG_INFO_T msg;
/* TODO: Don't react if in orbit of a planet. Merge with guard. */
has_alien = fscmap_get_rangeinfo(punit->owner,
punit->pos,
punit->sensor_range,
&ti,
1,
FSCMAP_FINFO_ALIENUNIT);
if(has_alien > 0)
{
msg_prepare(0, punit->owner, MSG_TYPE_INFO, &msg);
msg.pos = punit->pos;
msg.num = MSG_OTHER_ALIENSEEN;
/* For possible use in AI */
msg.args[0] = ti.pos;
msg_send(&msg, 1);
if(punit->moves[UI_VALUE_FULL] > 0)
{
/* Exclude outposts/starbases from changing order*/
punit->order.what = ORDER_NONE;
}
}
}
/*
* Name:
* unitmove_guard
* Description:
* Checks if an alien unit is within units sensor range. If true,
* the given unit is set to ORDER_NONE.
* Input:
* punit *: Pointer on unit to handle
* Output:
* Result of movement
*/
static void unitmove_guard(UNIT_T *punit)
{
FSCMAP_TILEINFO_T ti[100];
int alien_pos;
MSG_INFO_T msg;
alien_pos = fscmap_get_rangeinfo(punit->owner,
punit->pos,
punit->sensor_range,
ti,
98,
FSCMAP_FINFO_ALIENUNIT);
if(alien_pos > 0)
{
msg_prepare(0, punit->owner, MSG_TYPE_INFO, &msg);
msg.pos = punit->pos;
msg.num = MSG_UNIT_SETFOCUS;
/* For possible use in AI */
msg.args[0] = alien_pos;
msg_send(&msg, 1);
punit->order.what = ORDER_NONE;
}
}
/*
* Name:
* unitmove_auto_attack
* Description:
* Does attack as soon as an enemy unit comes into sensor range.
* Sends a message to player that it happened.
* Input:
* punit *: Pointer on unit to handle
*/
static void unitmove_auto_attack(UNIT_T *punit)
{
FSCMAP_TILEINFO_T ti[100];
int alien_pos;
alien_pos = fscmap_get_rangeinfo(punit->owner,
punit->pos,
punit->sensor_range,
ti,
98,
FSCMAP_FINFO_ALIENUNIT);
if(alien_pos > 0)
{
/* TODO: Attack ship at alien position */
}
}
/*
* Name:
* unitmove_auto_retreat
* Description:
* Does retreat as soon as an enemy unit comes into sensor range.
* At the moment only uses up the moves of given unit
* Input:
* punit *: Pointer on unit to handle
*/
static void unitmove_auto_retreat(UNIT_T *punit)
{
FSCMAP_TILEINFO_T ti[100];
int alien_pos;
alien_pos = fscmap_get_rangeinfo(punit->owner,
punit->pos,
punit->sensor_range,
ti,
98,
FSCMAP_FINFO_ALIENUNIT);
if(alien_pos > 0)
{
/* TODO: Retreat from ship at alien position */
}
}
/*
* Name:
* unitmove_build
* Description:
* Does the 'build' comand for given unit, using its order values
* Input:
* punit *: Pointer on unit to handle
* unit_no: Number of given unit
*/
static void unitmove_build(UNIT_T *punit, int unit_no)
{
GAME_EFFECT_T ge;
/* ------ Build in more the one turn in a later stage */
if(punit->order.build > 0 && punit->order.target > 0)
{
/* BASICALLY: No buildtime, in a later stage: more then one turn */
colonizeDoConstruct(unit_no, punit->order.build, punit->order.target);
/* #TESTCODE: As the buildpoints are > 0, the outpost is built immediately */
/*
starsys_build_outpost(punit->owner,
punit->order.target,
punit->order.build,
punit->moves[UI_VALUE_ACT]);
*/
starsys_build_outpost(punit->owner,
punit->order.target,
punit->order.build,
100);
}
else
{
/* Change unit to starbase -- BASICALLY: No buildtime, add range effect */
/* TODO: Change unit to starbase, if there's already an outpost here */
/* TODO: Ignore order, if there is already a starbase ==> add extension */
unit_change_to_ability(punit, UNIT_ABILITY_OUTPOST);
punit->order.what = ORDER_SENTRY;
unittype_get_effect(punit->type, &ge);
effect_set(&ge, punit->pos, 0, 0, punit->owner);
}
punit->moves[UI_VALUE_ACT] = 0;
/* ---- Tell the caller, that he has to choose the next unit ---- */
msg_send_short(0, punit->owner, MSG_TYPE_UNIT, MSG_UNIT_DONE);
}
/*
* Name:
* unitmove_terraform
* Description:
* Does terraforming with given unit on 'target'
* Input:
* punit *: Pointer on unit to handle
*/
static void unitmove_terraform(UNIT_T *punit)
{
MSG_INFO_T msg;
int map_pos;
if(punit->moves[UI_VALUE_ACT] > 0)
{
map_pos = starsys_planet_doterraform(punit->order.target, 'M', punit->moves[UI_VALUE_ACT]);
if(map_pos > 0)
{
/*** Send message that planets terraforming is done ***/
msg_prepare(0, punit->owner, MSG_TYPE_INFO, &msg);
msg.num = MSG_PLANET_TERRAFORMED;
msg.planet_no = punit->order.target;
msg.pos = map_pos;
msg_send(&msg, 0);
/* Reset the units order */
punit->order.what = ORDER_NONE;
punit->order.target = 0;
}
punit->moves[UI_VALUE_ACT] = 0;
}
}
/*
* Name:
* unitmoveColonize
* Description:
* Do the colonize command, if possible with given unit
* Input:
* punit *: Pointer on unit to handle
* punit_info: Pointer on manage info, because unit has to be removed
*/
static void unitmoveColonize(UNIT_T *punit, UNIT_INFO_T *punit_info)
{
MSG_INFO_T msg;
int result, act_unit_no, planet_no;
/* --- Save values for message --- */
planet_no = punit->order.target;
/* Know target, exec */
result = colonizeDoColonize(punit_info->unit_no, planet_no);
if(result == COLONIZE_SUCCESS)
{
msg_prepare(0, punit->owner, MSG_TYPE_UNIT, &msg);
msg.num = GRES_UNIT_COLONIZED;
msg.planet_no = planet_no;
msg.unit_no = punit_info->unit_no;
msg_send(&msg, 0);
/* ------- Delete unit, set cursor to new unit, if possible --- */
act_unit_no = punit_info->unit_no;
punit_info->unit_no = unit_find_next(punit->owner, act_unit_no, 0);
/* ------- Remove the unit from the map ------------ */
fscmap_unit_remove(punit_info->map_pos, act_unit_no);
/* ------- Delete unit -------- */
unit_delete(punit);
}
}
/*
* Name:
* unitmove_build_outpost
* Description:
* Do build something on a planet from space
* Input:
* punit *: Pointer on unit to handle
*/
static void unitmove_build_outpost(UNIT_T *punit)
{
MSG_INFO_T msg;
if(starsys_build_outpost(punit->owner, punit->order.target, punit->order.build, punit->moves[UI_VALUE_ACT]) > 0)
{
/*** Send message to nation that the work is done */
msg_prepare(punit->owner, 0, MSG_TYPE_INFO, &msg);
msg.num = MSG_UNIT_OUTPOSTBUILT;
msg.pos = punit->pos;
msg.planet_no = punit->order.target;
msg_send(&msg, 0);
/* ------------ Work is done ----------------- */
punit->order.what = ORDER_NONE;
punit->order.target = 0;
punit->order.build = 0;
}
punit->moves[UI_VALUE_ACT] = 0;
}
/*
* Name:
* unitmove_build_space
* Description:
* Do build something on a planet from space
* Input:
* punit *: Pointer on unit to handle
*/
static void unitmove_build_space(UNIT_T *punit)
{
GAME_EFFECT_T ge;
if(punit->moves[UI_VALUE_ACT] > 0)
{
punit->hp[UI_VALUE_ACT] += punit->moves[UI_VALUE_ACT];
if(punit->hp[UI_VALUE_ACT] >= punit->hp[UI_VALUE_FULL])
{
punit->order.what = ORDER_SENTRY;
/* Change to outpost */
unit_change_to_ability(punit, UNIT_ABILITY_OUTPOST);
unittype_get_effect(punit->type, &ge);
effect_set(&ge, punit->pos, 0, 0, punit->owner);
}
}
}
#if 0
/*
* Name:
* unitmoveStartBuildSpace
* Description:
* Given order started something to build in space
* Input:
* punit *: Pointer on unit to handle
*/
static void unitmoveStartBuildSpace(UNIT_T *punit)
{
MSG_INFO_T msg;
if(punit->order.what == ORDER_BUILD && punit->order.build == 0)
{
/* Handle special 'building outpost' in space */
punit->hp[UI_VALUE_ACT] = 1;
/* Are added to 'punit->hp[UI_VALUE_ACT]' every turn */
}
/* Send message, that the unit got an order */
msg_prepare(0, punit->owner, MSG_TYPE_UNIT, &msg);
msg.num = MSG_UNIT_DONE;
msg_send(&msg, 1);
}
#endif
/*
* Name:
* unitmove_find_with_order
* Description:
* Looks for a unit having an ORDER and MOVES left. Starting with
* actual unit.
* If the actual unit has no order, then the focus is moved to the
* next unit, until a UNIT_T for movement is found or we have returned to
* the actual unit.
* If a unit is found, the cursor is set to this unit
* Input:
* punit_info *: Pointer on unit management struct
* Output:
* We found a unit with an ORDER having moves left
* Last change:
* 2010-09-26 <bitnapper>
*/
static int unitmove_find_with_order(UNIT_INFO_T *punit_info)
{
UNIT_T *punit;
int old_unit;
int count;
if(!punit_info->unit_no)
{
/* No unit available */
return 0;
}
old_unit = punit_info->unit_no;
count = 0;
do
{
punit_info->unit_no = unit_find_next(punit_info->owner_no, punit_info->unit_no, 1);
punit = unit_get(punit_info->unit_no);
if(punit->order.what > 0)
{
/* TODO: Take value 'order.target_type'
==> UNIT_T_TARGET_* into account */
/* It's assumed, that a target is given, where a target is needed */
/* Checking for a valid target has to be done in 'SetOrder' */
/* Set the cursor in given management struct */
return 1;
}
/* For safety reasons */
count++;
if(count > 600) return 0;
}
while(punit_info->unit_no != old_unit);
/* Didn't find a unit with order an moves */
return 0;
}
/*
* Name:
* unitmove_set_order_build
* Description:
* Validates the order for a 'build' command.
* It's assumed, that the order values are already set, save 'build'.
* If the given target / order is invalid, the order is cleared
* Input:
* punit *: Pointer on unit to validate / set order
* build: Build this at 'target', depending on order
*/
static void unitmove_set_order_build(UNIT_T *punit, int build)
{
FSCMAP_TILEINFO_T ti;
UNITTYPE_T *put;
fscmap_get_tileinfo(punit->owner, punit->pos, &ti, 0xFF);
put = unittype_get(punit->type, NULL);
switch(put->ability)
{
case UNIT_ABILITY_CONSTRUCT:
if(build > 0 && ti.star_type >= 0)
{
/* TODO: Check if unit is at same position as 'target' (planet) */
punit->order.build = (char)build;
}
else if(build <= 0 && ti.star_type < 0)
{
/* TODO: Check, if there's already an outpost,
in this case, expand to a starbase.
If starbase: Expand with 'starbase-extension */
punit->order.build = 0; /* Build in space */
}
else
{
/* ----- Ignore the command, not allowed -------- */
punit->order.what = 0;
punit->order.target = 0;
}
break;
case UNIT_ABILITY_COLONIZE:
if(ti.star_type > 0 && ti.star_planets[0] > 0)
{
/* TODO: Build a colony, if a usable planet at given position */
punit->order.what = 0;
punit->order.target = 0;
return;
}
default:
punit->order.what = 0;
punit->order.target = 0;
break;
}
}
/*
* Name:
* unitmove_choose_next
* Description:
* Finds the next unit which has moves left and no order.
* If there's any, the command struct is filled with all info needed
* for the player to move that unit
* Input:
* punit_info *: Mangament info for player
* skip: Skip given unit yes/no
* Output:
* Focus has changed to new unit yes/no
* Last change:
* 2017-07-19 bitnapper
*/
static int unitmove_choose_next(UNIT_INFO_T *punit_info, int skip)
{
UNIT_T *punit;
int old_unit;
int count;
if(skip)
{
punit = unit_get(punit_info->unit_no);
punit->moves[UI_VALUE_ACT] = 0;
punit_info->moves[UI_VALUE_ACT] = 0;
}
/* Find next unit which has moves left and no order */
old_unit = punit_info->unit_no;
count = 0;
do
{
punit_info->unit_no = unit_find_next(punit_info->owner_no, punit_info->unit_no, 1);
punit = unit_get(punit_info->unit_no);
if(punit->moves[UI_VALUE_ACT] > 0 && punit->order.what == 0)
{
/* ------ Do an update of the management info --- */
unitmove_update_info(punit_info->unit_no, punit_info);
return 1;
}
/* For safety reasons */
count++;
if(count > 600) return 0;
}
while(old_unit != punit_info->unit_no);
return 0; /* No unit found with moves left */
}
/* ========================================================================== */
/* ======================== PUBLIC FUNCTIONS ============================= */
/* ========================================================================== */
/*
* Name:
* unitmove_fill_manageinfo
* Description:
* Fills in the given struct with info for the management of units
* Input:
* player_no: For this nation (-1: Only fill in data for actual unit)
* unit_no: Number of actal unit
* punit_info *: Struct to fill with data
*/
void unitmove_fill_manageinfo(char player_no, int unit_no, UNIT_INFO_T *punit_info)
{
if(player_no >= 0)
{
memset(punit_info, 0, sizeof(UNIT_INFO_T));
punit_info->owner_no = player_no;
punit_info->unit_no = unit_no;
}
if(punit_info->unit_no > 0)
{
unitmove_update_info(punit_info->unit_no, punit_info);
}
}
/*
* Name:
* unitmove_get_planetactions
* Description:
* Adds possible actions that can be taken by given unit.
* @TODO:
* If the possible order is 'ORDER_BUILD', then the
* 'proj_list' is filled, including the p '-1:
* Input:
* punit_info *: Pointer on info about unit to manage
* Output:
* Main action, that can be executed:
* ORDER_BUILD: It's possible to build an kind of outpost here
* ORDER_TERRAFORM: It's possible to terrraform the given planet
* ORDER_COLONIZE: It's possible to colonize this planet
*/
char unitmove_get_planetactions(UNIT_INFO_T *punit_info)
{
/* There are only actions possible, if the unit has moves left at all */
if(punit_info->moves[UI_VALUE_ACT] <= 0)
{
return 0;
}
/* @TODO: Fill in the 'proj_list' into the 'build' menu */
return 0;
}
/*
* Name:
* unitmoveExecuteOrder
* Description:
* Executes the order of the actual unit, if possible.
* Otherwise finds the next unit with an order and moves it
* Input:
* punit_info *: Pointer on management struct of units
* Last change:
* 2010-01-19 bitnapper
*/
int unitmoveExecuteOrder(UNIT_INFO_T *punit_info)
{
UNIT_T *punit;
int goto_result;
/* ---- SECOND: Find a unit with orders starting with the actual one */
if(! unitmove_find_with_order(punit_info))
{
return 0;
}
/* Fill in INFO about actual chosen UNIT_T */
unitmove_update_info(punit_info->unit_no, punit_info);
punit = unit_get(punit_info->unit_no);
/* Check, if the unit can move at all. If stuck, skip it. */
switch(punit->order.what)
{
case ORDER_AUTOATTACK:
/* Attack, if enemy unit comes in sensor range */
unitmove_auto_attack(punit);
break;
case ORDER_AUTORETREAT:
/* Do retreat, if enemy unit comes into sensor range */
unitmove_auto_retreat(punit);
break;
case ORDER_PATROL:
/* Patrol (multiple patrol points) */
unitmove_patrol(punit_info, punit);
break;
case ORDER_BORDERPATROL:
/* Patrols the borders */
/* Moves along the border */
unitmove_range_border(punit_info, punit);
break;
case ORDER_GOTO: /* Autopilot */
goto_result = unitmove_goto(punit_info, punit, punit->order.target);
if(goto_result == UNITMOVE_GOTO_REACHED) {
/* ----- We reached our target ----- */
punit->order.what = 0;
punit->order.target = 0;
}
else if(goto_result == UNITMOVE_GOTO_STUCK) {
/* For AI-Movement: If stuck, handle it */
punit->order.what = 0;
punit->order.target = 0;
return 0;
}
break;
case ORDER_EXPLORE: /* Auto explore */
unitmove_explore(punit_info, punit);
break;
case ORDER_COLONIZE:
unitmoveColonize(punit, punit_info);
break;
/* -------- Activities at end of turn ------------------------- */
case ORDER_BUILD:
unitmove_build(punit, punit_info->unit_no);
/* TODO: Use 'unitmoveStartBuildSpace(punit); for building in space */
break;
default:
return 0;
}
return 1;
}
/*
* Name:
* unitmove_choose_unit
* Description:
* Chooses a unit, depending on 'which'
* Sets the focus to the unit chosen. If no unit is given, the unit
* at given position is chosen, if any.
* Input:
* punit_info *: Command struct to fill in with info about unit
* value: Position or unit_no, depending on given 'UNITMOVE_CHOOSE'
* which: Which kind of unit to choose
* Output:
* Number of unit chosen, if any is found
* Last Change:
* 2017-06-20 <bitnapper>
*/
int unitmove_choose_unit(UNIT_INFO_T *punit_info, int value, char which)
{
FSCMAP_TILEINFO_T ti;
switch(which)
{
case UNITMOVE_CHOOSE_POSITION:
/* Choose unit at this position */
fscmap_get_tileinfo(punit_info->owner_no, value, &ti, FSCMAP_FINFO_UNIT);
if(ti.unit_no > 0 && ti.unit_owner == punit_info->owner_no)
{
punit_info->unit_no = ti.unit_no;
unitmove_update_info(ti.unit_no, punit_info);
}
break;
case UNITMOVE_CHOOSE_NUMBER:
/* Choose unit with this number */
break;
case UNITMOVE_CHOOSE_CURRENT:
/* Choose unit at list-cursor: Fill in data about unit */
unitmove_update_info(punit_info->unit_no, punit_info);
break;
case UNITMOVE_CHOOSE_NEXT:
unitmove_choose_next(punit_info, 0);
break;
case UNITMOVE_CHOOSE_NEXTSKIP:
unitmove_choose_next(punit_info, 1);
break;
default:
/* Call with invalid 'which' */
value = 0;
break;
}
return value;
}
/*
* Name:
* unitmove_choose_nextunit
* Description:
* Chooses a unit, depending on 'which'
* Sets the focus to the unit chosen. If no unit is given, the unit
* at given position is chosen, if any.
* Input:
* owner_no: For this owner (for AI)
* act_unit_no: Number of actual unit
* Output:
* Number of next unit, which has moves left
* Last Change:
* 2017-08-03 <bitnapper>
*/
int unitmove_choose_nextactive(char owner_no, int act_unit_no)
{
return unit_find_next(owner_no, act_unit_no, 1);
}
/*
* Name:
* unitmove_set_order
* Description:
* First validates the given order. If valid, sets the order, and fills in
* the possible additional info, if available
*
* Input:
* punit_info *: Pointer on info about unit to manage
* order_no: Set this order
* target: For movement or where to build, depending on order
* build: Build this at 'target', depending on order
*/
void unitmove_set_order(UNIT_INFO_T *punit_info, int order_no, int target, int build)
{
UNIT_T *punit;
punit = unit_get(punit_info->unit_no);
punit->order.what = (char)order_no;
punit->order.target = target;
punit->order.build = 0;
if(order_no == ORDER_GOTO)
{
/* More 'beautiful' movement for long range 'GOTO' movement */
punit->goto_start = punit->pos;
}
else if(order_no == ORDER_BUILD)
{
unitmove_set_order_build(punit, build);
/* Tell the owner, that he has to choose the next unit */
msg_send_short(0, punit->owner, MSG_TYPE_UNIT, MSG_UNIT_DONE);
}
else if(order_no == ORDER_TERRAFORM)
{
/* Tell the owner, that he has to choose the next unit */
msg_send_short(0, punit->owner, MSG_TYPE_UNIT, MSG_UNIT_DONE);
}
}
/*
* Name:
* unitmove_attack
* Description:
* Calculates the outcome of a fight between 'attacker_no' and 'defenderno'
* The attacker has the first move
* Input:
* attacker_no: Number of attackers unit
* defender_no: Number of the defenders unit
*/
void unitmove_attack(int attacker_no, int defender_no)
{
UNIT_T *pattacker, *pdefender;
int attack_val, defend_val;
pattacker = unit_get(attacker_no);
pdefender = unit_get(defender_no);
while(pattacker->hp[UI_VALUE_ACT] > 0 && pdefender->hp[UI_VALUE_ACT] > 0)
{
/* As long as one of both units is 'alive'... */
/* @todo: Add some randomness to the attack and the defense */
attack_val = pattacker->attack;
defend_val = pdefender->defense;
attack_val -= defend_val;
if(attack_val > 0)
{
pdefender->hp[UI_VALUE_ACT] -= (char)attack_val;
if(pdefender->hp[UI_VALUE_ACT] <= 0)
{
/* Defender is defeated... */
/* @todo: Remove defender, send message to defenders owner and move the attackers unit on the defenders position */
}
}
/* Now its the move of the defender */
/* @todo: Add some randomness to the attack and the defense */
attack_val = pdefender->attack;
defend_val = pattacker->defense;
if(attack_val > 0)
{
pattacker->hp[UI_VALUE_ACT] -= (char)attack_val;
if(pattacker->hp[UI_VALUE_ACT] <= 0)
{
/* Attacker is defeated... */
/* @todo: Remove attacker, send message to attackers owner */
}
}
}
}
/*
* Name:
* unitmoveEndTurn
* Description:
* Does all the auto-moves / commands, which have an effect at end of turn.
* Input:
* player_no: For this nation
*/
void unitmoveEndTurn(char player_no)
{
UNIT_T *punit;
int unit_no;
unit_no = 0;
do
{
unit_no = unit_find_next(player_no, unit_no, 1);
if(unit_no > 0)
{
punit = unit_get(unit_no);
switch(punit->order.what)
{
case ORDER_SENTRY:
/* Keep in place, but send a message, if an alien is seen */
unitmove_sentry(punit);
break;
case ORDER_GUARD:
/* Guard mode */
unitmove_guard(punit);
break;
case ORDER_TERRAFORM:
unitmove_terraform(punit);
break;
case ORDER_BUILD:
/* TODO: Use more then one turn to build an outpost/starbase */
if(punit->order.build > 0)
{
unitmove_build_outpost(punit);
}
else
{
unitmove_build_space(punit);
}
break;
}
}
}
while(unit_no > 0);
}