/******************************************************************************* * UNITMOVE.C * * - Declarations and functions for unit movement * * * * FREE SPACE COLONISATION * * (c)2002 - 2017 Paul Mueller * * * * 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 #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 */ 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 */ 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 */ 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); }