Gobligine/stratagus/selection.cpp

422 lines
10 KiB
C++

// ___________ _________ _____ __
// \_ _____/______ ____ ____ \_ ___ \____________ _/ ____\/ |_
// | __) \_ __ \_/ __ \_/ __ \/ \ \/\_ __ \__ \\ __\\ __\
// | \ | | \/\ ___/\ ___/\ \____| | \// __ \| | | |
// \___ / |__| \___ >\___ >\______ /|__| (____ /__| |__|
// \/ \/ \/ \/ \/
// ______________________ ______________________
// T H E W A R B E G I N S
// FreeCraft - A free fantasy real time strategy game engine
//
/**@name selection.c - The units' selection. */
/*
** (c) Copyright 1999,2000 by Patrice Fortier
**
** $Id$
*/
//@{
/*----------------------------------------------------------------------------
-- Includes
----------------------------------------------------------------------------*/
#include <stdio.h>
#include "freecraft.h"
#include "video.h"
#include "sound_id.h"
#include "unitsound.h"
#include "unittype.h"
#include "player.h"
#include "unit.h"
#include "interface.h"
#include "tileset.h"
#include "map.h"
/*----------------------------------------------------------------------------
-- Variables
----------------------------------------------------------------------------*/
global int NumSelected; /// Number of selected units
global Unit* Selected[MaxSelectable] = {
NoUnitP,NoUnitP,NoUnitP,
NoUnitP,NoUnitP,NoUnitP,
NoUnitP,NoUnitP,NoUnitP
}; /// All selected units
/*----------------------------------------------------------------------------
-- Functions
----------------------------------------------------------------------------*/
/**
** Unselect all the units in the current selection
*/
global void UnSelectAll(void)
{
while( NumSelected ) {
Selected[--NumSelected]->Selected=0;
Selected[NumSelected]=NoUnitP; // FIXME: only needed for old code
}
}
/**
** Replace a group of selected units by an other group of units.
**
** @param units Array of units to be selected.
** @param count Number of units in array to be selected.
*/
global void ChangeSelectedUnits(Unit** units,int count)
{
int i;
DebugCheck( count>MaxSelectable );
UnSelectAll();
for( i=0; i<count; i++ ) {
Selected[i]=units[i];
Selected[i]->Selected=1;
}
NumSelected=count;
}
/**
** Add a unit to the other selected units.
**
** @param unit Pointer to unit to add.
** @return false if NumSelected == MaxSelectable or
** unit is already selected, true otherwise.
*/
global int SelectUnit(Unit* unit)
{
if( NumSelected == MaxSelectable ) {
return 0;
}
if( unit->Selected ) {
return 0;
}
Selected[NumSelected++]=unit;
unit->Selected=1;
return 1;
}
/**
** Select a single unit, unselecting the previous ones
**
** @param unit Pointer to unit to be selected.
*/
global void SelectSingleUnit(Unit* unit)
{
ChangeSelectedUnits(&unit,1);
}
/**
** Unselect unit
**
** @param unit Pointer to unit to be unselected.
*/
global void UnSelectUnit(Unit* unit)
{
int i;
if( !unit->Selected ) {
return;
}
for( i=0; Selected[i]!=unit; i++) {
;
}
DebugCheck( i>=NumSelected );
if( i<--NumSelected ) {
Selected[i]=Selected[NumSelected];
}
Selected[NumSelected]=NoUnitP; // FIXME: only needed for old code
unit->Selected=0;
}
/**
** Toggle the selection of a unit in a group of selected units
**
** @param unit Pointer to unit to be toggled.
** @return 0 if unselected, 1 otherwise
*/
global int ToggleSelectUnit(Unit* unit)
{
if( unit->Selected ) {
UnSelectUnit(unit);
return 0;
}
SelectUnit(unit);
return 1;
}
/**
** Select units from a particular type and belonging to the local player.
**
** The base is included in the selection and defines
** the type of the other units to be selected.
**
** @param base Select all units of same type.
** @return Number of units found, 0 means selection unchanged
**
** FIXME: 0 can't happen. Maybe when scripting will use it?
*/
global int SelectUnitsByType(Unit* base)
{
Unit* unit;
Unit* table[MAX_UNITS];
UnitType* type;
int r;
int i;
type=base->Type;
DebugLevel3(__FUNCTION__"(%d)\n",base->UnitType->Type);
// select all visible units.
r=SelectUnits(MapX-1,MapY-1,MapX+MapWidth+1,MapY+MapHeight+1,table);
// if unit is a cadaver or hidden (not on map)
// no unit can be selected.
if( base->Removed || base->Command.Action==UnitActionDie ) {
return 0;
}
UnSelectAll();
Selected[0]=base;
base->Selected=1;
NumSelected=1;
// if unit isn't belonging to the player, or is a static unit
// (like a building), only 1 unit can be selected at the same time.
if( base->Player!=ThisPlayer || !type->SelectableByRectangle ) {
return NumSelected;
}
//
// Search for other visible units of the same type
//
// FIXME: peon/peasant with gold/wood & co are considered from
// different type... idem for tankers
for( i=0; i<r; ++i ) {
unit=table[i];
if( unit->Player!=ThisPlayer || unit->Type!=type ) {
continue;
}
if( UnitUnusable(unit) ) { // guess SelectUnits doesn't check this
continue;
}
if( unit==base ) { // no need to have the same unit twice :)
continue;
}
Selected[NumSelected++]=unit;
unit->Selected=1;
if( NumSelected==MaxSelectable ) {
break;
}
}
return NumSelected;
}
/**
** Change selected units to units from group #group_number
** Doesn't change the selection if the group has no unit.
**
** @param group_number number of the group to be selected.
** @return number of units in the group.
*/
global int SelectGroup(int group_number)
{
int nunits;
DebugCheck(group_number>NUM_GROUPS);
if( !(nunits=GetNumberUnitsOfGroup(group_number)) ) {
return 0;
}
ChangeSelectedUnits(GetUnitsOfGroup(group_number),nunits);
return NumSelected;
}
/**
** Select units from group of a particular unit.
** Doesn't change the selection if the group has no unit,
** or the unit doesn't belong to any group.
**
** @param unit unit belonging to the group to be selected.
** @return 0 if the unit doesn't belong to a group,
** or the number of units in the group.
*/
global int SelectGroupFromUnit(Unit *unit)
{
if( unit->GroupId==-1 ) {
return 0;
}
return SelectGroup(unit->GroupId);
}
/**
** Select the units selecteable by rectangle in a local table.
** Act like a filter: The source table is modified.
** Return the original table if no unit is found.
**
** @param table Input/Output table of units.
** @param num_units Number of units in input table.
** @return the number of units found.
*/
local int SelectOrganicUnitsInTable(Unit** table,int num_units)
{
Unit* unit;
int n,i;
for( n=i=0; i<num_units; i++ ) {
unit=table[i];
if( unit->Player!=ThisPlayer || !unit->Type->SelectableByRectangle ) {
continue;
}
if( UnitUnusable(unit) ) { // guess SelectUnits doesn't check this
continue;
}
table[n++]=unit;
if( n==MaxSelectable ) {
break;
}
}
return n;
}
/**
** Add the units in the rectangle to the current selection
**
** @param tx X start of selection rectangle in tile coordinates
** @param ty Y start of selection rectangle in tile coordinates
** @param w Selection rectangle width.
** @param h Selection rectangle height.
** @return the _total_ number of units selected.
*/
global int AddSelectedUnitsInRectangle(int tx,int ty,int w,int h)
{
Unit* table[MAX_UNITS];
int toggle_num;
int n,i;
// If there is no selected unit yet, do a simple selection.
if( !NumSelected ) {
return SelectUnitsInRectangle(tx, ty, w, h);
}
// If no unit in rectangle area... do nothing
if( !(toggle_num=SelectUnits(tx,ty,tx+w+1,ty+h+1,table)) ) {
return NumSelected;
}
// Check if the original selected unit (if it's alone) is ours,
// and can be selectable by rectangle.
// In this case, do nothing.
if( NumSelected == 1 &&
( Selected[0]->Player!=ThisPlayer ||
!Selected[0]->Type->SelectableByRectangle )) {
return NumSelected;
}
// Now we should only have mobile (organic) units belonging to us,
// so if there's no such units in the rectangle, do nothing.
if( !(n=SelectOrganicUnitsInTable(table,toggle_num)) ) {
return NumSelected;
}
for( i=0; i<n && NumSelected<MaxSelectable; i++ ) {
SelectUnit(table[i]);
}
return NumSelected;
}
/**
** Select units in a rectangle. Proceed in order in none found:
** - select local player mobile units
** - select one local player static unit (random)
** - select one neutral unit (critter, mine...)
** - select one enemy unit (random)
**
** @param tx X start of selection rectangle in tile coordinates
** @param ty Y start of selection rectangle in tile coordinates
** @param w Selection rectangle width.
** @param h Selection rectangle height.
** @return the number of units found.
*/
global int SelectUnitsInRectangle(int tx,int ty,int w,int h)
{
Unit* unit;
Unit* table[MAX_UNITS];
UnitType* type;
int r;
int n;
int i;
DebugLevel3(__FUNCTION__"(%d,%d,%d,%d)\n",tx,ty,w,h);
r=SelectUnits(tx,ty,tx+w+1,ty+h+1,table);
//
// 1) search for the player units selectable with rectangle
//
if( (n=SelectOrganicUnitsInTable(table,r)) ) {
ChangeSelectedUnits(table,n);
return n;
}
//
// 2) If no unit found, try a player's unit not selectable by rectangle
//
for( i=0; i<r; ++i ) {
unit=table[i];
if( unit->Player!=ThisPlayer ) {
continue;
}
if( !unit->Removed && unit->Command.Action!=UnitActionDie ) {
SelectSingleUnit(unit);
return 1;
}
}
//
// 3) If no unit found, try a resource or a neutral critter
//
for( i=0; i<r; ++i ) {
unit=table[i];
// Unit visible
if( !UnitVisible(unit) ) {
continue;
}
type=unit->Type;
if( type->Critter || type->GoldMine
|| (type->OilPatch && !unit->Removed) ) { // no oil platform!
SelectSingleUnit(unit);
return 1;
}
}
//
// 4) If no unit found, select an enemy unit (first found)
//
for( i=0; i<r; ++i ) {
unit=table[i];
// Unit visible
if( !UnitVisible(unit) ) {
continue;
}
if( !unit->Removed && unit->Command.Action!=UnitActionDie ) {
SelectSingleUnit(unit);
return 1;
}
}
return 0;
}
//@}