/*******************************************************************************
* UNIT.C *
* - Declarations and functions for handling game part of units *
* *
* 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 <stdio.h> /* sprintf() */
#include <string.h> /* memset() */
#include "fscfile.h"
#include "fsctool.h"
#include "unitinfo.h" /* ORDER_* */
#include "unittype.h"
/* -- Own header -- */
#include "unit.h"
/*******************************************************************************
* DEFINES *
*******************************************************************************/
#define UNIT_FREESLOT_TYPE (char)(-1)
#define UNIT_MAX 600
/*******************************************************************************
* TYPEDEFS *
*******************************************************************************/
/*******************************************************************************
* DATA *
*******************************************************************************/
static UNIT_T Units[UNIT_MAX + 2]; /* 0 is invalid, shared with frontend */
/*******************************************************************************
* CODE *
*******************************************************************************/
/*
* Name:
* unit_set_name
* Description:
* Creates a name for given unit, based on given unittype
* Input:
* punit *: Pointer on unit to create name for
* put *: Pointer on unit type
*/
static void unit_set_name(UNIT_T *punit, UNITTYPE_T *put)
{
char u_name[100];
sprintf(u_name, "%s %d", put->name, fsctool_rand_no(600));
/* Clamp to maximum length */
strncpy(punit->name, u_name, UNIT_NAMELEN);
}
/* ========================================================================== */
/* ======================= PUBLIC FUNCTIONS ================================= */
/* ========================================================================== */
/*
* Name:
* unit_init
* Description:
* Initializes the buffer for the units
* Input:
* None
*/
void unit_init(void)
{
memset(&Units[0], 0, sizeof(UNIT_T) * (UNIT_MAX + 2));
}
/*
* Name:
* unit_get
* Description:
* Returns a pointer on the unit with given id. If the id is invalid
* then a pointer on unit 0 is returned.
* Input:
* unit_no
*/
UNIT_T *unit_get(int unit_no)
{
if((unit_no < 1) || (unit_no >= UNIT_MAX))
{
/* Unit is invalid */
Units[0].type = -1;
return &Units[0];
}
return &Units[unit_no];
}
/*
* Name:
* unit_get_name
* Description:
* Returns the name of the unit in 'pdest'
* Input:
* unit_no: For this unit
* pdest *: Where to return the units name
*/
void unit_get_name(int unit_no, char *pdest)
{
strcpy(unit_get(unit_no)->name, pdest);
}
/*
* Name:
* unit_get_list
* Description:
* Fills in the id' of the players units into given list
* Counts all units for given owner.
* Input:
* owner: Number of owner to counts unit for(-1: all players)
* plist *: Where to return the numbers of units
* withorder: Only units with this order (-1: all units)
* Output:
* Number of units in list.
*/
int unit_get_list(int owner, int *plist, char withorder)
{
int i;
int count;
count = 0;
for(i = 1; i < UNIT_MAX; i++)
{
/* First check if we have to bail out */
if(Units[i].type == 0)
{
break;
}
if(owner < 0 || Units[i].pi.owner == owner)
{
if((withorder < 0) || (Units[i].order.what == withorder))
{
/* Save number of unit */
*plist = i;
plist++;
count++;
}
}
}
/* Sign end of list */
*plist = 0;
return count;
}
/* ============== Unit list functions ============== */
/*
* Name:
* unit_list_remove
* Description:
* Removes unit with given if from given linked list
* Input:
* pbase *:
* unit_no:
*/
void unit_list_remove(short int *pbase, short int unit_no)
{
UNIT_T *pactual;
short int actual_unit;
if(pbase == NULL)
{
/* Error, should never happen */
return;
}
actual_unit = *pbase;
do
{
pactual = &Units[actual_unit];
if(actual_unit == unit_no)
{
/* found it, remove it */
(*pbase) = pactual->next_id; /* Attach next to prevoius unit */
pactual->next_id = 0; /* Set next to zero */
return;
}
pbase = &pactual->next_id; /* Where to attach next, if */
/* actual unit is removed */
actual_unit = pactual->next_id;
}
while(actual_unit);
}
/*
* Name:
* unit_list_insert
* Description:
* Adds a unit to the given list. Add it at the front of possible units
* already in existing list on tile
* Input:
* pbase *: Pointer on base of linked list
* unit_no: Id of unit to add to list
*/
void unit_list_insert(short int *pbase, short int unit_no)
{
short int old_base;
old_base = (*pbase); /* Save posible actual unit for attachment */
/* If none available, is anyway 0! */
(*pbase) = unit_no; /* Insert new unit at base */
/* Attach previous unit(s) to base */
Units[unit_no].next_id = old_base;
}
/*
* Name:
* unit_list_find
* Description:
* Looks for a unit in given list.
* Input:
* base *: Pointer on base of linked list
* unitno: Id of unit to look in list for
* Output:
* Number of given unit, if succeeded, else 0
*/
int unit_list_find(short int *pbase, short int unit_no)
{
UNIT_T *punit;
int next;
next = (*pbase);
while (next)
{
punit = &Units[next];
if(next == unit_no)
{
return next;
}
next = punit->next_id;
}
return 0;
}
/*
* Name:
* unit_list_count
* Description:
* Counts the units in the given list.
* Input:
* base *: Pointer on base of linked list
* Output:
* Number of units in list.
*/
int unit_list_count(short int *pbase)
{
UNIT_T *punit;
short int next;
int cnt;
cnt = 0;
next = (*pbase);
while(next)
{
punit = &Units[next];
cnt++;
next = punit->next_id;
}
return cnt;
}
/*
* Name:
* unit_delete
* Description:
* Removes a unit from thr games internal table
* Input:
* punit *: Pointer on unit to delete.
* Replaces:
* wipe_unit
*/
void unit_delete(UNIT_T *punit)
{
memset(punit, 0, sizeof(UNIT_T));
/* Is a free slot */
punit->type = UNIT_FREESLOT_TYPE;
}
/*
* Name:
* unit_create
* Description:
* On success, returns the id of a new created unit, else 0.
* Input:
* unit_type: Type of unit to create
* key_code: Get type from this keycode
* owner: Owner of unit
* x, y: Position of unit
* cargo_load: Cargo to load on unit, if any
* Output:
* Pointer on unit slot, or pointer on Slot 0, if failed
*/
int unit_create(char unit_type, char key_code[4], char owner, char x, char y, char cargo_load)
{
int i;
UNIT_T *punit;
UNITTYPE_T *put;
for(i = 1; i < UNIT_MAX; i++)
{
if(Units[i].type <= 0)
{
punit = &Units[i];
/* We found a free slot - initialize it */
memset(punit, 0, sizeof(UNIT_T));
/* -------- Add the info part --------- */
punit->type = unit_type;
punit->pi.owner = owner;
punit->pi.x = x;
punit->pi.y = y;
punit->pi.range_bits = (0x01 << UNITTYPE_RANGE_SHORT);
punit->cargo_load = cargo_load;
punit->order.what = ORDER_NONE;
/* Add the additional info from unittype */
put = unittype_get(unit_type, key_code);
punit->hp[UI_VALUE_FULL] = put->hit;
punit->hp[UI_VALUE_ACT] = put->hit;
punit->moves[UI_VALUE_FULL] = put->moves;
punit->moves[UI_VALUE_ACT] = put->moves;
punit->attack = put->att;
punit->defense = put->def;
punit->sensor_range = put->sensor_range;
punit->ability = put->ability;
unit_set_name(punit, put);
return i;
}
}
/* Invalid unit */
return 0;
}
/*
* Name:
* unit_create_from_unit
* Description:
* On success, returns the id of a new created unit, else 0.
* The unit is created using the given unit as template.
* Input:
* unit_no: Unit to use as template
* Output:
* Id of new created unit
*/
int unit_create_from_unit(int unit_no)
{
int i;
for(i = 1; i < UNIT_MAX; i++)
{
if(Units[i].pi.owner <= 0 || Units[i].type < 0)
{
memcpy(&Units[i], &Units[unit_no], sizeof(UNIT_T));
return i;
}
}
return 0;
}
/*
* Name:
* unit_find_next
* Description:
* Finds the next unit for given 'owner_no'
* Input:
* owner_no: Number of units owner
* act_unit_no: Number of unit to start from, 0: Find from start of list
* with_moves: A unit with moves left, 2: and order
* Output:
* > 0: New unit found
*/
int unit_find_next(char owner_no, int act_unit_no, char with_moves)
{
int i;
for(i = act_unit_no + 1; i < UNIT_MAX; i++)
{
if(Units[i].pi.owner == owner_no)
{
if(!with_moves)
{
return i;
}
else if(with_moves > 0)
{
if(Units[i].moves[UI_VALUE_ACT] > 0)
{
if(with_moves == 2 && Units[i].order.what > 0)
{
return i;
}
else if(with_moves == 1)
{
return i;
}
}
}
}
}
return 0;
}
/*
* Name:
* unit_restore_moves
* Description:
* Restore the move points of all units in game
* Input:
* None
*/
void unit_restore_moves(void)
{
UNIT_T *punit;
punit = &Units[1];
while(punit->type != 0)
{
punit->moves[UI_VALUE_ACT] = punit->moves[UI_VALUE_FULL];
punit++;
}
}
/*
* Name:
* unitGetMilitaryMightSum
* Description:
* Returns the sum of all attack and defense values of active units
* for every player in '*vallist'
* Input:
* attackval *: Sum of attack points
* defenseval *: Sum of defense values
* numvalues: Number of values in arrays
*/
void unitGetMilitaryMightSum(int *attackval, int *defenseval, int numvalues)
{
UNIT_T *punit;
memset(attackval, 0, numvalues * sizeof(int));
memset(defenseval, 0, numvalues * sizeof(int));
punit = &Units[1];
while(punit->type != 0)
{
if(punit->pi.owner > 0)
{
attackval[(int)punit->pi.owner] += punit->attack;
defenseval[(int)punit->pi.owner] += punit->defense;
}
punit++;
}
}
/*
* Name:
* unit_change_to_ability
* Description:
* Change the given unit to the type given in argument
* Input:
* punit *: Pointer on unit to change given unit to
* ability: Target ability
*/
void unit_change_to_ability(UNIT_T *punit, int ability)
{
UNITTYPE_T *put, *putold;
char type;
int hitpoints, attack, defense;
type = unittype_best_roleunit(0, ability);
put = unittype_get(type, NULL);
putold = unittype_get(punit->type, NULL); /* For calculation of bonus */
/* taken from unit changed from */
/* Add the additional info from unittype */
if(putold->hit > 0)
{
hitpoints = put->hit * punit->hp[UI_VALUE_FULL] / putold->hit;
}
if(putold->att > 0)
{
attack = put->att * punit->attack / putold->att;
}
if(putold->def > 0)
{
defense = put->def * punit->defense / putold->def;
}
/* New type */
punit->type = (char)type;
punit->hp[UI_VALUE_FULL] = (char)hitpoints;
punit->hp[UI_VALUE_ACT] = (char)hitpoints;
punit->moves[UI_VALUE_FULL] = 0;
punit->moves[UI_VALUE_ACT] = 0;
punit->attack = (char)attack;
punit->defense = (char)defense;
unit_set_name(punit, put);
}
/* ========= Load and save data of units ========= */
/*
* Name:
* unit_get_loadsave_info
* Description:
* If 'save' is true then fill in struct 'info' with data needed for
* saving data given in 'data *'. 'numrec' must hold the maximum number
* of records that can be filled, 'recsize' the maximum size of record to
* be saved.
* Input:
* recsize *: Where to fill in the size of the record
* numrec *: Where to put the number of records to be saved
* save: Return info for save / load given info into game
* Output:
* Number of data-descriptors
*/
int unit_get_loadsave_info(FSCFILE_DATADESC_T *pdatadesc, char save)
{
UNIT_T *punit;
short int num_rec;
if(save)
{
/* Start with first unit */
punit = &Units[1];
num_rec = 0;
while(punit->type != 0)
{
num_rec++;
punit++;
}
}
else
{
/* Maximum number of units that can be loaded */
num_rec = UNIT_MAX;
/* Initialize the buffer for the unit data */
unit_init();
}
/* == Info about the Units == */
pdatadesc->rec_info.data_name = FSCFILE_UNIT;
pdatadesc->rec_info.rec_size = sizeof(UNIT_T);
pdatadesc->rec_info.num_rec = num_rec;
pdatadesc->pdata = &Units[1];
return 1;
}