warzone2100/src/intorder.cpp

1118 lines
31 KiB
C++

/*
This file is part of Warzone 2100.
Copyright (C) 1999-2004 Eidos Interactive
Copyright (C) 2005-2013 Warzone 2100 Project
Warzone 2100 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.
Warzone 2100 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 Warzone 2100; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "lib/framework/frame.h"
#include "lib/ivis_opengl/bitimage.h"//bitmap routines
#include "hci.h"
#include "intdisplay.h"
#include "intorder.h"
#include "objects.h"
#include "order.h"
#include "scriptextern.h"
#include <set>
#define ORDER_X 23
#define ORDER_Y 45
#define ORDER_WIDTH RET_FORMWIDTH
#define ORDER_HEIGHT 273
#define ORDER_BUTX 8
#define ORDER_BUTY 16
#define ORDER_BUTGAP 4
#define ORDER_BOTTOMY 318 + E_H
#define MAX_DISPLAYABLE_ORDERS 11 // Max number of displayable orders.
#define MAX_ORDER_BUTS 5 // Max number of buttons for a given order.
#define NUM_ORDERS 12 // Number of orders in OrderButtons list.
#define IDORDER_REPAIR_LEVEL 8020
#define IDORDER_ATTACK_LEVEL 8030
#define IDORDER_PATROL 8040
#define IDORDER_RETURN 8060
#define IDORDER_RECYCLE 8070
#define IDORDER_ASSIGN_PRODUCTION 8080
#define IDORDER_ASSIGN_CYBORG_PRODUCTION 8090
#define IDORDER_FIRE_DESIGNATOR 8100
#define IDORDER_ASSIGN_VTOL_PRODUCTION 8110
#define IDORDER_CIRCLE 8120
enum ORDBUTTONTYPE
{
ORD_BTYPE_RADIO, // Only one state allowed.
ORD_BTYPE_BOOLEAN, // Clicking on a button toggles it's state.
ORD_BTYPE_BOOLEAN_DEPEND, // Clicking on a button toggles it's state, button
// is only enabled if previous button is down.
ORD_BTYPE_BOOLEAN_COMBINE, // Clicking on a button toggles it's state, all
// the buttons states are OR'ed together to get the order state
};
enum ORDBUTTONCLASS
{
ORDBUTCLASS_NORMAL, // A normal button, one order type per line.
ORDBUTCLASS_FACTORY, // A factory assignment button.
ORDBUTCLASS_CYBORGFACTORY, // A cyborg factory assignment button.
ORDBUTCLASS_VTOLFACTORY, // A VTOL factory assignment button.
};
/*
NOTE:
ORD_BTYPE_BOOLEAN_DEPEND only supports two buttons
ie button 1 = enable destruct, button 2 = destruct
*/
enum ORDBUTTONJUSTIFY
{
ORD_JUSTIFY_LEFT, // Pretty self explanatory really.
ORD_JUSTIFY_RIGHT,
ORD_JUSTIFY_CENTER,
ORD_JUSTIFY_COMBINE, // allow the button to be put on the same line
// as other orders with the same justify type
ORD_NUM_JUSTIFY_TYPES,
};
// maximum number of orders on one line
#define ORD_MAX_COMBINE_BUTS 3
#define ORD_JUSTIFY_MASK 0x0f
#define ORD_JUSTIFY_NEWLINE 0x10 // Or with ORDBUTTONJUSTIFY enum type to specify start on new line.
struct ORDERBUTTONS
{
ORDBUTTONCLASS Class; // The class of button.
SECONDARY_ORDER Order; // The droid order.
UDWORD StateMask; // It's state mask.
ORDBUTTONTYPE ButType; // The group type.
unsigned ButJustify; // Button justification. Type ORDBUTTONJUSTIFY, possibly ored with ORD_JUSTIFY_NEWLINE.
UDWORD ButBaseID; // Starting widget ID for buttons
UWORD NumButs; // Number of buttons ( = number of states )
UWORD AcNumButs; // Actual bumber of buttons enabled.
UWORD ButImageID[MAX_ORDER_BUTS]; // Image ID's for each button ( normal ).
UWORD ButGreyID[MAX_ORDER_BUTS]; // Image ID's for each button ( greyed ).
UWORD ButHilightID[MAX_ORDER_BUTS]; // Image ID's for each button ( hilight overlay ).
UWORD ButTips[MAX_ORDER_BUTS]; // Tip string id for each button.
unsigned States[MAX_ORDER_BUTS]; // Order state relating to each button, combination of SECONDARY_STATEs ored together.
};
struct AVORDER
{
bool operator <(AVORDER const &b) const { return OrderIndex < b.OrderIndex; }
bool operator ==(AVORDER const &b) const { return OrderIndex == b.OrderIndex; }
UWORD OrderIndex; // Index into ORDERBUTTONS array of available orders.
};
enum
{
STR_DORD_REPAIR1,
STR_DORD_REPAIR2,
STR_DORD_REPAIR3,
STR_DORD_FIRE1,
STR_DORD_FIRE2,
STR_DORD_FIRE3,
STR_DORD_PATROL,
STR_DORD_RETREPAIR,
STR_DORD_RETBASE,
STR_DORD_EMBARK,
STR_DORD_ARMRECYCLE,
STR_DORD_RECYCLE,
STR_DORD_FACTORY,
STR_DORD_CYBORG_FACTORY,
STR_DORD_FIREDES,
STR_DORD_VTOL_FACTORY,
STR_DORD_CIRCLE,
};
// return translated text
static const char *getDORDDescription(int id)
{
switch ( id )
{
case STR_DORD_REPAIR1 : return _("Retreat at Medium Damage");
case STR_DORD_REPAIR2 : return _("Retreat at Heavy Damage");
case STR_DORD_REPAIR3 : return _("Do or Die!");
case STR_DORD_FIRE1 : return _("Fire-At-Will");
case STR_DORD_FIRE2 : return _("Return Fire");
case STR_DORD_FIRE3 : return _("Hold Fire");
case STR_DORD_PATROL : return _("Patrol");
case STR_DORD_RETREPAIR : return _("Return For Repair");
case STR_DORD_RETBASE : return _("Return To HQ");
case STR_DORD_EMBARK : return _("Go to Transport");
case STR_DORD_ARMRECYCLE : return _("Return for Recycling");
case STR_DORD_RECYCLE : return _("Recycle");
case STR_DORD_FACTORY : return _("Assign Factory Production");
case STR_DORD_CYBORG_FACTORY : return _("Assign Cyborg Factory Production");
case STR_DORD_FIREDES : return _("Assign Fire Support");
case STR_DORD_VTOL_FACTORY : return _("Assign VTOL Factory Production");
case STR_DORD_CIRCLE : return _("Circle");
default : return ""; // make compiler shut up
}
};
// Define the order button groups.
static ORDERBUTTONS OrderButtons[NUM_ORDERS] =
{
{
ORDBUTCLASS_NORMAL,
DSO_REPAIR_LEVEL,
DSS_REPLEV_MASK,
ORD_BTYPE_RADIO,
ORD_JUSTIFY_CENTER | ORD_JUSTIFY_NEWLINE,
IDORDER_REPAIR_LEVEL,
3,0,
{IMAGE_ORD_REPAIR3UP, IMAGE_ORD_REPAIR2UP, IMAGE_ORD_REPAIR1UP},
{IMAGE_ORD_REPAIR3UP, IMAGE_ORD_REPAIR2UP, IMAGE_ORD_REPAIR1UP},
{IMAGE_DES_HILIGHT, IMAGE_DES_HILIGHT, IMAGE_DES_HILIGHT},
{STR_DORD_REPAIR3, STR_DORD_REPAIR2, STR_DORD_REPAIR1},
{DSS_REPLEV_NEVER, DSS_REPLEV_HIGH, DSS_REPLEV_LOW}
},
{
ORDBUTCLASS_NORMAL,
DSO_ATTACK_LEVEL,
DSS_ALEV_MASK,
ORD_BTYPE_RADIO,
ORD_JUSTIFY_CENTER | ORD_JUSTIFY_NEWLINE,
IDORDER_ATTACK_LEVEL,
3,0,
{IMAGE_ORD_FATWILLUP, IMAGE_ORD_RETFIREUP, IMAGE_ORD_HOLDFIREUP},
{IMAGE_ORD_FATWILLUP, IMAGE_ORD_RETFIREUP, IMAGE_ORD_HOLDFIREUP},
{IMAGE_DES_HILIGHT, IMAGE_DES_HILIGHT, IMAGE_DES_HILIGHT},
{STR_DORD_FIRE1, STR_DORD_FIRE2, STR_DORD_FIRE3},
{DSS_ALEV_ALWAYS, DSS_ALEV_ATTACKED, DSS_ALEV_NEVER}
},
{
ORDBUTCLASS_NORMAL,
DSO_FIRE_DESIGNATOR,
DSS_FIREDES_MASK,
ORD_BTYPE_BOOLEAN,
ORD_JUSTIFY_COMBINE,
IDORDER_FIRE_DESIGNATOR,
1,0,
{IMAGE_ORD_FIREDES_UP, 0, 0},
{IMAGE_ORD_FIREDES_UP, 0, 0},
{IMAGE_DES_HILIGHT, 0, 0},
{STR_DORD_FIREDES, 0, 0},
{DSS_FIREDES_SET, 0, 0}
},
{
ORDBUTCLASS_NORMAL,
DSO_PATROL,
DSS_PATROL_MASK,
ORD_BTYPE_BOOLEAN,
ORD_JUSTIFY_COMBINE,
IDORDER_PATROL,
1,0,
{IMAGE_ORD_PATROLUP, 0, 0},
{IMAGE_ORD_PATROLUP, 0, 0},
{IMAGE_DES_HILIGHT, 0, 0},
{STR_DORD_PATROL, 0, 0},
{DSS_PATROL_SET, 0, 0}
},
{
ORDBUTCLASS_NORMAL,
DSO_CIRCLE,
DSS_CIRCLE_MASK,
ORD_BTYPE_BOOLEAN,
ORD_JUSTIFY_COMBINE,
IDORDER_CIRCLE,
1,0,
{IMAGE_ORD_CIRCLEUP, 0, 0},
{IMAGE_ORD_CIRCLEUP, 0, 0},
{IMAGE_DES_HILIGHT, 0, 0},
{STR_DORD_CIRCLE, 0, 0},
{DSS_CIRCLE_SET, 0, 0}
},
{
ORDBUTCLASS_NORMAL,
DSO_RETURN_TO_LOC,
DSS_RTL_MASK,
ORD_BTYPE_RADIO,
ORD_JUSTIFY_CENTER | ORD_JUSTIFY_NEWLINE,
IDORDER_RETURN,
3,0,
{IMAGE_ORD_RTRUP, IMAGE_ORD_GOTOHQUP, IMAGE_ORD_EMBARKUP},
{IMAGE_ORD_RTRUP, IMAGE_ORD_GOTOHQUP, IMAGE_ORD_EMBARKUP},
{IMAGE_DES_HILIGHT, IMAGE_DES_HILIGHT, IMAGE_DES_HILIGHT},
{STR_DORD_RETREPAIR, STR_DORD_RETBASE, STR_DORD_EMBARK},
{DSS_RTL_REPAIR, DSS_RTL_BASE, DSS_RTL_TRANSPORT},
},
{
ORDBUTCLASS_NORMAL,
DSO_RECYCLE,
DSS_RECYCLE_MASK,
ORD_BTYPE_BOOLEAN_DEPEND,
ORD_JUSTIFY_CENTER | ORD_JUSTIFY_NEWLINE,
IDORDER_RECYCLE,
2,0,
{IMAGE_ORD_DESTRUCT1UP, IMAGE_ORD_DESTRUCT2UP, 0},
{IMAGE_ORD_DESTRUCT1UP, IMAGE_ORD_DESTRUCT2GREY, 0},
{IMAGE_DES_HILIGHT, IMAGE_DES_HILIGHT, 0},
{STR_DORD_ARMRECYCLE, STR_DORD_RECYCLE, 0},
{DSS_RECYCLE_SET, DSS_RECYCLE_SET, 0}
},
{
ORDBUTCLASS_FACTORY,
DSO_ASSIGN_PRODUCTION,
DSS_ASSPROD_MASK,
ORD_BTYPE_BOOLEAN_COMBINE,
ORD_JUSTIFY_CENTER | ORD_JUSTIFY_NEWLINE,
IDORDER_ASSIGN_PRODUCTION,
5,0,
{IMAGE_ORD_FAC1UP, IMAGE_ORD_FAC2UP, IMAGE_ORD_FAC3UP, IMAGE_ORD_FAC4UP, IMAGE_ORD_FAC5UP },
{IMAGE_ORD_FAC1UP, IMAGE_ORD_FAC2UP, IMAGE_ORD_FAC3UP, IMAGE_ORD_FAC4UP, IMAGE_ORD_FAC5UP },
{IMAGE_ORD_FACHILITE, IMAGE_ORD_FACHILITE,IMAGE_ORD_FACHILITE,IMAGE_ORD_FACHILITE,IMAGE_ORD_FACHILITE },
{STR_DORD_FACTORY, STR_DORD_FACTORY, STR_DORD_FACTORY, STR_DORD_FACTORY, STR_DORD_FACTORY},
{ 0x01 << DSS_ASSPROD_SHIFT, 0x02 << DSS_ASSPROD_SHIFT, 0x04 << DSS_ASSPROD_SHIFT,
0x08 << DSS_ASSPROD_SHIFT, 0x10 << DSS_ASSPROD_SHIFT }
},
{
ORDBUTCLASS_CYBORGFACTORY,
DSO_ASSIGN_CYBORG_PRODUCTION,
DSS_ASSPROD_MASK,
ORD_BTYPE_BOOLEAN_COMBINE,
ORD_JUSTIFY_CENTER | ORD_JUSTIFY_NEWLINE,
IDORDER_ASSIGN_CYBORG_PRODUCTION,
5,0,
{IMAGE_ORD_FAC1UP, IMAGE_ORD_FAC2UP, IMAGE_ORD_FAC3UP, IMAGE_ORD_FAC4UP, IMAGE_ORD_FAC5UP },
{IMAGE_ORD_FAC1UP, IMAGE_ORD_FAC2UP, IMAGE_ORD_FAC3UP, IMAGE_ORD_FAC4UP, IMAGE_ORD_FAC5UP },
{IMAGE_ORD_FACHILITE, IMAGE_ORD_FACHILITE,IMAGE_ORD_FACHILITE,IMAGE_ORD_FACHILITE,IMAGE_ORD_FACHILITE },
{STR_DORD_CYBORG_FACTORY, STR_DORD_CYBORG_FACTORY, STR_DORD_CYBORG_FACTORY, STR_DORD_CYBORG_FACTORY, STR_DORD_CYBORG_FACTORY},
{ 0x01 << DSS_ASSPROD_CYBORG_SHIFT, 0x02 << DSS_ASSPROD_CYBORG_SHIFT, 0x04 << DSS_ASSPROD_CYBORG_SHIFT,
0x08 << DSS_ASSPROD_CYBORG_SHIFT, 0x10 << DSS_ASSPROD_CYBORG_SHIFT }
},
{
ORDBUTCLASS_VTOLFACTORY,
DSO_ASSIGN_VTOL_PRODUCTION,
DSS_ASSPROD_MASK,
ORD_BTYPE_BOOLEAN_COMBINE,
ORD_JUSTIFY_CENTER | ORD_JUSTIFY_NEWLINE,
IDORDER_ASSIGN_VTOL_PRODUCTION,
5,0,
{IMAGE_ORD_FAC1UP, IMAGE_ORD_FAC2UP, IMAGE_ORD_FAC3UP, IMAGE_ORD_FAC4UP, IMAGE_ORD_FAC5UP },
{IMAGE_ORD_FAC1UP, IMAGE_ORD_FAC2UP, IMAGE_ORD_FAC3UP, IMAGE_ORD_FAC4UP, IMAGE_ORD_FAC5UP },
{IMAGE_ORD_FACHILITE, IMAGE_ORD_FACHILITE,IMAGE_ORD_FACHILITE,IMAGE_ORD_FACHILITE,IMAGE_ORD_FACHILITE },
{STR_DORD_VTOL_FACTORY, STR_DORD_VTOL_FACTORY, STR_DORD_VTOL_FACTORY, STR_DORD_VTOL_FACTORY, STR_DORD_VTOL_FACTORY},
{ 0x01 << DSS_ASSPROD_VTOL_SHIFT, 0x02 << DSS_ASSPROD_VTOL_SHIFT, 0x04 << DSS_ASSPROD_VTOL_SHIFT,
0x08 << DSS_ASSPROD_VTOL_SHIFT, 0x10 << DSS_ASSPROD_VTOL_SHIFT }
},
};
static std::vector<DROID *> SelectedDroids;
static STRUCTURE *psSelectedFactory = NULL;
static std::vector<AVORDER> AvailableOrders;
bool OrderUp = false;
// Build a list of currently selected droids.
// Returns true if droids were selected.
//
static bool BuildSelectedDroidList(void)
{
DROID *psDroid;
for(psDroid = apsDroidLists[selectedPlayer]; psDroid; psDroid = psDroid->psNext) {
if(psDroid->selected) {
SelectedDroids.push_back(psDroid);
}
}
return !SelectedDroids.empty();
}
// Build a list of orders available for the selected group of droids.
//
static std::vector<AVORDER> buildDroidOrderList(void)
{
std::set<AVORDER> orders;
for (unsigned j = 0; j < SelectedDroids.size(); j++)
{
for (unsigned OrdIndex = 0; OrdIndex < NUM_ORDERS; ++OrdIndex)
{
// Is the order available?
if (secondarySupported(SelectedDroids[j], OrderButtons[OrdIndex].Order))
{
AVORDER avOrder;
avOrder.OrderIndex = OrdIndex;
// Sort by Order index, A bubble sort? I know but it's only
// a small list so what the hell.
// Herein lies the remains of a bubble sort.
orders.insert(avOrder);
}
}
}
return std::vector<AVORDER>(orders.begin(), orders.end());
}
// Build a list of orders available for the selected structure.
static std::vector<AVORDER> buildStructureOrderList(STRUCTURE *psStructure)
{
ASSERT_OR_RETURN(std::vector<AVORDER>(), StructIsFactory(psStructure), "BuildStructureOrderList: structure is not a factory");
//this can be hard-coded!
std::vector<AVORDER> orders(2);
orders[0].OrderIndex = 0;//DSO_REPAIR_LEVEL;
orders[1].OrderIndex = 1;//DSO_ATTACK_LEVEL;
return orders;
}
// return the state for an order for all the units selected
// if there are multiple states then don't return a state
static SECONDARY_STATE GetSecondaryStates(SECONDARY_ORDER sec)
{
SECONDARY_STATE state, currState;
bool bFirst;
state = (SECONDARY_STATE)0;
bFirst = true;
if (psSelectedFactory)
{
if (getFactoryState(psSelectedFactory, sec, &currState))
{
state = currState;
}
}
else //droids
{
for (unsigned i = 0; i < SelectedDroids.size(); ++i)
{
currState = secondaryGetState(SelectedDroids[i], sec, ModeQueue);
if (bFirst)
{
state = currState;
bFirst = false;
}
else if (state != currState)
{
state = (SECONDARY_STATE)0;
}
}
}
return state;
}
static UDWORD GetImageWidth(IMAGEFILE *ImageFile,UDWORD ImageID)
{
return iV_GetImageWidth(ImageFile,(UWORD)ImageID);
}
static UDWORD GetImageHeight(IMAGEFILE *ImageFile,UDWORD ImageID)
{
return iV_GetImageHeight(ImageFile,(UWORD)ImageID);
}
// Add the droid order screen.
// Returns true if the form was displayed ok.
//
//changed to a BASE_OBJECT to accomodate the factories - AB 21/04/99
bool intAddOrder(BASE_OBJECT *psObj)
{
bool Animate = true;
SECONDARY_STATE State;
UWORD OrdIndex;
UWORD Height, NumDisplayedOrders;
UWORD NumButs;
UWORD NumJustifyButs, NumCombineButs, NumCombineBefore;
bool bLastCombine, bHidden;
DROID *Droid;
STRUCTURE *psStructure;
if(bInTutorial)
{
// No RMB orders in tutorial!!
return(false);
}
// Is the form already up?
if(widgGetFromID(psWScreen,IDORDER_FORM) != NULL)
{
intRemoveOrderNoAnim();
Animate = false;
}
// Is the stats window up?
if(widgGetFromID(psWScreen,IDSTAT_FORM) != NULL)
{
intRemoveStatsNoAnim();
Animate = false;
}
if (psObj)
{
if (psObj->type == OBJ_DROID)
{
Droid = (DROID *)psObj;
psStructure = NULL;
}
else if (psObj->type == OBJ_STRUCTURE)
{
Droid = NULL;
psStructure = (STRUCTURE *)psObj;
psSelectedFactory = psStructure;
ASSERT_OR_RETURN(false, StructIsFactory(psSelectedFactory), "Trying to select a %s as a factory!",
objInfo((BASE_OBJECT *)psSelectedFactory));
}
else
{
ASSERT(false, "Invalid object type");
Droid = NULL;
psStructure = NULL;
}
}
else
{
Droid = NULL;
psStructure = NULL;
}
setWidgetsStatus(true);
AvailableOrders.clear();
SelectedDroids.clear();
// Selected droid is a command droid?
if ((Droid != NULL) && (Droid->droidType == DROID_COMMAND))
{
// displaying for a command droid - ignore any other droids
SelectedDroids.push_back(Droid);
}
else if (psStructure != NULL)
{
AvailableOrders = buildStructureOrderList(psStructure);
if (AvailableOrders.empty())
{
return false;
}
}
// Otherwise build a list of selected droids.
else if(!BuildSelectedDroidList()) {
// If no droids selected then see if we were given a specific droid.
if(Droid != NULL) {
// and put it in the list.
SelectedDroids.push_back(Droid);
}
}
// Build a list of orders available for the list of selected droids. - if a factory has not been selected
if (psStructure == NULL)
{
AvailableOrders = buildDroidOrderList();
if (AvailableOrders.empty())
{
// If no orders then return;
return false;
}
}
WIDGET *parent = psWScreen->psForm;
/* Create the basic form */
IntFormAnimated *orderForm = new IntFormAnimated(parent, Animate); // Do not animate the opening, if the window was already open.
orderForm->id = IDORDER_FORM;
orderForm->setGeometry(ORDER_X, ORDER_Y, ORDER_WIDTH, ORDER_HEIGHT);
// Add the close button.
W_BUTINIT sButInit;
sButInit.formID = IDORDER_FORM;
sButInit.id = IDORDER_CLOSE;
sButInit.x = ORDER_WIDTH - CLOSE_WIDTH;
sButInit.y = 0;
sButInit.width = CLOSE_WIDTH;
sButInit.height = CLOSE_HEIGHT;
sButInit.pTip = _("Close");
sButInit.pDisplay = intDisplayImageHilight;
sButInit.UserData = PACKDWORD_TRI(0,IMAGE_CLOSEHILIGHT , IMAGE_CLOSE);
if (!widgAddButton(psWScreen, &sButInit))
{
return false;
}
sButInit = W_BUTINIT();
sButInit.formID = IDORDER_FORM;
sButInit.id = IDORDER_CLOSE+1;
sButInit.pDisplay = intDisplayButtonHilight;
sButInit.y = ORDER_BUTY;
Height = 0;
NumDisplayedOrders = 0;
for (unsigned j = 0; j < AvailableOrders.size() && NumDisplayedOrders < MAX_DISPLAYABLE_ORDERS; ++j)
{
OrdIndex = AvailableOrders[j].OrderIndex;
// Get current order state.
State = GetSecondaryStates(OrderButtons[OrdIndex].Order);
// Get number of buttons.
NumButs = OrderButtons[OrdIndex].NumButs;
// Set actual number of buttons.
OrderButtons[OrdIndex].AcNumButs = NumButs;
// Handle special case for factory -> command droid assignment buttons.
switch (OrderButtons[OrdIndex].Class) {
case ORDBUTCLASS_FACTORY:
NumButs = countAssignableFactories((UBYTE)selectedPlayer,FACTORY_FLAG);
break;
case ORDBUTCLASS_CYBORGFACTORY:
NumButs = countAssignableFactories((UBYTE)selectedPlayer,CYBORG_FLAG);
break;
case ORDBUTCLASS_VTOLFACTORY:
NumButs = countAssignableFactories((UBYTE)selectedPlayer,VTOL_FLAG);
break;
default:
break;
}
sButInit.id = OrderButtons[OrdIndex].ButBaseID;
NumJustifyButs = NumButs;
bLastCombine = false;
switch (OrderButtons[OrdIndex].ButJustify & ORD_JUSTIFY_MASK) {
case ORD_JUSTIFY_LEFT:
sButInit.x = ORDER_BUTX;
break;
case ORD_JUSTIFY_RIGHT:
sButInit.x = orderForm->width() - ORDER_BUTX -
(NumJustifyButs * GetImageWidth(IntImages, OrderButtons[OrdIndex].ButImageID[0]) +
(NumJustifyButs - 1) * ORDER_BUTGAP);
break;
case ORD_JUSTIFY_CENTER:
sButInit.x = (orderForm->width() -
(NumJustifyButs * GetImageWidth(IntImages, OrderButtons[OrdIndex].ButImageID[0]) +
(NumJustifyButs - 1) * ORDER_BUTGAP))/2;
break;
case ORD_JUSTIFY_COMBINE:
// see how many are on this line before the button
NumCombineBefore = 0;
for (unsigned i = 0; i < j; ++i)
{
if ((OrderButtons[AvailableOrders[i].OrderIndex].ButJustify & ORD_JUSTIFY_MASK)
== ORD_JUSTIFY_COMBINE)
{
NumCombineBefore += 1;
}
}
NumCombineButs = (UWORD)(NumCombineBefore + 1);
// now see how many in total
for (unsigned i = j + 1; i < AvailableOrders.size(); ++i)
{
if ((OrderButtons[AvailableOrders[i].OrderIndex].ButJustify & ORD_JUSTIFY_MASK)
== ORD_JUSTIFY_COMBINE)
{
NumCombineButs += 1;
}
}
// get position on line
NumCombineButs = (UWORD)(NumCombineButs - (NumCombineBefore - (NumCombineBefore % ORD_MAX_COMBINE_BUTS)));
if (NumCombineButs >= ORD_MAX_COMBINE_BUTS)
{
// the buttons will fill the line
sButInit.x = (SWORD)(ORDER_BUTX +
(GetImageWidth(IntImages,OrderButtons[OrdIndex].ButImageID[0]) + ORDER_BUTGAP ) * NumCombineBefore);
}
else
{
// center the buttons
sButInit.x = orderForm->width()/2 -
(NumCombineButs * GetImageWidth(IntImages, OrderButtons[OrdIndex].ButImageID[0]) +
(NumCombineButs - 1) * ORDER_BUTGAP)/2;
sButInit.x = (SWORD)(sButInit.x +
(GetImageWidth(IntImages,OrderButtons[OrdIndex].ButImageID[0]) + ORDER_BUTGAP ) * NumCombineBefore);
}
// see if need to start a new line of buttons
if ((NumCombineBefore + 1) == (NumCombineButs % ORD_MAX_COMBINE_BUTS))
{
bLastCombine = true;
}
break;
}
for (unsigned i = 0; i < OrderButtons[OrdIndex].AcNumButs; ++i)
{
sButInit.pTip = getDORDDescription(OrderButtons[OrdIndex].ButTips[i]);
sButInit.width = (UWORD)GetImageWidth(IntImages,OrderButtons[OrdIndex].ButImageID[i]);
sButInit.height = (UWORD)GetImageHeight(IntImages,OrderButtons[OrdIndex].ButImageID[i]);
sButInit.UserData = PACKDWORD_TRI(OrderButtons[OrdIndex].ButGreyID[i],
OrderButtons[OrdIndex].ButHilightID[i],
OrderButtons[OrdIndex].ButImageID[i]);
if(!widgAddButton(psWScreen, &sButInit))
{
return false;
}
// Set the default state for the button.
switch(OrderButtons[OrdIndex].ButType) {
case ORD_BTYPE_RADIO:
case ORD_BTYPE_BOOLEAN:
if((State & OrderButtons[OrdIndex].StateMask) == (UDWORD)OrderButtons[OrdIndex].States[i]) {
widgSetButtonState(psWScreen, sButInit.id, WBUT_CLICKLOCK);
} else {
widgSetButtonState(psWScreen, sButInit.id, 0);
}
break;
case ORD_BTYPE_BOOLEAN_DEPEND:
if((State & OrderButtons[OrdIndex].StateMask) == (UDWORD)OrderButtons[OrdIndex].States[i]) {
widgSetButtonState(psWScreen, sButInit.id, WBUT_CLICKLOCK);
} else {
if(i == 0) {
widgSetButtonState(psWScreen, sButInit.id, 0);
} else {
widgSetButtonState(psWScreen, sButInit.id, WBUT_DISABLE);
}
}
break;
case ORD_BTYPE_BOOLEAN_COMBINE:
if( State & (UDWORD)OrderButtons[OrdIndex].States[i] )
{
widgSetButtonState(psWScreen, sButInit.id, WBUT_CLICKLOCK);
}
break;
}
// may not add a button if the factory doesn't exist
bHidden = false;
switch (OrderButtons[OrdIndex].Class)
{
case ORDBUTCLASS_FACTORY:
if (!checkFactoryExists(selectedPlayer, FACTORY_FLAG, i))
{
widgHide(psWScreen, sButInit.id);
bHidden = true;
}
break;
case ORDBUTCLASS_CYBORGFACTORY:
if (!checkFactoryExists(selectedPlayer, CYBORG_FLAG, i))
{
widgHide(psWScreen, sButInit.id);
bHidden = true;
}
break;
case ORDBUTCLASS_VTOLFACTORY:
if (!checkFactoryExists(selectedPlayer, VTOL_FLAG, i))
{
widgHide(psWScreen, sButInit.id);
bHidden = true;
}
break;
default:
break;
}
if (!bHidden)
{
sButInit.x = (SWORD)(sButInit.x + sButInit.width + ORDER_BUTGAP);
}
sButInit.id++;
}
if (((OrderButtons[OrdIndex].ButJustify & ORD_JUSTIFY_MASK) != ORD_JUSTIFY_COMBINE) ||
bLastCombine)
{
sButInit.y = (SWORD)(sButInit.y + sButInit.height + ORDER_BUTGAP);
Height = (UWORD)(Height + sButInit.height + ORDER_BUTGAP);
}
NumDisplayedOrders ++;
}
// Now we know how many orders there are we can resize the form accordingly.
int newHeight = Height + CLOSE_HEIGHT + ORDER_BUTGAP;
orderForm->setGeometry(orderForm->x(), ORDER_BOTTOMY - newHeight, orderForm->width(), newHeight);
OrderUp = true;
return true;
}
// Do any housekeeping for the droid order screen.
// Any droids that die now get set to NULL - John.
// No droids being selected no longer removes the screen,
// this lets the screen work with command droids - John.
void intRunOrder(void)
{
// Check to see if there all dead or unselected.
for (unsigned i = 0; i < SelectedDroids.size(); i++)
{
if (SelectedDroids[i]->died)
{
SelectedDroids[i] = NULL;
}
}
// Remove any NULL pointers from SelectedDroids.
SelectedDroids.erase(std::remove(SelectedDroids.begin(), SelectedDroids.end(), (DROID *)NULL), SelectedDroids.end());
if (psSelectedFactory != NULL && psSelectedFactory->died)
{
psSelectedFactory = NULL;
}
// If all dead then remove the screen.
// If droids no longer selected then remove screen.
if (SelectedDroids.empty())
{
// might have a factory selected
if (psSelectedFactory == NULL)
{
intRemoveOrder();
return;
}
}
}
// Set the secondary order state for all currently selected droids. And Factory (if one selected)
// Returns true if successful.
//
static bool SetSecondaryState(SECONDARY_ORDER sec, unsigned State)
{
// This code is similar to kfsf_SetSelectedDroidsState() in keybind.cpp. Unfortunately, it seems hard to un-duplicate the code.
for (unsigned i = 0; i < SelectedDroids.size(); ++i)
{
if (SelectedDroids[i])
{
//Only set the state if it's not a transporter.
if (SelectedDroids[i]->droidType != DROID_TRANSPORTER && SelectedDroids[i]->droidType != DROID_SUPERTRANSPORTER)
{
if (!secondarySetState(SelectedDroids[i], sec, (SECONDARY_STATE)State))
{
return false;
}
}
}
}
// set the Factory settings
if (psSelectedFactory)
{
if (!setFactoryState(psSelectedFactory, sec, (SECONDARY_STATE)State))
{
return false;
}
}
return true;
}
// Process the droid order screen.
//
void intProcessOrder(UDWORD id)
{
UWORD i;
UWORD OrdIndex;
UDWORD BaseID;
UDWORD StateIndex;
UDWORD CombineState;
if(id == IDORDER_CLOSE) {
intRemoveOrder();
if (intMode == INT_ORDER)
{
intMode = INT_NORMAL;
}
else
{
/* Unlock the stats button */
widgSetButtonState(psWScreen, objStatID, 0);
intMode = INT_OBJECT;
}
return;
}
for(OrdIndex = 0; OrdIndex < NUM_ORDERS; OrdIndex++) {
BaseID = OrderButtons[OrdIndex].ButBaseID;
switch(OrderButtons[OrdIndex].ButType) {
case ORD_BTYPE_RADIO:
if( (id >= BaseID) && (id < BaseID + OrderButtons[OrdIndex].AcNumButs) ) {
StateIndex = id - BaseID;
for(i=0; i<OrderButtons[OrdIndex].AcNumButs; i++) {
widgSetButtonState(psWScreen, BaseID+i, 0);
}
if (SetSecondaryState(OrderButtons[OrdIndex].Order,
OrderButtons[OrdIndex].States[StateIndex] & OrderButtons[OrdIndex].StateMask))
{
widgSetButtonState(psWScreen, id, WBUT_CLICKLOCK);
}
}
break;
case ORD_BTYPE_BOOLEAN:
if( (id >= BaseID) && (id < BaseID + OrderButtons[OrdIndex].AcNumButs) ) {
StateIndex = id - BaseID;
if(widgGetButtonState(psWScreen, id) & WBUT_CLICKLOCK) {
widgSetButtonState(psWScreen, id, 0);
SetSecondaryState(OrderButtons[OrdIndex].Order,0);
} else {
widgSetButtonState(psWScreen, id, WBUT_CLICKLOCK);
SetSecondaryState(OrderButtons[OrdIndex].Order,
OrderButtons[OrdIndex].States[StateIndex] & OrderButtons[OrdIndex].StateMask);
}
}
break;
case ORD_BTYPE_BOOLEAN_DEPEND:
// Toggle the state of this button.
if(id == BaseID) {
if(widgGetButtonState(psWScreen, id) & WBUT_CLICKLOCK) {
widgSetButtonState(psWScreen, id, 0);
// Disable the dependant button.
widgSetButtonState(psWScreen, id+1, WBUT_DISABLE);
} else {
widgSetButtonState(psWScreen, id, WBUT_CLICKLOCK);
// Enable the dependant button.
widgSetButtonState(psWScreen, id+1, 0);
}
} if( (id > BaseID) && (id < BaseID + OrderButtons[OrdIndex].AcNumButs) ) {
// If the previous button is down ( armed )..
if(widgGetButtonState(psWScreen,id-1) & WBUT_CLICKLOCK) {
// Toggle the state of this button.
if(widgGetButtonState(psWScreen, id) & WBUT_CLICKLOCK) {
widgSetButtonState(psWScreen, id, 0);
SetSecondaryState(OrderButtons[OrdIndex].Order,0);
} else {
StateIndex = id - BaseID;
widgSetButtonState(psWScreen, id, WBUT_CLICKLOCK);
SetSecondaryState(OrderButtons[OrdIndex].Order,
OrderButtons[OrdIndex].States[StateIndex] & OrderButtons[OrdIndex].StateMask);
}
}
}
break;
case ORD_BTYPE_BOOLEAN_COMBINE:
if( (id >= BaseID) && (id < BaseID + OrderButtons[OrdIndex].AcNumButs) ) {
// Toggle the state of this button.
if(widgGetButtonState(psWScreen, id) & WBUT_CLICKLOCK) {
widgSetButtonState(psWScreen, id, 0);
} else {
widgSetButtonState(psWScreen, id, WBUT_CLICKLOCK);
}
// read the state of all the buttons to get the final state
CombineState = 0;
for (StateIndex = 0; StateIndex < OrderButtons[OrdIndex].AcNumButs; StateIndex++)
{
if ( widgGetButtonState(psWScreen, BaseID + StateIndex) & WBUT_CLICKLOCK )
{
CombineState |= OrderButtons[OrdIndex].States[StateIndex];
}
}
// set the final state
SetSecondaryState(OrderButtons[OrdIndex].Order,
CombineState & OrderButtons[OrdIndex].StateMask);
}
break;
}
}
}
// check whether the order list has changed
static bool CheckObjectOrderList(void)
{
std::vector<AVORDER> NewAvailableOrders;
if (psSelectedFactory != NULL)
{
NewAvailableOrders = buildStructureOrderList(psSelectedFactory);
}
else
{
NewAvailableOrders = buildDroidOrderList();
}
// now check that all the orders are the same
return NewAvailableOrders == AvailableOrders;
}
static bool intRefreshOrderButtons(void)
{
SECONDARY_STATE State;
UWORD OrdIndex;
UDWORD id;
for (unsigned j = 0; j < AvailableOrders.size() && j < MAX_DISPLAYABLE_ORDERS; ++j)
{
OrdIndex = AvailableOrders[j].OrderIndex;
// Get current order state.
State = GetSecondaryStates(OrderButtons[OrdIndex].Order);
// Set actual number of buttons.
OrderButtons[OrdIndex].AcNumButs = OrderButtons[OrdIndex].NumButs;
id = OrderButtons[OrdIndex].ButBaseID;
for (unsigned i = 0; i < OrderButtons[OrdIndex].AcNumButs; ++i)
{
// Set the state for the button.
switch(OrderButtons[OrdIndex].ButType) {
case ORD_BTYPE_RADIO:
case ORD_BTYPE_BOOLEAN:
if((State & OrderButtons[OrdIndex].StateMask) == (UDWORD)OrderButtons[OrdIndex].States[i]) {
widgSetButtonState(psWScreen, id, WBUT_CLICKLOCK);
} else {
widgSetButtonState(psWScreen, id, 0);
}
break;
case ORD_BTYPE_BOOLEAN_DEPEND:
if((State & OrderButtons[OrdIndex].StateMask) == (UDWORD)OrderButtons[OrdIndex].States[i]) {
widgSetButtonState(psWScreen, id, WBUT_CLICKLOCK);
} else {
if(i == 0) {
widgSetButtonState(psWScreen, id, 0);
} else {
widgSetButtonState(psWScreen, id, WBUT_DISABLE);
}
}
break;
case ORD_BTYPE_BOOLEAN_COMBINE:
if( State & (UDWORD)OrderButtons[OrdIndex].States[i] )
{
widgSetButtonState(psWScreen, id, WBUT_CLICKLOCK);
}
break;
}
id ++;
}
}
return true;
}
// Call to refresh the Order screen, ie when a droids boards it.
//
bool intRefreshOrder(void)
{
// Is the Order screen up?
if ((intMode == INT_ORDER) &&
(widgGetFromID(psWScreen,IDORDER_FORM) != NULL))
{
bool Ret;
SelectedDroids.clear();
// check for factory selected first
if (!psSelectedFactory)
{
if (!BuildSelectedDroidList())
{
// no units selected - quit
intRemoveOrder();
return true;
}
}
// if the orders havn't changed, just reset the state
if (CheckObjectOrderList())
{
Ret = intRefreshOrderButtons();
}
else
{
// Refresh it by re-adding it.
Ret = intAddOrder(NULL);
if (Ret == false)
{
intMode = INT_NORMAL;
}
}
return Ret;
}
return true;
}
// Remove the droids order screen with animation.
//
void intRemoveOrder(void)
{
widgDelete(psWScreen, IDORDER_CLOSE);
// Start the window close animation.
IntFormAnimated *form = (IntFormAnimated *)widgGetFromID(psWScreen, IDORDER_FORM);
if (form != nullptr)
{
form->closeAnimateDelete();
OrderUp = false;
SelectedDroids.clear();
psSelectedFactory = NULL;
}
}
// Remove the droids order screen without animation.
//
void intRemoveOrderNoAnim(void)
{
widgDelete(psWScreen, IDORDER_CLOSE);
widgDelete(psWScreen, IDORDER_FORM);
OrderUp = false;
SelectedDroids.clear();
psSelectedFactory = NULL;
}
//new function added to bring up the RMB order form for Factories as well as droids
void intAddFactoryOrder(STRUCTURE *psStructure)
{
if(!OrderUp)
{
intResetScreen(false);
intAddOrder((BASE_OBJECT *)psStructure);
intMode = INT_ORDER;
}
else
{
intAddOrder((BASE_OBJECT *)psStructure);
}
}